? ? ? ?Google2019I/O大會上被谷歌推薦,原本谷歌的provide被棄用,與大部分狀態(tài)管理一樣使用了InheritedWidget?;?a target="_blank">Provider3.0
上一篇Flutter狀態(tài)管理Provider(一)
ChangeNotifierProvider()
? ? ? ?它與scoped_model差不多,不同的是它使用mixin而scoped_model使用的繼承,首先狀態(tài)類mixin一個ChangeNotifier,然后通過調(diào)用notifyListeners()來通知狀態(tài)更新。
? ? ? ?和ValueListenableProvider一樣也有兩種方式,ChangeNotifierProvider()和ChangeNotifierProvider.value(),區(qū)別在于ChangeNotifierProvider()會在銷毀時自動調(diào)用ChangeNotifier中的dispose()方法釋放一些資源。
? ? ? ?下面使用ChangeNotifierProvider()寫一個需求,與微信一樣底部4個按鈕切換界面,然后跳到二級頁面點擊二級頁面按鈕控制一級頁面的切換,先上圖:

下面上代碼:
import "package:flutter/material.dart";
import 'index_page.dart';
import 'package:provider/provider.dart';
import 'index_provider.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
//MultiProvider可以添加多個狀態(tài)管理
//包裹在MaterialApp外面,作用范圍是全局
return MultiProvider(
providers: [
// 兩種方式,這里使用ChangeNotifierProvider,因為可以自動調(diào)用dispose()方法,幫我釋放資源
ChangeNotifierProvider(builder: (_) => IndexProvider()),
// ChangeNotifierProvider.value(value: IndexProvider())
],
child: MaterialApp(
title: "Flutter Demo",
theme: ThemeData(primarySwatch: Colors.blue),
home: IndexPage(),
),
);
}
}
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'index_provider.dart';
class IndexPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Consumer<IndexProvider>(
//優(yōu)化:在狀態(tài)改變時viewpage子頁面不會走build方法
child: PageView(
physics: NeverScrollableScrollPhysics(), //禁止?jié)L動
//獲取pageController后不監(jiān)聽改變
controller: Provider.of<IndexProvider>(context,listen: false).pageController,
children: [ChildPage("第一頁"), ChildPage("第二頁"), ChildPage("第三頁")],
),
builder: (context, indexProvider, child) {
return Scaffold(
body: child,
bottomNavigationBar: BottomNavigationBar(
onTap: (index) {
indexProvider.index = index;
},
currentIndex: indexProvider.index,
items: [
BottomNavigationBarItem(icon: Icon(Icons.android), title: Text("android")),
BottomNavigationBarItem(icon: Icon(Icons.home), title: Text("home")),
BottomNavigationBarItem(icon: Icon(Icons.person), title: Text("person")),
]),
floatingActionButton: FloatingActionButton(onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => SecondPage()));
}),
);
});
}
}
class ChildPage extends StatefulWidget {
final String title;
ChildPage(this.title);
@override
_ChildPageState createState() => _ChildPageState();
}
class _ChildPageState extends State<ChildPage> with AutomaticKeepAliveClientMixin {
@override
bool get wantKeepAlive => true;
@override
void initState() {
super.initState();
print("${widget.title}: initState");
}
@override
Widget build(BuildContext context) {
super.build(context);
print("${widget.title}: build");
return Scaffold(
backgroundColor: widget.title == '第一頁'
? Colors.red.withOpacity(0.5)
: widget.title == '第二頁'
? Colors.yellow.withOpacity(0.5)
: Colors.green.withOpacity(0.5),
appBar: AppBar(title: Text(widget.title)),
body: Center(child: Text(widget.title)),
);
}
}
class SecondPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FlatButton(onPressed: () => changePageIndex(context, 0), child: Text("切換1"), color: Colors.red),
FlatButton(onPressed: () => changePageIndex(context, 1), child: Text("切換2"), color: Colors.yellow),
FlatButton(onPressed: () => changePageIndex(context, 2), child: Text("切換3"), color: Colors.green),
],
),
),
);
}
changePageIndex(context, int index) {
Provider.of<IndexProvider>(context, listen: false).index = index;
}
}
import 'package:flutter/material.dart' show ChangeNotifier, PageController;
class IndexProvider with ChangeNotifier {
int _index = 0;
PageController pageController;
int get index => _index;
set index(int value) {
_index = value;
pageController.jumpToPage(this._index);
notifyListeners();
}
IndexProvider() {
pageController = PageController(initialPage: _index);
}
//使用ChangeNotifierProvider會在銷毀時調(diào)用dispose()方法釋放資源
@override
void dispose() {
pageController?.dispose();
super.dispose();
}
}
源碼
StreamProvider
? ? ? ?它有三種使用方式,StreamProvider、StreamProvider.value()和StreamProvider.controller(),StreamProvider和StreamProvider.value()幾乎一樣,StreamProvider.controller()有一點不一樣,先上圖:

