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讨论

相关文章
相关标签/搜索