Passing data between screens in Flutter with Riverpod

Dipak Prasad
5 min readJan 2, 2023

--

Photo by Keith Johnston on Unsplash

I started my flutter journey in the year 2019, back then state management was a big topic of debate. Flutter was at a young age and most of the people were searching for a good state management solution. Provider became a popular choice when google backed it and also because of its simplicity. Bloc came with some more advanced features and was made on top of the provider but due to its complexity and large codes, it remained sour for beginners. Finally, the Getx came with huge promises of fast execution and easy implementation with lesser code.

Though there are many other state management solutions available on flutter, I mentioned here Provider, Bloc and Getx because I used these in many of my production apps. All these mentioned state management solutions have pros and cons which is not a topic of discussion in this article. Finally, I have switched to Riverpod and I personally liked it. Today I am going to show how easy it is to use this in Flutter.

First thing first

Let’s add flutter_riverpod in our project by running the below given command

flutter pub add flutter_riverpod

OR

Add it in pubspec.yaml file

dependencies:
flutter_riverpod: ^2.1.1

Then run flutter pub get

Well done !!!

Now wrap the entire application in the “ProviderScope” widget so that widgets can read the provider.

void main() {
runApp(const ProviderScope(child: MyApp()));
}

If you are a beginner in Flutter, I highly recommend to you read my previous post “Flutter animation example for your next application”.

Today we will cover Provider and StateProvider in our login_flow project. You can download the project from GitHub to follow along.

The provider is the most basic of all providers. It is generally used to pass a value to any level of a widget tree without UI updates.

As per the official documentation, you can use the provider for in following cases:

  • caching computations
  • exposing a value to other providers (such as a Repository/HttpClient).
  • offering a way for tests or widgets to override a value.
  • reducing rebuilds of providers/widgets without having to use select.

We can define a provider as:

final modelProvider = Provider<Model>(...);

To read a provider you need to extend the ConsumerWidget widget instead of StatelessWidget with this we will get an extra WidgetRef argument in the build method which let us read other providers.

//Example Code
class SampleWidget extends ConsumerWidget {
const SampleWidget({Key? key}) : super(key: key);

@override
Widget build(BuildContext context, WidgetRef ref) {
//Watch provider here
//Return any widgets
}
}

We can also use Consumer widget instead of extending ConsumerWidget for performance optimization by updating only relevant widget in the widget tree. We will use it later in this article.

We can read a provider inside the build method

//Example code
final userProvider = ref.watch(modelProvider)
Taking email from user on login screen and showing username on home screen.

I will use a basic provider to take the email from the input field and show it as a username on the home screen. Since we want to display the name on the next screen, I am using a basic ‘Provider’. (Don’t use it if you want to display changes in the same screen as it requires to rebuild the screen to show changes and Provider is not made for that.)

final userProvider = Provider<User>((ref) => User());

//You can declare this provider wherever you want as it is a global variable it can be accessed anywhere.

Now, we can use the user object anywhere in the project, we just need access to the WidgetRef.

class Home extends ConsumerWidget {
const Home({Key? key}) : super(key: key);

@override
Widget build(BuildContext context, WidgetRef ref) {
final user = ref.watch(userProvider);
return Scaffold(
appBar: AppBar(title: Text("Home, ${user.email.split("@").first}"),),
body: //Not showing rest of the body for simplicity,
);
}
}

So far, we have simplified the value passing in the widget tree with ease.

Let’s start with StateProvider.

StateProvider allows the modification of simple variables by the user interface. You should not use it with complex objects.

Here, I will show you how to change the indicator of the text slider by simply storing the index of the selected page which is an int value.

Firstly we will create a StateProvider of type int.

final textSliderIndex = StateProvider<int>((ref) => 0);

I have created this text slider using PageView and I want to rebuild only the part of the slider indicator which shows the current index. So I am going to wrap the indicator part in Consumer widget (not ConsumerWidget) so the other parts of the screen do not rebuild.

Consumer widget has a builder parameter which takes BuildContext, WidgetRef and Widget as arguments. We don’t require the last argument so we are skipping it.

Inside the builder, we will watch for any changes on textSliderIndex. So, now any changes on the selected page index will get reflected on the indicator.

Consumer(
builder: (context, ref, _){
final selectedIndex = ref.watch(textSliderIndex);
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
...List.generate(sampleText.length, (index) =>
IndicatorBar(active: index == selectedIndex))
.toList()
],
);
})

So far we have done all our major tasks. The only thing left is changing our StateProvider value. For that, we will read the provider and will make changes when the page changes.

ref.read(textSliderIndex.notifier).state = value you want set

Note: Inside the build method, use “watch”. Inside click handlers and other events, use “read”

Complete code:

Get Complete Project at idipak/login_flow at riverpod (github.com)

Thank you for taking interest in this article. If you enjoyed it, don’t forget to hit the clap (👏) button.

--

--

Dipak Prasad
Dipak Prasad

Responses (1)