Beginning with Riverpod

Swaroop Sambhayya
4 min readNov 22, 2022

We might have all come across this state management ambiguity “Which state management approach shall I use for my app?” so the only solution is to examine the use case and find the best one which suits you! In this article, we will learn about one of the best state management solutions Riverpod. Let’s learn some basic concepts on Riverpod with a simple demonstrated example.

What is Riverpod?

Riverpod is an enhanced version for Provider built for resolving issues and adding more flexibility and simplification.

Ever wondered how Riverpod’s name was derived? It’s nothing but an anagram of Provider!!.

Why Riverpod?

  1. Riverpod removes the widget tree dependencies and can be used outside Flutter (ex: for building command line tools).
  2. More flexibility, simplification of logic and reduced boilerplate code.
  3. Combining multiple providers is much easier in the case of Riverpod compared to the provider package.
  4. Riverpod has an autoDispose method which disposes the state if the state is not being listened to or used.

Caveats

  1. Instead of extending StatelessWidget, with Riverpod we should extend ConsumerWidget
  2. Instead of extending StatefulWidget, with Riverpod we should extend ConsumerStatefulWidget

How to define a provider in Riverpod?

If you use a Riverpod package, the provider is not a widget it is a simple dart object, but typically in the case of a conventional Provider package provider is a widget. Let’s define a provider using Riverpod.

// User class having user methods and properties
class User extends ChangeNotifier{...}

// creating userProvider for storing User state
final userProvider = ChangeNotifierProvider<User>((ref) => User());

void main() {
runApp(
// This widget enables Riverpod for the entire project
ProviderScope(
child: MyApp(),
),
);
}

In the above snippet, I have already defined the User class extending ChangeNotifier. I have created an instance of ChangeNotifierProvider of return type User and assigned it to userProvider. In order to read or watch the userProviderwe must enclose the parent widget with ProviderScope which defines the scope of the provider usage.

Reading and watching the state changes

When we extend ConsumerWidget or ConsumerStatefulWidget we get an additional parameter apart from context called ref. We can read, watch and listen to changes by executing methods exposed by WidgetRef ref. Let’s explore some of the use cases of ref in the below snippets

// creating a provider state having return type User
final userProvider = Provider<User>(...);

// creating a widget by extending ConsumerWidget exposed by riverpod
class Example extends ConsumerWidget {
// build method exposes one more property ref which is provider by ConsumerWidget
@override
Widget build(BuildContext context, WidgetRef ref) {
// watch the changes to userProvider state by ref.watch
User model = ref.watch(userProvider);

return Inkwell(
// use ref.read when you are performing actions such as tap,slide,etc
onTap:() => ref.read(userProvider).edit();
child: Text("Change user name")
);
}
}

In the above snippet, I am reading and watching the state of userProvider by using watch and read methods of ref.

There are three main methods of ref commonly used,

  1. ref.read(provider) This method reads the current state of the provider but doesn’t listen to any changes to the state.
  2. ref.watch(provider) This method watches any changes to the state of provider and notifies the listeners. Based on the provider scope the widget rebuilds.
  3. ref.listen(provider,callback) This method listens to the provider's state and if there are any changes then a callback gets executed. callback has two parameters previousState and currentState.

Combining Providers

Let’s see how to combine providers in Riverpod with an example snippet

class UserDetail extends ChangeNotifier{...}

const userDetalProvider = ChangeNotifierProvider<UserDetail>((ref) => UserDetail());

class ProfileUpdate extends ChangeNotifier{...}

const profileUpdateProvider = ChangeNotifierProvider<ProfileUpdate>((ref){
const profileUpdateData = ProfileUpdate();
// listen to the previous and current state and update the profile only if there are changes in the current state
ref.listen(userDetailProvider,(previousDetail,currentDetail){
if(previousDetail.id!=currentDetail.id){
profileUpdateData.update(currentDetail.id);
}
});
return profileUpdateData;
});

In the above example, I’m listening to userProvider state changes inside profileUpdateProvider definition by using ref.listen and updating the profile only if previousDetail is different from currentDetail.This is how we can combine the providers with simplicity. If you use a conventional Provider we would have ended up using ProxyProvider which might be prone to errors and much more complicated.

Conclusion

That’s it for today! , I hope this helps you guys while beginning with Riverpod! Thank you for reading stay tuned!

--

--