为了账号安全,请及时绑定邮箱和手机立即绑定

TodoList深入Flutter状态管理(中篇-redux)

标签:
Android

今天的任务是将昨的代码用redux整理一下。
在此之前先说统一几个名词在本文中的叫法。本文源码见github

store       : 仓库 dispatch    : 分发 action      : 动作 reducer     : 分解器 connector   : 连接器 provider    : 供应器 converter   : 转换器 builder     : 构造器 依赖: flutter_redux: ^0.5.3 复制代码

1.初始项目的Redux化

大家应该都还记得初始项目吧,下面是它的梳理图,磨刀不误砍柴工。
我打算从它开始入手,向你简单介绍redux是什么?


1



1.1:分析行为及变化

很简单,行为是点击,变化是数字的自增长。
关于reducer,不想是什么纯不纯,在我看来它就是一个独立的逻辑单元,
不依靠外界存活,在逻辑上便可存在:给定一个输入就会返回一个预期的输出

enum Actions {   increment//定义增加行为 } //使用counterReducer将行为从类中抽离分解,成为独立逻辑单元 int counterReducer(int input, dynamic action) {   var output;   switch(action){     case Actions.increment:       output=input+1;       break;   }   return output; } 复制代码

1.2:新建ReduxPage组件

redux核心之一便是Store,是一个仓库用来储存,供应,分发。
返回一个仓库提供器,它是一个Widget,需要store和child属性。

class ReduxPage extends StatelessWidget {   final Store<int> store;   ReduxPage({Key key, this.store,}) : super(key: key);      return  StoreProvider<int>(     store: store,     child: child,   ); } 复制代码

1.3:现在的焦点在于孩子是如何构建的

这里为了看得清楚些,将countTextfab两个与状态有关的组件抽离一下

var child = MaterialApp(   theme: ThemeData(     primarySwatch: Colors.blue,   ),   home: Scaffold(     appBar: AppBar(       title: Text("Flutter Redux Demo"),     ),     body: Center(       child: Column(         mainAxisAlignment: MainAxisAlignment.center,         children: [           Text(             'You have pushed the button this many time'           ),           countText//显示数字的Text         ],       ),     ),     floatingActionButton: fab,//点击的按钮   ), ); 复制代码
  • 显示数字的Text:countText

如果你想要到仓库拿东西,你需要什么?钥匙呗。StoreConnector仓库连接器就是这把钥匙
converter转换器中回调出store对象,你就可以通过store去取值了,通过构造器生成组件返回出去

var countText= StoreConnector<int, String>(   converter: (store) => store.state.toString(),//转换器,获取仓库,从仓库拿值   builder: (context, count) {//构造器,构建Widget     return Text(       count,       style: Theme.of(context).textTheme.display1,     );   }, ); 复制代码
  • 处理动作的按钮

处理动作也是需要仓库,使用进行分发(dispatch)相应动作(action)
在构造器中,你就可以使用该动作逻辑了。

var fab= StoreConnector<int, VoidCallback>(   converter: (store) {     return () => store.dispatch(Actions.increment);//分发动作   },   builder: (context, callback) {//构造器,使用动作逻辑     return FloatingActionButton(       onPressed: callback,       tooltip: 'Increment',       child: Icon(Icons.add),     );   }, ); 复制代码

这里将动作方成为攻方,响应方成为受方,下面的图阐释了两方Widget如何构建




1.4:仓库对象的构建

可以说这核心便是仓库store了,看一下对象如何生成

void main() {   final store =  Store<int>(counterReducer, initialState: 0);   runApp(ReduxPage(     store: store,   )); } 复制代码

2.redux优势

也许你会说:"感觉也不咋地啊,感觉好麻烦。"

2.1:增加一个功能时

比如我想要点一下加10该怎么办?使用redux你需要定义一个行为,及响应。
在行为分发时修改行为即可。也许你说我不用redux,改行就行了。如果逻辑非常多怎么办
之后又要改回来怎么办?抽象出一个行为来管理逻辑切换起来是非常方便的
而且想要修改直接在reducer中进行即可,就避免了污染封装的组件源码。

enum Actions{   increment,   increment10 } int counterReducer(int input, dynamic action) {   var output;   switch(action){     case Actions.increment:       output=input+1;       break;     case Actions.increment10:       output=input+10;       break;   }   return output; } var fab= StoreConnector<int, VoidCallback>(       converter: (store) {         return () => store.dispatch(Actions.increment10);       }, 复制代码

2.2:全局的状态共享

另一个界面如何轻松享有上个界面的数据,这是个很大的问题。
当然可以通过构造传参,但这显然十分麻烦,不仅乱,而且还要接收个参数。


1


import 'package:flutter/material.dart'; import 'package:flutter_redux/flutter_redux.dart'; class SecondPage extends StatelessWidget {   SecondPage({Key key}) : super(key: key);   @override   Widget build(BuildContext context) {     var text = StoreConnector<int, String>(//直接从状态取值       converter: (store) => store.state.toString(),       builder: (context, count) {         return Text(           count.toString(),           style: Theme.of(context).textTheme.display1,         );       },     );     return Scaffold(       appBar: AppBar(         title: Text("SecondPage"),       ),       body: Align(         alignment: Alignment.topCenter,         child: text,       ),     );   } } 复制代码
  • ReduxPage中为文字添加点击跳转到SecondPage

Builder _skipToSecondPage(StoreConnector<int, String> countText) {   return Builder(     builder: (context) =>         InkWell(child: countText, onTap: () {           Navigator.of(context)               .push(MaterialPageRoute(builder: (BuildContext context) {             return SecondPage();           }));         },),   ); } 复制代码

3.对昨天TodoList的改造

还是一样的界面效果。


16c2c4195288537e?imageslim



3.1:定义Todo描述类
class Todo {   String sth; //待做事项   bool done;//是否已完成   Todo({this.sth, this.done}); //是否已做完 } 复制代码

3.2:定义状态类和动作及变化

昨天分析了有个有三个状态和四个动作

class TodoState {   List<Todo> todos; //列表数据   String text; //当前输入文字   ShowType showType;//显示类型   TodoState({this.todos, this.text, this.showType}); //显示类型 } enum Acts {   add, //添加到todo   selectAll, //筛选所有   selectTodo, //筛选待完成   selectDone, //筛选已完成 } TodoState todoReducer(TodoState input, dynamic action) {   switch (action) {     case Acts.add:       if (input.text != null && input.text != "") {         input.todos.add(Todo(sth: input.text, done: false));         input.text = "";       }       break;     case Acts.selectAll:       input.showType=ShowType.all;       break;     case Acts.selectTodo:       input.showType=ShowType.todo;       break;     case Acts.selectDone:       input.showType=ShowType.done;       break;   }   return input; } final todoListStore = Store<TodoState>(todoReducer,       initialState://初始状态       TodoState(todos: <Todo>[], text: "", showType: ShowType.all)); 复制代码

3.3:组件的封装

和上面一样,使用StoreConnector来从仓库拿资源,只不过这里资源是TodoState对象
动作的话,作为攻方,依旧是那回调来执行相应动作。

class TodoList extends StatefulWidget {   final Store<TodoState> store;   TodoList({     Key key,     this.store,   }) : super(key: key);   @override   _TodoListState createState() => _TodoListState(); } class _TodoListState extends State<TodoList> {   @override   Widget build(BuildContext context) {     var textField= StoreConnector<TodoState, TodoState>(       converter: (store) =>store.state,//转换器,获取仓库,从仓库拿值       builder: (context, state) {//构造器,构建Widget         return TextField(           controller: TextEditingController(text: state.text),           keyboardType: TextInputType.text,           textAlign: TextAlign.start,           maxLines: 1,           cursorColor: Colors.black,           cursorWidth: 3,           style: TextStyle(               fontSize: 16, color: Colors.lightBlue, backgroundColor: Colors.white),           decoration: InputDecoration(             filled: true,             fillColor: Colors.white,             hintText: '添加一个待办项',             hintStyle: TextStyle(color: Colors.black26, fontSize: 14),             contentPadding: EdgeInsets.only(left: 14.0, bottom: 8.0, top: 8.0),             focusedBorder: OutlineInputBorder(               borderSide: BorderSide(color: Colors.white),               borderRadius: BorderRadius.only(                   topLeft: Radius.circular(10), bottomLeft: Radius.circular(10)),             ),             enabledBorder: UnderlineInputBorder(               borderSide: BorderSide(color: Colors.white),               borderRadius: BorderRadius.only(                   topLeft: Radius.circular(10), bottomLeft: Radius.circular(10)),             ),           ),          onChanged: (str){            state.text=str;          },         );       },     );     var btn = StoreConnector<TodoState, VoidCallback>(       converter:(store) {       return () => store.dispatch(Acts.add);//分发动作     },       builder: (context, callback) {         return RaisedButton(           child: Icon(Icons.add),           padding: EdgeInsets.zero,           onPressed: (){             callback();             FocusScope.of(context).requestFocus(FocusNode());           });       },     );     var inputBtn = Row(       mainAxisAlignment: MainAxisAlignment.center,       children: <Widget>[         Container(           child: textField,           width: 200,         ),         ClipRRect(           borderRadius: BorderRadius.only(               topRight: Radius.circular(10), bottomRight: Radius.circular(10)),           child: Container(             child: btn,             width: 36,             height: 36,           ),         ),       ],     );     var listInfo = [       ["全部", Acts.selectAll],       ["已完成", Acts.selectDone],       ["未完成", Acts.selectTodo],     ];     var op = Row(//操作按钮       mainAxisAlignment: MainAxisAlignment.spaceEvenly,       children: listInfo.map((e) {         return StoreConnector<TodoState, VoidCallback>(           converter: (store) {             return () => store.dispatch(e[1]);           },           builder: (context, callback) {             return RaisedButton(               onPressed: callback,               child: Text(e[0]),               color: Colors.blue,             );           },         );       }).toList(),     );     var listView = StoreConnector<TodoState, TodoState>(         converter: (store) => store.state, //转换器,获取仓库,从仓库拿值         builder: (context, state) {           var result;           //构造器,构建Widget           switch(state.showType){             case ShowType.all:               result= formList(state.todos);               break;             case ShowType.todo:               result= formList(List.of( state.todos.where((e)=>!e.done)));               break;             case ShowType.done:               result= formList(List.of( state.todos.where((e)=>e.done)));               break;           }          return result;         });     return StoreProvider<TodoState>(       store: widget.store,       child: Column(         children: <Widget>[inputBtn, op, Expanded(child: listView)],       ),     );   }   Widget formList(List<Todo> todos) {     return ListView.builder(       itemCount: todos.length,       padding: EdgeInsets.all(8.0),       itemExtent: 50.0,       itemBuilder: (BuildContext context, int index) {         var key = todos[index].sth;         var value = todos[index].done;         var text = Align(           child: Text(             key,             style: TextStyle(                 decorationThickness: 3,                 decoration:                     value ? TextDecoration.lineThrough : TextDecoration.none,                 decorationColor: Colors.blue),           ),           alignment: Alignment.centerLeft,         );         return Card(           child: Row(             children: <Widget>[               Checkbox(                 onChanged: (b) {                   todos[index].done = b;                   setState(() {});                 },                 value: todos[index].done,               ),               text             ],           ),         );       },     );   } }




点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消