flutter中使用redux之多界面互动
在上一篇文章,我们介绍了如何在flutter中使用redux。在上一篇文章的例子中,我们使用了单界面集成redux,但是在实际项目中,我们通常涉及多个模块,每个模块涉及多个界面,那么如何使用redux整合模块,并实现模块和界面间的消息传递呢?
例子:登录
接着上篇文章,这次的例子稍微复杂一点:
目标:
- 实现登录界面,实现基本登录逻辑
- 成功之后将结果通知到其他界面
先将AppState等状态有关的移动到reducers.dart,创建LoginPage.dart
LoginPage.dart代码如下:
import 'package:flutter/material.dart'; import 'package:flutter_redux/flutter_redux.dart'; import 'package:flutter_use_redux/reducers.dart'; import 'package:redux/redux.dart'; import 'dart:async'; import 'package:flutter/cupertino.dart'; typedef Future CallLogin(String account,String pwd); class LoginPageState extends State<LoginPage>{ String _account; String _pwd; bool isLogin; @override void initState() { super.initState(); } @override void dispose() { super.dispose(); } @override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( title: new Text("登录"), ), body: new Form( onChanged: (){ print("changed"); }, onWillPop: () async{ return true; }, child: new Padding(padding: new EdgeInsets.all(10.0),child: new Column( children: <Widget>[ new TextFormField( decoration:new InputDecoration(labelText: "请输入账号") , onSaved: (String value){ _account = value; }, ///保持一下输入的账号 validator: (String value)=> value.isEmpty ? "请输入账号" : null, ), new TextFormField(decoration:new InputDecoration(labelText: "请输入密码"), onSaved: (String value)=>_pwd = value, ///保持一下输入的密码 validator: (String value)=> value.isEmpty ? "请输入密码" : null), new FormField(builder: (FormFieldState s){ return new Center( child: new RaisedButton(onPressed: () async{ FormState state = Form.of(s.context); if(state.validate()){ //如果验证成功,那么执行登录逻辑 state.save(); print("Login success $_account" ); //这里交给设置好的逻辑去执行操作 try{ await widget.callLogin(_account,_pwd); showDialog(context: context,builder: (c){ return new CupertinoAlertDialog( title: new Text("登录成功"), actions: <Widget>[ new Center( child: new RaisedButton( onPressed:(){ Navigator.of(context).pop(); Navigator.of(context).pop(); }, child: new Text("ok"), ) ) ], ); }); // Navigator.of(context).pop(); }catch(e){ showDialog(context: context,builder: (c){ return new CupertinoAlertDialog( title: new Text("登录失败$e"), actions: <Widget>[ new Center( child: new RaisedButton( onPressed:(){ Navigator.of(context).pop(); }, child: new Text("ok"), ) ) ], ); }); ///登录失败,提示一下用户 print("登录失败! $e"); } } },child: new Text("提交"),) ); }) ], ),)), ); } } class LoginPage extends StatefulWidget{ CallLogin callLogin; LoginPage({this.callLogin}); @override State<StatefulWidget> createState() { return new LoginPageState(); } }
注意:这个组件其实并没有使用redux,登录逻辑使用外部传递过来的函数来处理:
CallLogin callLogin; LoginPage({this.callLogin}); ... //执行登录逻辑 await widget.callLogin(_account,_pwd); ...
为什么要这么做呢?好处有哪些?
- 减少组件之间的依赖关系
- 减少本类的职责,将本类的职责变成只展示ui
- 本类可单独单元测试,单独工作,只要传进来一个模拟的逻辑函数
那么在哪里将登录逻辑传递进来呢?
···
routes: {
"login":(BuildContext context)=>new StoreConnector(builder: ( BuildContext context,Store<AppState> store ){ return new LoginPage(callLogin: (String account,String pwd) async{ print("正在登录,账号$account,密码:$pwd"); // 为了模拟实际登录,这里等待一秒 await new Async.Future.delayed(new Duration(milliseconds: 1000)); if(pwd != "123456"){ throw ("登录失败,密码必须是123456"); } print("登录成功!"); store.dispatch(new LoginSuccessAction(account: account)); },); }, converter: (Store<AppState> store){ return store; }),
···
在导入这个登录组件到应用程序路由上面的时候,这个时候将逻辑和Store进行对接,这样做,就将逻辑和ui彻底的分开了。
这里涉及到异步操作,那么就会遇到所谓“副作用”问题。
随着项目的增大,reducer也会越来越大,那么有什么办法可以管理呢?
这些问题我们改天再分享。
附件:
老规矩,代码在这里:
https://github.com/jzoom/flut...
如有疑问,请加qq群854192563讨论