State management is of Paramount importance in Flutter, and there is always a lot to talk about when discussing this topic.
In formal introductionScopeModel
Why do we need state management? If this is clear to you, it is advisable to skip this section. If our application is simple enough,Flutter
As a declarative framework, you might just need to map data to views. You probably don’t need state management, as follows.But as you add functionality, your application will have dozens or even hundreds of states. This is what your application should look like.What the hell is this? It’s hard to test and maintain our state clearly because it seems so complicated! There are also multiple pages that share the same status. For example, when you enter a “like” post and exit to the external thumbnail display, the external thumbnail display also needs to show the number of likes, so you need to synchronize the two states.Flutter
It actually gives us a way to manage our state in the first place, which isStatefulWidget
. But we soon found out that it was the culprit in the first place. inState
Belonging to a particularWidget
In the multipleWidget
While you can usecallback
Solution, but when the nesting is deep enough, we add an awful lot of junk code. At this point, we urgently needed a framework to help us clarify these relationships, and a state management framework came into being.
What is ScopeModel
ScopeModel allows you to easily pass a data model from a parent Widget to its descendants. In addition, when a model is updated, it rebuilds all the children that use the model. The library was originally extracted from the Fuchsia code base
First add it in YAML
Scoped_model: ^ 0.3.0Copy the code
Then run
flutter pub get
Copy the code
Obtain the latest package to the local, in the required folder to import
import 'package:scoped_model/scoped_model.dart';
Copy the code
We also use the example of clicking a button to add a number
Create a new Basemodel inheritance model
class _BaseModel extends Model {
int _count = 0;
void add(int a) {
_count += a;
notifyListeners();
}
int get count => _count;
}
Copy the code
The widget is then wrapped in the ScopedModel, typically used to include a page or widget that needs to be updated, and the interaction and display content need to be under the same ScopedModel.
class BaseScopedPateRoute extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ScopedModel<_BaseModel>(
model: _BaseModel(),
child: _body(),
);
}
static String get routeName => 'BaseScopedPate';
Widget _body() => BaseScopedPate();
}
Copy the code
And then we have our UI
class BaseScopedPate extends StatefulWidget {
BaseScopedPate({Key key}) : super(key: key);
@override
_BaseScopedPateState createState() => _BaseScopedPateState();
}
class _BaseScopedPateState extends State<BaseScopedPate> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('ScopedModel'),
),
body: ScopedModelDescendant<_BaseModel>(
builder: (context, child, model) {
return Text('${model.count}');
},
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
ScopedModel.of<_BaseModel>(context).add(1); },),); }}Copy the code
The net effect is
The principle of
BaseModel inherits Model from ScopedModel, which inherits Model from Listenable, and uses Microtask to remove listeners when needed.
/// Inheritance can listen to Listenable
abstract class Model extends Listenable{
/// Callback all listeners in the next event loop
@protected
void notifyListeners() {
if(_microtaskVersion == _version) { _microtaskVersion++; scheduleMicrotask(() { _version++; _microtaskVersion = _version; _listeners.toList().forEach((VoidCallback listener) => listener()); }); }}}}Copy the code
Listener add time
ScopedModel
(){} Calls the ModelBuilder widget when executing builder, which implements widget.model.addlisten (_onChange) in initState(){}, Void _onChange() => setState(() {}); .
Listeners have been added at this point, and notifyListeners() are then called in the inherited Model whenever a refresh is needed. Refresh in this way is also business independent, similar to MVVM.
Local refresh
When the page is a little more complex, a simple click event causes the entire page to refresh, which is not what we want to see, so how do we implement a local refresh?
We define multiple ModelAs and modelB to each inherit model, and then listen at the top of the widget, as follows, and then in the separate Builder.
class _BaseModel extends Model {
int _count = 0;
void add(int a) {
_count += a;
notifyListeners();
}
int get count => _count;
}
class _BaseModel2 extends Model {
int _count = 0;
void add(int a) {
_count += a;
notifyListeners();
}
int get count => _count;
}
Copy the code
Monitor separately:
class BaseScopedPateRoute extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ScopedModel<_BaseModel>(
model: _BaseModel(),
child: ScopedModel<_BaseModel2>(
model: _BaseModel2(),
child: _body(),
),
);
}
static String get routeName => 'BaseScopedPate';
Widget _body() => BaseScopedPate();
}
Copy the code
Show it where you need it. This is just the key code. For the full code, see the link at the bottom of the article.
ScopedModelDescendant<_BaseModel>(
builder: (context, child, model) {
_build += '_BaseModel build';
return Center(
child: Text('${model.count}')); }, child: Text('External Widgets'),
),
ScopedModelDescendant<_BaseModel2>(
builder: (context, child, model) {
_build += '_BaseModel2 build';
return Center(
child: Text('${model.count}')); }, child: Text('External Widgets'),),Copy the code
We use P1 build to indicate that the whole page is refreshed once, B1 to indicate that control B is refreshed once, b2 to indicate that B2 is refreshed once, and several times is refreshed.
Take a look at the final result:
Why does p1 always come after B1 and b2?
I’ll leave you with a question to think about, and the answer will be published next time.
Full code view: Github view
Series of articles:
The article summary
Dart asynchrony and multithreading
Insight into state management –ScopeModel
Learn more about Flutter management –Redux
Detailed explanation of Flutter (iii. In-depth understanding of state management –Provider)
4. Deeper understanding of state management –BLoC
A detailed explanation of Flutter
1, Learn about the Stream
7. Understand the principle of drawing deeply
Detailed explanation of Flutter
Project recommend
- A cool loading animation library
- Flutter100 + component usage examples
- Flutter entry to advanced ebook