What's new

Welcome to xuiaf | Welcome My Forum

Join us now to get access to all our features. Once registered and logged in, you will be able to create topics, post replies to existing threads, give reputation to your fellow members, get your own private messenger, and so, so much more. It's also quick and totally free, so what are you waiting for?

Riverpod 2.0 – Full Information (Flutter Tutorial)

Hoca

Administrator
Staff member
Joined
Mar 22, 2024
Messages
167
Reaction score
0
Points
16
logo.png


If you happen to’ve been at the very least a bit energetic relating to Flutter packages within the final 12 months or so, you’ve certainly heard about Riverpod, a reactive caching and data-binding, or as some would say, state administration bundle that’s type of an improve of the beloved Supplier. I truly coated it with a tutorial fairly a while in the past when its API was nonetheless unstable.


Riverpod has come a great distance since then – it’s far more mature, useful, and versatile. All these adjustments naturally imply that it’s time for a brand new tutorial to arrange you to totally make the most of the ability of Riverpod 2.0 and, most certainly, additionally its upcoming variations.

The fundamental rules of Riverpod stay the identical irrespective of which model you employ; it is simply the best way you do sure issues that is totally different. Subsequently, if you would like to see the areas the place the Supplier bundle comes brief and why Riverpod is so good, try my older tutorial, the place I’m going into extra element relating to Supplier. This text will focus purely on Riverpod, and you do not want any earlier data to observe alongside.
In case you are a complete newbie with little or no expertise with state administration, I like to recommend you learn this official Flutter article earlier than persevering with to study Riverpod.

The aim of Riverpod has loads in widespread with courses and packages like InheritedWidget, Supplier, get_it, and partly GetX. That’s, to will let you entry objects throughout totally different components of your app with out passing all types of callbacks and objects as constructor parameters to the Widgets.

So what units it aside from all of those different choices supplied to you from all sides? It is the truth that it combines ease of use, clear coding practices, full independence from Flutter (nice for testing!), compile-time security (versus coping with run-time errors), and efficiency optimization in a single bundle. To realize all this, Riverpod has a singular method to the way you declare the objects you need to present round your app.

The model of the bundle we’re utilizing is 2.0.0-dev.5, so ensure to make use of at the very least that model in your pubspec. The whole lot we write on this tutorial can be legitimate as soon as the steady model is launched too.


Code:
dependencies:
  flutter:
    sdk: flutter
  flutter_riverpod: ^2.0.0-dev.5

Suppliers​



Let’s first cope with the best doable instance and say that you really want a String to be accessible all through your app.


Code:
// Supplier declaration is top-level (world)
last myStringProvider = Supplier((ref) => 'Whats up world!');

If we break down the road of code above, you’ve simply declared a supplier beneath the title myStringProvider, which can present the “Whats up world!” String wherever you want. That is essentially the most primary sort of a Supplier that merely exposes a read-only worth. We’ll check out the opposite extra superior sorts of suppliers shortly. The ref parameter is of sort ProviderReference and it’s used, amongst different issues, to work together with different suppliers – we’ll discover the ref parameter afterward as effectively.


To any extent further, we’ll name the supplied object “state”


This supplier declaration is extremely much like declaring a category. A category declaration is accessible globally, however when you instantiate an object, it is now not world, which tremendously helps with the app’s maintainability and makes testing doable since hard-coded use of globals does not permit mocking.


Code:
class MyClass {
  int myField;

  MyClass(this.myField);
}

// The article must be handed into the operate.
// We will not entry it globally.
void myFunction(MyClass object) {

  object.myField = 5;

}

In a lot the identical approach, a supplier declaration is world, however the precise state it supplies is just not world. It’s as an alternative saved and managed in a widget known as ProviderScope, at which we’ll take a better look quickly, and this retains our app maintainable and testable. Primarily, lets say that we get all the advantages of world variables with none of their drawbacks. Sure, typically issues that sound too good to be true are certainly true.

Riverpod & Widget Tree​

001-smartphone.png


Let’s now have a look at a extra real-world instance – a counter app! Sure, exactly that counter app that you just’re so fed up with however do not despair as a result of you’ll study one thing new this time. I promise!

This is the tip consequence:


