类关系
Provider常用的类有:
- Provider
- ListenableProvider
- ChangeNotifierProvider
- ValueListenableProvider
- StreamProvider
- FutureProvider
- MultiProvider
- ProxyProvider
- ChangeNotifierProxyProvider
我们阅读源码,绘出下面的UML类关系图,读者朋友们如果看不清楚,可以尝试将放大下浏览器视图百分比。
例子
看到这么多类,我们就疯掉了:什么情况下用什么类?
以下内容理解来自于博客:https://medium.com/flutter-community/making-sense-all-of-those-flutter-providers-e842e18f45dd,这篇文章对于初次了解Provider的学习者来说,非常容易入门,强烈推荐读者阅读。
1. 建立项目

我们先建立项目,演示很简单,有两个Container,左边Container中有个按钮 Do something ,右边Container中有个文本,目前是 Show something 。后面将演示,点击左边按钮,右边文本同步发生变化的同步机制。
先看下初始的代码。
import 'package:flutter/material.dart';void main() => runApp(MyApp());class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(home: Scaffold(appBar: AppBar(title: Text('My App')),body: Row(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[Container(padding: const EdgeInsets.all(20),color: Colors.green[200],child: RaisedButton(child: Text('Do something'),onPressed: () {},),),Container(padding: const EdgeInsets.all(35),color: Colors.blue[200],child: Text('Show something'),),],),),);}}
2. 引入Provider
在 pubspec.yaml 文件中引入 Provider 库:
dependencies:provider: ^4.0.1
类中使用 Provider 的时候,引入类:
import 'package:provider/provider.dart';
3. 使用Provider
Provider 顾名思义,是一个提供者,或者说提供器,它可以在 widget 树的任何地方提供一个 value ,通常是一个数据 model 。然而,这个基本的 Provider 在 value 发生变化的时候并不会更新 widget 树。
我们定义一个基本的 model :
class MyModel {String someValue = 'Hello';void doSomething() {someValue = 'Goodbye';print(someValue);}}
model 定义好了,如何使用 Provider 来将它提供出去呢?
1)在 wiget 树的某处(这里demo中选择了最顶层),使用 Provider , Provider 内部有个 create 函数,这里我们返回 MyModel 实例。
2)在需要使用 MyModel 实例的 widget 中,使用 Consumer 进行包裹一层, Consumer 为消费者的含义,意思是表达对 MyModel 实例的关注以及消费使用。
import 'package:flutter/material.dart';import 'package:provider/provider.dart';void main() => runApp(MyApp());class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return Provider<MyModel>( // <--- Providercreate: (context) => MyModel(),child: MaterialApp(home: Scaffold(appBar: AppBar(title: Text('My App')),body: Row(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[Container(padding: const EdgeInsets.all(20),color: Colors.green[200],child: Consumer<MyModel>( // <--- Consumerbuilder: (context, myModel, child) {return RaisedButton(child: Text('Do something'),onPressed: (){// We have access to the model.myModel.doSomething();},);},)),Container(padding: const EdgeInsets.all(35),color: Colors.blue[200],child: Consumer<MyModel>( // <--- Consumerbuilder: (context, myModel, child) {return Text(myModel.someValue);},),),],),),),);}}class MyModel { // <--- MyModelString someValue = 'Hello';void doSomething() {someValue = 'Goodbye';print(someValue);}}
运行一下:
:::info
- 右侧文本”Hello”来自MyModel的someValue
- 点击左侧按钮将会执行model的doSomething方法。doSomething虽然改变了model的someValue值,但是右侧文本并不会发生变化,因为Provider widget并没有监听它提供的值的变化。
:::
4. 使用ChangeNotifierProvider
和 Provider widget不同,ChangeNotifierProvider会监听模型的变化。当模型发生变化时, Consumer 下的 widgets 都会被重建。
下面代码中,我们把 Provider 修改成了ChangeNotifierProvider。 MyModel 类需要继承 ChangeNotifier 或者使用 mixin 。这样我们就可以在模型发生变化的时候调用 notifyListeners() ,然后 ChangeNotifierProvider 就会被通知到然后 Consumer 的 widgets 就会被重建。
import 'package:flutter/material.dart';import 'package:provider/provider.dart';void main() => runApp(MyApp());class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return ChangeNotifierProvider<MyModel>( // <--- ChangeNotifierProvidercreate: (context) => MyModel(),child: MaterialApp(home: Scaffold(appBar: AppBar(title: Text('My App')),body: Row(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[Container(padding: const EdgeInsets.all(20),color: Colors.green[200],child: Consumer<MyModel>( // <--- Consumerbuilder: (context, myModel, child) {return RaisedButton(child: Text('Do something'),onPressed: (){myModel.doSomething();},);},)),Container(padding: const EdgeInsets.all(35),color: Colors.blue[200],child: Consumer<MyModel>( // <--- Consumerbuilder: (context, myModel, child) {return Text(myModel.someValue);},),),],),),),);}}class MyModel with ChangeNotifier { // <--- MyModelString someValue = 'Hello';void doSomething() {someValue = 'Goodbye';print(someValue);notifyListeners();}}

:::info
- 我们看下上面的代码,可以发现按钮的点击事件因为用了MyModel实例的doSomething方法,也作为了Consumer使用。但MyModel模型发生变化的时候,按钮是没有必要更新的。所以可以使用
Provider.of,并且设置listener为false。这样当模型变化时按钮就不会重建。下面是如何使用Provider.of。 :::class MyButton extends StatelessWidget {@overrideWidget build(BuildContext context) {final myModel = Provider.of<MyModel>(context, listen: false);return RaisedButton(child: Text('Do something'),onPressed: () {myModel.doSomething();},);}}
5. 使用FutureProvider
FutureProvider 是对 FutureBuilder widget的封装。我们可以提供给一些初始化数据来展示UI,并且可以提供一个Future对象。 FutureProvider 监听到Future完成后,便通知 Consumers widgets进行重建。
下面代码展示了右侧文本初始以 ‘default value’ 展现,然后3秒后Future完成,返回’new data’展示。
import 'package:flutter/material.dart';import 'package:provider/provider.dart';void main() => runApp(MyApp());class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return FutureProvider<MyModel>( // <--- FutureProviderinitialData: MyModel(someValue: 'default value'),create: (context) => someAsyncFunctionToGetMyModel(),child: MaterialApp(home: Scaffold(appBar: AppBar(title: Text('My App')),body: Row(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[Container(padding: const EdgeInsets.all(20),color: Colors.green[200],child: Consumer<MyModel>( // <--- Consumerbuilder: (context, myModel, child) {return RaisedButton(child: Text('Do something'),onPressed: (){myModel.doSomething();},);},)),Container(padding: const EdgeInsets.all(35),color: Colors.blue[200],child: Consumer<MyModel>( // <--- Consumerbuilder: (context, myModel, child) {return Text(myModel.someValue);},),),],),),),);}}Future<MyModel> someAsyncFunctionToGetMyModel() async { // <--- async functionawait Future.delayed(Duration(seconds: 3));return MyModel(someValue: 'new data');}class MyModel { // <--- MyModelMyModel({this.someValue});String someValue = 'Hello';Future<void> doSomething() async {await Future.delayed(Duration(seconds: 2));someValue = 'Goodbye';print(someValue);}}
6. 使用StreamProvider
import 'package:flutter/material.dart';import 'package:provider/provider.dart';void main() => runApp(MyApp());class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return StreamProvider<MyModel>( // <--- StreamProviderinitialData: MyModel(someValue: 'default value'),create: (context) => getStreamOfMyModel(),child: MaterialApp(home: Scaffold(appBar: AppBar(title: Text('My App')),body: Row(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[Container(padding: const EdgeInsets.all(20),color: Colors.green[200],child: Consumer<MyModel>( // <--- Consumerbuilder: (context, myModel, child) {return RaisedButton(child: Text('Do something'),onPressed: (){myModel.doSomething();},);},)),Container(padding: const EdgeInsets.all(35),color: Colors.blue[200],child: Consumer<MyModel>( // <--- Consumerbuilder: (context, myModel, child) {return Text(myModel.someValue);},),),],),),),);}}Stream<MyModel> getStreamOfMyModel() { // <--- Streamreturn Stream<MyModel>.periodic(Duration(seconds: 1),(x) => MyModel(someValue: '$x')).take(10);}class MyModel { // <--- MyModelMyModel({this.someValue});String someValue = 'Hello';void doSomething() {someValue = 'Goodbye';print(someValue);}}
7. 使用ValueListenableProvider
import 'package:flutter/material.dart';import 'package:provider/provider.dart';void main() => runApp(MyApp());class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return Provider<MyModel>(// <--- Providercreate: (context) => MyModel(),child: Consumer<MyModel>( // <--- MyModel Consumerbuilder: (context, myModel, child) {return ValueListenableProvider<String>.value( // <--- ValueListenableProvidervalue: myModel.someValue,child: MaterialApp(home: Scaffold(appBar: AppBar(title: Text('My App')),body: Row(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[Container(padding: const EdgeInsets.all(20),color: Colors.green[200],child: Consumer<MyModel>( // <--- Consumerbuilder: (context, myModel, child) {return RaisedButton(child: Text('Do something'),onPressed: (){myModel.doSomething();},);},)),Container(padding: const EdgeInsets.all(35),color: Colors.blue[200],child: Consumer<String>(// <--- String Consumerbuilder: (context, myValue, child) {return Text(myValue);},),),],),),),);}),);}}class MyModel { // <--- MyModelValueNotifier<String> someValue = ValueNotifier('Hello'); // <--- ValueNotifiervoid doSomething() {someValue.value = 'Goodbye';print(someValue.value);}}

:::info
- 点击’Do something’按钮因为
ValueListenableProvider的作用会将”Hello”修改为”Goodbye”。 - 使用
Provider.of<MyModel>(context, listen: false)比在widget树上层使用Consumer更好,不然每次都要重建整个树。 - 这个例子中,在widget树顶层使用Consumer并不是一个很好的做法,只是方便演示。
:::
8. 使用ListenableProvider
没有太好的使用场景。9. 使用MultiProvider
import 'package:flutter/material.dart';import 'package:provider/provider.dart';void main() => runApp(MyApp());class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MultiProvider( // <--- MultiProviderproviders: [ChangeNotifierProvider<MyModel>(create: (context) => MyModel()),ChangeNotifierProvider<AnotherModel>(create: (context) => AnotherModel()),],child: MaterialApp(home: Scaffold(appBar: AppBar(title: Text('My App')),body: Column(children: <Widget>[Row(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[Container(padding: const EdgeInsets.all(20),color: Colors.green[200],child: Consumer<MyModel>( // <--- MyModel Consumerbuilder: (context, myModel, child) {return RaisedButton(child: Text('Do something'),onPressed: (){// We have access to the model.myModel.doSomething();},);},)),Container(padding: const EdgeInsets.all(35),color: Colors.blue[200],child: Consumer<MyModel>( // <--- MyModel Consumerbuilder: (context, myModel, child) {return Text(myModel.someValue);},),),],),// SizedBox(height: 5),Row(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[Container(padding: const EdgeInsets.all(20),color: Colors.red[200],child: Consumer<AnotherModel>( // <--- AnotherModel Consumerbuilder: (context, myModel, child) {return RaisedButton(child: Text('Do something'),onPressed: (){myModel.doSomething();},);},)),Container(padding: const EdgeInsets.all(35),color: Colors.yellow[200],child: Consumer<AnotherModel>( // <--- AnotherModel Consumerbuilder: (context, anotherModel, child) {return Text('${anotherModel.someValue}');},),),],),],),),),);}}class MyModel with ChangeNotifier { // <--- MyModelString someValue = 'Hello';void doSomething() {someValue = 'Goodbye';print(someValue);notifyListeners();}}class AnotherModel with ChangeNotifier { // <--- AnotherModelint someValue = 0;void doSomething() {someValue = 5;print(someValue);notifyListeners();}}
10. 使用ProxyProvider
MultiProvider(providers: [ChangeNotifierProvider<MyModel>(create: (context) => MyModel(),),ProxyProvider<MyModel, AnotherModel>(update: (context, myModel, anotherModel) => AnotherModel(myModel),),],
import 'package:flutter/material.dart';import 'package:provider/provider.dart';void main() => runApp(MyApp());class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MultiProvider( // <--- MultiProviderproviders: [ChangeNotifierProvider<MyModel>( // <--- ChangeNotifierProvidercreate: (context) => MyModel(),),ProxyProvider<MyModel, AnotherModel>( // <--- ProxyProviderupdate: (context, myModel, anotherModel) => AnotherModel(myModel),),],child: MaterialApp(home: Scaffold(appBar: AppBar(title: Text('My App')),body: Column(children: <Widget>[Row(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[Container(padding: const EdgeInsets.all(20),color: Colors.green[200],child: Consumer<MyModel>( // <--- MyModel Consumerbuilder: (context, myModel, child) {return RaisedButton(child: Text('Do something'),onPressed: (){myModel.doSomething('Goodbye');},);},)),Container(padding: const EdgeInsets.all(35),color: Colors.blue[200],child: Consumer<MyModel>( // <--- MyModel Consumerbuilder: (context, myModel, child) {return Text(myModel.someValue);},),),],),Container(padding: const EdgeInsets.all(20),color: Colors.red[200],child: Consumer<AnotherModel>( // <--- AnotherModel Consumerbuilder: (context, anotherModel, child) {return RaisedButton(child: Text('Do something else'),onPressed: (){anotherModel.doSomethingElse();},);},)),],),),),);}}class MyModel with ChangeNotifier { // <--- MyModelString someValue = 'Hello';void doSomething(String value) {someValue = value;print(someValue);notifyListeners();}}class AnotherModel { // <--- AnotherModelMyModel _myModel;AnotherModel(this._myModel);void doSomethingElse() {_myModel.doSomething('See you later');print('doing something else');}}

11. Provider builder函数和值构造函数
在前面的例子中, Provider 的model使用的是 create 创建函数来创建返回的
Provider<MyModel>(create: (context) => MyModel(),child: ...)
如果我们用已存在的对象值呢?
使用value来引用:
final myModel = MyModel();...Provider<MyModel>.value(value: myModel,child: ...)
参考学习资料
- Git地址:https://github.com/rrousselGit/provider
- https://medium.com/flutter-community/making-sense-all-of-those-flutter-providers-e842e18f45dd
- https://www.didierboelens.com/2019/07/provider—-points-of-interest—-points-to-care-about/
- https://www.youtube.com/watch?v=MkFjtCov62g
- A Closer Look at the Provider Package
- Simple app state management
- Flutter Architecture — My Provider Implementation Guide
- Remi Rousselet, author of the Provider package
