在之前的文章中介紹了 Google 官方倉庫下的一個狀態管理 Provide。乍一看這倆玩意可能很容易就被認為是同一個東西,仔細一看,這不就差了一個字嗎,有什么區別呢。
首先,你要知道的最大的一個區別就是,Provide 被 Provider 干掉了...假如你就是用了 Provide 的,你的內心應該已經開始罵了,這不是坑爹嗎 。不過幸運的是,你要從 Provide 遷移到 Provider 並不是太難。
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不止提供了
具體可以看 wiki.
如果想管理多個對象可以用
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), ), ); } }
效果圖: