flutter之provider使用与简单封装

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把当前model没有包含进去,所以需要用build来取含有此modelcontext.
  • 通过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是生成childbuilder, 不直接传入child还是因为之前说过的原因,构造对象的context需要是含有当前modelcontext才能取到此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

你可能感兴趣的:(flutter之provider使用与简单封装)