[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...哈哈哈,大半夜的啤酒加歌有点嗨。 说下遇到的两个点比较坑的: