前些日子在Google connect上和Flutter的开发人员聊天时,我也向他们吐槽过Flutter开发上的一些痛点,他说他们也在致力于提升开发者开发体验,比如说Dot shorthand,这个语法糖对于Swift使用者来说可以说是不涌再熟悉了,而在Dart 3.10中,我们即将迎来这个新特性。
欢迎关注我的公众号:OpenFlutter,感恩
使用“点语法糖”以更简洁的方式访问 Dart 中的枚举。
引言
Dart 3.10 引入了点语法糖(dot shorthands),这是一种上下文感知的访问静态成员、构造函数和枚举值的方式,无需重复类型名。只要 Dart 能从上下文中推断出预期的类型,你就可以用 .blue 来代替 Color.blue,用 .new(args) 代替 T.new(args)。这样做能让 Flutter 的 Widget 树更简洁、switch 语句更精炼、静态调用更易读。
本文将解释这项功能,展示它的优势(以及局限),并提供迁移建议、代码模式和需要避免的陷阱。
什么是点语法糖?
点语法糖是在明确的上下文类型上的隐式静态访问。
如果一个变量、参数或表达式期望的类型是 T,那么在有效的情况下, .id 将被视为 T.id, .new(args) 将被视为 T.new(args)。
快速示例
Color color = .blue; // -> Color.blue
crossAxisAlignment: .start, // -> CrossAxisAlignment.start
mainAxisSize: .min, // -> MainAxisSize.min
int value = .parse("42").abs(); // -> int.parse("42").abs()
List<int> xs = .filled(3, 0); // -> List<int>.filled(3, 0)
Zone z = .current.errorZone; // -> Zone.current.errorZone
为什么这很重要
- 减少 Flutter UI 中的冗余:
CrossAxisAlignment.start这样的属性可以写成.start。 - 更整洁的
switch语句:case语句读起来更像自然语言。 - 静态方法和构造函数: 当类型显而易见时,代码更专注于意图。
语法(通俗易懂)
- 新的以点
.开头的形式,可以在出现后缀表达式的地方使用。 - 头部包括
.id和.new(无名构造函数)。 - 当目标是
const构造函数或静态const时,常量形式支持const .id(args)和const .new(args)。
类型推断和语义
- 编译器会从周围的表达式中,为其分配一个语法糖上下文类型。
- 静态查找会使用该上下文来解析
.id或.new,就像T.id或T.new一样。 - 运行时行为与显式形式完全相同——纯粹是语法糖。
链式选择器
Future<String> s = .wait([lazy(), lazy()]).then((v) => v.join());
// -> Future.wait([…]).then(…)
第一个语法糖的上下文来自整个表达式;后续的选择器则像往常一样运行。
什么时候可以使用,什么时候时不可以
允许
Endian e = .little; // static const
if (Endian.host == .big) {} // 特殊情况下的比较
const zero = (.zero); // const static getter
List<String> l = .filled(2, "x"); // constructor
不允许(没有上下文)
int v = .parse("42"); // ❌ 没有办法可以推断出类型是 `int`
var f = .new<int>(); // ❌ 能用的 `.new` 是不合法的
if (.host == Endian.host) {} // ❌ 相等性比较只对右侧(RHS)的语法糖进行特殊处理。
Flutter的关注点
以前
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: widgets,
);
以后
Column(
crossAxisAlignment: .start,
mainAxisSize: .min,
children: widgets,
);
Material states
final style = ButtonStyle(
visualDensity: .compact,
alignment: .center,
);
Switches
switch (themeMode) {
case .system: …
case .light: …
case .dark: …
}
测试相关
expect(actualAlignment, .centerLeft);
expect(Endian.host == .big, isFalse);
迁移技巧
- 从 Widget 树中的枚举开始(
.start、.spaceBetween、.stretch)。 - 转换
switch/case代码块。 - 在预期类型明确的静态构造函数中使用。
- 在可以推断出多个候选类型的地方避免使用语法糖——优先选择清晰度。
陷阱和注意事项
- 没有上下文? 就没有语法糖。
generic .new是无效的。==仅在右侧(RHS)是字面量语法糖时才进行特殊处理。
风格指南
- ✅ 推荐在 Flutter 属性和
switch case中使用语法糖。 - ⚠️ 考虑静态方法的可读性(例如,
int.parse与.parse)。 - ❌ 不要在长链式调用中堆砌多个语法糖,如果这会影响清晰度。
语言上的相似性
- Swift: 当类型已知时,有类似的枚举语法糖。
- Kotlin/TS: 通常需要限定符;Dart 提供了一个简洁的中间地带。
完整示例
// Futures
Future<List<int>> xs = .wait([.value(1), .value(2)]);
// Parsing
final n = (int?).tryParse("42") ?? 0; // 上下文来自 ?/??
// Zones
final errorZone = .current.errorZone;
// Lists
final list = <String>[].toList(growable: true);
final fixed = .filled(3, "x"); // 上下文: List<String>
结论
点语法糖虽小但强大:样板代码更少,意图表达更清晰。在上下文明确的地方采用它——尤其是在 Flutter UI 代码和大量使用枚举的逻辑中——而在能够提高清晰度的地方,则优先使用完整的类型限定。