Flutter作為響應(yīng)式開發(fā)的框架,狀態(tài)管理是Flutter中非常重要的一個部分,接下來就來看看Flutter中都有哪些狀態(tài)管理的方式。
State/InheritedWidget
State和InheritedWidget在Flutter狀態(tài)管理中扮演著重要的角色,不少的系統(tǒng)控件都采用了這種方式進行狀態(tài)管理。
State
因為Widget是不可變的,但是State支持跨幀保存數(shù)據(jù),所以Widget可以實現(xiàn)跨幀的狀態(tài)恢復(fù)/刷新。當我們調(diào)用setState((){});方法的時候,State內(nèi)部會通過調(diào)用markNeedsLayout方式,將對應(yīng)的Widget設(shè)置為_diry(臟標記),從而在下一幀執(zhí)行WidgetBinding.darwFrame時調(diào)用performLayout進行更新。
InheritedWidget
InheritedWidget在Flutter常用于數(shù)據(jù)的共享(從上至下),被InheritedWidget包裹起來的child可以通過BuildContext來獲取相對應(yīng)的數(shù)據(jù)。
所以State和InheritedWidget組成了Flutter中最基礎(chǔ)的狀態(tài)管理模式,通過State保存數(shù)據(jù)和管理狀態(tài),通過InheritedWidget來進行數(shù)據(jù)的共享,從而實現(xiàn)了跨頁面的數(shù)據(jù)傳遞。
示例
class InheritedText extends InheritedWidget {
final String text;
InheritedText({this.text, Widget child})
: super(child: child);
@override
bool updateShouldNotify(covariant InheritedText oldWidget) {
return text != oldWidget.text;
}
static InheritedText of(BuildContext context) {
// 此方法已被標記位過期方法,建議使用下面的方法
// return context.inheritFromWidgetOfExactType(InheritedText);
return context.dependOnInheritedWidgetOfExactType<InheritedText>() ?? null;
}
}
class DemoPage extends StatefulWidget {
@override
_DemoPageState createState() => _DemoPageState();
}
class _DemoPageState extends State<DemoPage> {
String _text = "init";
@override
Widget build(BuildContext context) {
return InheritedText(
text: _text,
child: Scaffold(
appBar: AppBar(),
body: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Builder(builder: (context) {
return Text(
InheritedText.of(context)?.text ?? "null",
style: TextStyle(color: Colors.blue),
);
}),
NextPage(),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
_text = "Hello";
});
},
),
),
);
}
}
class NextPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(child: Text(InheritedText.of(context)?.text ?? ""));
}
}
Stream
Stream達標事件流或者管道,通過Stream可以快速的實現(xiàn)給予事件驅(qū)動的業(yè)務(wù)邏輯,界面通過訂閱事件,并針對事件進行變換處理(非必須),最后可以實現(xiàn)界面跟著事件流/管道進行更新。如下圖:

簡單Demo
如何通過Stream更新StatelessWidget?
class StreamDemoPage extends StatelessWidget {
final StreamController<int> _controller;
StreamDemoPage({Key key}):
_controller = StreamController<int>(),
super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Stream"),),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text("StatelessWidget 利用 Stream 完成UI刷新"),
StreamBuilder(
initialData: 0,
stream: _controller.stream,
builder: (context, snapshot) {
return Center(child: Text("${snapshot.data}"),);
},
)
],
),
floatingActionButton: FloatingActionButton(
onPressed: () {
// 導(dǎo)入math package
// import 'dart:math';
_controller.add(Random.secure().nextInt(1000));
},
child: Icon(Icons.refresh),
),
);
}
}
效果如下:

Stream的工作流程
Flutter中的Stream、StreamController、StreamSink、StreamSubscription都是對外開放的接口抽象,內(nèi)部實現(xiàn)都是私有類。那么他們具體是什么關(guān)系呢?又是怎么實現(xiàn)事件流的呢?查看下圖

