github:
https://github.com/brianegan/scoped_model
示例demo:
https://github.com/brianegan/scoped_model/tree/master/example
步骤:
1、 初始化
在pubspec
添加scoped_model依赖
2、新建自定义Model
// Note: It must extend from Model.
class CounterModel extends Model {
int _counter = 0;
int get counter => _counter;
void increment() {
// First, increment the counter
_counter++;
// Then notify all the listeners.
notifyListeners();
}
}
Model继承Listenable ,实现数据统一管理和提供观察者模式接口
abstract class Model extends Listenable {
final Set<VoidCallback> _listeners = Set<VoidCallback>();
int _version = 0;
int _microtaskVersion = 0;
/// [listener] will be invoked when the model changes.
@override
void addListener(VoidCallback listener) {
_listeners.add(listener);
}
/// [listener] will no longer be invoked when the model changes.
@override
void removeListener(VoidCallback listener) {
_listeners.remove(listener);
}
/// Returns the number of listeners listening to this model.
int get listenerCount => _listeners.length;
/// Should be called only by [Model] when the model has changed.
@protected
void notifyListeners() {
// We schedule a microtask to debounce multiple changes that can occur
// all at once.
if (_microtaskVersion == _version) {
_microtaskVersion++;
scheduleMicrotask(() {
_version++;
_microtaskVersion = _version;
// Convert the Set to a List before executing each listener. This
// prevents errors that can arise if a listener removes itself during
// invocation!
_listeners.toList().forEach((VoidCallback listener) => listener());
});
}
}
}
3、将Model放入顶层Widget
顶层Widget ScopedModel 子Widget 继承于InheritedWidget
class MyApp extends StatelessWidget {
final CounterModel model;
const MyApp({Key key, @required this.model}) : super(key: key);
@override
Widget build(BuildContext context) {
// At the top level of our app, we'll, create a ScopedModel Widget. This
// will provide the CounterModel to all children in the app that request it
// using a ScopedModelDescendant.
return ScopedModel<CounterModel>(
model: model,
child: MaterialApp(
title: 'Scoped Model Demo',
home: CounterHome('Scoped Model Demo'),
),
);
}
}
ScopedModel build()方法使用了 AnimatedBuilder来构建 InheritedWidget ,
AnimatedBuilder会自动监听 animation 数据的更改,model变化时构建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) {
return AnimatedBuilder(
animation: model,
builder: (context, _) => _InheritedModel<T>(model: model, child: child),
);
}
static T of<T extends Model>(
BuildContext context, {
bool rebuildOnChange = false,
}) {
final Type type = _type<_InheritedModel<T>>();
Widget widget = rebuildOnChange
? context.inheritFromWidgetOfExactType(type)
: context.ancestorInheritedElementForWidgetOfExactType(type)?.widget;
if (widget == null) {
throw ScopedModelError();
} else {
return (widget as _InheritedModel<T>).model;
}
}
static Type _type<T>() => T;
InheritedWidget 中updateShouldNotify 决定数据变化时,是否通知依赖数据的Widget
/// Provides [model] to its [child] [Widget] tree via [InheritedWidget]. When
/// [version] changes, all descendants who request (via
/// [BuildContext.inheritFromWidgetOfExactType]) to be rebuilt when the model
/// changes will do so.
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);
}
4、子Widget中使用Model数据
ScopedModel of 函数 rebuildOnChange决定Widget是否依赖于变化
class CounterHome extends StatelessWidget {
final String title;
CounterHome(this.title);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('You have pushed the button this many times:'),
// Create a ScopedModelDescendant. This widget will get the
// CounterModel from the nearest parent ScopedModel<CounterModel>.
// It will hand that CounterModel to our builder method, and
// rebuild any time the CounterModel changes (i.e. after we
// `notifyListeners` in the Model).
ScopedModelDescendant<CounterModel>(
builder: (context, child, model) {
return Text(
model.counter.toString(),
style: Theme.of(context).textTheme.display1,
);
},
),
],
),
),
// Use the ScopedModelDescendant again in order to use the increment
// method from the CounterModel
floatingActionButton: ScopedModelDescendant<CounterModel>(
builder: (context, child, model) {
return FloatingActionButton(
onPressed: model.increment,
tooltip: 'Increment',
child: Icon(Icons.add),
);
},
),
);
}