手把手入門Fish-Redux開發flutter(中)

擁小抱 發表於 2019-10-12

手把手入門Fish-Redux開發flutter(上) (中)

上一次我們建立了專案,整合了 fish-redux ,安裝了外掛,並寫出第一個頁面,這次我們更詳細的瞭解 fish-redux 。並實現一個簡單的列表。效果:

手把手入門Fish-Redux開發flutter(中)

1 跳轉到一個新的頁面

1.1 建立一個新頁面

我們用上一篇講述的的方式:建立 package 、建立 FishReduxTemplate 來得到新的頁面,取名叫 Grid 。

手把手入門Fish-Redux開發flutter(中)

把這個頁面也新增到app.dart的頁面路由中。app.dart修改如下


Widget createApp() {
  final AbstractRoutes routes = PageRoutes(
    pages: <String, Page<Object, dynamic>>{
      'entrance_page': EntrancePage(),
      'grid_page': GridPage(),  //新增這一行
    },
  );

  //省略 ...
}

複製程式碼

然後編輯這個頁面的 view.dart,暫且讓它顯示一行文字。/grid/view.dart如下

import 'package:fish_redux/fish_redux.dart';
import 'package:flutter/material.dart';
import 'state.dart';

Widget buildView(GridState state, Dispatch dispatch, ViewService viewService) {
  return Scaffold(
    appBar: new AppBar(
      title: new Text('Grid頁面'),
    ),
    body: Text("Grid頁面"),
  );
}

複製程式碼

1.2 完成頁面的跳轉

理清思路:為了從 entrance 頁面跳轉到 grid 頁面。我們需要:

  1. 為 entrance 頁面定義跳轉的 action
  2. 在 entrance 頁面跳轉的地方 dispatch 這個 action
  3. effect 處理這個 action,即進行頁面跳轉(這裡不涉及到狀態的更新,所以不在 reducer 裡處理啦)

第一步 定義 action

我們開啟 /entrance/action.dart 發現由模板建立的程式碼中為我們預設定義了一個action,我們照它的樣子新增自己的 action ,取名叫 openGrid 表示開啟 grid 頁面的事件。修改後的 /entrance/action.dart 如下

import 'package:fish_redux/fish_redux.dart';

enum EntranceAction { action, openGrid }//增加openGrid

class EntranceActionCreator {
  static Action onAction() {
    return const Action(EntranceAction.action);
  }
  //我們定義的
  static Action onOpenGrid() {
    return const Action(EntranceAction.openGrid);
  }
}
複製程式碼

第二步 傳送Action

我們開啟 /entrance/view.dart,為 RaisedButton 寫點選事件 onPress。在這裡dispatch我們定義的OpenGrid事件。

            dispatch(EntranceActionCreator.onOpenGrid());
複製程式碼

修改後的 /entrance/view.dart如下

手把手入門Fish-Redux開發flutter(中)

第三步 接收並處理事件

開啟 /entrance/effect.dart,讓它接收並處理我們定義的 OpenGrid Action。 /entrance/effect.dart程式碼如下

import 'package:fish_redux/fish_redux.dart';
import 'package:flutter/widgets.dart' hide Action;  //注意1
import 'action.dart';
import 'state.dart';

Effect<EntranceState> buildEffect() {
  return combineEffects(<Object, Effect<EntranceState>>{
    EntranceAction.action: _onAction,
    EntranceAction.openGrid: _onOpenGrid,   //接收openGrid事件 
  });
}

void _onAction(Action action, Context<EntranceState> ctx) {
}

//處理openGrid事件
void _onOpenGrid(Action action, Context<EntranceState> ctx) {
  Navigator.of(ctx.context).pushNamed('grid_page', arguments: null);    //注意2
}

複製程式碼

這裡有兩個地方要注意。第一個地方是由於我們跳轉頁面引入的 widgets.dart 包含 Action 類,會與 fish-redux 的 Action 衝突並報錯,所以在 import 時需要 hide Action ,之後就不在贅述。另外,pushNamed() 方法第一個引數就是我們在 app.dart 裡面定義的頁面路由。

然後我們執行看看效果:點選進入,跳轉到了grid頁面。

手把手入門Fish-Redux開發flutter(中)

2 簡單的列表

現在我們來把 grid 頁面改造成一個簡單的列表。

2.1 定義資料格式

我新建一個 model.dart 存放我的資料實體類 。我在 GridModel 簡單的定義了一個 name 欄位 model.dart 如下

手把手入門Fish-Redux開發flutter(中)

然後在 grid 頁面的 state.dart 中,建立資料的集合。修改後端 /grid/state.dart如下

import 'package:fish_redux/fish_redux.dart';
import '../model.dart';

class GridState implements Cloneable<GridState> {
  
  List<GridModel> models;   // 存放資料