先介紹StreamProvider和StreamProvider.value()
class StreamPage extends StatefulWidget {
@override
_StreamPageState createState() => _StreamPageState();
}
class _StreamPageState extends State<StreamPage> {
StreamController _streamController;
int _count=4;
@override
void initState() {
super.initState();
_streamController = StreamController<int>();
}
@override
void dispose() {
_streamController.close();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("StreamProvider")),
// body: StreamProvider<int>.value(
// value: _streamController.stream,
// initialData: _count,
//// catchError: ,
// child: MyText(),
// ),
body: StreamProvider<int>(
builder: (_) => _streamController.stream,//builder等于value
initialData: _count,//initialData:初始化時的值,不寫為null
catchError: (BuildContext context, Object error) {
//catchError:異常時調(diào)用,返回值與StreamController范型一樣
//如果catchError不寫,當(dāng)報錯時屏幕直接爆紅出錯
print("哈哈:${error.toString()}");
return 10000;
},
child: MyText(),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
if(_count<8) _streamController.sink.add(++_count);
else _streamController.sink.addError("異常啦");
},
child: Icon(Icons.add),
),
);
}
}
class MyText extends StatelessWidget {
@override
Widget build(BuildContext context) {
final count = Provider.of<int>(context);
return Center(child: Text("$count"));
}
}
StreamProvider<T>.controller()代碼:
class StreamPage extends StatefulWidget {
@override
_StreamPageState createState() => _StreamPageState();
}
class _StreamPageState extends State<StreamPage> {
StreamController _streamController;
int _count = 4;
@override
void initState() {
super.initState();
_streamController = StreamController<int>();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("StreamProvider")),
body: StreamProvider<int>.controller(
builder: (_) => _streamController,
initialData: _count, //initialData:初始化時的值,不寫為null
catchError: (BuildContext context, Object error) {
//catchError:異常時調(diào)用,返回值與StreamController范型一樣
//如果catchError不寫,當(dāng)報錯時屏幕直接爆紅出錯
print("哈哈:${error.toString()}");
return 10000;
},
child: MyText(),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
if (_count < 8)
_streamController.sink.add(++_count);
else
_streamController.sink.addError("異常啦");//手動拋異常
},
child: Icon(Icons.add),
),
);
}
}
class MyText extends StatelessWidget {
@override
Widget build(BuildContext context) {
final count = Provider.of<int>(context);
return Center(child: Text("$count"));
}
}
? ? ? ?嗯?!看上去好像沒什么不同,但你仔細看StreamProvider<T>.controller代碼里面我沒有重寫dispose()方法,原因是StreamProvider<T>.controller會幫我們在銷毀是調(diào)用StreamController的close()方法。
進入源碼可以看到


總結(jié):
- Provider()與Provider.value()區(qū)別是Provider()有一個dispose參數(shù)傳遞一個方法可以幫助我們銷毀的時候釋放資源,它們都不提供狀態(tài)改變監(jiān)聽。
- ValueListenableProvider.value()與ValueListenableProvider()是差不多的,只支持一個狀態(tài)值。
- ChangeNotifierProvider.value()與ChangeNotifierProvider()區(qū)別是ChangeNotifierProvider()在銷毀的時候調(diào)用dispose()釋放資源,在需要使用多個狀態(tài)值時可以使用ChangeNotifierProvider。
- ListenableProvider代碼沒有寫出來,它是ChangeNotifierProvider的父類,
ListenableProvider.value()和ChangeNotifierProvider.value()功能一樣,ListenableProvider()與ValueListenableProvider()差不多,但ListenableProvider()多了一個dispose參數(shù),需要自己傳,在銷毀的時候調(diào)用釋放資源. - StreamProvider.value()和StreamProvider()基本一樣,都需要手動關(guān)閉流,而StreamProvider.controller()自動關(guān)閉流。
- MultiProvider()可以提供多個狀態(tài)。
- Provider.of<T>()用來獲取Widget樹中的狀態(tài),在使用 ValueListenableProvider、ChangeNotifierProvider和StreamProvider時Provider.of<T>()中的listen參數(shù)可以控制是否監(jiān)聽狀態(tài)改變。
- Consumer<T>()與Provider.of<T>()都是用來獲取Widget樹中的狀態(tài),但Consumer可以用在沒有context的地方,也可以用來優(yōu)化性能,使用child參數(shù)可以縮小重繪的范圍。
- 狀態(tài)管理包裹在MaterialApp()外面作用域是全局,其他作用域在本頁面或本頁的子Widget中;