个人博客
前言
从进行开发OpenGit_Flutter项目以来,在项目中选择哪种架构困扰了很久。近段时间,分别在项目中尝试了BloC
、Redux
这两种架构,通过开发中遇到的问题,已经找到了合适的方案。为了演示方便,我选择了该项目的登录流程来为大家做演示,下面对登录流程做下拆解。
- 登录首先需要输入账号和密码,只有在账号和密码都有输入的时候,底部登录按钮才能点击,所以需要监听账号和密码输入框的输入状态,用来控制登录按钮的点击状态;
- 账号输入框需要支持一键删除的功能;
- 密码输入框需要支持对密码可见的功能;
- 点击登录按钮触发登录逻辑,在登录过程中需要展示loading界面,当登录失败后,取消loading界面,并进行toast提示;当登录成功之后,跳转的主界面,展示用户的基本信息;
- 用户资料和token等信息的保存,在本文中不会提到,如需查看该部分代码,点击OpenGit_Flutter;
最终的演示效果如下所示
登录界面的布局代码,不做过多的介绍,如果需要了解更多,可以查看相关源码,地址会在本文的最后贴出。
工程结构
flutter_architecture根目录是一个Flutter Package
,其下面分别创建了bloc
、mvc
、mvp
、redux
四个工程,lib
目录分别是四个工程的公用模块,例如网络请求、日志打印、toast提示、主页信息展示等。如下图所示
MVC
该架构是在写flutter_architecture例子时最后加上的,因为在进行Android
开发的过程中,经常用它来与MVP
做对比。
架构视图
程序入口
main.dart
是程序的入口,完成登录界面的启动,相关代码如下所示
void main() => runApp(MVCApp());
class MVCApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primaryColor: Colors.black,
),
home: LoginPage(),
);
}
}
登录流程
由于登录状态涉及到界面相关控件的刷新,所以继承的是StatefulWidget
。
文本监听
文本监听需要监听账号和密码输入框的输入状态,需声明两个TextEditingController
对象,相关代码如下所示
final TextEditingController _nameController = new TextEditingController();
final TextEditingController _passwordController = new TextEditingController();
在initState
处理输入框的监听事件,当输入状态改变时,并刷新页面,更新登录按钮状态,相关代码如下所示
@override
void initState() {
super.initState();
_nameController.addListener(() {
setState(() {});
});
_passwordController.addListener(() {
setState(() {});
});
}
登录按钮状态判断需要通过账号和密码输入字符串长短来判断,当长度都大于0的时候,按钮才能点击,逻辑层相关代码如下所示
_isValidLogin() {
String name = _nameController.text;
String password = _passwordController.text;
return name.length > 0 && password.length > 0;
}
登录按钮UI层代码如下所示
Align _buildLoginButton(BuildContext context) {
return Align(
child: SizedBox(
height: 45.0,
width: 270.0,
child: RaisedButton(
child: Text(
'登录',
style: Theme.of(context).primaryTextTheme.headline,
),
color: Colors.black,
onPressed: _isValidLogin()
? () {
_login();
}
: null,
shape: StadiumBorder(side: BorderSide()),
),
),
);
}
清空账号输入框
清空输入框只需调用TextEditingController
clear方法,如下面代码所示
TextFormField _buildNameTextField() {
return new TextFormField(
controller: _nameController,
decoration: new InputDecoration(
labelText: 'Github账号:',
suffixIcon: new GestureDetector(
onTap: () {
_nameController.clear();
},
child: new Icon(_nameController.text.length > 0 ? Icons.clear : null),
),
),
maxLines: 1,
);
}
密码是否可见
密码是否可见主要是通过更新变量_obscureText
实现,点击事件处理逻辑很简单,只是对_obscureText
做下取反操作,并刷新页面,代码如下所示
TextFormField _buildPasswordTextField(BuildContext context) {
return new TextFormField(
controller: _passwordController,
decoration: new InputDecoration(
labelText: 'Github密码:',
suffixIcon: new GestureDetector(
onTap: () {
setState(() {
_obscureText = !_obscureText;
});
},
child:
new Icon(_obscureText ? Icons.visibility_off : Icons.visibility),
),
),
maxLines: 1,
obscureText: _obscureText,
);
}
触发登录
View
层点击登录按钮,触发Control
层登录逻辑,在Control
层通过state控制loading界面的展示和隐藏,而loading的最终状态是由Model
层的loading状态决定,loading UI相关代码如下所示:
Offstage(
offstage: !Con.isLoading,
child: new Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
color: Colors.black54,
child: new Center(
child: SpinKitCircle(
color: Theme.of(context).primaryColor,
size: 25.0,
),
),
),
),
定义Control层
首先创建单例对象,并初始化Model
层数据,向View
层提供登录、加载状态、用户资料等状态的接口,相关代码如下所示