Flutter小技巧总结之dart/flutter 中的代码规范
一,概述
官方规范文档
- 官方代码规范:dart语言官方有自己的代码规范和相关的说明,在dartlang官网上,英文好的建议阅读原文
- 官方连接地址: https://www.dartlang.org/guides/language/effective-dart/style
正文文档中图片的说明
- 绿色部分为正例,右上角带
good
标识。 - 红色是反例,右上角带
bad
标识
二,规范总结
-
标识方案
-
- 在dart有3种常规标识方案
- 第一个为大写字母开头的驼峰式 如
UserInterface
每个词的首字母为大写 - 第二个是小写开头的驼峰式,如
testRun
,第一个单词是小写,后续每个单词首字母大写 - 第三个是每个单词均为小写,以下划线分隔,如
user_response
- 第一个为大写字母开头的驼峰式 如
- 项目中的常规用法
-
文件名: 小写+下划线
-
类型名(类名,函数类型名):大写开头驼峰
-
变量名(包含const final 常量):使用小写开头驼峰, 项目有特殊要求 const可以使用大写+下划线的方式,如同
java
中一样 -
导包as后的名称为小写+下划线
-
不要用匈牙利命名法中的kXXXX 这样的命名方式,应该去掉k
-
超过两位的英文缩写一律按该单词为普通小写单词处理,使用小写
-
导包有顺序要求,且每"部分"间空行分隔开,每部分内按字母排序,按如下顺序排序
-
dart sdk内的库
-
flutter内的库
-
第三方库
-
自己的库
-
相对路径引用
-
-
- 在dart有3种常规标识方案
-
-
-
- 先全部import再export,不要交替进行
-
-
-
-
- 单行字符建议不要超过80个
- 流程控制语句无论如何都使用花括号包裹,即使只有单行
-
- 类型名称
-
- 适用于类名,注解名,typedef定义的函数名
- 这里有一个特例,当你的注解是一个const的常量时,使用
@foo
的方式作为注解
-
库名称,文件名用小写+下划线

-
- 使用小写+下划线方式命名library,文件名
- 原因如下:
某些文件系统不区分大小写,因此许多项目要求文件名全部为小写。使用分隔字符允许名称仍以该形式可读。使用下划线作为分隔符可确保名称仍然是有效的Dart标识符
-
导入时

-
- 当导入包时, 如果涉及到as, 一律使用小写+下划线方式
- 当导入包时, 如果涉及到as, 一律使用小写+下划线方式
- 其他标识符

-
- 包含顶级成员,类成员,方法内成员,参数名,命名参数名,一律使用小写驼峰式
- 包含顶级成员,类成员,方法内成员,参数名,命名参数名,一律使用小写驼峰式
- 常量名称
-
- 建议使用小写驼峰式命名
- 但是你的项目中如果使用大写+下划线分割单词的方式,则可以继续使用这种方式
- 这里有个小说明:最初dart中采用的大写+下划线方式,但是后来有一些变量需要修改为非const变量,就需要修改为小写驼峰式,后一律使用小写驼峰式
- 缩写相关

-
- 超过两位的使用常规方式,两位以内使用大写
- 在小写驼峰式中,会出现一些约定俗成的缩写,如
http
ftp
io
等,这些在英文词法中都应该是大写,但大写连续会破坏可读性,如HTTPSFTP
,你不知道是HTTPS FTP 还是 HTTP SFTP,所以采用如上图绿色的方式来命名
- 不要使用前缀字母

-
- 匈牙利命名法中使用缩写开头的小写驼峰命名法时,是因为当时编辑器无法很好帮助理解你的代码,这样命名法能够很好的帮助你去理解代码,现在dart语言中,编辑器可以很好的通过声明等方式帮你理解你的代码,故,不要使用前缀字母,如上图绿色
-
规定代码的顺序
为了使文件保持整洁,我们有一个规定代码顺序。每个“部分”应该用空行分隔。
-
- 保证dart的导入顺序在所有其他包之前

-
- 保证带包名的引用方式在相对路径引用之前
-
- 首先import第三方的包,
导入自己的包在后面一个单独的部分
- 首先import第三方的包,
-
- 全部导入完毕后,再export 导出

-
- 每个部分按字母顺序排序
- 流程控制使用花括号

