2. Dart语言核心与工程规范
Flutter 的 UI 层用 Dart 编写,Dart 的语法特性、类型系统与异步模型直接决定了 Flutter 代码的可读性与稳定性。本节围绕四个工程要点展开:Dart 语法基础、空安全(null safety)、异步编程(Future/Stream/async-await),以及代码风格与静态分析工具。目标不是罗列语法,而是建立一套“写得出、看得懂、查得出错”的工程习惯。
一、Dart 语法基础:类型、变量与函数
Dart 是静态类型语言,但支持类型推断。声明变量时优先使用 final 与 const:final 表示运行期只赋值一次,const 表示编译期常量。在 Flutter 中,const 构造函数能显著减少 Widget 重建开销,因此“能用 const 就用 const”是一条实用准则。
final DateTime now = DateTime.now(); // 运行期确定
const double pi = 3.14159; // 编译期常量
const List<int> ids = [1, 2, 3]; // const 集合
class Point {
final double x, y;
const Point(this.x, this.y); // const 构造函数
}
Dart 的命名参数与可选参数在 Flutter 组件中极为常见。使用 {} 声明命名参数,配合 required 强制必填,可以让调用点自解释:
void showToast(String message, {required int duration, bool isError = false}) {
// duration 必填,isError 默认 false
}
此外,级联操作符 .. 适合连续配置同一对象,集合的 map/where/fold 适合数据转换。这些语法在构建列表型 UI 时使用频率很高。
二、空安全:把“可能为空”写进类型
Dart 2.12 起默认启用空安全。核心规则是:类型默认非空(String 不能为 null),可空类型必须显式加 ?(String?)。编译器据此在编译期拦截空引用风险。工程中常见的处理方式有三类:
- 空合并:
name ?? '匿名用户',左侧为 null 时取右侧默认值。 - 安全调用:
user?.address?.city,链上任一环节为 null 即整体返回 null。 - 非空断言:
user!.name,向编译器承诺“此处一定非空”,若判断错误会在运行期抛异常,应谨慎使用。
一个典型场景是解析接口返回的 JSON。字段可能缺失,因此先按可空读取,再给出默认值或提前返回:
Map<String, dynamic> json = {'name': 'Lily'};
String name = json['name'] as String? ?? '未知';
int age = (json['age'] as int?) ?? 0;
对于“延迟初始化但保证非空”的字段,可用 late 修饰,例如在 initState 中赋值的控制器。但 late 只是把检查推迟到运行期,若忘记赋值仍会抛 LateInitializationError,因此它更适合生命周期明确的场景。
三、异步编程:Future、Stream 与 async/await
Dart 采用单线程事件循环模型,耗时操作通过异步返回结果,避免阻塞 UI 线程。Future<T> 表示“未来会完成的单个结果”,Stream<T> 表示“随时间到达的多个结果”。
使用 async/await 可以把异步代码写成接近同步的顺序结构,可读性远高于层层回调:
Future<List<Article>> loadArticles() async {
try {
final response = await http.get(Uri.parse('https://api.example.com/articles'));
if (response.statusCode != 200) {
throw Exception('请求失败: ${response.statusCode}');
}
final data = jsonDecode(response.body) as List<dynamic>;
return data.map((e) => Article.fromJson(e)).toList();
} catch (e) {
return <Article>[];
}
}
需要注意:await 之后的代码会在事件循环的后续微任务中执行,因此“await 前后 UI 状态可能已变化”。在 Flutter 中,若 await 之后要调用 setState,应先判断 mounted,避免组件已销毁仍更新状态。
对于连续数据(如搜索框输入、WebSocket 消息),使用 Stream 配合 StreamBuilder 更自然。若只需监听一次,用 await for 或 listen;若需要取消订阅,务必保存 StreamSubscription 并在 dispose 中调用 cancel()。
四、代码风格与静态分析工具
Dart 官方风格指南(Effective Dart)给出了命名、格式与文档约定:类名用 UpperCamelCase,变量与函数用 lowerCamelCase,常量用 lowerCamelCase,私有成员以下划线开头。这些约定不是审美问题,而是让团队代码在阅读时减少认知负担。
工程中真正落地风格约束的是 analysis_options.yaml 与 dart analyze。Flutter 项目默认继承 flutter_lints 规则集,可在项目根目录的配置文件中启用更严格的规则:
include: package:flutter_lints/flutter.yaml
linter:
rules:
- prefer_const_constructors
- avoid_print
- always_declare_return_types
- unawaited_futures
analyzer:
errors:
avoid_print: warning
unawaited_futures: error
其中 unawaited_futures 对异步工程尤其重要:它会把“忘记 await 的 Future”标记为错误,避免异步异常被静默吞掉。avoid_print 则提醒开发者改用 debugPrint 或日志库,防止发布包中残留调试输出。
执行 dart analyze 或 flutter analyze 后,输出会按严重级别分为 error、warning、info。工程实践中建议把 CI 流水线配置为“存在 error 即失败”,warning 逐步清零。配合 dart format . 统一格式,可以在提交前自动完成风格收敛,让代码评审聚焦在逻辑而非空格与换行上。
analysis_options.yaml 中先启用少量高价值规则(如 prefer_const_constructors、unawaited_futures),待团队适应后再逐步增加,避免一次性引入大量告警导致无人处理。把以上四点连起来看:语法与类型决定代码如何表达,空安全把“可能为空”提前到编译期,异步模型决定耗时操作如何不阻塞 UI,而静态分析工具则把团队约定变成可自动执行的检查。四者共同构成 Flutter 工程中 Dart 代码的质量底座。