Flutter 的 switch 语句补遗

我的 App 里,一个消息气泡变成空白了,非常奇怪,此前一直是没问题的,经过调试定位我发现:

  static TextSpan _buildRootSpan(BuildContext ctx, List<LinkifyElement> parts, TextStyle? style) {
    List<InlineSpan> children = [];

    for (var p in parts) {
      switch (p.runtimeType) {
        case TextElement _:
          children.add(TextSpan(text: p.text));
          break;
        case UrlElement _:
          var url = p as UrlElement;
          var recognizer = TapGestureRecognizer();
          recognizer.onTap = () {
            AutoRouter.of(ctx).push(WebRoute(targetUrl: url.url));
          };
          children.add(TextSpan(
            text: url.text,
            style: TextStyle(
              color: Theme.of(ctx).colorScheme.secondary,
              decoration: TextDecoration.underline,
              decorationColor: Theme.of(ctx).colorScheme.secondary,
            ),
            recognizer: recognizer,
          ));
          break;
      }
    }

    return TextSpan(
      style: style,
      children: children,
    );
  }

问题出在这段代码里,最后的 TextSpan 返回的内容是空的。怎么会如此呢?从没改过这段代码。加了很多 Log 后,我发现,Switch 语句的所有 case 都没命中!我对 p.runtimeType 进行 switch 判断,但是明明类型没错,但是就是不命中。

问了一下 ChatGPT,给出了明确答案:

在 Dart 中,你不能直接在 switch 语句中对类型进行判断,因为 switch 语句要求其表达式的结果必须是编译时常量,而类型检查(使用 is 关键字)不满足这个条件。但是,你可以使用一些替代方案来根据对象的类型执行不同的逻辑。

一种常见的做法是使用 if 语句和 is 类型检查操作符,像这样:

void performAction(Object obj) {
  if (obj is TypeA) {
    // 处理 TypeA
  } else if (obj is TypeB) {
    // 处理 TypeB
  } else {
    // 处理其他类型
  }
}

另一种方法是使用 Dart 2.15 引入的 switchcase 语法的增强特性,结合枚举和 when 函数(如果你定义了一个这样的函数),你可以模仿类型判断的行为。虽然这不是直接的类型判断,但它允许你在相似的上下文中根据不同的条件执行不同的代码块,特别是当你处理的是固定集合中的值时。

如果你真的需要根据类型来分支逻辑,使用 if-is 检查可能是最直接和最清晰的方式。 Dart 的类型系统和 is 操作符非常强大,可以满足大多数需要类型判断的场景。

说实在的,我觉得我穿越了,代码一直是这样的,也没改过,功能正常也好多个版本了,怎么突然就跟我说,dart 压根不支持对类型进行 switch 这不是搞笑么???

改成 ifis 后,立刻全部正常了,我服了。引以为戒吧。

又查了点资料,感觉跟 3.0 新语法有关: Dart 3.0 语法新特性 | switch 匹配加强