Provider是目前Google推荐的状态管理库,国内镜像的地址,现在的最新版本是4.1.1,但是我的sdk版本是1.16,不支持这个最新的,所以改版本用的4.0.0
基本使用
我先是以最简单的主题更改来做基本的更改
第一步,添加Provider依赖,pubspec.yaml
dependencies:
provider: ^4.0.0
第二步,创建Model
import 'package:flutter/material.dart';
class ThemeModel with ChangeNotifier {
ThemeData themeData = light();
bool lighted = true;
changeTheme() {
if (lighted) {
themeData = dark();
} else {
themeData = light();
}
lighted = !lighted;
notifyListeners();
}
static ThemeData light() {
return ThemeData(
textTheme: TextTheme(
subtitle1: TextStyle(
color: Colors.black,
fontSize: 16,
)),
backgroundColor: Colors.white,
brightness: Brightness.light,
primaryColor: Color(0xff248bfe),
appBarTheme: AppBarTheme(
elevation: 0,
textTheme: TextTheme(
subtitle1: TextStyle(
color: Colors.white,
fontSize: 16,
)),
),
);
}
ThemeData dark() {
return ThemeData(
textTheme: TextTheme(
subtitle1: TextStyle(
color: Colors.white,
fontSize: 16,
)),
backgroundColor: Colors.black,
brightness: Brightness.dark,
primaryColor: Color(0xff000000),
appBarTheme: AppBarTheme(
elevation: 0,
textTheme: TextTheme(
subtitle1: TextStyle(
color: Colors.white,
fontSize: 16,
)),
),
);
}
}
一个白天模式一个夜间模式,数据需要混入ChangeNotifier
,数据更改后需要调用notifyListeners();
来发出通知。
第三步,使用ChangeNotifierProvider
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context1) {
return ChangeNotifierProvider(
create: (_)=>new ThemeModel(),
child: //这里必须用Builder,因为Provider.of(context,listen: true)要拿这里传入的context
Builder(
builder: (context)=>
MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: Provider.of(context,listen: true).themeData,//通过Provider.of取值
routes: {
"main/mainpage":(BuildContext context1)=>new MainPage(), //为什么这里可以拿到context1,provider就不行呢???因为该context中并没有Provider
},
home: SplashPage(),
)
),
);
}
}
- 用
ChangeNotifierProvider
提供数据,如果用Provider
以后取的的model就感受不到变化了 -
child
需要传入Builder
,因为外层build
方法的context
把当前mode
l没有包含进去,所以需要用build
来取含有此model
的context
. - 通过
Provider.of
取到对应的model
第四步,通过点击按钮,获取model并更改模式
InkWell(
onTap: (){
Provider.of(context,listen:false).changeTheme();
},
child: Icon(Icons.cake),
)
简单的封装
用provider对数据更改来刷新界面时,我们需要有效的控制数据的范围,如果全部放在最上层肯定会影响性能及数据的安全性。所以我们需要对provider写一个通用的组件,方便我们使用。
ProviderWidget封装
import 'package:flutter/cupertino.dart';
import 'package:provider/provider.dart';
class ProviderWidget extends StatefulWidget{
final T model;
final Widget Function(BuildContext context, T value, Widget child) builder;
final Function(T) onReady;
ProviderWidget({this.model,this.onReady,this.builder});
@override
_ProviderWidgetState createState() => _ProviderWidgetState();
}
class _ProviderWidgetState extends State> {
@override
void initState() {
super.initState();
if(widget.onReady!=null){
widget.onReady(widget.model);
}
}
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) => widget.model,
child: Consumer(//因为child
builder: widget.builder,
),
);
}
}
- 泛型T是我们model的类型
-
builder
是生成child
的builder
, 不直接传入child
还是因为之前说过的原因,构造对象的context
需要是含有当前model
的context
才能取到此model
,不然后报错。 - 为了能取到
context
,我们此处用到了Consumer
,合理的用Consumer
可以减少刷新范围,这个类的具体说明请参考使用Provider前你应了解Consumer
使用示例
model类
class TestModel with ChangeNotifier {
int clickNum=0;
void add() {
clickNum++;
notifyListeners();
}
}
用widget的地方直接使用
ProviderWidget(
model:TestModel(),
onReady:(model){
model.toString();
},
builder:(context, model, child){
return Column(
children: [
Text("${model.clickNum}"),
RaisedButton(child:Text("add"),onPressed: (){
model.add();
})
],
);
},
)
本文完整源码请移步github