简介
scoped_model是一个简单的第三方状态管理框架,它的源码只有一个dart文件,代码量非常少,巧妙的利用了InheritedWidget和AnimatedBuilder的特性,达到了状态管理的目的。
官网地址:https://pub.flutter-io.cn/packages/scoped_model
如何安装
- pubspec.yaml中:
dependencies:
scoped_model: ^1.0.1
- 运行: flutter pub get
- dart文件中引用:import 'package:scoped_model/scoped_model.dart';
代码示例
还是那个点击按钮+1的示例,改用scoped_model实现:
image.png
import 'package:flutter/material.dart';
import 'package:scoped_model/scoped_model.dart';
class ScopedModelTest extends StatefulWidget {
@override
_ScopedModelTestState createState() => _ScopedModelTestState();
}
class _ScopedModelTestState extends State<ScopedModelTest> {
// 创建model
CountModel _model = CountModel();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("ScopedModel"),
centerTitle: true,
),
body: Center(
child: ScopedModel<CountModel>(
model: _model,
child: CountWidget(),// 要将使用model中数据的widget成为ScopedModel的子widget
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: (){
_model.increment();// 出发model的+1方法
},
),
);
}
}
// 自定义Model
class CountModel extends Model{
// 要共享的状态数据
int _counter = 0;
int get counter => _counter;
// +1的方法
void increment(){
++_counter;
notifyListeners();
}
}
// 使用了状态数据的Widget
class CountWidget extends StatefulWidget {
@override
_CountWidgetState createState() => _CountWidgetState();
}
class _CountWidgetState extends State<CountWidget> {
@override
Widget build(BuildContext context) {
return Container(
child: ScopedModelDescendant<CountModel>(
builder: (context,child,model){
return Text(model.counter.toString());
},
),
);
}
}
解析:
- 自定义CountModel 继承 Model,成员_counter是状态数据;同时定义了方法increment(),方法内部是对状态数据的更改,然后调用notifyListeners()方法,告诉所有依赖此状态数据的widget,此状态数据更新了,内部最终是调用了widget的setState方法进行了页面重新build,渲染了新的状态数据。
- 自定义CountWidget,模拟了一个使用了状态数据的Widget。需要注意使用ScopedModelDescendant<CoutModel>,这是一个StatelessWidget,在内部调用ScopedModel.of<T>(context, rebuildOnChange: rebuildOnChange)获取到泛型CountModel在渲染树中的实例,通过builder传递回来,然后通过model.counter获取到model中定义的状态数据。
- 使用ScopedModel
ScopedModel<CountModel>(
model: _model,
child: CountWidget(),// 要将使用model中数据的widget成为ScopedModel的子widget
),
接收两个参数:model是前面创建的CountModel,也就是_counter所在的数据,是需要通过floatingActionButton去进行操作的。child是依赖CountModel的Widget。ScopedModel内部最终是将model和child传递给了一个InheritedWidget,利用InheritedWidget的特性将model从渲染树中下传到了CountWidget。
- floatingActionButton中通过_model.increment()更改model中的状态数据,进而出发notifyListeners().
核心原理
- 数据共享:ScopedModel内部是一个InheritedWidget,CountWidget是ScopedModel的child,实际是InheritedWidget的child,model是InheritedWidget中的共享数据,这样通过InheritedWidget的特性,CountWidget就能获取到model,从而显示model的数据。
- 跨Widget更新:Model的本质是一个Listenable,内部有一个Set<VoidCallback> _listeners,是所有使用到该model位置的的Widget的回调,比如这里的ScopedModel。AnimatedBuilder是ScopedModel的一个中间件 ,其本质是一个AnimatedWidget,AnimatedWidget是一个StatefulWidget,在initState内部会去调用model的addListener方法,方法参数是一个内容是setState的方法。model内更改状态数据去notifyListeners时,会调用到该setState,进而触发重新build。
源码分析
注意这里列出的源码都是删减版的,只保留了核心内容
- Model
abstract class Model extends Listenable {
// 维护了一个回调的set
final Set<VoidCallback> _listeners = Set<VoidCallback>();
/// 在AnimatedWidget的initState内调用该方法
@override
void addListener(VoidCallback listener) {
_listeners.add(listener);
}
/// CountModel中increment方法内部调用该方法,将所有的回调给执行。
@protected
void notifyListeners() {
_listeners.toList().forEach((VoidCallback listener) => listener());
}
}
- ScopedModel
注意深追
AnimatedBuilder --> AnimatedWidget
_InheritedModel --> InheritedWidget
class ScopedModel<T extends Model> extends StatelessWidget {
/// The [Model] to provide to [child] and its descendants.
final T model;
/// The [Widget] the [model] will be available to.
final Widget child;
ScopedModel({@required this.model, @required this.child})
: assert(model != null),
assert(child != null);
@override
Widget build(BuildContext context) {
// 注意深追AnimatedBuilder,--> AnimatedWidget
return AnimatedBuilder(
animation: model,
builder: (context, _) => _InheritedModel<T>(model: model, child: child),
);
}
}
- AnimatedWidget的state中
这里的listenable就是前面的model
initState中去addListener,参数方法内是setState
abstract class AnimatedWidget extends StatefulWidget {
const AnimatedWidget({
Key key,
@required this.listenable,
}) : assert(listenable != null),
super(key: key);
final Listenable listenable;
@override
_AnimatedState createState() => _AnimatedState();
}
class _AnimatedState extends State<AnimatedWidget> {
@override
void initState() {
super.initState();
widget.listenable.addListener(_handleChange);
}
void _handleChange() {
setState(() {
// The listenable's state is our build state, and it changed already.
});
}
}
- _InheritedModel
这是一个InheritedWidget,前面的model和CountWidget最终都是传递到这里来
class _InheritedModel<T extends Model> extends InheritedWidget {
final T model;
final int version;
_InheritedModel({Key key, Widget child, T model})
: this.model = model,
this.version = model._version,
super(key: key, child: child);
@override
bool updateShouldNotify(_InheritedModel<T> oldWidget) =>
(oldWidget.version != version);
}
- ScopedModelDescendant 是为了获取共享的model而做的一层封装,通过ScopedModel.of<T>(context, rebuildOnChange: rebuildOnChange)获取到model,在通过builder传递出去。build的类型是ScopedModelDescendantBuilder
class ScopedModelDescendant<T extends Model> extends StatelessWidget {
@override
Widget build(BuildContext context) {
return builder(
context,
child,
ScopedModel.of<T>(context, rebuildOnChange: rebuildOnChange),
);
}
}
typedef Widget ScopedModelDescendantBuilder<T extends Model>(
BuildContext context,
Widget child,
T model,
);
网友评论