https://zhuanlan.zhihu.com/p/392551608
在各種前端開發(fā)中,由于狀態(tài)管理對(duì)于App的開發(fā)維護(hù)成本敲才,性能等方面都起著至關(guān)重要的作用承粤,所以選擇合適的狀態(tài)管理框架顯得尤為重要。Flutter作為跨平臺(tái)框架的后起之秀扰法,背靠Google大樹,短時(shí)間內(nèi)開發(fā)者們?cè)陂_源社區(qū)提供了多種狀態(tài)管理框架毅厚。而Provider是官方推薦的狀態(tài)管理方式之一塞颁,可用作跨組件的數(shù)據(jù)共享。本文對(duì)Provider框架的使用方法做了介紹,并在最后對(duì)主流的狀態(tài)管理框架進(jìn)行比較祠锣。
使用
Provider酷窥,通常使用ChangeNotifierProvider配合ChangeNotifier一起使用來實(shí)現(xiàn)狀態(tài)的管理與Widget的更新。
其中 ChangeNotifier 是系統(tǒng)提供的伴网,用來負(fù)責(zé)數(shù)據(jù)的變化通知蓬推。 ChangeNotifier 是一個(gè)類
ChangeNotifierProvider本質(zhì)上其實(shí)就是Widget,它作為父節(jié)點(diǎn)Widget澡腾,可將數(shù)據(jù)共享給其所有子節(jié)點(diǎn)Widget使用或更新沸伏。
所以通常我們只需要三步即可利用Provider來實(shí)現(xiàn)狀態(tài)管理。
1.創(chuàng)建混合或繼承ChangeNotifier的Model动分,用來實(shí)現(xiàn)數(shù)據(jù)更新的通知并監(jiān)聽數(shù)據(jù)的變化毅糟。
2.創(chuàng)建ChangeNotifierProvider 或者用MultiProvider組合,用來聲明Provider澜公,實(shí)現(xiàn)跨組建的數(shù)據(jù)共享姆另。
3.接收共享數(shù)據(jù)。
1坟乾、model部分
import 'package:flutter/material.dart';
class CountModel extends ChangeNotifier {
int _count;
CountModel() : _count = 1;
int get count => _count;
set count(int count) {
_count = count;
notifyListeners();
}
void add() {
this.count = _count+1;
}
void minus() {
this.count = _count-1;
}
}
model部分的寫法:
1迹辐、 類
class 普通類 (成員方法必須要實(shí)現(xiàn))
mixin 多繼承,混入類(不能有構(gòu)造方法)
abstract class 抽象類 (定義抽象方法)
2甚侣、類繼承明吩, 3個(gè)關(guān)鍵字:
extends 繼承
implements 繼承(父類方法必須要實(shí)現(xiàn))
with 多繼承(父類不能有構(gòu)造方法)
3、約束:
on 約束(繼承mixin類的類殷费,必須同時(shí)繼承約束類)
調(diào)用notifyListeners方法通知widget刷新界面
2贺喝、 視圖部分
接受共享數(shù)據(jù)
使用ChangeNotifierProvider
Container(
width: double.maxFinite,
child: ChangeNotifierProvider<CountModel>.value(
value: this.countModel,
builder: (context, child) {
return Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
ChildAPage(),
Text(
context.watch<CountModel>().count.toString()),
],
),
);
},
),
)
NO.1:
ChangeNotifierProvider的 builder方法與child方法區(qū)別
ChangeNotifierProvider.value({
Key key,
@required T value,
TransitionBuilder builder,
Widget child,
})
builder: (context, child)
builder的類型是= Widget Function(BuildContext context, Widget child);
這里可以獲取當(dāng)前的context。
@override
Widget buildWithChild(BuildContext context, Widget child) {
assert(
builder != null || child != null,
'$runtimeType used outside of MultiProvider must specify a child',
);
return _InheritedProviderScope<T>(
owner: this,
child: builder != null
? Builder(
builder: (context) => builder(context, child),
)
: child,
);
}
NO.2:
ChangeNotifierProvider<CountModel>.value value方法和 create方法
ChangeNotifierProvider<CountModel>.value(
value: this.countModel
ChangeNotifierProvider<CountModel>(
create: (context) => CountModel(),
ChangeNotifierProvider 非常聰明宗兼,它 不會(huì) 重復(fù)實(shí)例化 CartModel躏鱼,除非在個(gè)別場(chǎng)景下。如果該實(shí)例已經(jīng)不會(huì)再被調(diào)用殷绍, ChangeNotifierProvider 也會(huì)自動(dòng)調(diào)用 CartModel 的 dispose() 方法染苛。
接受共享數(shù)據(jù)
調(diào)用方法獲取model 變化的值。 這兩種方法作用相同:
1主到、context.watch<CountModel>().count
2茶行、Provider.of<CountModel>(context).count
3、 Consumer<CountModel>(
builder: (context, model, child) {
return Text(
'Consumer: ${model.count.toString()}');
},
)
使用 Consumer:
Consumer widget 唯一必須的參數(shù)就是 builder登钥。當(dāng) ChangeNotifier 發(fā)生變化的時(shí)候會(huì)調(diào)用 builder 這個(gè)函數(shù)畔师。(換言之,當(dāng)你在模型中調(diào)用 notifyListeners() 時(shí)牧牢,所有相關(guān)的 Consumer widget 的 builder 方法都會(huì)被調(diào)用看锉。)
builder 在被調(diào)用的時(shí)候會(huì)用到三個(gè)參數(shù)姿锭。第一個(gè)是 context。在每個(gè) build 方法中都能找到這個(gè)參數(shù)伯铣。
builder 函數(shù)的第二個(gè)參數(shù)是 ChangeNotifier 的實(shí)例呻此。它是我們最開始就能得到的實(shí)例。你可以通過該實(shí)例定義 UI 的內(nèi)容腔寡。
第三個(gè)參數(shù)是 child焚鲜,用于優(yōu)化目的。如果 Consumer 下面有一個(gè)龐大的子樹放前,當(dāng)模型發(fā)生改變的時(shí)候忿磅,該子樹 并不會(huì) 改變,那么你就可以僅僅創(chuàng)建它一次凭语,然后通過 builder 獲得該實(shí)例葱她。
3、修改model
在外層父widget可以調(diào)用:
countModel.minus();
在子widget可以調(diào)用:
Provider.of<CountModel>(context, listen: false).add();
4叽粹、 使用MultiProvider
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:provider_test/test/MultiProvider/CountA.dart';
class MultiProviderPage extends StatefulWidget {
const MultiProviderPage({Key key}) : super(key: key);
@override
_MultiProviderState createState() => _MultiProviderState();
}
class _MultiProviderState extends State<MultiProviderPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('MultiProvider使用'),
),
body: SafeArea(
child: Container(
margin: EdgeInsets.only(top: 20, left: 20, right: 20),
child: MultiProvider(
providers: [
//Could not find the correct Provider<CountA> above this MultiProviderPage Widget
ChangeNotifierProvider<CountA>(
create: (_) => CountA(),
),
ChangeNotifierProvider<CountB>(
create: (_) => CountB(),
),
],
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
WidgetA(),
],
),
),
),
));
}
}
class WidgetA extends StatelessWidget {
const WidgetA({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
Provider.of<CountA>(context).astr,
style: TextStyle(color: Colors.black),
),
Text(
context.watch<CountA>().astr,
style: TextStyle(color: Colors.black),
),
Text(
Provider.of<CountB>(context).astr,
style: TextStyle(color: Colors.black),
),
Text(
context.watch<CountB>().astr,
style: TextStyle(color: Colors.black),
),
],
),
ButtonBar(
alignment: MainAxisAlignment.spaceAround,
children: <Widget>[
InkWell(
onTap: () {
DateTime now = new DateTime.now();
Provider.of<CountA>(context, listen: false).astr = 'a:$now';
},
child: Text(
'修改a',
),
),
InkWell(
onTap: () {
DateTime now = new DateTime.now();
Provider.of<CountB>(context, listen: false).astr = 'b:$now';
},
child: Text(
'修改b',
),
),
],
),
],
);
}
}
原理
Provider實(shí)際上是個(gè)無狀態(tài)的StatelessWidget,通過包裝了InheritedWidget實(shí)現(xiàn)父子組件的數(shù)據(jù)共享却舀,
通過自定義InheritedElement實(shí)現(xiàn)刷新虫几。
Provider通過與ChangeNotifier配合使用,實(shí)現(xiàn)了觀察者模式挽拔,Provider會(huì)將子組件添加到父組件的依賴關(guān)系中辆脸,
在notifyListeners()時(shí),會(huì)執(zhí)行InheritedContext.markNeedsNotifyDependents()螃诅,將組件標(biāo)記為dirty等待重繪啡氢。
Consumer會(huì)只將被它包裹住的子組件注冊(cè)給父的_dependents形成依賴關(guān)系,從而實(shí)現(xiàn)了局部更新术裸。
demo
AddPage.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:provider_test/test/ChildPage.dart';
import 'package:provider_test/test/model/count.dart';
class AddPage extends StatefulWidget {
AddPage({this.countModel});
CountModel countModel;
@override
_AddPageState createState() => _AddPageState()..countModel = countModel;
}
class _AddPageState extends State<AddPage> {
CountModel countModel;
@override
void setState(fn) {
// TODO: implement setState
super.setState(fn);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Container(
color: Colors.white,
child: Column(
// mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'我是父widget',
style: TextStyle(
fontSize: 20,
color: Colors.black,
),
),
Stack(
alignment: Alignment.center,
children: <Widget>[
Container(
width: double.maxFinite,
child: ChangeNotifierProvider<CountModel>.value(
value: this.countModel,
builder: (context, child) {
return Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
ChildAPage(),
Text(
'countModel: ${countModel.count.toString()}'),
Text(
'Provider.of: ${Provider.of<CountModel>(context).count.toString()}'),
Text(
'context.watch: ${context.watch<CountModel>().count.toString()}'),
Consumer<CountModel>(
builder: (context, model, child) {
return Text(
'Consumer: ${model.count.toString()}');
},
),
],
),
);
},
),
),
Positioned(
bottom: 20,
right: 20,
child: Container(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
FlatButton(
color: Colors.yellow,
onPressed: () {
countModel.add();
},
child: Icon(Icons.add)),
FlatButton(
color: Colors.red,
onPressed: () {
countModel.minus();
},
child: Icon(Icons.remove)),
],
),
),
),
],
),
],
),
),
),
);
}
}
ChildPage.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:provider_test/test/model/count.dart';
class ChildAPage extends StatelessWidget {
const ChildAPage({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
color: Colors.greenAccent,
child: Column(
children: <Widget>[
Text(
'我是子widget',
style: TextStyle(
fontSize: 20,
),
),
Text('${Provider.of<CountModel>(context).count}'),
Text('${context.watch<CountModel>().count}'),
FlatButton.icon(
color: Colors.red,
icon: Icon(Icons.add_circle_outline),
label: Text('加'),
onPressed: () {
//Provider.of<CountModel>(context).add();
Provider.of<CountModel>(context, listen: false).add();
},
),
FlatButton.icon(
icon: Icon(Icons.remove_circle_outline),
color: Colors.yellow,
label: Text('減'),
onPressed: () {
//Provider.of<CountModel>(context).minus();
Provider.of<CountModel>(context, listen: false).minus();
},
)
],
),
);
}
}
CountModel.dart
import 'package:flutter/material.dart';
class CountModel extends ChangeNotifier {
int _count;
CountModel() : _count = 1;
int get count => _count;
set count(int count) {
_count = count;
notifyListeners();
}
void add() {
this.count = _count+1;
}
void minus() {
this.count = _count-1;
}
}
幾種狀態(tài)同步框架的對(duì)比和選擇
- [1][Flutter狀態(tài)管理-Provider的使用和源碼解析]https://mp.weixin.qq.com/s/vUhDvHaStrTwbE4tovOqtA