Flutter状态管理Provider
在之前的文章中介绍了 Google 官方仓库下的一个状态管理 Provide。乍一看这俩玩意可能很容易就被认为是同一个东西,仔细一看,这不就差了一个字吗,有什么区别呢。
首先,你要知道的最大的一个区别就是,Provide 被 Provider 干掉了...假如你就是用了 Provide 的,你的内心应该已经开始骂了,这不是坑爹吗 。不过幸运的是,你要从 Provide 迁移到 Provider 并不是太难。
Provider 从名字上就很容易理解,它就是用于提供数据,无论是在单个页面还是在整个 app 都有它自己的解决方案,我们可以很方便的管理状态。可以说,Provider 的目标就是完全替代StatefulWidget。
Provider非全局状态管理:
import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; class HomePage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('搜索结果'), ), body: Provider<String>.value( //provider value: 'This is from MyHomePage', child: MyHomePage(), ), ); } } //Provider.of class MyHomePage extends StatelessWidget { @override Widget build(BuildContext context) { return Center( child: Text('${Provider.of<String>(context)}'), ); } } //Consumer同样实现 class MyHomePage extends StatelessWidget { @override Widget build(BuildContext context) { return Center( //child: Text('${Provider.of<String>(context)}'), child:Consumer<String>(builder:(context, data, child){ return Text(data); }), ); } }
官方示例代码:
import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; class Counters with ChangeNotifier { int _count = 0; int get count => _count; void increment() { _count++; notifyListeners(); } } class ProviderPage extends StatelessWidget { @override Widget build(BuildContext context) { return MultiProvider( providers: [ ChangeNotifierProvider(builder: (_) => Counters()), ], child: Consumer<Counters>( builder: (context, counter, _) { return MaterialApp( supportedLocales: const [Locale('en')], localizationsDelegates: [ DefaultMaterialLocalizations.delegate, DefaultWidgetsLocalizations.delegate, _ExampleLocalizationsDelegate(counter.count), ], home: const MyHomePage(), ); }, ), ); } } class ExampleLocalizations { static ExampleLocalizations of(BuildContext context) => Localizations.of<ExampleLocalizations>(context, ExampleLocalizations); const ExampleLocalizations(this._count); final int _count; String get title => 'Tapped $_count times'; } class _ExampleLocalizationsDelegate extends LocalizationsDelegate<ExampleLocalizations> { const _ExampleLocalizationsDelegate(this.count); final int count; @override bool isSupported(Locale locale) => locale.languageCode == 'en'; @override Future<ExampleLocalizations> load(Locale locale) => SynchronousFuture(ExampleLocalizations(count)); @override bool shouldReload(_ExampleLocalizationsDelegate old) => old.count != count; } class MyHomePage extends StatelessWidget { const MyHomePage({Key key}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Title()), body: const Center(child: CounterLabel()), floatingActionButton: const IncrementCounterButton(), ); } } class IncrementCounterButton extends StatelessWidget { const IncrementCounterButton({Key key}) : super(key: key); @override Widget build(BuildContext context) { return FloatingActionButton( onPressed: () { // `listen: false` is specified here because otherwise that would make // `IncrementCounterButton` rebuild when the counter updates. Provider.of<Counters>(context, listen: false).increment(); }, tooltip: 'Increment', child: const Icon(Icons.add), ); } } class CounterLabel extends StatelessWidget { const CounterLabel({Key key}) : super(key: key); @override Widget build(BuildContext context) { final counter = Provider.of<Counters>(context); return Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ const Text( 'You have pushed the button this many times:', ), Text( '${counter.count}', style: Theme.of(context).textTheme.display1, ), RaisedButton( onPressed: (){ Navigator.push(context, MaterialPageRoute(builder: (context)=>ProviderSecond())); }, child: Text('下一页'), ) ], ); } } class Title extends StatelessWidget { const Title({Key key}) : super(key: key); @override Widget build(BuildContext context) { return Text(ExampleLocalizations.of(context).title); } } class ProviderSecond extends StatelessWidget { const ProviderSecond({Key key}) : super(key: key); @override Widget build(BuildContext context) { final counter = Provider.of<Counters>(context); return Scaffold( appBar: AppBar( leading: IconButton( //返回按钮 onPressed: (){ Navigator.pop(context); //返回上级页面 }, icon: Icon(Icons.arrow_back), ), title: Text('第二页'), ), body: Text( '${counter.count}', style: Theme.of(context).textTheme.display1, ), ); } }
效果图:
点击+到7,然后点击下一页,下一页的内容同样是7。
Provider2.0到3.0的改变:
2.0: ChangeNotifierProvider.value(notifier: myNotifier), StreamProvider(builder: (_) => StreamController<int>()), 3.0: ChangeNotifierProvider.value(value: myNotifier), StreamProvider.controller(builder: (_) => StreamController<int>()),
下面基于Provider v-3.0 写个简单的示例:
第一步,添加Provider依赖
provider: ^3.1.0+1
pub地址:https://pub.dev/packages/provider
第二步,创建Model
class Counter with ChangeNotifier { int _count = 0; int get count => _count; void increment() { _count++; notifyListeners(); } }
简单的一个Counters
对象,里面只有一个字段_count
- 这里需要混入
ChangeNotifier
- 写一个增加的方法,然后需要调用
notifyListeners();
这个方法是通知用到Counters
对象的widget刷新用的。 -
get
方法
第三步,使用ChangeNotifierProvider
我们要监听改变要在MyApp()外面套一层,这个是全局的,于是代码如下 :
import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import './provider/counter.dart'; void main() { //runApp(new MyApp()); runApp( ChangeNotifierProvider<Counter>.value(//ChangeNotifierProvider调用value()方法,里面传出value和child value: Counter(),//value设置了默认的Counter() child: MyApp(), ) ); }
当然Provider不止提供了ChangeNotifierProvider
,还有Provider、
ListenableProvider、
ValueListenableProvider
、StreamProvider
,
具体可以看wiki.
如果想管理多个对象可以用MultiProvider
,如下:
void main() { //runApp(new MyApp()); runApp( // ChangeNotifierProvider<Counter>.value(//ChangeNotifierProvider调用value()方法,里面传出value和child // value: Counter(),//value设置了默认的Counter() // child: MyApp(), // ) MultiProvider( providers: [ ChangeNotifierProvider.value(value: Counter()), //ChangeNotifierProvider(builder: (_) => Counter()), ], child: MyApp(), ), ); }
第四步,使用Provider获取Counter的值
import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../provider/counter.dart'; class HomePage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Home"), actions: <Widget>[ FlatButton( child: Text("下一页"), onPressed: () => Navigator.push(context, MaterialPageRoute(builder: (context) { return SecondPage(); })), ), ], ), body: Center( child: Text("${Provider.of<Counter>(context).count}"),//用Provider.of<Counter>(context).count获取_count的值,Provider.of<T>(context)相当于Provider去查找它管理的Counter() ), floatingActionButton: FloatingActionButton( onPressed: () { Provider.of<Counter>(context).increment();//用Provider.of<Counter>(context).increment();调用Counter()中的increment()方法 }, child: Icon(Icons.add), ), ); } }
同样第二个页面也这样写,如下
class SecondPage extends StatelessWidget { @override Widget build(BuildContext context) { var counter = Provider.of<Counter>(context).count; return Scaffold( appBar: AppBar( title: Text("SecondPage"), ), body: Center( child: Text("${counter}"), //child: Text("${Provider.of<Counter>(context).count}"), ), floatingActionButton: FloatingActionButton( onPressed: () { Provider.of<Counter>(context).increment(); }, child: Icon(Icons.add), ), ); } }
这样,当每个页面都点击+号按钮时,_count
便会+1,同时通知并更新到使用它的地方。
完整代码写到一个页面,copy后可直接运行:
import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; main() { runApp(ChangeNotifierProvider<Counter>.value( value: Counter(), child: MyApp(), )); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: "Provider", home: HomePage(), ); } } class Counter with ChangeNotifier {//混入ChangeNotifier int _count = 0; get count => _count; void increment() { _count++; notifyListeners();//通知 } } class HomePage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Home"), actions: <Widget>[ FlatButton( child: Text("下一页"), onPressed: () => Navigator.push(context, MaterialPageRoute(builder: (context) { return SecondPage(); })), ), ], ), body: Center( child: Text("${Provider.of<Counter>(context).count}"),//用Provider.of<Counter>(context).count获取_count的值,Provider.of<T>(context)相当于Provider去查找它管理的Counter() ), floatingActionButton: FloatingActionButton( onPressed: () { Provider.of<Counter>(context).increment();//用Provider.of<Counter>(context).increment();调用Counter()中的increment()方法 }, child: Icon(Icons.add), ), ); } } class SecondPage extends StatelessWidget { @override Widget build(BuildContext context) { var counter = Provider.of<Counter>(context).count; return Scaffold( appBar: AppBar( title: Text("SecondPage"), ), body: Center( child: Text("${counter}"), //child: Text("${Provider.of<Counter>(context).count}"),//1 ), floatingActionButton: FloatingActionButton( onPressed: () { Provider.of<Counter>(context).increment();//2 }, child: Icon(Icons.add), ), ); } }
效果图: