本文詳細講述怎樣在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討論