Flutter - Provider,我们应该学会的状态管理

初步接触

刚开始接触Provider的时候,感觉比较懵逼,因为之前连RN的状态管理Redux都没搞明白,主要是觉得太繁琐了吧。在使用Provider之前,看过一些BLOC,ScopeModel等框架,但是感觉起来还是Provider更好用吧。Provider也是谷歌官方所推荐的。

学习Provider,网上看到最多的是Counter计数器。这里我们不讲计数器,我们谈一个项目中比较实际一点的的LoaderContainer (加载器)。

首先,LoaderContainer是我编写的一个加载器类

  • LoaderContainer 加载器组件,声明了加载器状态值,加载器在不同状态下呈现的视图。
import 'package:flutter/material.dart';
import 'package:project_name/core/widget/rounded_button.dart';
import 'package:project_name/data/app/app_constants.dart';

enum LoaderState { NoAction, Loading, Succeed, Failed, NoData }

class LoaderContainer extends StatefulWidget {
  LoaderContainer({
    Key key,
    @required this.contentView,
    this.loadingView,
    this.errorView,
    this.emptyView,
    @required this.loaderState,
    this.onReload,
  }) : super(key: key);

  final LoaderState loaderState;
  final Widget loadingView;
  final Widget errorView;
  final Widget emptyView;
  final Widget contentView;
  final Function onReload;

  @override
  State createState() => _LoaderContainerState();
}

class _LoaderContainerState extends State<LoaderContainer> {
  @override
  Widget build(BuildContext context) {
    Widget currentWidget;
    switch (widget.loaderState) {
      case LoaderState.Loading:
        currentWidget = widget.loadingView ?? _ClassicalLoadingView();
        break;
      case LoaderState.Failed:
        currentWidget = widget.errorView ??
            _ClassicalErrorView(
              onReload: () => widget.onReload(),
            );
        break;
      case LoaderState.NoData:
        currentWidget = widget.emptyView ?? _ClassicalNoDataView();
        break;
      case LoaderState.Succeed:
      case LoaderState.NoAction:
        currentWidget = widget.contentView;
        break;
    }
    return currentWidget;
  }
}

class _ClassicalLoadingView extends StatelessWidget {
  @override
  Widget build(BuildContext context) => Center(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            CircularProgressIndicator(
              valueColor: AlwaysStoppedAnimation(AppConstants.themeColor),
            ),
            Padding(
              padding: const EdgeInsets.only(top: 15),
              child: Text(
                '正在拼命加载中...',
                style: TextStyle(
                  fontSize: 13,
                  color: Color(0xff999999),
                ),
              ),
            ),
          ],
        ),
      );
}

class _ClassicalErrorView extends StatelessWidget {
  _ClassicalErrorView({@required this.onReload}) : super();

  final Function onReload;

  @override
  Widget build(BuildContext context) => Center(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Image.asset(
              'asset/img/ic_default_load_data_failed.png',
              width: 80,
              height: 80,
              color: Color(0xff999999),
            ),
            Padding(
              padding: const EdgeInsets.only(top: 12),
              child: Text(
                '加载失败,请稍后点击重试',
                style: TextStyle(
                  fontSize: 13,
                  color: Color(0xff999999),
                ),
              ),
            ),
            Padding(
              padding: const EdgeInsets.only(top: 20),
              child: RoundedButton(
                bgColor: AppConstants.themeColor,
                onPressed: onReload,
                child: Padding(
                  padding:
                      const EdgeInsets.symmetric(vertical: 5, horizontal: 20),
                  child: Text(
                    '重新加载',
                    style: TextStyle(color: Theme.of(context).buttonColor),
                  ),
                ),
              ),
            ),
          ],
        ),
      );
}

class _ClassicalNoDataView extends StatelessWidget {
  @override
  Widget build(BuildContext context) => Center(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Image.asset(
              'asset/img/ic_default_load_data_failed.png',
              width: 80,
              height: 80,
              color: Color(0xff999999),
            ),
            Padding(
              padding: const EdgeInsets.only(top: 0),
              child: Text(
                '暂无相关数据 /(ㄒoㄒ)/~~',
                style: TextStyle(
                  fontSize: 13,
                  color: Color(0xff999999),
                ),
              ),
            ),
          ],
        ),
      );
}
  • 准备一个加载器状态的Model,在Provider中,要求变化的值能处理相关变化,必须继承相关类,如:ChangeNotifier(常用)等。
import 'package:flutter/cupertino.dart';
import 'package:project_name/core/widget/loader_container.dart'
    as LoaderStater;

