移動端中經(jīng)常有這樣的場景比如電商界面中商品詳情頁點(diǎn)擊收藏后同步至列表頁對于這種需求一般實(shí)現(xiàn)起來比較繁瑣,而前端開發(fā)中經(jīng)常聽到redux等進(jìn)行一些狀態(tài)管理這方便了很多功能的實(shí)現(xiàn)而在flutter中也有狀態(tài)管理Scoped_Model就是其中之一
本文簡單的記錄一下Scoped_Model的用法
先介紹一下Scoped_Model
A set of utilities that allow you to easily pass a data Model from a parent Widget down to it's descendants. In addition, it also rebuilds all of the children that use the model when the model is updated. This library was originally extracted from the Fuchsia codebase.
一組實(shí)用程序姆打,允許您輕松地將數(shù)據(jù)模型從父窗口小部件傳遞給它的后代。此外粱甫,它還重建了模型更新時(shí)使用模型的所有子代鞠鲜。這個庫最初是從 Fuchsia 基代碼中提取的疚宇。
其中有一個概念就是InheritedWidget
InheritedWidget是Flutter中非常重要的一個功能型Widget籽腕,它可以高效的將數(shù)據(jù)在Widget樹中向下傳遞乾蓬、共享,這在一些需要在Widget樹中共享數(shù)據(jù)的場景中非常方便壁榕,如Flutter中矛紫,正是通過InheritedWidget來共享應(yīng)用主題(Theme)和Locale(當(dāng)前語言環(huán)境)信息的。
我們先看一下如何使用Scoped_Model
先聲明一個CounterModel官方文檔中說明我們聲明的CounterModel必須繼承與Model
// Start by creating a class that has a counter and a method to increment it.
//
// 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();
}
}
點(diǎn)擊進(jìn)去觀看Model源碼
abstract class Model extends Listenable {
final Set<VoidCallback> _listeners = Set<VoidCallback>();
int _version = 0;
int _microtaskVersion = 0;
/// [listener] 將在Model更改時(shí)調(diào)用护桦。
@override
void addListener(VoidCallback listener) {
_listeners.add(listener);
}
/// [listener] 移除時(shí)調(diào)用含衔。
@override
void removeListener(VoidCallback listener) {
_listeners.remove(listener);
}
/// Returns the number of listeners listening to this model.
int get listenerCount => _listeners.length;
/// 僅當(dāng)Model已更改時(shí)由[model]調(diào)用。
@protected
void notifyListeners() {
// 我們安排一個微任務(wù)來消除可能同時(shí)發(fā)生的多個更改二庵。
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());
});
}
}
}
我們不難發(fā)現(xiàn)Model 繼承了 Listenable,所以我們可以調(diào)用notifyListeners();
然后我們在需要使用這個Model的地方用ScopedModelDescendant來獲取
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,
);
},
),
Container(
child: RaisedButton(
onPressed: () {
Navigator.push(context,MaterialPageRoute(builder:(context) => ScopedTestWidget()));
},
child: Text(title),
),
),
],
),
),
// 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),
);
},
),
);
}
我們可以點(diǎn)擊ScopedModelDescendant看一下它的內(nèi)部結(jié)構(gòu)
ScopedModelDescendant({
@required this.builder,
this.child,
this.rebuildOnChange = true,
});
可以看到ScopedModelDescendant<T extends Model>是一個Stateless Widget,它接收三個參數(shù)其中一個是builder我們再點(diǎn)擊builder一看究竟
// Builds a child for a [ScopedModelDescendant].
typedef Widget ScopedModelDescendantBuilder<T extends Model>(
BuildContext context,
Widget child,
T model,
);
發(fā)現(xiàn)builder中有一個model他可以獲取到我們定義的CountModel
看到這里似乎明白了
我們可以通過ScopedModelDescendant在任何一個widget里面拿到我們想要的model
我們新建一個dart文件
import 'package:flutter/material.dart';
import 'package:scoped_model/scoped_model.dart';
import '../main.dart';
class ScopedTestWidget extends StatefulWidget {
@override
_ScopedTestWidgetState createState() => _ScopedTestWidgetState();
}
class _ScopedTestWidgetState extends State<ScopedTestWidget> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('lisi'),
),
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
);
}
}
我們引入
import 'package:scoped_model/scoped_model.dart';
這樣在我們想獲取count的地方我們可以通過ScopedModelDescendant拿到達(dá)到了狀態(tài)的同步
本篇文章就介紹到這里當(dāng)然Scoped Model還有很多高級的用法因?yàn)椴攀鑼W(xué)淺還在研究中,以后將會進(jìn)行補(bǔ)充