从登录页进入Flutter的开发规范

[TOC]

前言

良好的规范让你的代码不仅看上来更简洁,也让你的代码更加具有可读性,及大大的减少了后期维护的成本。

以下从几个方面谈谈我对Flutter良好的规范的理解。

一、页面Page规范前后效果对比

我们先从页面开始(以登录页的优化为例),然后逐步的从点到面铺开描述。

目标:页面布局、控件定义、事件处理分离。

1、演示的Page的UI图

用户名登录的UI图

2、规范前的效果

规范前:未进行任何处理的时候,你的代码是这样的。

LoginPage_widgetUnsplit

3、规范后的效果

规范后**:按规范处理后你代码的效果是这样的,

LoginPage_widgetSplit

可见通过处理后Page的整体代码更加简洁了。

二、页面Page开发规范的细则

1、规范一:将每个Widget的定义整理到单独的函数里

写:

Flutter规范_Widget_right

不写:

Flutter规范_Widget_wrong

2、规范二:将各个Widget组成界面的布局放到widgets()方法里处理

写法如下:

Flutter规范_Widgets布局_right

如此通过以上两步后,Widget build中的代码即会变为如下:

Flutter规范_WidgetBuild_right

如此通过以上几个拆分,我们的Page页面就显得很简洁和很好维护了。

3、规范三、Widget的封装

细心的你,可能发现了我们这里有一个系统没有的LoginTextField文本框类。是的,该类是我们自定义封装到Package后来使用的。

对于一个控件怎么封装,详情查看之前的文章《Flutter进阶(2):控件Widget的自定义与封装》

三、页面Page规范小结

  • 1、Widget build方法

build方法里尽量用最少的代码实现整体视图。如下,我们将登录页中的所有Widget通过loginWidgets()函数封装起来。

  • 2、各个Widget组成的视图布局

loginWidgets()方法里实现页面Widgets的布局。而对于Widget的定义为了不让页面布局控件定义因写在一堆,而造成代码一大坨。我们对每个Widget单独提供定义的函数,如userNameTextField()即为定义用户文本框的函数。

  • 3、抽离每个Widget的定义

  • 4、封装单个Widget

四、业务逻辑的开发规范

说完了页面上的Widgets的开发规范,下面就轮到页面上的业务逻辑的开发规范了。

拿登录页需要获取上次登录的账号来说,它的业务是getDefaultLoginAccountAction,下面以混编时候的项目为例

1、未去解耦业务时候的登录页

getDefaultLoginAccountAction在Page中的实现一般为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/// LoginPage.dart 中的业务代码
static const callNativeMethodChannel = const MethodChannel('com.dvlproad.ciyouzen/callNativeLoginMethodChannel');
Future<Null> _getDefaultLoginAccountAction() async {
try {
final Map nativeResponse = await callNativeMethodChannel.invokeMethod('getDefaultLoginAccountAction');
final Map nativeResult = callNativeNativeResult(nativeResponse);

setState(() {
userName = nativeResult["userName"];
password = nativeResult["password"];
print(userName + ":" + password);
_usernameController.text = userName;
_passwordController.text = password;
});
} on PlatformException {}
}

相应的它的调用为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/// LoginPage.dart 未去解耦时候的登录页
class MyLoginPage extends StatefulWidget {
MyLoginPage({Key key, this.title}) : super(key: key);

final String title;

@override
_MyLoginPageState createState() => _MyLoginPageState();
}

class _MyLoginPageState extends State<MyLoginPage> {
@override
void initState() {
// TODO: implement initState
super.initState();
print("Login Page initState");

_getDefaultLoginAccountAction();
}

// other things
}

可见上述的业务处理getDefaultLoginAccountAction中,掺杂了对页面Page的setState操作,这不适合我们以后只对业务或者只对页面做修改的处理。所以下面我们将它们解耦。

2、解耦业务时候的登录页

Flutter的ViewModel

getDefaultLoginAccountAction在ChannelModel中的实现一般为:

1
2
3
4
5
6
7
8
9
10
11
/// LoginChannelModel.dart 文件
Future<Map<String, dynamic>> getDefaultLoginAccountAction() async {
try {
final Map nativeResponse = await callNativeMethodChannel.invokeMethod('getDefaultLoginAccountAction');
final Map nativeResult = callNativeNativeResult(nativeResponse);

return nativeResult;
} on PlatformException {
return null;
}
}

相应的它的调用为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/// LoginPage.dart 解耦时候的登录页
class MyLoginPage extends StatefulWidget {
MyLoginPage({Key key, this.title}) : super(key: key);

final String title;

@override
_MyLoginPageState createState() => _MyLoginPageState();
}

class _MyLoginPageState extends State<MyLoginPage> {
@override
void initState() {
// TODO: implement initState
super.initState();
print("Login Page initState");

getDefaultLoginAccountAction().then((nativeResult){
setState(() {
userName = nativeResult["userName"];
password = nativeResult["password"];
print(userName + ":" + password);
_usernameController.text = userName;
_passwordController.text = password;
});
});
}

// other things
}

五、Flutter Channel的数据规范

在使用Flutter与原生项目混编的时候,因为涉及到数据传输,所以我们有必要在前期就对传输数据的进行规范统一。