- 异步
- 优先使用async/await代替原始的futures
async/await语法提高了可读性,允许你在异步代码中使用所有Dart控制流结构。
Future<int> countActivePlayers(String teamName) async { try { var team = await downloadTeam(teamName); if (team == null) return 0; var players = await team.roster; return players.where((player) => player.isActive).length; } catch (e) { log.error(e); return 0; } }
- 当异步没有任何用处时,不要使用它
如果可以在不改变函数行为的情况下省略异步,那么就这样做。
Future afterTwoThings(Future first, Future second) { return Future.wait([first, second]); }
不推荐写法:
Future afterTwoThings(Future first, Future second) async { return Future.wait([first, second]); }
- 优先使用async/await代替原始的futures
-
不要使用new
Dart2使new 关键字可选
-
推荐写法:
Widget build(BuildContext context) { return Row( children: [ RaisedButton( child: Text('Increment'), ), Text('Click!'), ], ); }
-
不推荐如下写法:
Widget build(BuildContext context) { return new Row( children: [ new RaisedButton( child: new Text('Increment'), ), new Text('Click!'), ], ); }
-
-
构造函数
尽可能使用初始化的形式- 不推荐如下写法:
class Point { num x, y; Point(num x, num y) { this.x = x; this.y = y; } }
- 不推荐如下写法:
-
- 推荐如下写法:
class Point { num x, y; Point(this.x, this.y); }
- 推荐如下写法:
-
优先使用final字段来创建只读属性
尤其对于StatelessWidget,在不需要的时候不要用this
-
不推荐如下写法:
class Box { var value; void clear() { this.update(null); } void update(value) { this.value = value; } }
-
推荐如下写法:
class Box { var value; void clear() { update(null); } void update(value) { this.value = value; } }
-
- 类成员
不要把不必要地将字段包装在getter和setter中
- 不推荐如下写法:
class Box { var _contents; get contents => _contents; set contents(value) { _contents = value; } }
- 不推荐如下写法:
- 避免储存你能计算的东西
在设计类时,您通常希望将多个视图公开到相同的底层状态。通常你会看到在构造函数中计算所有视图的代码,然后存储它们:- 应该避免的写法:
class Circle { num radius; num area; num circumference; Circle(num radius) : radius = radius, area = pi * radius * radius, circumference = pi * 2.0 * radius; }
如上代码问题:
-
浪费内存
-
缓存的问题是无效——如何知道何时缓存过期需要重新计算?
-
- 应该避免的写法:
-
- 推荐的写法如下:
class Circle { num radius; Circle(this.radius); num get area => pi * radius * radius; num get circumference => pi * 2.0 * radius; }
- 推荐的写法如下:
- 变量
不要显式地将变量初始化为空
- 在Dart中,未显式初始化的变量或字段自动被初始化为null。不要多余赋值null
int _nextId; class LazyId { int _id; int get id { if (_nextId == null) _nextId = 0; if (_id == null) _id = _nextId++; return _id; } }
-
不推荐如下写法:
int _nextId = null; class LazyId { int _id = null; int get id { if (_nextId == null) _nextId = 0; if (_id == null) _id = _nextId++; return _id; } }
- 在Dart中,未显式初始化的变量或字段自动被初始化为null。不要多余赋值null
- 参数的使用
- 使用=将命名参数与其默认值分割开
-
由于遗留原因,Dart均允许“:”和“=”作为指定参数的默认值分隔符。为了与可选的位置参数保持一致,使用“=”。
void insert(Object item, {int at = 0}) { ... }
-
不推荐如下写法:
void insert(Object item, {int at: 0}) { ... }
-
- 不要使用显式默认值null
-
如果参数是可选的,但没有给它一个默认值,则语言隐式地使用null作为默认值,因此不需要编写它
void error([String message]) { stderr.write(message ?? '\n'); }
-
不推荐如下写法:
void error([String message = null]) { stderr.write(message ?? '\n'); }
-
- 使用=将命名参数与其默认值分割开
- 不要使用List.from(),除非打算更改结果的类型
- 给定一个迭代,有两种明显的方法可以生成包含相同元素的新列表
var copy1 = iterable.toList(); var copy2 = List.from(iterable);
- 明显的区别是第一个比较短。重要的区别是第一个保留了原始对象的类型参数
// Creates a List<int>: var iterable = [1, 2, 3]; // Prints "List<int>": print(iterable.toList().runtimeType); // Creates a List<int>: var iterable = [1, 2, 3]; // Prints "List<dynamic>": print(List.from(iterable).runtimeType);
- 给定一个迭代,有两种明显的方法可以生成包含相同元素的新列表
- 避免使用带有函数字面量的Iterable.forEach()
-
在Dart中,如果你想遍历一个序列,惯用的方法是使用循环。
for (var person in people) { ... }
-
不推荐如下写法:
people.forEach((person) { ... });
-
- 考虑使用高阶方法转换序列
如果有一个集合,并且希望从中生成一个新的修改后的集合,那么使用.map()、.where()和Iterable上的其他方便的方法通常更短,也更具有声明性
var aquaticNames = animals .where((animal) => animal.isAquatic) .map((animal) => animal.name);
- 尽可能使用集合字面量
- 如果要创建一个不可增长的列表,或者其他一些自定义集合类型,那么无论如何,都要使用构造函数。
var points = []; var addresses = {}; var lines = <Lines>[];
- 不推荐如下写法:
var points = List(); var addresses = Map();
- 如果要创建一个不可增长的列表,或者其他一些自定义集合类型,那么无论如何,都要使用构造函数。
- 不要使用.length查看集合是否为空
- 推荐写法
if (lunchBox.isEmpty) return 'so hungry...'; if (words.isNotEmpty) return words.join(' ');
- 不推荐如下写法:
if (lunchBox.length == 0) return 'so hungry...'; if (!words.isEmpty) return words.join(' ');
- 推荐写法
- 使用相邻字符串连接字符串文字
如果有两个字符串字面值(不是值,而是实际引用的字面值),则不需要使用+连接它们。就像在C和c++中,简单地把它们放在一起就能做到。这是创建一个长字符串很好的方法但是不适用于单独一行。- 推荐写法
raiseAlarm( 'ERROR: Parts of the spaceship are on fire. Other ' 'parts are overrun by martians. Unclear which are which.'
); - 不推荐写法:
raiseAlarm(
'ERROR: Parts of the spaceship are on fire. Other ' + 'parts are overrun by martians. Unclear which are which.'
);
- 推荐写法
- 优先使用模板字符串
'Hello, $name! You are ${year - birth} years old.';
-
在不需要的时候,避免使用花括号
'Hi, $name!' "Wear your wildest $decade's outfit."
-
不推荐如下写法:
'Hello, ' + name + '! You are ' + (year - birth).toString() + ' y...';
-
不推荐如下写法:
'Hi, ${name}!' "Wear your wildest ${decade}'s outfit."
-
-
注释
-
要像句子一样格式化
除非是区分大小写的标识符,否则第一个单词要大写。以句号结尾(或“!”或“?”)。对于所有的注释都是如此:doc注释、内联内容,甚至TODOs。即使是一个句子片段。-
不推荐写法
greet(name) { // Assume we have a valid name. print('Hi, $name!'); }
-
不推荐如下写法:
greet(name) { /* Assume we have a valid name. */ print('Hi, $name!'); }
可以使用块注释(/…/)临时注释掉一段代码,但是所有其他注释都应该使用//
-
-
- Doc注释
- 使用///文档注释来记录成员和类型。
使用doc注释而不是常规注释,可以让dartdoc找到并生成文档。
/// The number of characters in this chunk when unsplit. int get length => ...
由于历史原因,达特茅斯学院支持道格评论的两种语法:///(“C#风格”)和/… /(“JavaDoc风格”)。我们更喜欢/// 因为它更紧凑。
/和/在多行文档注释中添加两个无内容的行。在某些情况下,///语法也更容易阅读,例如文档注释包含使用*标记列表项的项目符号列表。
- 使用///文档注释来记录成员和类型。
- 考虑为私有api编写文档注释
Doc注释并不仅仅针对库的公共API的外部使用者。它们还有助于理解从库的其他部分调用的私有成员
- 用一句话总结开始doc注释
以简短的、以用户为中心的描述开始你的文档注释,以句号结尾。
/// Deletes the file at [path] from the file system. void delete(String path) { ... }
- 不推荐如下写法:
/// Depending on the state of the file system and the user's permissions, /// certain operations may or may not be possible. If there is no file at /// [path] or it can't be accessed, this function throws either [IOError] /// or [PermissionError], respectively. Otherwise, this deletes the file. void delete(String path) { ... }
- 不推荐如下写法:
- “doc注释”的第一句话分隔成自己的段落
在第一个句子之后添加一个空行,把它分成自己的段落
/// Deletes the file at [path]. /// /// Throws an [IOError] if the file could not be found. Throws a /// [PermissionError] if the file is present but could not be deleted. void delete(String path) { ... }
- 用一句话总结开始doc注释