/// 加载器的状态Model类
class LoaderStateModel extends ChangeNotifier {
  LoaderStater.LoaderState _state = LoaderStater.LoaderState.Loading;

  /// 构造函数
  LoaderStateModel([this._state]) {
    this._state = this._state ?? LoaderStater.LoaderState.Loading;
  }

  /// 修改状态
  ///
  /// [state]  LoaderState的取值
  void changeState(LoaderStater.LoaderState state) {
    _state = state;
    notifyListeners();
  }

  /// 获取当前加载器的状态
  LoaderStater.LoaderState get state => _state;
}
  • 页面中处理加载器状态, 首页要给Page包装一个Provider,这里写在跳转这个页面的方法处。
return MultiProvider(providers: [
        ChangeNotifierProvider<LoaderStateModel>.value(
            value: LoaderStateModel(LoaderState.Succeed))
      ], child: PresaleOrderDetailInfoPage());
  • 之前包装过Provider,那么在PresaleOrderDetailInfoPage中就能接收到Provider共享的状态值了。一般情况下,我们接收值使用Provider.of<xxx>(context)来接收,如果只是接收值可以考虑使用Consumer或其相关扩展函数。
class PresaleOrderDetailInfoPage extends StatefulWidget {
 PresaleOrderDetailInfoPage({Key key}) : super(key: key);

 @override
 State<StatefulWidget> createState() => _PresaleOrderDetailInfoPageState();
}

class _PresaleOrderDetailInfoPageState
   extends BasePageState<PresaleOrderDetailInfoPage>
   with TickerProviderStateMixin {
/// 加载状体模型
 LoaderStateModel _loaderStateModel;

 /// 调用网络获取订单详情信息
 void _getOrderDetailInfo() async {
   _loaderStateModel.changeState(LoaderState.Loading);
   Future.delayed(Duration(seconds: 3), () { //模拟数据调用
     _loaderStateModel.changeState(LoaderState.Succeed);
   });
 }

@override
 void didChangeDependencies() {
   super.didChangeDependencies();

   /// 初始化加载器的状态
   final loaderStateModel =
       Provider.of<LoaderStateModel>(context);
   if (this._loaderStateModel != loaderStateModel) {
     this._loaderStateModel = loaderStateModel;
     Future.microtask(() => _getOrderDetailInfo()); //必须要这么做,不然可能会抛出异常,使用Future.microtask执行初始化任务
   }
 }
@override
 Widget build(BuildContext context) => MaterialApp(
       theme: ThemeData(primaryColor: Colors.white),
       home: Scaffold(
         appBar: _buildAppBar(),
         backgroundColor: AppConstants.backgroundColor,
         body: _buildContentView(context),
       ),
     );

 /// 构建AppBar视图
 Widget _buildAppBar() => AppBar(
     backgroundColor: Colors.white,
     title: Text('订单详情', style: TextStyle(color: Color(0xff333333))),
     centerTitle: true,
     elevation: 0.5,
     leading: FlatButton(
       child: Image.asset(_imageAssetPaths['NavigationBack'],
           fit: BoxFit.contain, color: Color(0xff333333)),
       padding: const EdgeInsets.all(20),
       shape: CircleBorder(),
       onPressed: () => finish(),
     ));

 /// 构建页面主视图
 Widget _buildContentView(BuildContext context) => Consumer<LoaderStateModel>( //使用Consumer处理共享值
       builder: (context, state, _) => LoaderContainer(
         loaderState: state.state,
         onReload: _getOrderDetailInfo,
         contentView: Column(
           crossAxisAlignment: CrossAxisAlignment.stretch,
           children: <Widget>[
             Expanded(
               child: SingleChildScrollView(
                 physics: BouncingScrollPhysics(),
                 child: Column(
                   crossAxisAlignment: CrossAxisAlignment.stretch,
                   children: <Widget>[
                     _buildOrderHeaderView(),
                     _buildRefundProcessView(),
                     _buildReceiverAddressView(),
                     _buildGoodsInfoContainerView(),
                     _buildStaticsInfoContentView(),
                   ],
                 ),
               ),
             ),
             _buildOperatorContainerView(),
           ],
         ),
       ),
     );

///...省略一些不重要的代码
}

最后,希望各位学习这个框架的朋友们加油,我也还在更加熟悉这个框架。目前来说,已经尝到这个框架所带来的甜处了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,539评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,594评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,871评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,963评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,984评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,763评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,468评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,357评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,850评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,002评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,144评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,823评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,483评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,026评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,150评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,415评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,092评论 2 355

推荐阅读更多精彩内容