Provider
是目前Google
推薦的狀態管理方式之一,建議你們能夠先看一下 Provider 的 Github 地址 瞭解基本的用法。git
網上大多數介紹Provider
的文章講的都是ChangeNotifierProvider
,看完以後確實知道它是幹什麼的,以及怎麼用。github
然而其實還有其它的Provider
供咱們使用,那麼它們之間的區別和聯繫是什麼呢,官方文檔對它們的使用也沒有詳細的Demo
,這篇文章就來總結一下它們的用法和區別。app
Provider
的分類有以下幾種:less
Provider
ListenableProvider
ChangeNotifierProvider
ValueListenableProvider
StreamProvider
FutureProvider
Provider
的構造函數以下:異步
Provider({
Key key,
@required ValueBuilder<T> builder,
Disposer<T> dispose,
Widget child,
})
複製代碼
builder
:T Function(BuildContext context)
,返回要共享的數據Model
。dispose
:void Function(BuildContext context, T value)
,在回調中釋放資源。Provider
的優勢:async
Provider.of<T>(context)
方法,能夠在以Provider
爲根節點的子樹中獲取到T
的對象。dispose
參數,能夠進行資源的釋放。可是Provider
也有一個明顯的缺點:ide
下面咱們用一個經典的計數器Demo
演示一下Provider
的使用,爲了方便對比,後面在介紹其它Provider
時,也使用該例子。函數
根據Provider
的兩個特色,咱們能夠用它來實現BLoc
中sink
的獲取,以及最後資源的釋放。ui
Bloc
模型。import 'dart:async';
class ProviderBloc {
int _count = 0;
var _countController = StreamController<int>.broadcast();
Stream<int> get stream => _countController.stream;
int get count => _count;
increment() {
_countController.sink.add(++_count);
}
dispose() {
_countController.close();
}
}
複製代碼
Provider.of<ProviderBloc>(context)
咱們能夠在任意的地方獲取到ProviderBloc
對象,得到sink
來更改數據。StreamBuilder
監聽Stream
中數據的變化,刷新界面。Bloc
模型上,頂層的Provider
提供了dispose
回調,用於資源的釋放。import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'provider_bloc.dart';
void main() => runApp(_ProviderApp());
class _ProviderApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Provider<ProviderBloc>(
builder: (context) => ProviderBloc(),
dispose: (context, bloc) => bloc.dispose(),
child: MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Provider Demo')),
body: CounterLabel(),
floatingActionButton: CounterButton(),
),
),
);
}
}
class CounterButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return FloatingActionButton(
onPressed: Provider.of<ProviderBloc>(context).increment,
child: const Icon(Icons.add),
);
}
}
class CounterLabel extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: StreamBuilder<int>(
builder: (context, snapshot) {
return Text('you have push ${snapshot.data} times');
},
initialData: 0,
stream: Provider.of<ProviderBloc>(context).stream,
),
);
}
}
複製代碼
ChangeNotifierProvider
應該是你們見的最多的,大多數介紹Provider
的文章都是以它爲例子,和 Provider 相比,它最大的優勢就是解決了數據改變後沒法監聽的問題。this
ChangeNotifierProvider
的構造函數爲以下:
ChangeNotifierProvider({
Key key,
@required ValueBuilder<T> builder,
Widget child,
})
複製代碼
builder
:T Function(BuildContext context)
,返回要共享的數據Model
。使用ChangeNotifierProvider
時,它要求builder
返回的數據Model
必須是ChangeNotifier
的子類。
notifyListener()
方法。dispose
方法,能夠完成和Provider
同樣的資源釋放工做。class Counter with ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
@override
void dispose() {
super.dispose();
}
}
複製代碼
在介紹Provider
的文章中,Provider.of<T>(context)
和Consumer
都會被拿來對比,通常都會推薦使用Consumer
,由於它會將數據發生變化後,把監聽者的 Widget 重建的範圍限制地更小。
項目中Provider
的使用,能夠分爲兩個角色,數據改變的 觸發者 和 監聽者:
model
,不須要監聽變化(例如點擊按鈕),推薦使用Provider.of<Counter>(context, listen: false)
來獲取數據model
。Consumer
。notifyListeners
方法。dispose
方法。import 'package:flutter/foundation.dart';
class Counter with ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
@override
void dispose() {
super.dispose();
}
}
複製代碼
import 'counter_model.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(_ProviderApp());
class _ProviderApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<Counter>(
builder: (context) => Counter(),
child: MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Provider Demo')),
body: CounterLabel(),
floatingActionButton: CounterButton(),
),
)
);
}
}
class CounterButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return FloatingActionButton(
onPressed: Provider.of<Counter>(context, listen : false).increment,
child: const Icon(Icons.add),
);
}
}
class CounterLabel extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Consumer<Counter>(
builder: (BuildContext context, Counter counter, Widget child) {
return Text('you have push ${counter.count} times');
}),
);
}
}
複製代碼
ListenableProvider
的構造函數爲:
ListenableProvider({
Key key,
@required ValueBuilder<T> builder,
Disposer<T> dispose,
Widget child,
})
複製代碼
builder
:T Function(BuildContext context)
,返回要共享的數據Model
。dispose
:void Function(BuildContext context, T value)
,在回調中釋放資源。先來一下ChangeNotifierProvider
的源碼:
class ChangeNotifierProvider<T extends ChangeNotifier> extends ListenableProvider<T> implements SingleChildCloneableWidget {
static void _disposer(BuildContext context, ChangeNotifier notifier) =>
notifier?.dispose();
ChangeNotifierProvider({
Key key,
@required ValueBuilder<T> builder,
Widget child,
}) : super(key: key, builder: builder, dispose: _disposer, child: child);
}
複製代碼
從源碼上能夠看出,ListenableProvider
和ChangeNotifierProvider
實際上是 父與子的關係,ChangeNotifierProvider
在它的基礎上:
model
的上限,要求必須是ChangeNotifier
的子類。ChangeNotifier.dispose()
來完成資源的釋放,不須要傳入dispose
參數給ChangeNotifierProvider
。使用ListenableProvider
時,假如咱們沒有將數據模型定義成ChangeNotifier
的子類,那麼須要本身進行監聽者的管理,爲了方便,咱們仍是繼續使用ChangeNotifier
,其它地方的使用和ChangeNotifierProvider
都是同樣的。
import 'counter_model.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(_ProviderApp());
class _ProviderApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListenableProvider<Counter>(
builder: (context) => Counter(),
dispose: (context, counter) => counter.dispose(),
child: MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Provider Demo')),
body: CounterLabel(),
floatingActionButton: CounterButton(),
),
)
);
}
}
class CounterButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return FloatingActionButton(
onPressed: Provider.of<Counter>(context, listen: false).increment,
child: const Icon(Icons.add),
);
}
}
class CounterLabel extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Consumer<Counter>(
builder: (BuildContext context, Counter counter, Widget child) {
return Text('you have push ${counter.count} times');
}),
);
}
}
複製代碼
ValueListenableProvider({
Key key,
@required ValueBuilder<ValueNotifier<T>> builder,
UpdateShouldNotify<T> updateShouldNotify,
Widget child,
})
複製代碼
builder
:T Function(BuildContext context)
,返回要共享的數據Model
。dispose
:void Function(BuildContext context, T value)
,在回調中釋放資源。ValueListenableProvider
要求builder
返回的對象必須是ValueNotifier<T>
的子類,T
是須要共享的數據類型。
ValueNotifier
的定義以下:
class ValueNotifier<T> extends ChangeNotifier implements ValueListenable<T> {
ValueNotifier(this._value);
@override
T get value => _value;
T _value;
set value(T newValue) {
if (_value == newValue)
return;
_value = newValue;
notifyListeners();
}
@override
String toString() => '${describeIdentity(this)}($value)';
}
複製代碼
ValueNotifier
是咱們前面屢次提到的ChangeNotifier
的子類,在改變_value
時,自動調用了notifyListeners
方法,那麼就參照以前的方法來使用它。
import 'package:flutter/foundation.dart';
class CounterModel {
int count;
CounterNotifier wrapper;
CounterModel(this.count);
}
class CounterNotifier extends ValueNotifier<CounterModel> {
CounterNotifier(CounterModel value) : super(value) {
value.wrapper = this;
}
@override
void dispose() {
super.dispose();
}
}
複製代碼
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'counter_notifier.dart';
void main() => runApp(_ProviderApp());
class _ProviderApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ValueListenableProvider<CounterModel>(
builder: (context) => CounterNotifier(CounterModel(0)),
updateShouldNotify: (model1, model2) {
print('updateShouldNotify');
return model1.count != model2.count;
},
child: MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Provider Demo')),
body: _CounterLabel(),
floatingActionButton: _CounterButton(),
),
)
);
}
}
class _CounterButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return FloatingActionButton(
onPressed: () {
CounterModel oldModel = Provider.of<CounterModel>(context, listen: false);
CounterModel newModel = CounterModel(oldModel.count + 1);
newModel.notifier = oldModel.notifier;
oldModel.notifier.value = newModel;
return;
},
child: const Icon(Icons.add),
);
}
}
class _CounterLabel extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Consumer<CounterModel>(
builder: (BuildContext context, CounterModel model, Widget child) {
return Text('you have push ${model.count} times');
}),
);
}
}
複製代碼
這裏有一個問題困擾了我好久:就是使用ValueNotifier<T>
時必需要改變_value
纔會觸發notifyListeners()
方法,而經過Provider.of<T>(context, listen: false)
拿到的對象是_value
,所以還須要在它裏面保存ValueNotifier<T>
的引用(或者將ValueNotifier
定義成單例的模式),再設置一次達到觸發的效果,感受用起來很奇怪,不知道是否是個人使用方式有問題,網上也沒有找到相關的例子。
StreamProvider
用來結合Bloc
使用。
StreamProvider({
Key key,
@required ValueBuilder<Stream<T>> builder,
T initialData,
ErrorBuilder<T> catchError,
UpdateShouldNotify<T> updateShouldNotify,
Widget child,
})
複製代碼
builder
:返回Bloc
中的Stream
。initialData
:初始數據。catchError
:發生錯誤時候的回調。StreamProvider.controller({
Key key,
@required ValueBuilder<StreamController<T>> builder,
T initialData,
ErrorBuilder<T> catchError,
UpdateShouldNotify<T> updateShouldNotify,
Widget child,
})
複製代碼
builder
:返回Bloc
中的StreamController
。import 'dart:async';
class ProviderBloc {
static ProviderBloc _instance;
ProviderBloc._internal() {
print("_internal");
}
static ProviderBloc _getInstance() {
if (_instance == null) {
_instance = ProviderBloc._internal();
}
return _instance;
}
factory ProviderBloc() => _getInstance();
static ProviderBloc get instance => _getInstance();
int _count = 0;
var _countController = StreamController<int>.broadcast();
Stream<int> get stream => _countController.stream;
int get count => _count;
increment() {
_countController.sink.add(++_count);
}
dispose() {
_countController.close();
}
}
複製代碼
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'provider_bloc.dart';
void main() => runApp(_ProviderApp());
class _ProviderApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return StreamProvider<int> (
builder: (context) {
return ProviderBloc().stream;
},
catchError: (BuildContext context, Object error) {},
initialData: 0,
child: MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Provider Demo')),
body: CounterLabel(),
floatingActionButton: CounterButton(),
),
)
);
}
}
class CounterButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return FloatingActionButton(
onPressed: ProviderBloc().increment,
child: const Icon(Icons.add),
);
}
}
class CounterLabel extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Consumer<int>(
builder: (BuildContext context, int value, Widget child) {
return Text('you have push $value times');
}),
);
}
}
複製代碼
FutureProvider({
Key key,
@required ValueBuilder<Future<T>> builder,
T initialData,
ErrorBuilder<T> catchError,
UpdateShouldNotify<T> updateShouldNotify,
Widget child,
})
複製代碼
builder
:返回一個Future<T>
對象,異步任務的返回結果,在結果返回後,會觸發Consumer
的重建。initialData
:初始數據。catchError
:發生錯誤的回調。FutureProvider
和咱們以前討論的Provider
場景不太同樣,它和FutureBuilder
比較相似,就是在數據返回以前加載一個組件,等待數據返回值後,重繪返回另外一個組件。
它和FutureBuilder
的區別在於:
FutureBuilder
的數據請求和展現都是在一個組件當中,而FutureProvider
是數據的請求者,Consumer
是展現者。FutureBuilder
的請求者和展現者是一對一的關係,而FutureProvider
能夠是一對多的關係。import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(_ProviderApp());
class _ProviderApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return FutureProvider<int>(
builder: (context) => _request(),
initialData: 0,
child: MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Provider Demo')),
body: CounterLabel(),
),
)
);
}
Future<int> _request() async {
return await Future<int>.delayed(Duration(milliseconds: 3000)).then((int value) {
return 300;
});
}
}
class CounterLabel extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Column(children: <Widget>[
Consumer<int>(
builder: (BuildContext context, int count, Widget child) {
return Text('Observer1=$count');
}),
Consumer<int>(
builder: (BuildContext context, int count, Widget child) {
return Text('Observer2=$count');
}),
],)
);
}
}
複製代碼
對比以上幾種Provider
的使用方式:仍是ChangeNotifierProvider
和StreamProvider
比較符合咱們平時的使用場景。
而其它三種:
Provider
:不能監聽數據改變,直接淘汰。ListenableProvider
:ChangeNotifierProvider
的原始版本。ValueListenableProvider
:ChangeNotifierProvider
的加強版本,可是怎麼感受用起來還更麻煩了。