Flutter代码规范

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<StatefulWidget> createState() {
    return EmailControllerState();
  }
}

class EmailControllerState extends State<EmailController> {
  //默认闭合眼睛
  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: <Widget>[
        Image.asset(
          'images/login/login_bg.png',
          width: TYTool.getScreenWidth(context),
          height: TYTool.getScreenHeight(context),
          fit: BoxFit.fill,
        ),
        //全屏取消编辑手势
        addCancelEditView(context),
        Column(
          children: <Widget>[
            Padding(
              padding: EdgeInsets.only(
                  top: TYTool.getSysStatsHeight(context) + 16.0),
            ),
            Stack(
              // alignment: Alignment.centerLeft,
              children: <Widget>[
                //返回按钮
                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: <Widget>[
                  //题目
                  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: <Widget>[
                      //验证码
                      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<StatefulWidget> createState() {
    return EmailControllerState();
  }
}

class EmailControllerState extends State<EmailController> {
  //默认闭合眼睛
  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: <Widget>[
        //背景
        bgImage(context),
        //全屏取消编辑手势
        addCancelEditView(context),
        Column(
          children: <Widget>[
            Padding(
              padding: EdgeInsets.only(
                  top: TYTool.getSysStatsHeight(context) + 16.0),
            ),
            Stack(
              // alignment: Alignment.centerLeft,
              children: <Widget>[
                //返回按钮
                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: <Widget>[
                  //题目
                  topTitle(context),
                  //邮箱
                  emailTextField(context),
                  Padding(
                    padding: EdgeInsets.only(bottom: 30.0),
                  ),
                  Stack(
                    children: <Widget>[
                      //验证码
                      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;
    });
  }

//网络请求-------------------------------------------

}

会发现代码括号层级少了很多,总算能看得下去了...
这里面有些我自己封装的框架和基类,直接使用有些方法会爆红的,仅仅是展示代码书写规范,这样整理后条理清晰了很多,在复杂的页面更能体现出来。