[toc]前端
通過不懈的軟磨硬泡以及各類安利...cto終於對Flutter動心了,項目2.15版本將會接入Flutter模塊,😂真的是喜大普奔... 考慮到將來的業務拓展,也是爲了打好一個基礎,我對接下來的flutter module進行了框架選型(實際上是爲了進一步安利,畢竟原生代碼真的寫得好死鬼煩啊...)。 其實目前flutter也沒有什麼特別好的框架,所謂框架,不太像android那樣的mvc,mvp,mvvm那麼成熟,更多的說的是狀態管理。 目前flutter成熟的狀態管理也就三(si)種:java
咱們簡單介紹下:android
FishRedux的gayhub地址爲:FishRedux 咱們clone項目,大體看下目錄結構: git
除了通用的global_store以外,頁面大體分爲三種類型:github
官網上介紹,page(頁面)是一個行爲豐富的組件,由於它的實現是在組件(component)的基礎上加強了aop能力,以及自有的state。 component也有本身的state,可是對比起來,page的具有了**initState()**方法而component沒有。 好比咱們後續的登陸頁面,咱們暫且貼上代碼,後面再作具體說明:web
login_quick_component代碼spring
import 'package:fish_redux/fish_redux.dart';
import 'package:flutter_module/page/dialog/component.dart';
import 'effect.dart';
import 'reducer.dart';
import 'state.dart';
import 'view.dart';
class LoginQuickComponent extends Component<LoginQuickState> {
LoginQuickComponent()
: super(
effect: buildEffect(),
reducer: buildReducer(),
view: buildView,
dependencies: Dependencies<LoginQuickState>(
adapter: null,
slots: <String, Dependent<LoginQuickState>>{
'dialog': DialogConner() + CommDialogComponent()
}),
);
}
複製代碼
login_page代碼數據庫
import 'package:fish_redux/fish_redux.dart';
import 'StateWithTickerProvider.dart';
import 'effect.dart';
import 'login_quick/component.dart';
import 'reducer.dart';
import 'state.dart';
import 'view.dart';
class LoginPage extends Page<LoginState, Map<String, dynamic>> {
@override
StateWithTickerProvider createState() => StateWithTickerProvider();
LoginPage()
: super(
initState: initState,
effect: buildEffect(),
reducer: buildReducer(),
view: buildView,
dependencies: Dependencies<LoginState>(
adapter: null,
slots: <String, Dependent<LoginState>>{
"login_quick": QuickConnector() + LoginQuickComponent(),
// "login_normal": QuickConnector() + LoginQuickComponent(),
}),
middleware: <Middleware<LoginState>>[],
);
}
複製代碼
*組件(Component)*是 Fish Redux 最基本的元素,其實page也是基於Component的,它與page的不一樣點除了:redux
/(ㄒoㄒ)/~~ 哈哈哈,我不要你以爲,我要我以爲!!!! 看到這裏是否是有點淚流滿面了,通篇不知所云,好不容易看到了一個親切的單詞了... 然而...名字叫適配器,可是和android的用法仍是有區別的。緩存
不過這個我也是在摸索使用中。
猶豫不決老是夢,其實上面說了那麼多,我都不知道我在說啥...咱們仍是直接看代碼吧。
這個是咱們具體實現的登陸頁面,我用安卓的行話講就是: 上面一個imageView,下面弄了一個tabLayout,裏面丟了兩個菜單類型:快速登陸和密碼登陸,最底下丟了一個viewpager,裏面丟了兩個fragment。 簡簡單單... 然而,flutter的代碼結構爲:
其實這塊是照抄demo的,是一個實現切換主題色的小功能,真爽啊!!!,這塊能夠略過不講,很容易看懂。
是頁面建立的根,主要用途有:
一個page(component)咱們能夠看到是由: action,effect,page(component),reducer,state,view這幾個模塊組成的,他們分別的做用,咱們先稍微瞭解下,以便後續的代碼講解:
用來定義在這個頁面中發生的動做,例如:登陸,清理輸入框,更換驗證碼框等。 同時能夠經過payload參數傳值,傳遞一些不能經過state傳遞的值。
這個dart文件在fish_redux中是定義來處理反作用操做的,好比顯示彈窗,網絡請求,數據庫查詢等操做。
這個dart文件在用來在路由註冊,同時完成註冊effect,reducer,component,adapter的功能。
這個dart文件是用來更新View,即直接操做View狀態。
state用來定義頁面中的數據,用來保存頁面狀態和數據。
view很明顯,就是flutter裏面當中展現給用戶看到的頁面。
反正我第一次看是頭暈腦脹的...怎麼這麼多東西,想我年少時,一個.xml和一個.java走天下。 在這裏建議下和我一個弄個記事本,把上面這塊抄上去,寫的時候忘記了就看看。
由上面的截圖能夠看出,登陸頁面由一個page加一個component組成。 咱們先逐個逐個看代碼,逐個逐個說:
數據先行,咱們看下state類,這裏比較重要的就是QuickConnector鏈接器了,等到咱們講login_quick的時候再細說:
import 'dart:async';
import 'package:fish_redux/fish_redux.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_module/global_store/state.dart';
import 'login_quick/state.dart';
class LoginState implements GlobalBaseState, Cloneable<LoginState> {
// tab控制器
TabController tabControllerForLoginType;
// 菜單list
List<String> loginType = [];
// 從緩存拿的帳號(可能有用到)
String accountFromCache;
// 倒數文字
String countDownTips;
// 最大倒數時間
int maxCountTime;
// 當前倒數時間
int currCountTime;
@override
LoginState clone() {
return LoginState()
..loginType = loginType
..tabControllerForLoginType = tabControllerForLoginType
..accountFromCache = accountFromCache;
}
@override
Color themeColor;
}
LoginState initState(Map<String, dynamic> args) {
LoginState state = new LoginState();
state.loginType.add('快速登陸');
state.loginType.add('密碼登陸');
return state;
}
class QuickConnector extends Reselect2<LoginState, LoginQuickState, String, String> {
@override
LoginQuickState computed(String sub0, String sub1) {
return LoginQuickState()
..account = sub0
..sendVerificationTips = sub1
..controllerForAccount = TextEditingController()
..controllerForPsd = TextEditingController();
}
@override
String getSub0(LoginState state) {
return state.accountFromCache;
}
@override
String getSub1(LoginState state) {
return state.countDownTips;
}
@override
void set(LoginState state, LoginQuickState subState) {
state.accountFromCache = subState.account;
state.countDownTips = subState.sendVerificationTips;
}
}
複製代碼
這個是用戶直接看到的視圖文件,咱們稍微理下: 其實也是和咱們上文說的佈局文件相似, 一imageView 一tabBar 一TabBarView 二login_quick Component 而已
import 'package:fish_redux/fish_redux.dart';
import 'package:flutter/material.dart';
import 'package:flutter_module/comm/ColorConf.dart';
import 'action.dart';
import 'state.dart';
Widget buildView(LoginState state, Dispatch dispatch, ViewService viewService) {
return Scaffold(
// appBar: AppBar(
// title: Text('Login'),
// ),
body: Column(
children: <Widget>[
Container(
child: Image.asset('images/img_logintop.webp'),
),
Container(
child: TabBar(
indicatorColor: ColorConf.color18C8A1,
indicatorPadding: EdgeInsets.zero,
controller: state.tabControllerForLoginType,
labelColor: ColorConf.color18C8A1,
indicatorSize: TabBarIndicatorSize.label,
unselectedLabelColor: ColorConf.color9D9D9D,
tabs: state.loginType
.map((e) => Container(
child: Text(
e,
style: TextStyle(fontSize: 14),
),
padding: const EdgeInsets.only(top: 8, bottom: 8),
))
.toList(),
),
),
Divider(
height: 1,
),
Expanded(
child: TabBarView(
children: <Widget>[
viewService.buildComponent('login_quick'),
viewService.buildComponent('login_quick'),
],
controller: state.tabControllerForLoginType,
),
flex: 3,
)
],
),
);
}
複製代碼
這裏其實就是定義操做,我以爲能夠有個類比,拿我半吊子的springboot來講, action就是一個service接口 effect就是一個serviceImpl實現類 reducer就是根據發出來的action進行頁面操做。
login_action只定義了一丟丟操做
import 'package:fish_redux/fish_redux.dart';
enum LoginAction { action, update }
class LoginActionCreator {
static Action onAction() {
return const Action(LoginAction.action);
}
static Action onUpdate(String countDownNumber) {
return Action(LoginAction.update, payload: countDownNumber);
}
}
複製代碼
import 'package:fish_redux/fish_redux.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_module/page/user/login_page/action.dart';
import 'StateWithTickerProvider.dart';
import 'state.dart';
Effect<LoginState> buildEffect() {
return combineEffects(<Object, Effect<LoginState>>{
LoginAction.action: _onAction,
Lifecycle.initState: _onInit,
});
}
void _onAction(Action action, Context<LoginState> ctx) {}
void _onInit(Action action, Context<LoginState> ctx) {
final TickerProvider tickerProvider = ctx.stfState as StateWithTickerProvider;
ctx.state.tabControllerForLoginType =
TabController(length: ctx.state.loginType.length, vsync: tickerProvider);
}
複製代碼
這個沒有什麼好說的,由於loginPage也沒有什麼特別的操做,惟一比較值得注意的是 這裏相對多了一個StateWithTickerProvider
這個文件主要是爲了給tabController提供TickerProvider,主要注意幾個地方
class StateWithTickerProvider extends ComponentState<LoginState> with TickerProviderStateMixin{
}
複製代碼
@override
StateWithTickerProvider createState() => StateWithTickerProvider();
複製代碼
void _onInit(Action action, Context<LoginState> ctx) {
final TickerProvider tickerProvider = ctx.stfState as StateWithTickerProvider;
ctx.state.tabControllerForLoginType =
TabController(length: ctx.state.loginType.length, vsync: tickerProvider);
}
複製代碼
這個頁面主要是用於更新view,怎麼更新呢?返回一個newState!!
import 'package:fish_redux/fish_redux.dart';
import 'action.dart';
import 'state.dart';
Reducer<LoginState> buildReducer() {
return asReducer(
<Object, Reducer<LoginState>>{
LoginAction.action: _onAction,
LoginAction.update: _onUpdate,
},
);
}
LoginState _onAction(LoginState state, Action action) {
final LoginState newState = state.clone();
return newState;
}
LoginState _onUpdate(LoginState state, Action action) {
print('this is the _onUpdate in the buildReducer');
final LoginState newState = state.clone()..countDownTips = action.payload;
return newState;
}
複製代碼
page文件,在構造方法裏面調用init初始化
import 'package:fish_redux/fish_redux.dart';
import 'StateWithTickerProvider.dart';
import 'effect.dart';
import 'login_quick/component.dart';
import 'reducer.dart';
import 'state.dart';
import 'view.dart';
class LoginPage extends Page<LoginState, Map<String, dynamic>> {
@override
StateWithTickerProvider createState() => StateWithTickerProvider();
LoginPage()
: super(
initState: initState,
effect: buildEffect(),
reducer: buildReducer(),
view: buildView,
dependencies: Dependencies<LoginState>(
adapter: null,
slots: <String, Dependent<LoginState>>{
"login_quick": QuickConnector() + LoginQuickComponent(),
// "login_normal": QuickConnector() + LoginQuickComponent(),
}),
middleware: <Middleware<LoginState>>[],
);
}
複製代碼
總結這就來了,是否是隻有一個想法:亂!混亂!!好亂!!!什麼鬼!!!!mmp!!!!! 咱們稍微理理,由於接下來就是點擊事件,網絡請求了。 咱們理下思路:
看,你能夠畫頁面了!!!完美!雖然它點了也沒反應。
在這裏咱們稍微講下難點的,好比說點擊登陸
#####咱們先雲coding一下:
話都說到了這裏了,你要不要試着按照上面的6個步驟試下,這樣體會更深哦,咱們貼下代碼:
import 'package:fish_redux/fish_redux.dart';
//TODO replace with your own action
enum TestPageAction { action, doLogin, showUserInfo }
class TestPageActionCreator {
static Action onAction() {
return const Action(TestPageAction.action);
}
static Action onDoLogin() {
return const Action(TestPageAction.doLogin);
}
static Action onShowUserInfo() {
return const Action(TestPageAction.showUserInfo);
}
}
複製代碼
import 'package:fish_redux/fish_redux.dart';
import 'action.dart';
import 'state.dart';
Effect<TestPageState> buildEffect() {
return combineEffects(<Object, Effect<TestPageState>>{
TestPageAction.action: _onAction,
TestPageAction.doLogin: _onDoLogin,
});
}
void _onAction(Action action, Context<TestPageState> ctx) {}
void _onDoLogin(Action action, Context<TestPageState> ctx) {
print('this is _onDoLogin method in the effect');
ctx.dispatch(TestPageActionCreator.onShowUserInfo());
}
複製代碼
import 'package:fish_redux/fish_redux.dart';
import 'effect.dart';
import 'reducer.dart';
import 'state.dart';
import 'view.dart';
class TestPagePage extends Page<TestPageState, Map<String, dynamic>> {
TestPagePage()
: super(
initState: initState,
effect: buildEffect(),
reducer: buildReducer(),
view: buildView,
dependencies: Dependencies<TestPageState>(
adapter: null,
slots: <String, Dependent<TestPageState>>{
}),
middleware: <Middleware<TestPageState>>[
],);
}
複製代碼
import 'package:fish_redux/fish_redux.dart';
import 'action.dart';
import 'state.dart';
Reducer<TestPageState> buildReducer() {
return asReducer(
<Object, Reducer<TestPageState>>{
TestPageAction.action: _onAction,
TestPageAction.showUserInfo: _onShowUserInfo,
},
);
}
TestPageState _onAction(TestPageState state, Action action) {
final TestPageState newState = state.clone();
return newState;
}
TestPageState _onShowUserInfo(TestPageState state, Action action) {
final TestPageState newState = state.clone();
newState..userInfoStr = "凌宇是個超級大帥逼!!!!";
return newState;
}
複製代碼
import 'package:fish_redux/fish_redux.dart';
import 'package:flutter/material.dart';
class TestPageState implements Cloneable<TestPageState> {
TextEditingController controllerForAccount, controllerForPsd;
String userInfoStr;
@override
TestPageState clone() {
return TestPageState()
..controllerForPsd = controllerForPsd
..controllerForAccount = controllerForAccount
..userInfoStr = userInfoStr;
}
}
TestPageState initState(Map<String, dynamic> args) {
return TestPageState()
..controllerForAccount = TextEditingController()
..userInfoStr=""
..controllerForPsd = TextEditingController();
}
複製代碼
import 'package:fish_redux/fish_redux.dart';
import 'package:flutter/material.dart';
import 'action.dart';
import 'state.dart';
Widget buildView(
TestPageState state, Dispatch dispatch, ViewService viewService) {
return Scaffold(
appBar: AppBar(
title: Text('測試'),
),
body: Column(
children: <Widget>[
Text(state.userInfoStr ?? '暫無信息'),
TextField(
controller: state.controllerForAccount,
),
TextField(
controller: state.controllerForAccount,
),
FlatButton(
onPressed: () {
dispatch(TestPageActionCreator.onDoLogin());
},
child: Text('凌宇是個大帥逼'))
],
),
);
}
複製代碼
其實子模塊的代碼也沒有必要貼了,無非就是action多了一點,加了判空,加了實際網絡請求而已,對着上面的代碼也是同樣的。 再而後, 再還有aop,adapter(其實我有用的,可是登陸模塊咋講嘛...且學且用吧)等等東西,越學越有趣,閒魚大佬真厲害。
突如其來的ending...哈哈哈,大半夜的啤酒加歌有點嗨。 說下遇到的兩個點比較坑的: