Google2019I/O大会上被谷歌推荐,原本谷歌的provide被弃用,与大部分状态管理一样使用了InheritedWidget。基于Provider3.0
上一篇Flutter状态管理Provider(一)
ChangeNotifierProvider()
它与scoped_model差不多,不同的是它使用mixin而scoped_model使用的继承,首先状态类mixin一个ChangeNotifier,然后通过调用notifyListeners()来通知状态更新。
和ValueListenableProvider一样也有两种方式,ChangeNotifierProvider()和ChangeNotifierProvider.value(),区别在于ChangeNotifierProvider()会在销毁时自动调用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可以添加多个状态管理
//包裹在MaterialApp外面,作用范围是全局
return MultiProvider(
providers: [
// 两种方式,这里使用ChangeNotifierProvider,因为可以自动调用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>(
//优化:在状态改变时viewpage子页面不会走build方法
child: PageView(
physics: NeverScrollableScrollPhysics(), //禁止滚动
//获取pageController后不监听改变
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会在销毁时调用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:异常时调用,返回值与StreamController范型一样
//如果catchError不写,当报错时屏幕直接爆红出错
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:异常时调用,返回值与StreamController范型一样
//如果catchError不写,当报错时屏幕直接爆红出错
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会帮我们在销毁是调用StreamController的close()方法。
进入源码可以看到
总结:
- Provider()与Provider.value()区别是Provider()有一个dispose参数传递一个方法可以帮助我们销毁的时候释放资源,它们都不提供状态改变监听。
- ValueListenableProvider.value()与ValueListenableProvider()是差不多的,只支持一个状态值。
- ChangeNotifierProvider.value()与ChangeNotifierProvider()区别是ChangeNotifierProvider()在销毁的时候调用dispose()释放资源,在需要使用多个状态值时可以使用ChangeNotifierProvider。
- ListenableProvider代码没有写出来,它是ChangeNotifierProvider的父类,
ListenableProvider.value()和ChangeNotifierProvider.value()功能一样,ListenableProvider()与ValueListenableProvider()差不多,但ListenableProvider()多了一个dispose参数,需要自己传,在销毁的时候调用释放资源. - StreamProvider.value()和StreamProvider()基本一样,都需要手动关闭流,而StreamProvider.controller()自动关闭流。
- MultiProvider()可以提供多个状态。
- Provider.of<T>()用来获取Widget树中的状态,在使用 ValueListenableProvider、ChangeNotifierProvider和StreamProvider时Provider.of<T>()中的listen参数可以控制是否监听状态改变。
- Consumer<T>()与Provider.of<T>()都是用来获取Widget树中的状态,但Consumer可以用在没有context的地方,也可以用来优化性能,使用child参数可以缩小重绘的范围。
- 状态管理包裹在MaterialApp()外面作用域是全局,其他作用域在本页面或本页的子Widget中;