本文详细讲述怎样在flutter中集成和使用redux,关于redux的概念、原理和实现,读者可自行百度,本文不作累述。git
https://pub.flutter-io.cn/pac...github
修改项目根目录下的pubsepc.yaml,添加依赖redux
flutter_redux: ^0.5.2
先使用flutter命令app
flutter create flutter_use_redux
建立一个flutter默认的Helloworld项目less
原来的代码以下,去掉了注释,以便显得更短...ide
import 'package:flutter/material.dart'; void main() => runApp(new MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return new MaterialApp( title: 'Flutter Demo', theme: new ThemeData( primarySwatch: Colors.blue, ), home: new MyHomePage(title: 'Flutter Demo Home Page'), ); } } class MyHomePage extends StatefulWidget { MyHomePage({Key key, this.title}) : super(key: key); final String title; @override _MyHomePageState createState() => new _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { int _counter = 0; void _incrementCounter() { setState(() { _counter++; }); } @override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( title: new Text(widget.title), ), body: new Center( child: new Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ new Text( 'You have pushed the button this many times:', ), new Text( '$_counter', style: Theme.of(context).textTheme.display1, ), ], ), ), floatingActionButton: new FloatingActionButton( onPressed: _incrementCounter, tooltip: 'Increment', child: new Icon(Icons.add), ), ); } }
这一步的目标是将原来的程序改为用redux来实现。
咱们知道redux是整个应用程序使用惟一一个store来管理整个应用程序的状态的。那么这里咱们先定义一个store,上述的程序里面的“状态”为一个计数器,因此咱们先定义一个int类型的状态,初始值为0:函数
import 'package:flutter_redux/flutter_redux.dart'; import 'package:redux/redux.dart'; int mainReducer(int state, dynamic action){ return state; } void main() { Store<int> store = new Store<int>(mainReducer,initialState: 0); runApp(new MyApp()); }
这里的mainReducer为应用程序的主reducer,咱们知道,reducer是一个函数,它接受一个状态(State),而且“必须”返回一个状态(State),若是状态没有变化,那么应该返回原来的状态。ui
传递State
按照上述程序的逻辑,当点击下面的按钮的时候,计数器+1。this
那么咱们首先咱们须要将这个计数器的状态传递给中间的Text,当点击按钮的时候,须要修改状态。spa
在Redux中修改状态,其实是使用Store.dispatch这个方法,分发一个“Action",由reducer这个函数对”Action“进行解析,并返回新的State。
传递状态,使用StoreConnector这个Widget
继续修改咱们的程序:
enum Actions{ Increase, } int mainReducer(int state, dynamic action){ if(Actions.Increase==action){ return state + 1; } return state; } void main() { Store<int> store = new Store<int>(mainReducer,initialState: 0); runApp(new MyApp(store: store,)); } class MyApp extends StatelessWidget { final Store<int> store; MyApp({this.store}); @override Widget build(BuildContext context) { return new StoreProvider(store: store, child: new MaterialApp( title: 'Flutter Demo', theme: new ThemeData( primarySwatch: Colors.blue, ), home: new StoreConnector(builder: (BuildContext context,int counter){ return new MyHomePage(title: 'Flutter Demo Home Page',counter:counter); }, converter: (Store<int> store){ return store.state; }) , )); } } class MyHomePage extends StatelessWidget { MyHomePage({Key key, this.title,this.counter}) : super(key: key); final String title; final int counter; @override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( title: new Text(title), ), body: new Center( child: new Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ new Text( 'You have pushed the button this many times:', ), new Text( '$counter', style: Theme .of(context) .textTheme .display1, ), ], ), ), floatingActionButton: new FloatingActionButton( onPressed: () { }, tooltip: 'Increment', child: new Icon(Icons.add), ), ); } }
这步跨度有点大,解释一下:
if(Actions.Increase==action){ return state + 1; }
new StoreConnector(builder: (BuildContext context,int counter){ return new MyHomePage(title: 'Flutter Demo Home Page',counter:counter); }, converter: (Store<int> store){ return store.state; })
这里实现了真正的将Store中的状态传递到组件MyHomePage中,必定须要使用converter将Store中的状态转变成组件须要的状态,这里的builder函数的第二个参数就是converter函数的返回值。
绑定Action
floatingActionButton: new StoreConnector<int,VoidCallback>(builder: ( BuildContext context,VoidCallback callback ){ return new FloatingActionButton( onPressed:callback, tooltip: 'Increment', child: new Icon(Icons.add), ); }, converter: (Store<int> store){ return ()=>store.dispatch(Actions.Increase); }),
这里使用StoreConnector<int,VoidCallback>这个Widget绑定Action,注意泛型必须写对,否则会报错,第一个泛型为Store存储的类型,第二个泛型为converter的返回值得类型。
到这里,就改造完毕了,和官方的helloworld功能同样。
上面这个例子中,状态为一个int,实际项目固然没有那么简单,咱们须要规划整个app的状态,这里咱们使用AppState这个类来管理
/// 这个类用来管理登陆状态 class AuthState{ bool isLogin; //是否登陆 String account; //用户名 AuthState({this.isLogin:false,this.account}); } /// 管理主页状态 class MainPageState{ int counter; MainPageState({this.counter:0}); } /// 应用程序状态 class AppState{ AuthState auth; //登陆 MainPageState main; //主页 AppState({this.main,this.auth}); }
那么下面的程序要跟着将全部的int替换成AppState,并修改reducer的代码,这里就只贴重要的代码了:
AppState mainReducer(AppState state, dynamic action){ if(Actions.Increase==action){ state.main.counter += 1; } return state; } void main() { Store<AppState> store = new Store<AppState>(mainReducer,initialState: new AppState( main: new MainPageState(), auth: new AuthState(), )); runApp(new MyApp(store: store,)); }
增长登陆逻辑
改造一下MyHomePage,新怎一些状态
MyHomePage({Key key, this.title,this.counter,this.isLogin,this.account}) : super(key: key); final String title; final int counter; final bool isLogin; final String account;
新增登陆ui的判断
/// 有登陆,展现你好:xxx,没登陆,展现登陆按钮 isLogin ? new RaisedButton(onPressed: (){ },child: new Text("您好:$account,点击退出"),) :new RaisedButton(onPressed: (){ },child: new Text("登陆"),)
为了快速看到效果,这里将登陆输入用户名省略掉。。
改造reducer
AppState mainReducer(AppState state, dynamic action){ print("state charge :$action "); if(Actions.Increase==action){ state.main.counter+=1; } if(Actions.LogoutSuccess == action){ state.auth.isLogin = false; state.auth.account = null; } if(action is LoginSuccessAction){ state.auth.isLogin = true; state.auth.account = action.account; } print("state changed:$state"); return state; }
改造build:
Widget loginPane; if(isLogin){ loginPane = new StoreConnector( key:new ValueKey("login"), builder: (BuildContext context,VoidCallback logout){ return new RaisedButton(onPressed: logout,child: new Text("您好:$account,点击退出"),); }, converter: (Store<AppState> store){ return ()=> store.dispatch( Actions.LogoutSuccess ); }); }else{ loginPane = new StoreConnector<AppState,VoidCallback>( key:new ValueKey("logout"), builder: (BuildContext context,VoidCallback login){ return new RaisedButton(onPressed:login,child: new Text("登陆"),); }, converter: (Store<AppState> store){ return ()=> store.dispatch( new LoginSuccessAction(account: "xxx account!") ); }); }
代码:
https://github.com/jzoom/flut...
若有疑问,请加qq群854192563讨论