美文网首页
fish-redux(三):Component

fish-redux(三):Component

作者: 谢伟浩 | 来源:发表于2019-07-10 15:58 被阅读0次

    今天通过一个实例讲解一下Component的使用。下面完成下图的个人信息界面。该界面分为基本信息界面和工作信息界面。两块信息通过两个不同的网络请求获取的,工作信息块可以点击右边的按钮来展开或收缩详情,点击右下角可刷新基本信息界面。

    捕获.PNG

    1. 不使用component的实现

    • 新建info_page文件夹,实现page
    class InfoPage extends Page<PageState, Map<String, dynamic>> {
      InfoPage() :super(
        initState: initState,
        effect: buildEffect(),
        view: buildView,
        reducer: buildReducer(),
      );
    }
    

    接下来添加state,action,effect,view,reducer文件

    • state.dart
    class PageState implements Cloneable<PageState> {
    
      String avatar;
      String name;
      int age;
    
      String company;
      String job;
      String detail;
    
      PageState();
    
      @override
      PageState clone() {
        return PageState()..avatar = avatar
        ..name = name
          ..age = age
          ..company = company
          ..job = job
          ..detail = detail
        ;
      }
    
    }
    
    PageState initState(Map<String, dynamic> params) {
      return PageState();
    }
    
    • action.dart
    enum PageAction {
    updateBase, //更新基本信息
    updateJob, //更新工作信息
    onRefresh //刷新事件
    }
    
    class PageActionCreator {
    
      static Action updateBaseAction(String avatar, String name, int age) {
        return Action(
          PageAction.updateBase,
          payload: <String, dynamic>{'avatar': avatar, 'name': name, 'age': age},
        );
      }
    
      static Action updateJobAction(String company, String job, String detail) {
        return Action(
          PageAction.updateJob,
          payload: <String, dynamic>{'company': company, 'job': job, 'detail': detail},
        );
      }
    
      static Action onRefreshAction() {
        return Action(
          PageAction.onRefresh,
        );
      }
    
    }
    
    • view.dart
    Widget buildView(PageState state,  dispatch, ViewService viewService) {
      print('_buildView');
      return Scaffold(
        appBar: AppBar(title: Text('个人信息'),),
    
        body: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: <Widget>[
            Card(
              margin: EdgeInsets.all(15),
              child: Padding(
                padding: EdgeInsets.all(20),
                child: Row(
                  children: <Widget>[
                    FadeInImage.assetNetwork(
                      placeholder: 'images/timg.jpg',
                      image: state.avatar ?? '',
                      height: 60,
                      width: 60,
                    ),
                    Padding(
                      padding: EdgeInsets.only(left: 30),
                      child: Column(
                        mainAxisSize: MainAxisSize.min,
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: <Widget>[
                          Text(state.name ?? ''),
                          Padding(
                            padding: EdgeInsets.only(top: 10),
                            child: Text(state.age == null ? '' : state.age.toString()),
                          )
                        ],
                      ),
                    ),
                  ],
                ),
              ),
            ),
    
            Card(
              margin: EdgeInsets.all(15),
              child: Padding(
                padding: EdgeInsets.all(20),
                child: MyExpand(
                  <Widget>[
                    Text('公司:${state.company}'),
                    Padding(
                      padding: EdgeInsets.only(top: 10),
                      child: Text('职位:${state.job}'),
                    ),
                    Padding(
                      padding: EdgeInsets.only(top: 10),
                      child: Text('详情:${state.detail}'),
                    )
                  ],
                ),
              ),
            ),
    
          ],
        ),
    
        floatingActionButton: FloatingActionButton(
            child: Icon(Icons.refresh),
            onPressed: (){
              dispatch(PageActionCreator.onRefreshAction());
            }
            ),
      );
    }
    
    • 这里MyExpand 是我们自定义的一个可收缩伸展的控件
    import 'package:flutter/material.dart';
    
    class MyExpand extends StatefulWidget {
    
      final List<Widget> widgets;
    
    //  MyExpand(this.widgets):super(){
    //    assert(widgets != null && widgets.length > 2);
    //  };
    
      MyExpand(this.widgets);
    
      @override
      State<StatefulWidget> createState() {
        // TODO: implement createState
        return MyExpandState();
      }
    }
    
    class MyExpandState extends State<MyExpand> {
    
      bool expand = false;
      List<Widget> widgets = <Widget>[];
    
      @override
      void initState() {
        super.initState();
    //    widgets = widget.widgets;
      }
    
      @override
      Widget build(BuildContext context) {
        if(expand == true) {
          widgets.clear();
          widgets.addAll(widget.widgets);
        } else {
          widgets.clear();
          widgets.add(widget.widgets[0]);
          widgets.add(widget.widgets[1]);
        }
        return Stack(
          children: <Widget>[
            Column(
              mainAxisSize: MainAxisSize.min,
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: widgets,
            ),
            Align(
              alignment: Alignment.topRight,
              child: IconButton(
                icon: Icon(expand == true ? Icons.keyboard_arrow_up : Icons.keyboard_arrow_down),
                onPressed: () {
                  setState(() {
                    expand = !expand;
                  });
                },
              ),
            ),
    
          ],
        );
      }
    
    }
    
    • effect.dart
    Effect<PageState> buildEffect() {
      return combineEffects(<Object, Effect<PageState>>{
        Lifecycle.initState: _init,
        PageAction.onRefresh: _onRefresh,
      });
    }
    
    
    void _init(Action action, Context<PageState> ctx) {
      println("Effect: _init");
    
      _getBaseInfo().then((_){
        ctx.dispatch(PageActionCreator.updateBaseAction(_.avatar, _.name, _.age));
      });
    
      _getJobInfo().then((_){
        ctx.dispatch(PageActionCreator.updateJobAction(_.company, _.job, _.detail));
      });
    
    
    }
    
    Future<BaseInfo> _getBaseInfo() async { //模拟通过网络获取基本信息
      return BaseInfo('http://b-ssl.duitang.com/uploads/item/201510/08/20151008192345_uPC5U.jpeg', '小恐龙', 24);
    }
    
    Future<JobInfo> _getJobInfo() async { //模拟通过网络获取工作信息
      return JobInfo('xxx传媒有限公司', '设计师', 'woshixiangqing');
    }
    
    void _onRefresh(Action action, Context<PageState> ctx) {
      _getBaseInfo().then((_){
        ctx.dispatch(PageActionCreator.updateBaseAction(_.avatar, _.name, _.age));
      });
    }
    
    • 添加两个model类
    class BaseInfo {
      String avatar;
      String name;
      int age;
      BaseInfo(this.avatar, this.name, this.age);
    }
    
    class JobInfo {
      String company;
      String job;
      String detail;
      JobInfo(this.company, this.job, this.detail);
    }
    

    实现效果如下


    2. 使用component的实现

    下面使用Component进行改造

    2.1 改造基本信息

    2.1.1 添加BaseComponent

    在info_page 文件夹下面添加base_component文件夹,由于基本信息块只显示信息没有动作事件,只需添加state, component, view三个文件

    • Component与Page类似,实际上Page是Component的子类
    class BaseComponent extends Component<BaseState> {
    
      BaseComponent():super(
        view: buildView,
      );
    }
    
    • BaseState只有头像,名称,年龄三个属性
    class BaseState implements Cloneable<BaseState> {
    
      String avatar;
      String name;
      int age;
    
      BaseState();
    
      @override
      BaseState clone() {
        return BaseState()..avatar = avatar
        ..name = name
          ..age = age
        ;
      }
    
    }
    
    
    BaseState initState(Map<String, dynamic> params) {
      return BaseState();
    }
    
    • view从Page的view中抽取
    Widget buildView(
      BaseState state,
      Dispatch dispatch,
      ViewService viewService,
    ) {
      return Card(
        margin: EdgeInsets.all(15),
        child: Padding(
          padding: EdgeInsets.all(20),
          child: Row(
            children: <Widget>[
              FadeInImage.assetNetwork(
                placeholder: 'images/timg.jpg',
                image: state.avatar ?? '',
                height: 60,
                width: 60,
              ),
              Padding(
                padding: EdgeInsets.only(left: 30),
                child: Column(
                  mainAxisSize: MainAxisSize.min,
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: <Widget>[
                    Text(state.name ?? ''),
                    Padding(
                      padding: EdgeInsets.only(top: 10),
                      child: Text(state.age == null ? '' : state.age.toString()),
                    )
                  ],
                ),
              ),
            ],
          ),
        ),
      );
    }
    

    2.1.2 实现BaseComponent与Page的连接

    在info_page下的state文件实现BaseConnector,BaseConnector继承自ConnOp,复写get和set方法,get方法数据从page传到Component,set方法数据从Component传到page。

    class BaseConnector extends ConnOp<PageState, BaseState> {
      @override
      BaseState get(PageState page) {
        final BaseState sub = BaseState();
        sub.avatar = page.avatar;
        sub.name = page.name;
        sub.age = page.age;
        return sub;
      }
    
      @override
      void set(PageState page, BaseState sub) {
        page.avatar = sub.avatar;
        page.name = sub.name;
        page.age = sub.age;
      }
    }
    

    在info_page的page文件下添加dependencies

    class InfoPage extends Page<PageState, Map<String, dynamic>> {
      InfoPage() :super(
        initState: initState,
        effect: buildEffect(),
        view: buildView,
        reducer: buildReducer(),
        dependencies: Dependencies<PageState>(
            slots: <String, Dependent<PageState>>{
              'base': BaseConnector() + BaseComponent()
            }),
      );
    }
    

    在info_page的view文件下,将刚刚基本信息块用下面的代码代替

    viewService.buildComponent('base'),
    

    重启应用,效果还是和原来一样

    2.2 改造工作信息块

    与2.1基本相同
    未完待续...

    相关文章

      网友评论

          本文标题:fish-redux(三):Component

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