Provider是目前Google推薦的狀態(tài)管理庫,國內(nèi)鏡像的地址,現(xiàn)在的最新版本是4.1.1惊楼,但是我的sdk版本是1.16,不支持這個最新的,所以改版本用的4.0.0
基本使用
我先是以最簡單的主題更改來做基本的更改
第一步草冈,添加Provider依賴,pubspec.yaml
dependencies:
provider: ^4.0.0
第二步棺蛛,創(chuàng)建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,
)),
),
);
}
}
一個白天模式一個夜間模式,數(shù)據(jù)需要混入ChangeNotifier
,數(shù)據(jù)更改后需要調(diào)用notifyListeners();
來發(fā)出通知敷硅。
第三步功咒,使用ChangeNotifierProvider
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context1) {
return ChangeNotifierProvider<ThemeModel>(
create: (_)=>new ThemeModel(),
child: //這里必須用Builder,因為Provider.of<ThemeViewModel>(context,listen: true)要拿這里傳入的context
Builder(
builder: (context)=>
MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: Provider.of<ThemeModel>(context,listen: true).themeData,//通過Provider.of取值
routes: <String,WidgetBuilder>{
"main/mainpage":(BuildContext context1)=>new MainPage(), //為什么這里可以拿到context1,provider就不行呢?绞蹦?力奋?因為該context中并沒有Provider
},
home: SplashPage(),
)
),
);
}
}
- 用
ChangeNotifierProvider
提供數(shù)據(jù),如果用Provider
以后取的的model就感受不到變化了 -
child
需要傳入Builder
,因為外層build
方法的context
把當前mode
l沒有包含進去幽七,所以需要用build
來取含有此model
的context
. - 通過
Provider.of
取到對應的model
第四步景殷,通過點擊按鈕,獲取model并更改模式
InkWell(
onTap: (){
Provider.of<ThemeModel>(context,listen:false).changeTheme();
},
child: Icon(Icons.cake),
)
簡單的封裝
用provider對數(shù)據(jù)更改來刷新界面時,我們需要有效的控制數(shù)據(jù)的范圍猿挚,如果全部放在最上層肯定會影響性能及數(shù)據(jù)的安全性咐旧。所以我們需要對provider寫一個通用的組件,方便我們使用绩蜻。
ProviderWidget封裝
import 'package:flutter/cupertino.dart';
import 'package:provider/provider.dart';
class ProviderWidget<T extends ChangeNotifier> 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<T> createState() => _ProviderWidgetState<T>();
}
class _ProviderWidgetState<T extends ChangeNotifier> extends State<ProviderWidget<T>> {
@override
void initState() {
super.initState();
if(widget.onReady!=null){
widget.onReady(widget.model);
}
}
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<T>(
create: (_) => widget.model,
child: Consumer<T>(//因為child
builder: widget.builder,
),
);
}
}
- 泛型T是我們model的類型
-
builder
是生成child
的builder
, 不直接傳入child
還是因為之前說過的原因铣墨,構(gòu)造對象的context
需要是含有當前model
的context
才能取到此model
,不然后報錯。 - 為了能取到
context
,我們此處用到了Consumer
办绝,合理的用Consumer
可以減少刷新范圍伊约,這個類的具體說明請參考使用Provider前你應了解Consumer
使用示例
model類
class TestModel with ChangeNotifier {
int clickNum=0;
void add() {
clickNum++;
notifyListeners();
}
}
用widget的地方直接使用
ProviderWidget<TestModel>(
model:TestModel(),
onReady:(model){
model.toString();
},
builder:(context, model, child){
return Column(
children: <Widget>[
Text("${model.clickNum}"),
RaisedButton(child:Text("add"),onPressed: (){
model.add();
})
],
);
},
)
本文完整源碼請移步github