  @override
  GridState clone() {
    return GridState()
    ..models = models;      //clone規則
  }
}

GridState initState(Map<String, dynamic> args) {
  return GridState();
}
複製程式碼

2.2 在介面上展示

接著我們在頁面中通過 gridview 展示我們的資料,列表的資料就採用 state 中的 models。修改 /grid/view.dart 如下

import 'package:fish_redux/fish_redux.dart';
import 'package:flutter/material.dart';

import 'state.dart';

Widget buildView(GridState state, Dispatch dispatch, ViewService viewService) {
  return Scaffold(
    appBar: new AppBar(
      title: new Text('Grid頁面'),
    ),
    body: new GridView.count(
      crossAxisCount: 2,//列數
      crossAxisSpacing: 20.0,// 左右間隔
      mainAxisSpacing: 20.0,// 上下間隔
      childAspectRatio: 1 / 1, //寬高比
      padding: EdgeInsets.all(20),
      children: new List.generate(state.models.length, (index) {//使用state裡面的models生成列表
        return Center(
          child: Card(
            color: Colors.lightBlueAccent,
            child: InkWell(
              splashColor: Colors.blue.withAlpha(100),
              onTap: () {
                //todo 點選事件
              },
              child: Container(
                width: 200,
                height: 200,
                child: Center(
                  child: Text(state.models[index].name),//展示name欄位
                ),
              ),
            ),
          ));
      }),
    ),
  );
}

複製程式碼

資料格式和展示樣式都已經完成,接下來我們來獲得資料。

2.3 資料來源

我建立一個 api.dart 用來進行資料請求。

手把手入門Fish-Redux開發flutter(中)
我建立一個單例 Api ,把它作為全域性的資料來源。然後完成一個獲得 GridModel 資料的函式來給我們 grid 頁面提供資料。 api.dart 如下

import 'model.dart';

class Api {
  factory Api() {
    return _get();
  }

  static Api _instance;

  Api._internal() {
    //init Api instance
  }

  static _get() {
    if (_instance == null) {
      _instance = Api._internal();
    }
    return _instance;
  }

  
  List<GridModel> getGridData() {
    return [
      GridModel(name: "第一塊"),
      GridModel(name: "第二塊"),
      GridModel(name: "第三塊"),
      GridModel(name: "第四塊"),
      GridModel(name: "第五塊"),
      GridModel(name: "第六塊"),
      GridModel(name: "第七塊"),
      GridModel(name: "第八塊"),
      GridModel(name: "第九塊"),
      GridModel(name: "第十塊"),
    ];
  }
}
複製程式碼

2.4 載入資料

我們需要在 grid 頁面進入時進行資料載入。首先通過 effect 接收 state 初始化的生命週期事件,在 state 初始化的時候,我們 dispatch 一個載入資料的 action 給 reducer,reducer 接收事件,請求資料並更新 state 。

第一步 定義action

/grid/action.dart 中新增一個 action, 取名 loadData。新增後如下

import 'package:fish_redux/fish_redux.dart';

enum GridAction { action, loadData }

class GridActionCreator {
  static Action onAction() {
    return const Action(GridAction.action);
  }
  
  static Action onLoadData() {
    return Action(GridAction.loadData);
  }
}

複製程式碼

第二步 初始化時傳送action

然後在 effect 中監聽初始化事件,併傳送 loadData Action。修改後的 /grid/effect.dart 如下

import 'package:fish_redux/fish_redux.dart';
import 'action.dart';
import 'state.dart';

Effect<GridState> buildEffect() {
  return combineEffects(<Object, Effect<GridState>>{
    Lifecycle.initState: _init, //頁面初始化
    GridAction.action: _onAction,
  });
}

void _onAction(Action action, Context<GridState> ctx) {
}

void _init(Action action, Context<GridState> ctx) {
  ctx.dispatch(GridActionCreator.onLoadData()); //傳送事件
}
複製程式碼

第三步 接收action並更新state

最後在 reducer 中接收 loadData 事件,呼叫 Api 請求資料, 並更新 state 。修改後的 /grid/reducer.dart如下

import 'package:fish_redux/fish_redux.dart';
import '../api.dart';
import 'action.dart';
import 'state.dart';

Reducer<GridState> buildReducer() {
  return asReducer(
    <Object, Reducer<GridState>>{
      GridAction.action: _onAction,
      GridAction.loadData: _onLoadData, //接收loadData Action
    },
  );
}

GridState _onAction(GridState state, Action action) {
  final GridState newState = state.clone();
  return newState;
}

//初始化資料
GridState _onLoadData(GridState state, Action action) {
  final GridState newState = state.clone()..models = Api().getGridData();//從Api請求資料
  return newState;
}
複製程式碼

執行一下看看效果

手把手入門Fish-Redux開發flutter(中)

🤗如果我的內容對您有幫助,歡迎點贊、評論、轉發、收藏。