Fish-Redux结构初始
1.工程结构
fish结构.png
2.工程初始
整个fish-redux的简单页面包括如下几个
Fish-Redux项目结构如何运行
根据上面对工程结构的初步认识,简单理解咸鱼的这个框架就是
1.创建页面的时候,所有页面的入口都再Page,类似Android里面的Activity
class AutoPage extends Page<AutoState, Map<String, dynamic>> {
AutoPage()
: super(
initState: initState,
effect: buildEffect(),
reducer: buildReducer(),
view: buildView,
dependencies: Dependencies<AutoState>(
adapter: null, slots: <String, Dependent<AutoState>>{}),
middleware: <Middleware<AutoState>>[],
);
}
Page里面有如下几个入参:
- initState 初始化所有的State属性,如:
class AutoState implements Cloneable<AutoState> {
var telTextEditingController = TextEditingController();
var autoCodeTextEditingController = TextEditingController();
var autoCodeFocusNode = new FocusNode();
var isCheckOK;
///必须要实现clone,这个是咸鱼的框架设定
@override
AutoState clone() {
return AutoState()
..telTextEditingController = telTextEditingController
..autoCodeTextEditingController = autoCodeTextEditingController
..autoCodeFocusNode = autoCodeFocusNode
..isCheckOK = isCheckOK;
}
}
///初始化所有需要用到的属性
AutoState initState(Map<String, dynamic> args) {
return AutoState()
..telTextEditingController = TextEditingController()
..autoCodeTextEditingController = TextEditingController()
..autoCodeFocusNode = new FocusNode()
..isCheckOK = false;
}
- action 类似Android声明的接口内部方法,并不会实现具体业务,需要跟Effect结合使用,如
enum AutoAction { Login }
class AutoActionCreator {
static Action onLogin(String type) {
return Action(AutoAction.Login, payload: type);
}
}
- effect 实现所有具体声明的方法,类似Android声明接口后,需要实现当前接口,操作具体业务逻辑,如:
Effect<PassWordState> buildEffect() {
return combineEffects(<Object, Effect<PassWordState>>{
///PassWordAction.action 类似Android定义的接口方法名
///_onAction 类似Android接口实现的具体方法
PassWordAction.Login: _onLogin,
});
}
Future _onLogin(Action action, Context<PassWordState> ctx) async {
Result<LoginRespEntity> result;
//第三方登录的参数
if (action.payload == "qq" || action.payload == "wechat") {
} else {
var loginReqUserEntity = LoginReqUserEntity()
..loginName = ctx.state.controllerForAccount.text
..password = ctx.state.controllerForPsd.text
..organizeType = -1;
result = await VvRequestClient.request<LoginRespEntity>(
ctx.context, LoginApiService.NORMAL_HOST + LoginApiService.LOGINURL,
queryParameters: loginReqUserEntity.toJson(),
showLoadingIndicator: true);
}
result
..yes((value) {
showToast(value.userCode);
log('userCode${value.userCode}');
})
..no((err) {
log('err${err.toString()}');
if (err.code == ErrorCode.BIND_TEL_ERROR_CODE) {}
});
}
-
reducer 通过学习发现,Effect主要用来触发action传递过来的事件,执行类似网络请求等业务,而reducer更偏向是执行了业务之后,需要更新State属性,进而刷新UI使用的
-
view 比较简单,就是写UI的地方了,如
Widget buildView(AutoState state, Dispatch dispatch, ViewService viewService) {
var themeData = Theme.of(viewService.context);
return Scaffold(
appBar: Toolbar(),
body: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
KeyboardUtils.hide();
},
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding:
const EdgeInsets.only(left: 37.0, top: 52.0, bottom: 50.0),
child: Text(
"手机号码登录",
style: TextStyle(fontSize: 18.0, color: color343434),
),
),
Padding(
padding: const EdgeInsets.only(left: 38.0, right: 38.0),
child: TextField(
// autofocus: true,
keyboardType: TextInputType.phone,
controller: state.telTextEditingController,
decoration: InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: colore3e3e3),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
width: 2.0, color: themeData.accentColor),
),
contentPadding: EdgeInsets.only(
left: 20.0, right: 20.0, top: 20.0, bottom: 10.0),
hintText: "请输入手机号",
hintStyle: TextStyle(
color: colorA4A4A4,
fontSize: 12.0,
fontWeight: FontWeight.w300)),
maxLength: 11,
buildCounter: (BuildContext context,
{int currentLength,
int maxLength,
bool isFocused}) =>
null,
style:
TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold)),
),
Padding(
padding: const EdgeInsets.only(left: 38.0, right: 38.0),
child: TextFormField(
focusNode: state.autoCodeFocusNode,
keyboardType: TextInputType.phone,
controller: state.autoCodeTextEditingController,
decoration: InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: colore3e3e3),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
width: 2.0, color: themeData.accentColor),
),
suffixIcon: CountDownButton(
CompleterUtils.produceCompleterAction(
dispatch, AutoActionCreator.onSendAutoCode),
state: state.telTextEditingController.text.length ==
11 //小于11位不能点击
? 1
: 0,
),
contentPadding: EdgeInsets.only(
left: 20.0, right: 20.0, top: 20.0, bottom: 10.0),
hintText: "请输入验证码",
hintStyle: TextStyle(
color: colorA4A4A4,
fontSize: 12.0,
fontWeight: FontWeight.w300)),
maxLength: 6,
buildCounter: (BuildContext context,
{int currentLength,
int maxLength,
bool isFocused}) =>
null,
style:
TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold)),
),
Container(
width: WindowUtils.getScreenWidth() - 38 * 2,
height: 45.0,
margin: const EdgeInsets.only(top: 22.0, left: 38, right: 38),
child: RaisedButton(
elevation: 0.0,
color: state.isCheckOK ? themeData.accentColor : colorE4E4E4,
shape: new RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(22.0)),
child: Text("立即登录s"),
textColor: colorWhite,
onPressed: () {
if(state.isCheckOK){
dispatch(AutoActionCreator.onLogin("tel"));
}else{
showToast("widget");
}
},
),
),
],
),
Stack(
alignment: Alignment.bottomCenter,
children: <Widget>[
Image.asset(
'assets/auto_page_bg.png',
fit: BoxFit.fitWidth,
width: WindowUtils.getScreenWidth(),
),
Padding(
padding: const EdgeInsets.only(bottom: 68.0),
child: RichText(
text: TextSpan(
text: "登录即同意 ",
children: [
TextSpan(
text: "《用户协议》",
recognizer: TapGestureRecognizer()
..onTap = () {
NavigatorHelper.pushWebPage(
viewService.context,
"用户协议",
"https://blog.csdn.net/yuzhiqiang_1993/article/details/88204031");
},
style: TextStyle(fontSize: 12.0, color: colorFF6000),
),
TextSpan(
text: "和",
style: TextStyle(fontSize: 12.0),
),
TextSpan(
text: "《隐私政策》",
recognizer: TapGestureRecognizer()
..onTap = () {
NavigatorHelper.pushWebPage(
viewService.context,
"隐私政策",
"${HttpConstants.BaseUrl}/assets/protocol.html");
},
style: TextStyle(fontSize: 12.0, color: colorFF6000),
),
],
style: TextStyle(fontSize: 12.0, color: color343434),
),
),
),
Padding(
padding: const EdgeInsets.only(bottom: 138.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Column(
children: <Widget>[
IconButton(
onPressed: () {
dispatch(AutoActionCreator.onLogin('qq'));
},
icon: Image.asset("assets/qq.png"),
),
Text("QQ")
],
),
Column(
children: <Widget>[
IconButton(
onPressed: () {
dispatch(AutoActionCreator.onLogin('wechat'));
},
icon: Image.asset("assets/wechat.png"),
),
Text("微信")
],
),
],
),
)
],
)
],
),
),
);
}
着重注意下 controller: state.autoCodeTextEditingController,这句代码,controller是输入框的一个监听器。类似Android的onTextChangeListener。代码也可以看到,是通过state来调用的。这个state就是,我们刚才说的,需要声明的变量。
同时,也注意这句:ctx.state.controllerForAccount.text
在effect里面执行具体业务操作,例如登录,需要获取输入框的账号密码,也是通过state.xxx来获取
总结
构建Page--》绘制View---》声明state属性--》声明/执行(dispatch)Action方法--》在Effect实现具体方法--》》刷新state--》更新View UI
Fish-Redux框架相对复杂,不适合初识Flutter的开发者,但是,也建议初学者尝试使用这个框架,因为这个框架把每个层次都相对合理的分层了。不至于耦合度太高。相信,很多人开始写的时候都是类似MVC的方式。Fish-Redux给我感觉,可能会介于MVP/MVVM中间吧。未完待续
网友评论