在Flutter中的事件流用Stream表示,為了能方便控制Stream,官方提供了StreamController作為管理接口;同時StreamController對外提供了StreamSink對象作為事件的輸入口,可以通過sink屬性訪問;又提供Stream對象用于監(jiān)聽和變化事件流;最后通過Stream訂閱得到Subscription,可以管理事件訂閱。
總結(jié)一下就是:
-
StreamController用于控制Stream的過程,提供各種借口用于創(chuàng)建各種事件流 -
StreamSink事件的輸入口,主要提供了add、addStream等方法 -
Stream事件源本身,一般用于監(jiān)聽事件流或者對事件流進行變換,如listen、where、map等 -
StreamSubscription事件訂閱后的得到的對象,可以用于取消訂閱、暫停等操作
作為事件的入口,當通過StreamSink.add添加一個事件是,事件最后會回調(diào)到Stream.listen中傳入的onData方法,從add到onData的這個過程,在Stream的內(nèi)部就是通過_zone.runUnaryGuarded進行銜接的,而完成這個銜接的恰好就是StreamSubscription。

1、Stream在listen時傳入了onData方法用于回調(diào),這個回調(diào)方法最終會被傳入StreamSubscription對象里,之后通過zone.registerUnaryCallback注冊得到_onData標識,這個_onData標識屬于當前Zone內(nèi)的全局標識,只要獲取_onData就可以通過Zone直接回調(diào)數(shù)據(jù)到listen中的onData。
2、StreamSink在添加事件的時候,會執(zhí)行StreamSubscription中的_sendData方法(會根據(jù)同步還是異步分別調(diào)用add/addPending),然后通過_zone.runUnaryGuarded(_onData, data)執(zhí)行上一步得到的_onData對象,觸發(fā)listen傳入的onData方法,返回數(shù)據(jù)給訂閱者。
上面的流程是同步Stream,那么他的異步流程是怎么樣實現(xiàn)的呢?
前面的部分都是相同的,只是在_sendData方法中略有不同。同步的調(diào)用add,異步的則是調(diào)用addPending

Stream 同步、異步
Stream除了異步執(zhí)行以外,還可以同步執(zhí)行,通過設(shè)置sync字段來控制,內(nèi)部就會通過同步/還是異步返回不同的具體實例對象。
Stream構(gòu)造方法
// 普通構(gòu)造方法
factory StreamController(
{void onListen()?,
void onPause()?,
void onResume()?,
FutureOr<void> onCancel()?,
bool sync = false}) {
return sync
? _SyncStreamController<T>(onListen, onPause, onResume, onCancel)
: _AsyncStreamController<T>(onListen, onPause, onResume, onCancel);
/// 廣播的stream
factory StreamController.broadcast(
{void onListen()?, void onCancel()?, bool sync = false}) {
return sync
? _SyncBroadcastStreamController<T>(onListen, onCancel)
: _AsyncBroadcastStreamController<T>(onListen, onCancel);
}
可以看出會根據(jù)sync返回不同的實例對象,根據(jù)構(gòu)造方法和sync不同,分別有四個實例對象。
SyncStreamController._sendData
void _sendData(T data) {
if (_isEmpty) return;
if (_hasOneListener) {
_state |= _BroadcastStreamController._STATE_FIRING;
_BroadcastSubscription<T> firstSubscription =
_firstSubscription as dynamic;
firstSubscription._add(data);
_state &= ~_BroadcastStreamController._STATE_FIRING;
if (_isEmpty) {
_callOnCancel();
}
return;
}
_forEachListener((_BufferingStreamSubscription<T> subscription) {
subscription._add(data);
});
}
ASyncStreamController._sendData
void _sendData(T data) {
for (var subscription = _firstSubscription;
subscription != null;
subscription = subscription._next) {
subscription._addPending(new _DelayedData<T>(data));
}
}
通過上面的兩個sendData方法的分析,可以看出兩者最大的區(qū)別就是一個是調(diào)用add方法另外一個是addPending方法。
其中addPending方法最終會調(diào)用到stream_impl.dart的schedule方法中
void schedule(_EventDispatch<T> dispatch) {
if (isScheduled) return;
assert(!isEmpty);
if (_eventScheduled) {
assert(_state == _STATE_CANCELED);
_state = _STATE_SCHEDULED;
return;
}
scheduleMicrotask(() {
int oldState = _state;
_state = _STATE_UNSCHEDULED;
if (oldState == _STATE_CANCELED) return;
handleNext(dispatch);
});
_state = _STATE_SCHEDULED;
}
整理一下異步的Stream發(fā)送流程,如下圖:

StreamController的種類
上已經(jīng)了解到Stream分區(qū)異步/同步兩種類型,他們最要的區(qū)別就是混入的接口不一致,同步的混入的是_SyncStreamControllerDispatch、異步混入的是_AsyncStreamControllerDispatch。
- 同步 _SyncStreamController
- 異步 _AsyncStreamController
- 同步廣播 _SyncBroadcastStreamController
- 異步廣播 _AsyncBroadcastStreamController

Stream 變換
Stream支持事件的變換處理,通過Stream變化可以讓事件經(jīng)過篩選和多次處理,從而達到最終效果。

一般操作符變換實現(xiàn)對象,都是繼承了_ForwardingStream,在它內(nèi)部的_ForwardingStreamSubscription中,會把上一個Stream的listen添加到新的_handleData回調(diào),之后再回調(diào)里面調(diào)用新的(變換之后的)Stream的_handleData,通過這樣子的嵌套回調(diào),讓Stream在多次變換之后一直往后執(zhí)行。
StreamBuilder
StreamBuilder是基于Stream的封裝,能夠讓開發(fā)者快速的根據(jù)Stream構(gòu)建應(yīng)用。比如上面的基于Stream讓StatelessWidget實現(xiàn)刷新效果的Demo。
那么StreamBuilder的內(nèi)部是怎么實現(xiàn)的呢?下面是關(guān)鍵代碼片段
class _StreamBuilderBaseState<T, S> extends State<StreamBuilderBase<T, S>> {
StreamSubscription<T> _subscription;
S _summary;
/// 進行監(jiān)聽
@override
void initState() {
super.initState();
_summary = widget.initial();
_subscribe();
}
/// 重新訂閱
@override
void didUpdateWidget(StreamBuilderBase<T, S> oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.stream != widget.stream) {
if (_subscription != null) {
_unsubscribe();
_summary = widget.afterDisconnected(_summary);
}
_subscribe();
}
}
@override
Widget build(BuildContext context) => widget.build(context, _summary);
/// 銷毀的是取消訂閱
@override
void dispose() {
_unsubscribe();
super.dispose();
}
/// 訂閱
void _subscribe() {
if (widget.stream != null) {
_subscription = widget.stream.listen((T data) {
setState(() {
_summary = widget.afterData(_summary, data);
});
}, onError: (Object error) {
setState(() {
_summary = widget.afterError(_summary, error);
});
}, onDone: () {
setState(() {
_summary = widget.afterDone(_summary);
});
});
_summary = widget.afterConnected(_summary);
}
}
/// 取消訂閱
void _unsubscribe() {
if (_subscription != null) {
_subscription.cancel();
_subscription = null;
}
}
}
整理之后的流程圖如下:

RxDart
其實從訂閱和變換的角度就可以看出,Dart中的Stream已經(jīng)有用了ReactiveX的設(shè)計思想,RxDart的出現(xiàn)就是為了能幫助那些了解過ReactiveX的框架的開發(fā)者,能夠快速的根據(jù)之前編寫習慣上手,其實RxDart底層也是基于Stream的一種封裝。下圖就是RxDart和Stream的對應(yīng)關(guān)系
| Dart | RxDart |
|---|---|
| StreamController | Subject |
| Stream | Observable |
下面用一個PublishSubject的發(fā)送和監(jiān)聽做示例:
創(chuàng)建Subject
class PublishSubject<T> extends Subject<T> {
PublishSubject._(StreamController<T> controller, Stream<T> stream)
: super(controller, stream);
/// 工廠方法內(nèi)部,可以很明顯的看到這里就是創(chuàng)建了一個廣播類型的StreamController
factory PublishSubject(
{void Function() onListen, void Function() onCancel, bool sync = false}) {
// ignore: close_sinks
final controller = StreamController<T>.broadcast(
onListen: onListen,
onCancel: onCancel,
sync: sync,
);
return PublishSubject<T>._(
controller,
controller.stream,
);
}
}
添加事件
因為PublishSubject繼承自Subject,所以add方法在Subject之中:
@override
void add(T event) {
if (_isAddingStreamItems) {
throw StateError(
'You cannot add items while items are being added from addStream');
}
_add(event);
}
/// 可以看到這里就是調(diào)用了controller.add方法
void _add(T event) {
onAdd(event);
_controller.add(event);
}
監(jiān)聽
因為PublishSubject繼承自Subject,所以listen方法在Subject之中:
// 這里也可以發(fā)現(xiàn),onListen也是controller的listen
@override
set onListen(void Function() onListenHandler) {
_controller.onListen = onListenHandler;
}
銷毀
因為PublishSubject繼承自Subject,所以close方法在Subject之中:
@override
Future<dynamic> close() {
if (_isAddingStreamItems) {
throw StateError(
'You cannot close the subject while items are being added from addStream');
}
/// 這里也是調(diào)用的controller的close
return _controller.close();
}
這里可能就有疑問了,如果只是提供了對Stream的一層封裝,那為什么ReactiveX還要如此大費周章呢?那是因為對于Stream的變換提供了很多的方法。包括buffer、bufferCount、bufferTest、bufferTime、contactWith、debounce、debounceTime等等,查看更多請點我
一個監(jiān)聽用戶輸入的demo
class RxDartDemoPage extends StatefulWidget {
@override
_RxDartDemoPageState createState() => _RxDartDemoPageState();
}
class _RxDartDemoPageState extends State<RxDartDemoPage> {
PublishSubject _subject;
TextEditingController _editingController;
Stream _keywordStream;
@override
void initState() {
super.initState();
_editingController = TextEditingController();
_subject = PublishSubject();
_keywordStream = _subject
.debounceTime(Duration(milliseconds: 500))
.map((event) => "搜索關(guān)鍵字: $event");
_editingController.addListener(() {
_subject.add(_editingController.text);
});
}
@override
void dispose() {
_subject?.close();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("RxDart")),
body: Column(
children: [
TextField(
controller: _editingController,
),
SizedBox(height: 40),
StreamBuilder(
stream: _keywordStream,
builder: (context, snapshot) {
return Center(child: Text(snapshot.data ?? "請輸入關(guān)鍵詞"));
},
)
],
),
);
}
}
BLoC
BLoC全稱是Bussiness Logic Component,是谷歌提出的一種設(shè)計模式,BLoC利用了Flutter響應(yīng)式構(gòu)建的特點,通過流的方式實現(xiàn)界面的異步渲染,開發(fā)者可以銅鼓BLoC可以快速實現(xiàn)業(yè)務(wù)與界面的分離效果。
BLoC主要是通過Stream和StreamBuilder結(jié)合實現(xiàn),目的就是把UI和邏輯分離。
demo
比如新建工程中的默認實現(xiàn)(點擊加號,數(shù)字加一),如果使用BLoC來實現(xiàn)就是新建一個類,內(nèi)部提供兩個對外開放的內(nèi)容,一個是stream另外一個則是add方法,通過stream和StreamBuilder進行關(guān)聯(lián),當數(shù)據(jù)發(fā)生改變時主動更新UI;通過add方法對外公開,提供給按鈕點擊調(diào)用。
class BLoCDemoPage extends StatefulWidget {
@override
_BLoCDemoPageState createState() => _BLoCDemoPageState();
}
class _BLoCDemoPageState extends State<BLoCDemoPage> {
final _CountBLoC _bLoC = _CountBLoC();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("BLoC"),
),
body: Column(
children: [
Text("BLoC大多是利用Stream和StreamBuilder實現(xiàn),更多的是一種設(shè)計模式的思路,好處就是分離UI和邏輯層"),
StreamBuilder(
initialData: 0,
stream: _bLoC.countStream,
builder: (context, snapshot) => Center(
child: Text(
"${snapshot.data}",
style: TextStyle(fontSize: 30, color: Colors.redAccent),
),
),
),
],
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: _bLoC.add,
),
);
}
@override
void dispose() {
_bLoC.dispose();
super.dispose();
}
}
class _CountBLoC {
int _count = 0;
StreamController<int> _streamController = StreamController<int>();
/// 提供給外界更新使用
Stream get countStream => _streamController.stream;
/// 觸發(fā)更新邏輯
void add() {
_count++;
_streamController.add(_count);
}
/// 銷毀
dispose() {
_streamController?.close();
}
}
流程圖
[圖片上傳失敗...(image-682214-1616915696007)]
scoped_model
scoped_model是Flutter中最簡單的第三方狀態(tài)的管理框架,它巧妙的利用了Flutter中的一些特性,只有一個dart的文件情況下,實現(xiàn)了實用的狀態(tài)管理模型。使用它一般需要三步。
- 1、新建一個類并繼承
Model,并且在想要更新UI的時候調(diào)用notifyListenrs - 2、使用
ScopedModel控件加載Model - 3、使用
ScopedModelDescendant或者ScopedModel.of<T>(context)加載model內(nèi)的數(shù)據(jù)進行顯示
demo
import 'package:flutter/material.dart';
import 'package:scoped_model/scoped_model.dart' as sc;
class ScopeModelDemoPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Scope model")),
body: SafeArea(
child: Container(
child: sc.ScopedModel<_CountModel>(
model: _CountModel(),
child: sc.ScopedModelDescendant<_CountModel>(
builder: (context, child, model) {
return Column(
children: [
Expanded(child: Center(child: Text(model.count.toString()))),
Center(child: FlatButton(
onPressed: model.add,
color: Colors.blue,
child: Icon(Icons.add),
),),
],
);
},
)
),
),
),
);
}
}
class _CountModel extends sc.Model {
static _CountModel of(BuildContext context) =>
sc.ScopedModel.of<_CountModel>(context);
int _count = 0;
int get count => _count;
void add() {
_count++;
notifyListeners();
}
}
流程圖
在查看ScopedModel的源碼之后,發(fā)現(xiàn)他首先使用AnimatedBuilder包裝起來,AnimatedBuilder繼承了AnimatedWidget,在AnimatedWidget的生命周期中會對Listenable接口添加監(jiān)聽,而Model恰好就實現(xiàn)了Listenable接口,從而可以達到刷新的效果。ScopedModel內(nèi)部除了使用AnimatedBuilder包裝起來之外,還使用_InheritedModel再次進行了包裝,保證了數(shù)據(jù)向下傳遞和共享。
ScopedModel.build方法源碼
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: model,
builder: (context, _) => _InheritedModel<T>(model: model, child: child),
);
}

