State meaning in layman’s term: “whatever data you need in order to rebuild your UI at any moment in time” — Flutter.dev
Experienced a mobile engineering team at Elitech Systems, I often encounter questions like which architecture or state management techniques we follow within the context of Flutter.
For Flutter state management, there are so many options available. We can majorly classify them into two different types Ephemeral State and Application State which is explained in great detail here.
As an Ephemeral State represents the only widget’s local state, so it’s very straightforward to realize using StatefulWidget and setState() methods. The real confusion starts once we want to implement the appliance State as there are some ways to realize an equivalent.
In this blog, we will try to understand the key differences of the opinionated state management options for the application state in the context of the MVVM design pattern.
Introduction to MVVM
In Flutter, the Widget represents the View of MVVM. The business logic sits in a separate ViewModel-class. The ViewModel is totally platform-independent. It contains no dependencies to Flutter and may , therefore, be easily reused e.g. in a web project.
That is one of MVVM’s biggest powers. We can create a Mobile App and an internet site that both share an equivalent ViewModel. You don’t have to reinvent and write the logic twice.
A Model-View-Viewmodel mainly concerns these layers:
View: Liable for visuals like UI elements on the screen
Model: It are often a Repository that takes care of managing data across the services like Database, Network, etc
ViewModel: It acts as a mediator to View & Model. The key responsibilities of this layer are in-taking actions, processing data and providing back the new state to the View layer.
1) Provider
A wrapper around InheritedWidget to make them easier to use and more reusable.
By using provider instead of manually writing InheritedWidget, you get:
- Simplified allocation/disposal of resources
- Lazy-loading
- A largely reduced boilerplate over making a new class every time
- Devtools friendly
- A common way to consume these InheritedWidgets (See Provider.of/Consumer/Selector)
- Increased scalability for classes with a listening mechanism that grows exponentially in complexity (such as ChangeNotifier, which is O(N²) for dispatching notifications).
FutureProvider( create: (_) async => doSomeHttpRequest(), lazy: false, child: ... )
You can refer to this example for understanding how to implement it.
2) setState
setState is the low-level approach to use for widget-specific, ephemeral state.
Notify the framework when the internal state of this object has changed.
Whenever you change the internal state of a State object, make the change in a function that you pass to setState
setState(() { _myState = newValue; });
setState notifies the framework that the interior state of this object has changed during a way which may impact the interface during this subtree, which causes the framework to schedule a build for this State object.
If you only change the state directly without calling setState, the framework won't schedule a build and therefore the interface for this subtree won't be updated to reflect the new state.
Generally it's recommended that the setState method only be wont to wrap the particular changes to the state, not any computation which may be related to the change. For example, here a worth employed by the build function is incremented, then the change is written to disk, but only the increment is wrapped within the setState:
Future _increaseCounter() async { setState(() { _counter++; }); Directory directory = await getApplicationDocumentsDirectory(); final String dirName = directory.path; await File('counter.txt').writeAsString('$_counter'); }
You can refer to Adding interactivity to your Flutter app for batter understanding how to implement it.
3) ChangeNotifier
ChangeNotifier is a built-in class that gives the notifyListeners() method to tell the changes to the listeners. The ViewModel either extends or mixes this class in order that it can notify the property changes to the View layer. You can ask this instance for understanding the way to implement it.
This is a really simple solution once you have a little number of listeners. Adding or removing a listener is O(N) operation and dispatching notifications is O(N²) operation.
Also, whenever notifyListeners() is named , all its listener widgets get rebuilt albeit the property getting used within the widget isn’t changed. One solution for this is often to use the subclass of it named ValueNotifier. We can wrap all the properties of the ViewModel which are exposed for the View layer with the ValueNotifier. But there are better solutions out there.
4) PropertyChangeNotifier
The Observable package has some powerful tools but they are not backward-compatible with ChangeNotifier, that's why a package named property_change_notifier is published.
This package provides a drop-in replacement class for ChangeNotifier named PropertyChangeNotifier.
When a property changes within the ViewModel, the name of the property is additionally included within the notification. Listener widgets can then prefer to observe just one or many properties.
Indeed this is often an easy solution without giving extra effort to adding reactive bindings between the 2 layers.
You can refer to this article for better understanding about how to implement and use PropertyChangeNotifier.
5) BLoC pattern
We can use Dart class as a ViewModel and keep the properties as reactive streams. The view layer can add an action within the ViewModel using StreamController’s Sink or Subject from the RxDart package. Those actions are often processed and converted to the View representable property streams within the ViewModel. From the View layer, widgets can react to the ViewModel streams with the help of the StreamBuilder widget.
This is the purest reactive form of state management where we can rebuild the necessary widgets by using a specific property stream in the StreamBuilder. The difference between BLoC and ViewModel is explained below.
Following this pattern has additional challenges though. As there will be multiple Sinks for accepting different actions and multiple streams exposed for UI reactivity, it’s very easy to lose tracking of the data flow. Let’s see what our rising community has done to solve this problem
You can see that there are separate data and business logic models. However, using BLoC there's not really a distinction like that. The classes that handle the business logic also handle the info , which may also apply to MVVM.
To be fair, there really isn't much of a difference. The key part to require away is the same for both: Isolating the business logic from the UI. Hence, the implementation of either of the 2 will look very similar, i.e. using Stream's and StreamBuilder's.
6) MobX
MobX may be a state management library that creates it simple to attach the reactive data of your application with the UI (or any observer). This wiring is totally automatic and feels very natural.
MobX supports the unidirectional data flow. From the View layer, Actions are dispatched to ViewModel. ViewModel processes the action and mutates the state of its properties which are denoted as Observables. The changes within the observable properties are automatically detected by the View layer and widgets get rebuilt as long as necessary! The facility of selective rebuilds lies within the Reactions. Reaction automatically tracks the observable properties utilized in it and only rebuilds the widget if one among those properties gets changed within the ViewModel. The in-depth guide to understanding the MobX within the context of Flutter is often found here.
Conclusion
The provider is the perfect blender of Dependency Injection and State Management. It is mostly the syntactic sugar round the InheritedWidget but has got to offer tons quite that. In fact, he has contributed other interesting packages as well which you may want to check out here.
Elitech Systems: Best Flutter Application Developent Company
Call us to develop your cross-platform mobile application in Flutterr, We are carrying out in-depth discussions, and we are studying how to put digital at the service of new consumer uses. Contact Us
Nikunj is a forward thinking developer at Elitech Systems, offering more than four years of experience building, integrating, testing and supporting, iOS and Flutter applications. He’s a big fan of cross-platform programming and an expert in new ways to create silky smooth iOS applications & Cross Platform applications. His framework of choice is Swift. When he is not building and scaling back end systems, you’ll find him to reading or writing on technology and photography.
Comments are closed.