I am fairly new to Flutter. I am using Flutter 2 ChangeNotifiers to share state between multiple StatelessWidget-derived screens. That lets me have truly declarative UI and navigation, with no StatefulWidgets used in my app explicitly. Everything works fine.
Now I would like to implement state restoration, but all Flutter state restoration examples rely on StatefulWidgets, effectively requiring potentially shared state to be associated with some specific widget just for the restoration purpose, even though my use case is shared state used by multiple stateless widgets. I mean I haven't had a need for StatefulWidgets so far and it feels like a really bad anti-pattern having to switch to StatefulWidgets just for state restoration and then try to figure out how multiple StatefulWidgets using some single piece of state are supposed to restore it. Am I missing something?
Related
I am currently learning to use the BLoC pattern for state management and architecture for Flutter and Dart.
I have come across 2 ways of providing access to a BLoC in a widget.
1 - Use BLoCProvider or BLoCProvider.value when accessing a widget and passing it to another screen.
2 - Wrap your MaterialApp widget in a BLoCProvider to provide access to a bloc globally.
It seems that using the 2nd option would always be easiest - a single place to manage your BLoCs, no issues with build context referencing, ensuring a single BLoC instance, and allowing global access!
Are there any drawbacks to creating and providing all an applications BLoCs in this way? Are there any performance issues, etc?
I answered this briefly in another question yesterday.
In short:
blocs that need to be globally accessible should of course be. This could e.g. be a bloc that is handling authentication or notifications...
other blocs should not be globally accessible, this could e.g. be a bloc that handles fetching information from a backend service etc. I'd love to hear any good reasoning why a screen far down a widget tree that handles something special should have it's bloc globally accessible that any other widget could access...
Having all blocs accessible globally can have a negative effect on performance and it would break common good programming practices.
Edit:
This is from the creator (Felix Angelov) of flutter bloc:
The main disadvantages of providing all blocs globally are:
The blocs are never closed so they are consuming resources even if they aren't being used by the current widget tree
The blocs can be accessed from anywhere even if the state of the bloc is scoped to just a particular feature
The blocs typically end up needing some sort of "reset" event to revert back to the initial state which is not necessary if they are
properly scoped and automatically disposed by BlocProvider
My recommendation is to create a bloc per feature and provide that
bloc only to the specific subtree that needs it. Hope that helps
There are so many Flutter state management approaches https://flutter.dev/docs/development/data-and-backend/state-mgmt/options
To avoid opinionated response, can someone provide a concrete example that supports why state management approach that doesn’t need a BuildContext is "bad" or "red flags"
One argument to use a state management package without BuildContext is this:
The fact that you need a BuildContext to access your objects made it
inaccessible from the Business layer.
Technically, there is no state management that does not need a BuildContext.
A widget does not exist by itself in Flutter. A Widget is accompanied by an Element. The Element maintains the position of the widget in the widget tree. If the element is mounted, then the widget is present in the widget tree. When the element is unmounted, it gets removed from the widget tree permanently. The Element contains functions like update, rebuild, performRebuild, updateChildren and many other functions that are used by the framework to manage the lifetime and under-the-hood behaviour of widgets, for updating state and for many other functionalities. You always need this Element to update the state of the corresponding Widget. Both StatelessWidget and StatefulWidget uses this element to re-render.
Surprise, surprise, the Element is actually an implementation of BuildContext as seen in framework.dart
I know that you meant using BuildContext to retrieve the object representing the state. This is more of a design choice than a technical requirement that package authors have to make. Essentially, you are using context to get the state object instance that were associated to the corresponding widget tree branch, by some parent widget. That's it. And you are doing that instead of the otherwise approach of passing around the class instance that represents state to children widgets. Both are "ok" approaches to state management.
Limiting to using the context alone is honestly in my opinion, a poor design decision. I personally believe that using context everytime to be able to incorporate state management, is just tideous. In many cases I have seen countless of apps that instantiate the Controller or ChangeNotifier only once and inject it to the root-level widget. Essentially using only one instance. One major purpose of using BuildContext is that you can provide multiple instances to multiple array of children and thereby differentiate their behaviour. But the reality is that a vast majority of apps DO NOT NEED this functionality. Another bonus of using Context is that you can retrieve object instances (of Controllers, ChangeNotifiers...) dynamically like Provider.of<MyClass>(context), without passing them around all the time. Essentially this is an example of dependency injection. But this is more of a poor design decision than great technical feat, as the source of your class instance is ambiguous. You only know the type of the instance. But ultimately, everything is personal preference, and if you are working, is often up to people who manage you to decide what architecture and state management solution to use. There are apps written using all of these "correct" approaches to state management.
You can checkout Turbo by the way. It is a very simple & efficient approach to state management I created, that does not use contexts to maintain and update state, at least not yet. It also has one of the easiest event systems so as to subscribe your widgets to only specific events.
I use the provider package to manage my application state. I have a few questions concerning how to manage the state in the application.
Is it a good practice to create a ChangeNotifier class for each screen in my application to handle the back end for this page (which is provided to only this screen) ?
In an example like this:
If i have a page that have multiple widgets and i need to rebuild a small part of the page like adding a new TextField on enabling a switch.
in the previous example
Should i use a provider to only rebuild the small part of the page or this part is mostly handled by only using changeState() ?
in case that the local-state of the page should be handled in the screen.
Where should i place the logic of the page, like http requests on button clicks and loading data for this page ?
If the state that you are updating is only needed within one widget with one or two down widgets down the tree, I would recommend using the built-in SetState. While both Provider and SetState would work for any form of state management, I tend to think of Provider as similar to React Context.
Provider is great for holding state at page levels or if you want to maintain the separation between widget builds and logic in different files.
SetState is great for local widgets with not a lot of nested widgets underneath.
Here is a great article that discusses the different state management solution approaches. At the end of the day, it's up to preference!
I have an application which required the writing of many blocs.
Several pages use the same bloc. For example, messaging pages use the MessagingBloc.
Currently, I use a global multi-provider to dispatch blocs to child widgets. The problem is: when I change pages (so I no longer use the messaging feature), the bloc is, logically, still present in the tree structure.
Do you know a way to have in the tree, only the blocs that are currently in use? (maybe working with one Navigator by feature but it seems too complicated/expensive)
I'm getting ready to write my first nontrivial app with Flutter and Provider. I've read up on how Provider facilitates immutable widgets (StatelessWidgets). My question is, is it always an antipattern to use StatefulWidgets when using Provider? If not, what are examples of when it better to use StatefulWidgets in a Provider app?
EDIT
It's been a couple months using Provider and I'm still favoring it over StatefulWidgets in every case. Every now and again I introduce a StatefulWidget, mostly to try to gain familiarity with them, and almost immediately regret it and refactor to Provider. The other day I ran into widgets not refreshing because they were identical types, so was looking at introducing keys so they would refresh. First couple attempts failed, so I refactored into Provider and everything just worked (without the need for keys).
Antipattern was not the proper term in my OP. I guess my question is, are there examples where StatefulWidgets are cleaner or otherwise easier/better to use?
provider doesn't care whether you write stateless/stateful or anything else (hooks?).
It removes the need to write a StatefulWidget in many situations, but it doesn't claim that you should use StatelessWidget only.
In the end, it's your job to decide if you need a StatefulWidget or not. You may need it when writing animations for example.
Adding to Rémi's answer and new to this implementation:
use Provider for shared models
use widget state to manage the model that is specific to that concern when it's needed
imagine a user object after auth, null before, shared through an app, with a form with state specific to editing fields, like a nickname, or whatever, updating local state and possibly propagating out to the rest of the product (when finished updating on the backend?...who knows) and that state is disposed of when that view isn't needed anymore, but the user reference remains via the provider reference. It doesn't make sense to manage all that state change in the provider model--that's where the result goes.
Making a few assumptions here based on my experience with React+Redux as well as passing objects around a native Web Components (similar lifecycle to both of these) as well as LitElement. The patterns seem similar if not the same so-far.
Provider.of<ProviderClass>(context, listen: false);
The Provider is one of state management mechanism which Flutter has, under the hood, Provider keeps track of the changes which are done inside the widget. It doesn't matter to Provider whether it's a Stateless widget or Stateful widget, It's gonna rebuild widget if anything gets change.
listen: false tells the Provider not to rebuild widget even if data gets modified. That means it can only re-build if the parent widget gets modified by setState() or by the ProviderClass class value gets modified.