flutter_reduce
redux是一種單向數(shù)據(jù)流架構(gòu)。在Redux中,數(shù)據(jù)都是存儲在單一信源(Store)中,然后數(shù)據(jù)存儲的時候通過Reducer進行更新,而觸發(fā)更新的動作就是Action。之所以說他是單向數(shù)據(jù)流,那是因為redux通過action發(fā)出的行為,通過reducer更新之后并把數(shù)據(jù)保存到store中,在加上Widget之后就變成了一個閉環(huán),如下圖:
流程圖

使用流程
- 1、創(chuàng)建
State - 2、創(chuàng)建
Action - 3、創(chuàng)建
Reducer - 4、創(chuàng)建/保存
Store - 5、關(guān)聯(lián)
Widget - 6、發(fā)出
Action,觸發(fā)第4步
demo
下面用一個兩個頁面之前的數(shù)據(jù)更新demo進行演示:
import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:redux/redux.dart';
/// 1、創(chuàng)建State
class ReduxCountState {
int _count;
int get count => _count;
ReduxCountState(this._count);
}
/// 2、創(chuàng)建Action
enum ReduxAction { increment }
/// 3、創(chuàng)建Reducer
ReduxCountState reducer(ReduxCountState state, dynamic action) {
switch (action) {
case ReduxAction.increment:
return ReduxCountState(state.count + 1);
default:
return state;
}
}
/// 攔截器,攔截器介于Action和Reducer之間,如果我們要對一些事件進行攔截,就可以在這里處理
/// 舉個例子:當我們更新用戶信息的時候(假設(shè)有頭像,名稱),需要去刷新,當我們只更新
/// 名稱的時候,由于頭像沒更新,我不希望頭像也倍刷新一次,此時就可以根據(jù)action,進行攔截不響應(yīng)處理
class ReduxCountMiddleware implements MiddlewareClass<ReduxCountState> {
@override
call(Store<ReduxCountState> store, action, next) {
/// 只更新偶數(shù),奇數(shù)不處理
if (store.state.count % 2 != 0) {
next(action);
print("xxxxxxxxx 我是攔截器,偶數(shù)通過");
} else {
next(action);
next(action);
print("xxxxxxxxx 我是攔截器,過濾奇數(shù)");
}
}
}
class FlutterReduxDemoPage extends StatefulWidget {
@override
_FlutterReduxDemoPageState createState() => _FlutterReduxDemoPageState();
}
class _FlutterReduxDemoPageState extends State<FlutterReduxDemoPage> {
/// 4、創(chuàng)建Store
final store = Store<ReduxCountState>(
reducer,
/// 攔截奇數(shù)
middleware: [ReduxCountMiddleware()],
initialState: ReduxCountState(0),
);
@override
Widget build(BuildContext context) {
return StoreProvider<ReduxCountState>(
store: store,
child: Scaffold(
appBar: AppBar(
title: Text("redux"),
),
body: Center(
/// 5、關(guān)聯(lián)Widget
child: StoreConnector<ReduxCountState, String>(
converter: (store) => store.state.count.toString(),
builder: (context, val) => Text(
val,
style: TextStyle(fontSize: 30, color: Colors.red),
),
),
),
/// 6、觸發(fā)
floatingActionButton: StoreConnector<ReduxCountState, VoidCallback>(
converter: (store) {
return () => store.dispatch(ReduxAction.increment);
},
builder: (context, callback) {
return FloatingActionButton(
child: Icon(Icons.add),
onPressed: callback,
);
},
),
),
);
}
}