Naturally, we’re going to make use of Riverpod for the state administration as an alternative of the basic StatefulWidget. By now, you’ve seen essentially the most primary Supplier class that gives read-only information. Since we need to increment the counter when the consumer presses a button, we clearly want to put in writing to the info too. The best technique to obtain that is with a StateProvider.


Code:
last counterProvider = StateProvider((ref) => 0);
 
void most important() => runApp(MyApp());
 
...

Riverpod is admittedly only a approach to offer objects across the app, and whereas it comes each with some built-in easy & superior methods to handle state, you aren’t restricted to them in any respect. You need to use ChangeNotifier, Bloc, Cubit or anything you need along with Riverpod.


Oh, and what in regards to the single ProviderScope widget I’ve talked about earlier? That simply merely must wrap the entire app widget within the most important methodology.


Code:
void most important() {
 runApp(
   ProviderScope(
     little one: MyApp(),
   ),
 );
}
 
class MyApp extends StatelessWidget {
 const MyApp({Key? key}) : tremendous(key: key);
 
 @override
 Widget construct(BuildContext context) {
   return MaterialApp(
     title: 'Counter App',
     house: const HomePage(),
   );
 }
}

We’re additionally including a bit twist to this app by not doing the counting proper within the “house” route. As a substitute, we’ll make the consumer first navigate to the CounterPage widget from the HomePage, so the HomePage will comprise just one button.


Code:
class HomePage extends StatelessWidget {
 const HomePage({Key? key}) : tremendous(key: key);
 
 @override
 Widget construct(BuildContext context) {
   return Scaffold(
     appBar: AppBar(
       title: const Textual content('Dwelling'),
     ),
     physique: Heart(
       little one: ElevatedButton(
         little one: const Textual content('Go to Counter Web page'),
         onPressed: () {
           Navigator.of(context).push(
             MaterialPageRoute(
               builder: ((context) => const CounterPage()),
             ),
           );
         },
       ),
     ),
   );
 }
}

As you possibly can see, we haven’t used the counterProvider anyplace up to now. We made it doable for use by declaring the supplier itself and organising the ProviderScope but when we ran the app proper now, no precise counterProvider would ever be created, not to mention utilized and incremented. Let’s change that within the CounterPage.


Code:
// ConsumerWidget is sort of a StatelessWidget
// however with a WidgetRef parameter added within the construct methodology.
class CounterPage extends ConsumerWidget {
 const CounterPage({Key? key}) : tremendous(key: key);
 
 @override
 Widget construct(BuildContext context, WidgetRef ref) {
   // Utilizing the WidgetRef to get the counter int from the counterProvider.
   // The watch methodology makes the widget rebuild every time the int adjustments worth.
   //   - one thing like setState() however automated
   last int counter = ref.watch(counterProvider);
 
   return Scaffold(
     appBar: AppBar(
       title: const Textual content('Counter'),
     ),
     physique: Heart(
       little one: Textual content(
         counter.toString(),
         model: Theme.of(context).textTheme.displayMedium,
       ),
     ),
     floatingActionButton: FloatingActionButton(
       little one: const Icon(Icons.add),
       onPressed: () {
         // Utilizing the WidgetRef to learn() the counterProvider only one time.
         //   - in contrast to watch(), it will by no means rebuild the widget robotically
         // We do not need to get the int however the precise StateNotifier, therefore we entry it.
         // StateNotifier exposes the int which we will then mutate (in our case increment).
         ref.learn(counterProvider.notifier).state++;
       },
     ),
   );
 }
}

Not Preserving the State​


The app now efficiently increments the counter and even preserves the state, on this case, the one counter integer for the lifetime of the app session. However what if we would like the consumer to at all times begin counting from zero as soon as the CounterPage is opened (even reopened after being closed beforehand)?

That’s easy! The one factor we have to add is the autoDispose modifier to the counterProvider on the very high of the file.


last counterProvider = StateProvider.autoDispose((ref) => 0);

How does this work? Why is the counterProvider’s state now disposed after the consumer closed and disposed the CounterPage?

Riverpod is aware of which widgets use the person suppliers. In spite of everything, we’re constantly subscribed to the counterProvider within the CounterPage by calling the watch methodology. In our case, this additionally occurs to be the one subscription to the counterProvider in your complete app, so as soon as that subscription now not exists as a result of the CounterPage widget has been closed and disposed, Riverpod is aware of that the counterProvider’s state will also be disposed.

