Manage Application Status in Flutter
Hi, Habr! I submit a translation of the article, let me help you understand and choose a state management solution for your app that interested me in the process of learning the basics of state management in Flutter. I will be glad to hear any criticism regarding this translation. In the return quotes (``) my personal thoughts and explanations will be written.
Flutter state management is a hot topic. There are many possible solutions to the problem and getting lost in them, choosing the most suitable one for your needs is extremely simple. I myself was confused, but I found the right solution. Let me share it with you.
To find a solution that fits your needs, you need to determine your own needs. In my case it is:
Given these requirements, suitable options remain:
Before diving into the analysis of selected solutions, it is necessary to understand the difference between a local and a global state. A practical example is suitable for this:
Imagine an authorization form, where the user is prompted to enter a username and password and receive the “user identity” object after submitting the form. In this example, any verification of the data entered in the form fields will be part of the local state of the `authorization form widget`, and the rest of the application should not be aware of this. And the “personality” object returned by ʻauthorization server` is a part of the global state. So how other components depend on this object that change the behavior depending on whether the user is authorized.
Using
ScopedModel is a third-party library by Brian Egan . It allows you to create special objects
In our widgets, we will be able to respond to changes in the model using the widget provided by this library
In contrast to the use of the approach
Considering all this, if the state of your application is not easy to manage - I do not recommend using the data approach. I just do not believe that he is able to productively ensure the growth and complexity of applications.
This pattern was coined by Google and used in the same place. He will help us achieve the following goals:
The idea of this approach is very simple:
Google has good examples of using this condition management pattern, because it is widely used and highly recommended by the company.
I myself highly recommend using this approach to manage the local state, but it is even suitable for managing the global state. However, in the latter case, you will encounter a problem - where and how to implement BLoC correctly, so that different components can access it, and Redux comes on the scene.
One of the goals that I described at the beginning of the article was to search for something widely used and predictable, and this Redux is a pattern and a set of tools that together help us manage the global state. It has three basic principles at its core:
The only source of truth is that the entire state of `
Link to the original post where the image was taken from.
This approach to managing the state is widely accepted by web developers, and its appearance on mobile devices will help to get the benefits of web and mobile-application developers.
Brian Egan is developing both the original Redux and flutter_redux , and he also created a terrific Todo application in which he applied many architectural patterns, including Redux.
Given all the qualities of Redux, I strongly advise using it to manage the global state, but you must be sure that you do not use it to manage the local state if you want to scale your application.
This article is not completely correct or incorrect decision. To decide on what approach to use in your project, you need to decide on your needs. For me and my goals, the combination of Redux and BLoC allows my projects to grow quickly and safely, and also facilitates the entry of third-party developers into these projects, thanks to accessible and understandable tools. However, not everyone has the same needs and over time, you can find both problems in the current tools and even better solutions. It is very important to always remain curious, learn and think about whether this or that tool suits you.
Flutter state management is a hot topic. There are many possible solutions to the problem and getting lost in them, choosing the most suitable one for your needs is extremely simple. I myself was confused, but I found the right solution. Let me share it with you.
To find a solution that fits your needs, you need to determine your own needs. In my case it is:
- Have the opportunity to develop the project without compromising the quality of the code
- Separate display logic from business logic
- Have a clear code that is hard to break.
- Predictable and clear code
Given these requirements, suitable options remain:
- Use method
setState()
and stateful widgets - `Library` ScopedModel
- Applying the BLoC pattern (Components of business logic)
- Redux
Difference between local and global state
Before diving into the analysis of selected solutions, it is necessary to understand the difference between a local and a global state. A practical example is suitable for this:
Imagine an authorization form, where the user is prompted to enter a username and password and receive the “user identity” object after submitting the form. In this example, any verification of the data entered in the form fields will be part of the local state of the `authorization form widget`, and the rest of the application should not be aware of this. And the “personality” object returned by ʻauthorization server` is a part of the global state. So how other components depend on this object that change the behavior depending on whether the user is authorized.
Brief conclusions for those who are tired of waiting
If you do not want to wait, or are not interested in my research, then here is a brief overview of the results:
My recommendation is to use BLoC to manage the local state and Redux for the global state, especially if you are creating a complex application that will grow with time.
Why you should not use setState ()
Using
setState()
inside your widgets is great for quickly creating prototypes and getting feedback on these changes, but this path does not help us achieve our goals, because the display logic is mixed with business logic, which violates the principle of cleanliness and quality of the code. Maintenance of this code will be difficult in the future, so apart from for prototyping, this approach is not recommended.ScopedModel - step in the right direction
ScopedModel is a third-party library by Brian Egan . It allows you to create special objects
Models
, as well as use the method notifyListeners()
when necessary. For example, to track on any change in the property of a model object:classCounterModelextendsModel{
int _counter = 0;
int get counter = _counter;
voidincrement(){
_counter++;
notifyListeners();
}
}
In our widgets, we will be able to respond to changes in the model using the widget provided by this library
ScopedModelDescendant
:classCounterAppextendsStatelessWidget{
@overrideWidget build(BuildContext context){
returnnew ScopedModel<CounterModel>(
model: new CounterModel(),
child: new Column(children: [
new ScopedModelDescendant<CounterModel>(
builder: (context, child, model) => new Text('${model.counter}'),
),
new Text("Другой виджет, который не зависит от CounterModel")
])
);
}
}
In contrast to the use of the approach
setState()
, this solution allows you to separate the display logic from the business logic. However, it imposes certain restrictions:- If
Model
it becomes complicated, then it becomes difficult to determine when to use the methodnotifyListeners()
, and when not, to avoid unnecessary interface updates. - The API provided
Model
, in general, does not accurately describe the asynchronous nature of the application interface.
Considering all this, if the state of your application is not easy to manage - I do not recommend using the data approach. I just do not believe that he is able to productively ensure the growth and complexity of applications.
Powerful Solution - BLoC
This pattern was coined by Google and used in the same place. He will help us achieve the following goals:
- Separating display logic from business logic
- Using asynchronous nature to display the interface
- Ability to reuse in different Dart applications, such as Flutter or AngularDart
The idea of this approach is very simple:
- BLoC uses
Api to describe asynchronously entering our data components.Sink<T>
- BLoC uses
Api to describe the data asynchronously returned by our data components.Stream<T>
- Finally, we can use the widget
StreamBuilder
to control the flow of data, without exerting effort on our part to subscribe to data updates and redraw widgets.
Google has good examples of using this condition management pattern, because it is widely used and highly recommended by the company.
I myself highly recommend using this approach to manage the local state, but it is even suitable for managing the global state. However, in the latter case, you will encounter a problem - where and how to implement BLoC correctly, so that different components can access it, and Redux comes on the scene.
Redux and BLoC - the perfect mix for me
One of the goals that I described at the beginning of the article was to search for something widely used and predictable, and this Redux is a pattern and a set of tools that together help us manage the global state. It has three basic principles at its core:
The only source of truth is that the entire state of `
state
` your application is stored in a tree object in a single repository ` store
`- The state is read-only — the only way to change the state is to call a special action object that describes what should happen to the state.
- Changes are made using pure functions - to determine what changes in the state you write a pure `
reducer
` function , which should not cause any side effects `Reference to example code`
Link to the original post where the image was taken from.
This approach to managing the state is widely accepted by web developers, and its appearance on mobile devices will help to get the benefits of web and mobile-application developers.
Brian Egan is developing both the original Redux and flutter_redux , and he also created a terrific Todo application in which he applied many architectural patterns, including Redux.
Given all the qualities of Redux, I strongly advise using it to manage the global state, but you must be sure that you do not use it to manage the local state if you want to scale your application.
Last words
This article is not completely correct or incorrect decision. To decide on what approach to use in your project, you need to decide on your needs. For me and my goals, the combination of Redux and BLoC allows my projects to grow quickly and safely, and also facilitates the entry of third-party developers into these projects, thanks to accessible and understandable tools. However, not everyone has the same needs and over time, you can find both problems in the current tools and even better solutions. It is very important to always remain curious, learn and think about whether this or that tool suits you.