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<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提供数据,如果用Provider以后取的的model就感受不到变化了
- 
child需要传入Builder,因为外层build方法的context把当前model没有包含进去,所以需要用build来取含有此model的context.
- 通过Provider.of取到对应的model
第四步,通过点击按钮,获取model并更改模式
InkWell(
          onTap: (){
            Provider.of<ThemeModel>(context,listen:false).changeTheme();
          },
          child: Icon(Icons.cake),
        )
简单的封装
用provider对数据更改来刷新界面时,我们需要有效的控制数据的范围,如果全部放在最上层肯定会影响性能及数据的安全性。所以我们需要对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还是因为之前说过的原因,构造对象的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