And by what sort of magic is that completed? Properly, the CounterPage is a subclass of ConsumerWidget that comes from the Riverpod bundle too, so all the required code accountable for disposing of supplier state is hidden in there.

Resetting the State Manually​


Disposing of the state and thus releasing sources when the supplier is now not in use is one factor, however you might typically need to manually reset the state, for instance, with a button. That is very simple with ref.invalidate.


Code:
class CounterPage extends ConsumerWidget {
  @override
  Widget construct(BuildContext context, WidgetRef ref) {
    return Scaffold(
      appBar: AppBar(
        title: const Textual content('Counter'),
        actions: [
          IconButton(
            onPressed: () {
              ref.invalidate(counterProvider);
            },
            icon: const Icon(Icons.refresh),
          ),
        ],
      ),
...

When you ought to favor what we have completed above, you might typically need to name ref.refresh as an alternative, which can return the newly reset state – in our case, that may be the integer 0.


Performing Actions Based mostly on the State​


002-action.png


By now we’ve seen that watch is used throughout the construct methodology for getting the supplier state and rebuilding a widget with it, whereas learn is for doing simply one-off actions with the supplier outdoors of the construct methodology – often in button onPressed or comparable callbacks.


However how can we, for instance, navigate, present snackbars, alerts, or do any type of different motion every time the state of the supplier adjustments to the specified worth? We will’t use the state we get from watch and simply do these actions instantly within the construct methodology as a result of we’ll get the notorious “setState() or markNeedsBuild() known as throughout construct” error thrown in our face. As a substitute, we have to use the hear methodology.

To illustrate that we take into account the quantity 5 to be dangerously massive and need to present a dialog warning the consumer about it like this:


The next ref.hear name is what wants to enter the CounterPage.


Code:
class CounterPage extends ConsumerWidget {
  
  @override
  Widget construct(BuildContext context, WidgetRef ref) {
    last int counter = ref.watch(counterProvider);

    ref.hear<int>(
      counterProvider,
      // "subsequent" is referring to the brand new state.
      // The "earlier" state is usually helpful for logic within the callback.
      (earlier, subsequent) {
        if (subsequent >= 5) {
          showDialog(
            context: context,
            builder: (context) {
              return AlertDialog(
                title: Textual content('Warning'),
                content material:
                    Textual content('Counter dangerously excessive. Take into account resetting it.'),
                actions: [
                  TextButton(
                    onPressed: () {
                      Navigator.of(context).pop();
                    },
                    child: Text('OK'),
                  )
                ],
              );
            },
          );
        }
      },
    );

    ...
}

Inter-Supplier Dependencies​


Apps aren’t at all times so simple as having a single supplier for the counter which suggests you’re often going to have a number of suppliers directly. As if that wasn’t sufficient, the objects that the suppliers present are sometimes going to rely on each other. For instance, you could have a Cubit or a ChangeNotifier that is determined by a Repository from which it will get the info.

Suppliers make coping with dependencies between courses completely easy. The truth is, you’ve got already seen the syntax that is used for it on this very article, and in the event you’re new to Riverpod, you might not have even seen.

To illustrate that we need to improve our acquainted counter app to be loads fancier. Everyone is aware of that holding all your state native is lame, and the cool youngsters put all the pieces on serverless servers, and we certainly do not need to fall behind! We’ll create the last word counter app that will get its counter integer worth via a WebSocket. Type of…


To maintain the app match for a tutorial, we’ll simply pretend the WebSocket and easily return a domestically generated Stream of integers which might be incremented each half a second. We’ll additionally make the most of an summary class to function an interface in order that the code could be simply swapped for an actual implementation or examined.


Code:
summary class WebsocketClient {
  Stream<int> getCounterStream();
}

class FakeWebsocketClient implements WebsocketClient {
  @override
  Stream<int> getCounterStream() async* {
    int i = 0;
    whereas (true) {
      await Future.delayed(const Length(milliseconds: 500));
      yield i++;
    }
  }
}

Offering the FakeWebsocketClient object may be very easy:


Code:
last websocketClientProvider = Supplier<WebsocketClient>(
  (ref) {
    return FakeWebsocketClient();
  },
);

This isn’t the tip supplier we would like the UI to have entry to although. We have to name the getCounterStream methodology on the WebsocketClient to get the counter Stream we’re in any case alongside.

Naturally, we’re going to create a brand new counterProvider of sort StreamProvider. To be able to name the getCounterStream methodology although, we first have to have the WebsocketClient object that’s supplied by the supplier we created within the code snippet above.


StreamProvider is simply one other sort of supplier that has its declaration syntax an identical to all the opposite suppliers we have already seen. Clearly, the item you present with it should be of sort Stream. This enables for some good syntax when consuming the info from the widget tree – no extra clunky StreamBuilders!


To get entry to the WebsocketClient, we will merely learn the websocketClientProvider utilizing the ref parameter that’s included in each single supplier’s creation callback.


Code:
last counterProvider = StreamProvider<int>((ref) {
  last wsClient = ref.watch(websocketClientProvider);
  return wsClient.getCounterStream();
});

The ref.watch name appears acquainted, doesn’t it? After all it does – it’s precisely the identical factor you do throughout the widget tree with the minor distinction being that right here the ref parameter is just not of sort WidgetRef however slightly StreamProviderRef<int>.


The ref parameter within the callback permits you to do all the pieces {that a} WidgetRef permits you to do (watch, learn, hear, invalidate…) and extra, corresponding to including totally different callbacks.


Our new and fancy CounterPage with the counter state administration outsourced to a pretend serverless server will now look as follows. Discover the benefit with which we devour the Stream throughout the UI. We truly don’t even see it because it will get reworked to an AsyncValue by Riverpod.


Code:
class CounterPage extends ConsumerWidget {
  @override
  Widget construct(BuildContext context, WidgetRef ref) {
    // AsyncValue is a union of three instances - information, error and loading
    last AsyncValue<int> counter = ref.watch(counterProvider);

    return Scaffold(
      appBar: AppBar(
        title: const Textual content('Counter'),
      ),
      physique: Heart(
        little one: Textual content(
          counter
              .when(
                information: (int worth) => worth,
                error: (Object e, _) => e,
                // Whereas we're ready for the primary counter worth to reach
                // we would like the textual content to show zero.
                loading: () => 0,
              )
              .toString(),
          model: Theme.of(context).textTheme.displayMedium,
        ),
      ),
    );
  }
}

Passing an Argument to a Supplier​


003-puzzle.png


The present implementation of the counter consumer at all times begins from zero however our clients began giving us one-star critiques saying that they want to have the ability to modify the beginning worth of the counter. So we naturally implement their characteristic request as follows:


Code:
summary class WebsocketClient {
  Stream<int> getCounterStream([int start]);
}

class FakeWebsocketClient implements WebsocketClient {
  @override
  Stream<int> getCounterStream([int start = 0]) async* {
    int i = begin;
    whereas (true) {
      await Future.delayed(const Length(milliseconds: 500));
      yield i++;
    }
  }
}

That might be it for the WebsocketClient and its “pretend” implementation however we additionally have to in some way cross the beginning worth to the counterProvider since that’s what the widgets truly use to get to the counter Stream.

Till now, we’ve by no means handed something to a supplier. We may learn, watch or hear to it nevertheless it at all times contained all the pieces wanted and didn’t get something handed from the surface. Passing arguments to suppliers is fortunately quite simple due to the household modifier.


Code:
// The "household" modifier's first sort argument is the kind of the supplier
// and the second sort argument is the kind that is handed in.
last counterProvider = StreamProvider.household<int, int>((ref, begin) {
  last wsClient = ref.watch(websocketClientProvider);

  return wsClient.getCounterStream(begin);
});

The household modifier could be mixed with the autoDispose modifier like StreamProvider.autoDispose.household<int, int>


The household modifier makes our counterProvider to be a callable class, so to cross in a begin worth from the CounterPage, we will merely do that:


Code:
class CounterPage extends ConsumerWidget {
  @override
  Widget construct(BuildContext context, WidgetRef ref) {
    // Simply hardcoding the beginning worth 5 for simplicity
    last AsyncValue<int> counter = ref.watch(counterProvider(5));
    ...
  }
}

Conclusion​


And similar to that, you’ve got discovered learn how to use the highly effective Riverpod bundle by constructing and increasing upon the easy counter app. You are now able to make use of all this data in your individual advanced and funky apps that Riverpod makes hassle-free to construct and simple to take care of – at the very least relating to getting objects across the app ?
 
Top Bottom