This post is based off a presentation I did at Devfest 2023 in Cape Town on 23 Nov 2023. VIDEO | PRESENTATION
What is Riverpod
As described by the creator Pemi Rousselet, Riverpod is a Reactive Caching and Data-binding framework, sow while it does make for a fantastic state management solution, that is not all that it is, along with its very flexible approach to state management, it can also help with networking, dependency injection and is extremely simple when it comes to wiring test and can even help improve the overall testability of your codebase.
In many ways Riverpod is the evolution of Provider, probably one of, if not the most popular state management solution for Flutter, this means for those currently using Provider, it is extremely easy to bring Riverpod in, and also very easy to roll it into your codebase gradually.
Let's take a look at some simple state setups as examples to showcase the differences, in each of these examples we are looking at the internets favourite, counter examples.
Provider
class SimpleCounter with ChangeNotifier { int _counter = 0; void incrementCounter() { _counter++; notifyListeners(); } void decrementCounter() { _counter--; notifyListeners(); }}
BLoC
class SimpleCounter extends Bloc<CounterEvents, CounterStates> { int counter = 0; CounterBloc() : super(InitialState()) { on<IncrementEvent>(onIncrement); on<DecremetnEvent>(onNumberDecrease); } void onIncrement(IncrementEvent event, Emitter<CounterStates> emit) async { counter = counter + 1; emit(UpdateState(counter)); } void onNumberDecrease(DecremetnEvent event, Emitter<CounterStates> emit) async { counter = counter - 1; emit(UpdateState(counter)); }}
As you can see above we have Provider and BLoC, many of you should be familiar with one or both of these setups. Now when we take a look at Riverpod using the StateNotifier pattern, you can also make use of the ChangeNotifer pattern which is identical to the above one from the original Provider
StateNotifier
class SimpleCountrer extends StateNotifier<CounterState> { CounterNotifier() : super(CounterState()); void increase() => state = state.copyWith(count: state.value + 1); void decrease() => state = state.copyWith(count: state.value - 1);}
Either of these patters would probably be the most common go-to's for general state management within your Flutter codebase.
Now let's take look at the simplest way one could active the same restyle using Riverpod:
final counterProvider = Provider<int>((ref) => 0);class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return ProviderScope( child: MaterialApp( home: Scaffold( body: Center( child: Consumer( builder: (context, watch, child) { final count = watch(counterProvider); return Text('Count: $count'); }, ), ), floatingActionButton: FloatingActionButton( onPressed: () { context.read(counterProvider).state++; }, child: Icon(Icons.add), ), ), ), ); }}
Now this is something one would probably not do in real-life, however if yo look at the first line, that is basically your state, using Riverpod, the counter example can be done in a literal 1 line.
Then taking a look further down at the onPressed
int eh FloatingActionButton
you can see how we can simply access that state via context and increment it directly.
So if the scenario arises, one could literally store a single value in a Riverpod state, with basically zero boilerplate.
While storing a single value in state would definitely be extremely rare, it leads me into another powerful feature of Riverpod, and that is dependency injection, as Riverbeds providers are globally available and locally instantiated, the data stored in this one-liner can be accessed and updated from anywhere within your application.
Next part we will cover the extension methods that Riverpod provides.
I hope you found this interesting, and if you have any questions, comments, or improvements, feel free to drop a comment. Enjoy your development journey :D
Thanks for reading.