flutter中使用redux之基礎

本文詳細講述怎樣在flutter中集成和使用redux,關於redux的概念、原理和實現,讀者可自行百度,本文不作累述。git

redux庫

https://pub.flutter-io.cn/pac...github

集成redux

修改項目根目錄下的pubsepc.yaml,添加依賴redux

flutter_redux: ^0.5.2

clipboard.png

基本集成

建立項目

先使用flutter命令app

flutter create flutter_use_redux

建立一個flutter默認的Helloworld項目less

目標:改造flutter的Helloworld項目使用redux實現

原來的代碼以下,去掉了註釋,以便顯得更短...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),
      ),
    );
  }
}

這步跨度有點大,解釋一下:

  • 在mainReducer中增長對action的處理:
if(Actions.Increase==action){
    return state + 1;
  }
  • 原來的MaterialApp外面套一層StoreProvider
    這一步是官方要求的,若是不作會報錯。
  • 將MyHomePage改爲 StatelessWidget
    因爲將狀態放在了Store中,因此無需使用StatefulWidget
  • 傳遞狀態
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功能同樣。

clipboard.png

敢不敢複雜一點?例子:登陸

上面這個例子中,狀態爲一個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討論

相關文章
相關標籤/搜索