Flutter代码通常涉及构建相当深的树状数据结构,在看官方的Flutter例子代码的时候,你会被一层一层的括号套的眼睛都花了...
所以我根据之前的web开发经验总结了一点Flutter代码书写规范。
用咸鱼的fish-redux也可以使代码分层更明确一点。
我们先看个我写的邮箱登录注册的界面例子:
如果是普通写代码的话,就会是这样:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:fitforce_coach/module/base/controller/BaseWidget.dart';
import 'package:fitforce_coach/utiliy/TYTool.dart';
class EmailController extends StatefulWidget {
// EmailControllerState emailState =
@override
State createState() {
return EmailControllerState();
}
}
class EmailControllerState extends State {
//默认闭合眼睛
bool closeEye = true;
//邮箱的控制器
TextEditingController emailControl = TextEditingController();
//密码的控制器
TextEditingController passwordControl = TextEditingController();
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
//设置状态栏颜色
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.light);
return Scaffold(
body: buildBody(context),
);
}
//视图-------------------------------------
Widget buildBody(BuildContext context) {
return Stack(
children: [
Image.asset(
'images/login/login_bg.png',
width: TYTool.getScreenWidth(context),
height: TYTool.getScreenHeight(context),
fit: BoxFit.fill,
),
//全屏取消编辑手势
addCancelEditView(context),
Column(
children: [
Padding(
padding: EdgeInsets.only(
top: TYTool.getSysStatsHeight(context) + 16.0),
),
Stack(
// alignment: Alignment.centerLeft,
children: [
//返回按钮
Align(
alignment: Alignment.centerLeft,
child: Container(
height: 30.0,
width: 30.0,
margin: EdgeInsets.only(left: 10.0),
child: IconButton(
padding: EdgeInsets.all(0.0),
icon: Icon(
Icons.arrow_back_ios,
color: Colors.white,
),
onPressed: () {
popLastController(context);
},
),
),
),
//logo图片
Align(
alignment: Alignment.center,
child: Image.asset(
'images/login/login_logo.png',
width: 112.0,
height: 22.0,
),
),
],
),
Container(
margin: EdgeInsets.fromLTRB(52.0, 46.0, 52.0, 10.0),
child: Column(
children: [
//题目
Container(
margin: EdgeInsets.only(bottom: 30.0),
//宽度尽可能大
width: double.infinity,
child: Text(
'邮箱登录',
textDirection: TextDirection.ltr,
textAlign: TextAlign.start,
style: TextStyle(
// background: backgroundPaint,
color: Colors.white,
fontSize: 18.0,
),
),
),
//邮箱
Theme(
data: ThemeData(
primaryColor: Colors.white, hintColor: Colors.white54),
child: TextField(
style: TextStyle(fontSize: 14.0, color: Colors.white),
keyboardType: TextInputType.emailAddress,
cursorColor: Color(0xFF20C6BA),
cursorWidth: 1.5,
controller: emailControl,
decoration: InputDecoration(
hintText: '请输入邮箱地址',
),
),
),
Padding(
padding: EdgeInsets.only(bottom: 30.0),
),
Stack(
children: [
//验证码
Theme(
data: ThemeData(
primaryColor: Colors.white,
hintColor: Colors.white54),
child: TextField(
style: TextStyle(fontSize: 14.0, color: Colors.white),
cursorColor: Color(0xFF20C6BA),
cursorWidth: 1.5,
controller: passwordControl,
obscureText: closeEye,
decoration: InputDecoration(
hintText: '请输入密码',
),
),
),
//小眼睛
Container(
margin: EdgeInsets.fromLTRB(
TYTool.getScreenWidth(context) - 104.0 - 38.0,
10.0,
20.0,
10.0),
height: 18.0,
child: MaterialButton(
padding: EdgeInsets.all(0.0),
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
child: Image.asset(
closeEye
? 'images/login/btn_login_eye_close.png'
: 'images/login/btn_login_eye_open.png',
width: 18.0,
height: 18.0,
),
onPressed: () {
setState(() {
closeEye = !closeEye;
});
},
),
),
],
),
],
),
),
//密码登录按钮
Container(
margin: EdgeInsets.fromLTRB(52.0, 0.0, 52.0, 15.0),
//垂直居中水平靠左对齐
alignment: Alignment.centerRight,
height: 18.0,
child: MaterialButton(
padding: EdgeInsets.all(0.0),
minWidth: 60.0,
height: 16.0,
//水波纹颜色透明
splashColor: Colors.transparent,
highlightColor: Colors.transparent,
child: Text(
'忘记密码?',
textDirection: TextDirection.ltr,
textAlign: TextAlign.left,
style: TextStyle(
color: Color(0xFF20C6BA),
fontSize: 12.0,
),
),
onPressed: () {
print('忘记密码?');
},
),
),
//未注册...
Text(
'未注册邮箱,输入密码可进行注册并登录 ',
textDirection: TextDirection.ltr,
textAlign: TextAlign.center,
style: TextStyle(
// background: backgroundPaint,
color: Colors.white.withOpacity(0.4),
fontSize: 10.0,
),
),
//登录
MaterialButton(
height: 45.0,
minWidth: TYTool.getScreenWidth(context) - 104.0,
color: Color(0xFF20C6BA),
child: Text(
'登录',
style: TextStyle(
color: Colors.white,
fontSize: 15.0,
),
),
onPressed: () {
// login(context);
},
),
],
),
//loading
loadingDialog,
],
);
}
}
你会被这一层一层的括号套的眼睛都花了...
所以我建议书写规范就是:
1、把布局控件放在一起,把展示控件用Container包裹起来写到方法里创建。
2、使用 ‘尾随逗号’,最后一个属性后加个“,”,这样在格式化代码的时候就会自动换行。
3、视图和视图写在一起,手势方法和手势方法写在一起,网络请求和网络请求写在一起
当遵循这3个规范之后上面的代码就变成了这样:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:fitforce_coach/module/base/controller/BaseWidget.dart';
import 'package:fitforce_coach/utiliy/TYTool.dart';
class EmailController extends StatefulWidget {
// EmailControllerState emailState =
@override
State createState() {
return EmailControllerState();
}
}
class EmailControllerState extends State {
//默认闭合眼睛
bool closeEye = true;
//邮箱的控制器
TextEditingController emailControl = TextEditingController();
//密码的控制器
TextEditingController passwordControl = TextEditingController();
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
//设置状态栏颜色
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.light);
return Scaffold(
body: buildBody(context),
);
}
//视图-------------------------------------
Widget buildBody(BuildContext context) {
return Stack(
children: [
//背景
bgImage(context),
//全屏取消编辑手势
addCancelEditView(context),
Column(
children: [
Padding(
padding: EdgeInsets.only(
top: TYTool.getSysStatsHeight(context) + 16.0),
),
Stack(
// alignment: Alignment.centerLeft,
children: [
//返回按钮
Align(
alignment: Alignment.centerLeft,
child: backButton(context),
),
//logo图片
Align(
alignment: Alignment.center,
child: topImage(context),
),
],
),
Container(
margin: EdgeInsets.fromLTRB(52.0, 46.0, 52.0, 10.0),
child: Column(
children: [
//题目
topTitle(context),
//邮箱
emailTextField(context),
Padding(
padding: EdgeInsets.only(bottom: 30.0),
),
Stack(
children: [
//验证码
codeTextField(context),
//小眼睛
eyeButton(context),
],
),
],
),
),
//密码登录按钮
forgetPasswordButton(context),
//未注册...
detailText(context),
//登录
loginButton(context),
],
),
//loading
loadingDialog,
],
);
}
//view---------------------------------------------
//背景图片
Widget bgImage(BuildContext context) {
return Image.asset(
'images/login/login_bg.png',
width: TYTool.getScreenWidth(context),
height: TYTool.getScreenHeight(context),
fit: BoxFit.fill,
);
}
//返回按钮
Widget backButton(BuildContext context) {
return Container(
height: 30.0,
width: 30.0,
margin: EdgeInsets.only(left: 10.0),
child: IconButton(
padding: EdgeInsets.all(0.0),
icon: Icon(
Icons.arrow_back_ios,
color: Colors.white,
),
onPressed: () {
popLastController(context);
},
),
);
}
//logo图片
Widget topImage(BuildContext context) {
return Image.asset(
'images/login/login_logo.png',
width: 112.0,
height: 22.0,
);
}
//邮箱登录
Widget topTitle(BuildContext context) {
return Container(
margin: EdgeInsets.only(bottom: 30.0),
//宽度尽可能大
width: double.infinity,
child: Text(
'邮箱登录',
textDirection: TextDirection.ltr,
textAlign: TextAlign.start,
style: TextStyle(
// background: backgroundPaint,
color: Colors.white,
fontSize: 18.0,
),
),
);
}
//邮箱
Widget emailTextField(BuildContext context) {
return Theme(
data: ThemeData(primaryColor: Colors.white, hintColor: Colors.white54),
child: TextField(
style: TextStyle(fontSize: 14.0, color: Colors.white),
keyboardType: TextInputType.emailAddress,
cursorColor: Color(0xFF20C6BA),
cursorWidth: 1.5,
controller: emailControl,
decoration: InputDecoration(
hintText: '请输入邮箱地址',
),
),
);
}
//验证码
Widget codeTextField(BuildContext context) {
return Theme(
data: ThemeData(primaryColor: Colors.white, hintColor: Colors.white54),
child: TextField(
style: TextStyle(fontSize: 14.0, color: Colors.white),
cursorColor: Color(0xFF20C6BA),
cursorWidth: 1.5,
controller: passwordControl,
obscureText: closeEye,
decoration: InputDecoration(
hintText: '请输入密码',
),
),
);
}
//小眼睛
Widget eyeButton(BuildContext context) {
return Container(
margin: EdgeInsets.fromLTRB(
TYTool.getScreenWidth(context) - 104.0 - 38.0, 10.0, 20.0, 10.0),
height: 18.0,
child: MaterialButton(
padding: EdgeInsets.all(0.0),
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
child: Image.asset(
closeEye
? 'images/login/btn_login_eye_close.png'
: 'images/login/btn_login_eye_open.png',
width: 18.0,
height: 18.0,
),
onPressed: () {
didClickEyeButton(context);
},
),
);
}
//忘记密码
Widget forgetPasswordButton(BuildContext context) {
return Container(
margin: EdgeInsets.fromLTRB(52.0, 0.0, 52.0, 15.0),
//垂直居中水平靠左对齐
alignment: Alignment.centerRight,
height: 18.0,
child: MaterialButton(
padding: EdgeInsets.all(0.0),
minWidth: 60.0,
height: 16.0,
//水波纹颜色透明
splashColor: Colors.transparent,
highlightColor: Colors.transparent,
child: Text(
'忘记密码?',
textDirection: TextDirection.ltr,
textAlign: TextAlign.left,
style: TextStyle(
color: Color(0xFF20C6BA),
fontSize: 12.0,
),
),
onPressed: () {
didClickForgetPasswordButton(context);
},
),
);
}
//未注册....
Widget detailText(BuildContext context) {
return Text(
'未注册邮箱,输入密码可进行注册并登录 ',
textDirection: TextDirection.ltr,
textAlign: TextAlign.center,
style: TextStyle(
// background: backgroundPaint,
color: Colors.white.withOpacity(0.4),
fontSize: 10.0,
),
);
}
//登录
Widget loginButton(BuildContext context) {
return MaterialButton(
height: 45.0,
minWidth: TYTool.getScreenWidth(context) - 104.0,
color: Color(0xFF20C6BA),
child: Text(
'登录',
style: TextStyle(
color: Colors.white,
fontSize: 15.0,
),
),
onPressed: () {
// login(context);
},
);
}
//其他事件-------------------------------------------
//忘记密码
void didClickForgetPasswordButton(BuildContext context) {
print('忘记密码?');
}
//点击眼睛
void didClickEyeButton(BuildContext context) {
setState(() {
closeEye = !closeEye;
});
}
//网络请求-------------------------------------------
}
会发现代码括号层级少了很多,总算能看得下去了...
这里面有些我自己封装的框架和基类,直接使用有些方法会爆红的,仅仅是展示代码书写规范,这样整理后条理清晰了很多,在复杂的页面更能体现出来。