總結(jié)
redux內(nèi)部是通過InheritedWidget和Stream以及StreamBuilder的自定義封裝。由于他的單向數(shù)據(jù)流思想,開發(fā)者的每個操作都只是一個Action,而這個行為所觸發(fā)的邏輯完全有middleware和reducer決定,這樣子的設(shè)計模式一定程度上將業(yè)務(wù)和UI進行了隔離,并且規(guī)范了整個事件流過程中的調(diào)用模式。
工具很強大,但是需要花費一定的學習成本,但是如果你之前是前端開發(fā)者,那么使用redux還是很得心應(yīng)手的。但如果你之前更多的是App 開發(fā),那可能Provider會更接近于你的使用方式。
Provide/Provider
在Provider之前官方推薦的狀態(tài)管理方式之一就是provide,它的特點不復(fù)雜、好理解、可控度高,但是后面就被Provider給替代了。
流程圖
1、被設(shè)置到ChangeNotifierProvider的ChangeNotifier會被執(zhí)行addListener,添加listener
2、listener內(nèi)部會調(diào)用StateDelegate的StateSetter方法,從而調(diào)用到StatefulWidget的setState
3、當執(zhí)行ChangeNotifier的notifyListeners,最終就會觸發(fā)setState更新

使用流程
1、創(chuàng)建
ChangeNotifier的子類,實現(xiàn)相關(guān)內(nèi)部邏輯2、使用包括但不限于的:
ChangeNotfierProvider、MultiProvider等provider封裝好的實體類把ChangeNotifier的子類加入到provider之中-
3、使用
Consummer、Provider.of(context)等引用provider的值進行關(guān)聯(lián)如果只是想獲取
provider的值,并不想根據(jù)狀態(tài)進行更新需要使用Provider.of<T>(context, listen: false)來獲取到Provider 4、調(diào)用
ChangeNotifier的子類的notifyListeners方法觸發(fā)更新
demo
之所以說簡單,我們通過官方的數(shù)字累加Demo就可以看出
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class ProviderDemoPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) => _CountProvider(),
child: Scaffold(
appBar: AppBar(title: Text("Provider")),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Builder(
builder: (context) {
return Text(
Provider.of<_CountProvider>(context).count.toString(),
style: TextStyle(fontSize: 30, color: Colors.orangeAccent),
);
},
),
Consumer<_CountProvider>(
builder: (context, provider, child) {
return Center(
child: Text(
provider.count.toString(),
style: TextStyle(fontSize: 30, color: Colors.orangeAccent),
),
);
},
)
],
),
floatingActionButton: Builder(
builder: (context) => FloatingActionButton(
onPressed: () {
Provider.of<_CountProvider>(context, listen: false)?.add();
},
child: Icon(Icons.add),
),
),
),
);
}
}
class _CountProvider extends ChangeNotifier {
int _count = 0;
int get count => _count;
void add() {
_count++;
notifyListeners();
}
}
總結(jié)
Provider提供了一種簡單、不復(fù)雜,可控性好的狀態(tài)管理方式,通過MultiProviders可以在一個頁面中插入多個Provider,在配合Consumer使用可以實現(xiàn)顆粒度級別的刷新,避免造成不必要的性能浪費。也是目前主流的狀態(tài)管理方式之一。