Last active
February 15, 2022 17:07
-
-
Save wingkit-leung/f203435e62adbcc3ce95da5277203533 to your computer and use it in GitHub Desktop.
Flutter Example of using StateNotifierProvider, FutureBuilder vs FutureProvider and StreamBuilder vs StreamProvider
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
import 'package:flutter_riverpod/flutter_riverpod.dart'; | |
void main() => | |
runApp(const ProviderScope(child: MaterialApp(home: HomePage()))); | |
Future<int> calculateSquare(int num) async { | |
await Future.delayed(const Duration(seconds: 2)); | |
return num * num; | |
} | |
Stream<int> stopwatch() async* { | |
int counter = 0; | |
while (true) { | |
await Future.delayed(const Duration(seconds: 1)); | |
yield counter++; | |
} | |
} | |
abstract class ProgressState { | |
const ProgressState(); | |
} | |
class InitialState extends ProgressState { | |
const InitialState(); | |
} | |
class LoadingState extends ProgressState { | |
const LoadingState(); | |
} | |
class SuccessState extends ProgressState { | |
const SuccessState(this.value); | |
final int value; | |
} | |
class CounterStateNotifier extends StateNotifier<ProgressState> { | |
CounterStateNotifier() : super(const InitialState()); | |
void start() { | |
if (state is InitialState) { | |
state = const LoadingState(); | |
final stream = stopwatch(); | |
stream.listen((value) { | |
state = SuccessState(value); | |
}); | |
} | |
} | |
} | |
class SquareStateNotifier extends StateNotifier<ProgressState> { | |
SquareStateNotifier() : super(const InitialState()); | |
Future<void> start() async { | |
if (state is InitialState) { | |
state = const LoadingState(); | |
final result = await calculateSquare(10); | |
state = SuccessState(result); | |
} | |
} | |
} | |
final futureCalculateProvider = FutureProvider<int>((ref) { | |
return calculateSquare(10); | |
}); | |
final squareStateNotifier = | |
StateNotifierProvider<SquareStateNotifier, ProgressState>((ref) { | |
final p = SquareStateNotifier(); | |
p.start(); | |
return p; | |
}); | |
final streamCounterProvider = StreamProvider<int>((ref) { | |
return stopwatch(); | |
}); | |
final counterStateNotifier = | |
StateNotifierProvider<CounterStateNotifier, ProgressState>((ref) { | |
final p = CounterStateNotifier(); | |
p.start(); | |
return p; | |
}); | |
Widget buildFutureBuilder() { | |
return Center( | |
child: FutureBuilder<int>( | |
future: calculateSquare(10), | |
builder: (context, snapshot) { | |
if (snapshot.connectionState == ConnectionState.done) { | |
return Text( | |
"Square = ${snapshot.data}", | |
style: | |
DefaultTextStyle.of(context).style.apply(fontSizeFactor: 1.5), | |
); | |
} | |
return const CircularProgressIndicator(); | |
}, | |
), | |
); | |
} | |
Widget buildFutureConsumer() { | |
return Consumer(builder: (context, ref, _) { | |
final result = ref.watch(futureCalculateProvider); | |
return result.when( | |
loading: () => const CircularProgressIndicator(), | |
error: (err, stack) => Text('Error: $err'), | |
data: (snapshot) { | |
return Text( | |
"Square = $snapshot", | |
style: DefaultTextStyle.of(context).style.apply(fontSizeFactor: 1.5), | |
); | |
}, | |
); | |
}); | |
} | |
buildStateNotifierConsumerForFuture() { | |
return Consumer(builder: (context, ref, _) { | |
final state = ref.watch(squareStateNotifier); | |
//loading | |
if (state is LoadingState || state is InitialState) { | |
return const CircularProgressIndicator(); | |
} else if (state is SuccessState) { | |
return Text( | |
"Square = ${state.value}", | |
style: DefaultTextStyle.of(context).style.apply(fontSizeFactor: 1.5), | |
); | |
} | |
return const Text('Unknown state'); | |
}); | |
} | |
Widget buildStreamBuilder() { | |
return Center( | |
child: StreamBuilder<int>( | |
stream: stopwatch(), | |
builder: (context, snapshot) { | |
if (snapshot.connectionState == ConnectionState.active) { | |
return Text( | |
"Stopwatch = ${snapshot.data}", | |
style: | |
DefaultTextStyle.of(context).style.apply(fontSizeFactor: 1.5), | |
); | |
} | |
return const CircularProgressIndicator(); | |
}, | |
), | |
); | |
} | |
buildStreamConsumer() { | |
return Consumer(builder: (context, ref, _) { | |
final result = ref.watch(streamCounterProvider); | |
return result.when( | |
loading: () => const CircularProgressIndicator(), | |
error: (err, stack) => Text('Error: $err'), | |
data: (snapshot) { | |
return Text( | |
"Stopwatch = $snapshot", | |
style: DefaultTextStyle.of(context).style.apply(fontSizeFactor: 1.5), | |
); | |
}, | |
); | |
}); | |
} | |
buildStateNotifierConsumerForStream() { | |
return Consumer(builder: (context, ref, _) { | |
final state = ref.watch(counterStateNotifier); | |
//loading | |
if (state is LoadingState || state is InitialState) { | |
return const CircularProgressIndicator(); | |
} else if (state is SuccessState) { | |
return Text( | |
"Stopwatch = ${state.value}", | |
style: DefaultTextStyle.of(context).style.apply(fontSizeFactor: 1.5), | |
); | |
} | |
return const Text('Unknown state'); | |
}); | |
} | |
class HomePage extends StatelessWidget { | |
const HomePage({Key? key}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar( | |
title: const Text('Providers Example'), | |
), | |
body: Column( | |
mainAxisAlignment: MainAxisAlignment.center, | |
children: <Widget>[ | |
const Text('FutureBuilder'), | |
buildFutureBuilder(), | |
const SizedBox(height: 10), | |
const Text('FutureProvider'), | |
buildFutureConsumer(), | |
const SizedBox(height: 10), | |
const Text('StateNotifierProvider (Future)'), | |
buildStateNotifierConsumerForFuture(), | |
const Divider(), | |
const Text('StreamBuilder'), | |
buildStreamBuilder(), | |
const SizedBox(height: 10), | |
const Text('StreamProvider'), | |
buildStreamConsumer(), | |
const SizedBox(height: 10), | |
const Text('StateNotifierProvider (Stream)'), | |
buildStateNotifierConsumerForStream(), | |
], | |
), | |
floatingActionButton: FloatingActionButton( | |
onPressed: () { | |
Navigator.push(context, | |
MaterialPageRoute(builder: (context) => const AnotherPage())); | |
}, | |
child: const Icon(Icons.play_arrow_rounded), | |
), | |
); | |
} | |
} | |
class AnotherPage extends StatelessWidget { | |
const AnotherPage({Key? key}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar( | |
title: const Text('Another Page'), | |
), | |
body: Center( | |
child: Padding( | |
padding: const EdgeInsets.all(20.0), | |
child: Column( | |
children: [ | |
const Text('Stream shared across pages?'), | |
const SizedBox(height: 80), | |
const Text('StreamBuilder ❌'), | |
buildStreamBuilder(), | |
const SizedBox(height: 30), | |
const Divider(), | |
const Text('StreamProvider ✅'), | |
buildStreamConsumer(), | |
const SizedBox(height: 30), | |
const Divider(), | |
const Text('StateNotifierProvider ✅'), | |
buildStateNotifierConsumerForStream(), | |
], | |
), | |
), | |
), | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment