Mobx.dart: How can i have one global persistent store for all widgets/screens?

Created on 13 Mar 2019  Â·  15Comments  Â·  Source: mobxjs/mobx.dart

How can i have one global persistent store for all widgets/screens, which are generated trough navigator. I would like to set some store value "X=5" in first screen and then go to next screen with navigator, and i would like to access to this store X value/observable and change it to "X=6"some new value, and when i go back to first screen the X value would be changed to 6?

discussion

Most helpful comment

No, simply do:

final myStore = Provider.of<MyStore>(context);

It's like a global, but with extra perks.

All 15 comments

you just have to make the store at the root of your widgets tree using an inherited widget and access it from the children.
one easy way to do is through a generic provider like Provider

Provider<MyStore>(
  value: MyStore(),
  child: // Your App 
)

@dukaric1991 I just pushed an example called multi_counter, which employs the concept of Provider and a global store that spans across pages.

Please take a look here.

I was trying to figure out a way to share it across several different screens for one of my stores and found the best way (so far) using the get_it package.

I have a global application class that I use to set my variables I want to use across screens

class Application {
  static GetIt getIt;
}

then I set info on the main.dart file

MyApp() { //Constructor
  final getIt = new GetIt();
  Dependencies.configureSingletons(getIt);
  getIt.registerSingleton<MyModelStore>(MyModelStore());
  Application.getIt = getIt;
}

then I can grab it wherever I need it withfinal MyModelStore myStore = Application.getIt<MyModelStore>();
Edit: Not the way to go. Use the StatefulProvider/Multiprovider from the Provider package to see better results. Chain below explains more details.

I was trying to figure out a way to share it across several different screens

Why not just wrap your MaterialApp into a provider?

StatefulProvider(
  valueBuilder: (context) => MyModelStore(), 
  child: MaterialApp(...)
)

This makes the store available for all screens.

@rrousselGit And how to access a store value then from any other screen? Do i need to instantiatefinal MyModelStore myStore = MyModelStore(); my store in my class/widget and just call it like that: myStore.someValue?

Sometimes you do need different providers at different levels. They will come and go as necessary according to the lifetime of the view.

I have faced the exception when navigating with a route. @rrousselGit might know better.

@dukaric1991 - You'll want to reference this for your example: https://pub.dartlang.org/packages/provider#statefulprovider

Globals are very bad. They prevent widgets from being customizable/reusable.

Inheritedwidgets and therefore Provider exists to replace globals

But with Provider i have to always pass store trough parameters to new widgets, if i understand correctly?

No, simply do:

final myStore = Provider.of<MyStore>(context);

It's like a global, but with extra perks.

It's like a global, but with extra perks.

Can you elaborate on this one? I'm still learning so I'd love to have some extra resources on how I can work smarter.

@rrousselGit In a new widget i need to declare my new store in build method like this:

@override
  Widget build(BuildContext context) {
      final store = Provider.of<MobxStore>(context);

?

Providers, or more broadly InheritedWidgets behaves like globals: They are accessible to all widgets without having to pass them to the constructor of a widget.

But they have other benefits:

  • If the value of a provider change, all widgets which depend on the value updates accordingly
  • Providers are scoped. Everybody can access to a global, but you can restrict who can access a provider by changing its location in the tree.

  • Having a circular dependency with providers is impossible.

  • Since they are scoped, it's possible to locally override provided values (like changing the theme just for a single page).


One potential example of their power is a tutorial screen:

Consider two routes:

  • Home, showing fancy graphics with real data
  • Tutorial, showing something similar to Home, but with mocked data and some overlays

If your Home is implemented using globals – then you'll have a hard time making your tutorial screen. What most peoples end up doing is making the tutorial with an image (duh).

If you're using providers – then you can implement your tutorial by using the actual Home. But by locally overriding the Store, with mock data. That's a lot more effective than a screenshot.

That was awesome - Thank you for elaborating on that! That makes sense and I can definitely see where that could've been causing some issues in my project. I appreciate laying all of that out there!

ObservableList<dynamic> transaction;

return Provider.value(
      value: transaction,
      child: <someChild>)

So I did this on my root widget which is the Home. ObservableList<dynamic> transaction; is declared outside main. Then I have this child widget below:

@override
  Widget build(BuildContext context) {
    final myStore = Provider.of<ObservableList<dynamic>>(context);

    return Observer(

      builder: (_) => ListView.separated(
        itemCount: 5,
        shrinkWrap: true,
        itemBuilder:(BuildContext context, int index){

          return TransactionItem(myStore[index].id.toString(), myStore[index].getCategory,index);
        },
        separatorBuilder: (BuildContext context, int index) => const Divider()

        ,
      ),
    );
  }

When i put the observer in this child widget somewhere down the line, it says No observables detected in the build method of Observer . When i changed the value of the items in my store which is ObservableList<dynamic> transaction . This child widget does not update its value. So what did i do wrong here that the debugger says No observables detected in the build method of Observer ?

Was this page helpful?
0 / 5 - 0 ratings