美文网首页Flutter圈子Flutter教程网Flutter
Flutter - Provider,我们应该学会的状态管理

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

作者: Cosecant | 来源:发表于2019-07-08 17:27 被阅读11次

    初步接触

    刚开始接触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(),
               ],
             ),
           ),
         );
    
    ///...省略一些不重要的代码
    }
    

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

    相关文章

      网友评论

        本文标题:Flutter - Provider,我们应该学会的状态管理

        本文链接:https://www.haomeiwen.com/subject/iesahctx.html