Flutter 3.47: material/cupertino终于解耦了

正如大家所知,最近又是召开了Google I/O China,很遗憾Google这次没给Flutter安排点场面,但好在Flutter 3.47 还是悄无声息的发布了。这次更新的主要有 Material / Cupertino 正式从 Flutter SDK 解耦、Impeller 成为 PC 端全系默认渲染器、Widget Preview 转 Stable、Wasm 默认化继续推进、 给 Xcode 27 / iOS 27 / macOS 27 适配做迁移

更新内容中规中矩,Desktop上还是得到了不少加强。

material/cupertino 分家了

material_uicupertino_ui终于迎来了真正的1.0.0:

yaml 复制代码
dependencies:
  material_ui: ^1.0.0
  cupertino_ui: ^1.0.0

以前都是这样的:

dart 复制代码
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';

现在可以这样导入了:

dart 复制代码
import 'package:material_ui/material_ui.dart';
import 'package:cupertino_ui/cupertino_ui.dart';

这么做最重要的一个好处就是UI相关包可以脱离Flutter的更新计划进行独立更新,毕竟Flutter一个季度一个Stable版本谁能受了?而Flutter目前也正计划让这些UI package 可以按照周级别 cadence 发布修复和新功能。

迁移起来也非常简单,只需要一个命令:

css 复制代码
dart fix --apply --code=migrate_design_widgets

这个工具会自动将 package:flutter/material.dartpackage:flutter/cupertino.dart 中的导入更新为新的独立软件包。

注意: 如果迁移工具在更新 pubspec.yaml 时遇到问题(这是早期版本中的一个已知 Bug),可以手动运行 flutter pub add material_ui 来解决。如果你还使用了 Cupertino,则同时添加 cupertino_ui。完成后,再运行一次 dart fix --apply

当然 3.47 版本并不会强迫你一定要转移,老的导入方式还在。 如果是纯app里的material/cupertino里的迁移,用上面的方法当然是没什么问题了,主要是项目的第三方依赖怎么办?比如:

bash 复制代码
App
 ├── material_ui
 │
 └── 模块 A
      └── package:flutter/material.dart

这时候出现新旧material混用怎么办?Flutter当不会坐视不管了,也是为大家提供了MaterialUiCompatibilityBridge这种方式来让大家渡过这个阵痛期:

dart 复制代码
import 'package:material_ui/material_ui.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF6750A4)),
      ),
      builder: (BuildContext context, Widget? child) {
        return MaterialUiCompatibilityBridge(child: child!);
      },
      home: const HomeScreen(),
    );
  }
}

与此同时,flutter_localizations这个包也被拆了,也就是说,现在 Material / Cupertino 自己对应的 localization 也迁到了各自 package: 以前:

dart 复制代码
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter/material.dart';

// ...
localizationsDelegates: const <LocalizationsDelegate<dynamic>>[
  GlobalCupertinoLocalizations.delegate,
  GlobalMaterialLocalizations.delegate,
  GlobalWidgetsLocalizations.delegate,
],

现在:

dart 复制代码
import 'package:material_ui/material_ui.dart';

// ...
localizationsDelegates: GlobalMaterialLocalizations.delegates,

目前按照官方的说法,核心 SDK 中原有的设计库计划在即将于 11 月发布的秋季稳定版中正式弃用,所以最好还是关注下依赖的包还有没有在维护,需不需要做迁移

如果你是flutter包的作者,那么把 package 从旧 Material/Cupertino API 迁到独立的design包,那这时候也应该发布为 major release ,做一个大版本的断档更新,毕竟这对用户来说也是一个破坏性更新。

Xcode 27 / iOS 27 / macOS 27

随着Apple要求iOS最低支持版本为15,Flutter 3.47 也把最低支持版本提升到了 15,macOS 也提到了 12 :

平台 原来 Flutter 3.47+
iOS 13 15
macOS 10.15 12

同时 UIScene 变成了强制要求,Xcode 27 没有适配 UIScene 会直接起动失败。

Apple的东西就这点特点好,强行推着你前进,不前进就带你玩。

此外,Intel Mac 也停止适配,Flutter 3.47 已经停止 Intel Mac 的自动化测试 ,Intel Mac 也开始进入退出阶段,所以推荐直接通过 flutter config --enable-macos-arm64-only 打包。

最后 SwiftPM 迁移已经接近完成,Top 100 iOS Flutter Plugins 中已经有 92 个迁移 Swift Package Manager ,如果你之前没有尝试过开启SPM,现在值得再尝试一次:flutter config --enable-swift-package-manager.

实际上,Flutter 3.44 起 SwiftPM 已经成为 iOS/macOS 默认的依赖管理器,CocoaPods 还会继续支持,但处于维护模式。

但是你不能抱有侥幸或者说拖延心理,毕竟 CocoaPods Registry 将在 2026 年 12 月 2 日进入永久只读模式了

CocoaPods is over。

而 3.47 还优化了一次 SwiftPM build pipeline,提前过滤掉没有必要构建的 package schemes,所以使用 SwiftPM 的 Flutter iOS/macOS 项目,构建时间也有进一步优化。

不过讲真,迁移SPM总体上问题不大,但总有那么些让人恶心的,比如微信SDK。

Impeller

从Flutter 3.47开始,Impeller也成为了macOS,Windows 和 Linux的默认渲染引擎了,不过官方也提供了回退机制。

  • macOS: 把 FLTEnableImpeller 设置成 false in Info.plist.
  • Windows: 在main.cpp中添加 project.set_impeller_switch(flutter::ImpellerSwitch::Disabled) .
  • Linux: 在my_application.cc中调用fl_dart_project_set_enable_impeller(project, FALSE).

现在Flutter除了在web上,已经全量使用Impeller了。

此外,macOS 现在默认启用广色域(Wide Gamut Color),可在支持的硬件上呈现更加丰富、鲜艳且精准的色彩。

Flutter 多窗口

Flutter多窗口问题真是一个老生常谈的问题了,虽然我本身没有Desktop的需求,但是评论区里总有人问,好在Flutter没让大家失望,多窗口仍然有新进展,比如 Windows / Linux 支持 popup window,所以一些以前很难做得真正"桌面原生"的东西,例如:

  • Context Menu
  • Floating palette
  • Tool window
  • Popup

现在,你还可以通过特定平台的控制器查询 windowHandle,直接获取底层原生窗口(HWNDNSWindowGtkWindow)的指针。借助这一功能,你可以使用更高级的原生特性,例如 Windows 上的可停靠窗格:

从上面这个Windows Dockable Pane demo可以看出,这一新特性对复杂的Desktop app真的非常有用。

与此同时,这次还修复了多个与窗口焦点和窗口创建显示有关的 Bug。在 Windows 上,激活某个窗口时,不会再把后台窗口带到前台;应用恢复运行时,也不会再出现窗口抢夺焦点的问题。

Linux 也修改了 window realization 顺序,避免第一帧时期的 warning / assertion,另外还新增 sized-to-content ,让普通 Window / Dialog 根据 Flutter content 自动计算窗口尺寸。

还有一个就是,Windows和Linux现在也支持Flavor了,比如asset可以通过Flavor区分了:

yaml 复制代码
flutter:
  assets:
    - path: assets/flavor_a/images
      flavors:
        - flavor_a
    - path: assets/flavor_b/images
      flavors:
        - flavor_c

然后使用对应命令进行打包:

  • flutter build windows --flavor flavor_a
  • flutter build linux --flavor flavor_a

Web

Wasm 开始支持 Deferred Loading ,3.47 在 main channel 上提供 experimental:

css 复制代码
flutter build web --release --wasm --enable-wasm-deferred-loading

这么做的好处就是,对于大型Web应用来说,可以将wasm拆分成多个更小的模式,并且按需延迟加载,从而提升应用的首次加载时间。

另外官方也表示了,后续 flutter build web --release --wasm 会变成默认,以后 Flutter Web 应该会完全转向 wasm 的唯一 render 。

Android

android 这次真是平平无奇,只有一些基线的更新,主要是:

项目 Flutter 3.47
Java 17,最低要求
Kotlin Gradle Plugin 2.4.0
Android Gradle Plugin 9.1.0
Gradle 9.3.1

另外 Flutter SDK 提供的默认 Android API 参数是:

ini 复制代码
compileSdkVersion = API 36
targetSdkVersion  = API 36
minSdkVersion     = API 24

也就是 3.47 SDK 当前提供的默认变量值 mini 已经提升到 24了

当然还有一个虚拟键盘问题:在处理按键事件时,Shift 等修饰键有时会一直处于按下状态。现在,按键响应器会跳过为虚拟键盘输入模拟物理按键的过程,从而让键盘交互更加稳定、准确。

其他

其他更新还有:

  • Widgets Preview终于正式版了,不过我从来没用过。

  • GenUI 更新到了 0.10.0,不过我还是没用过。 a2ui_core 抽成了独立的核心 package ,同时支持 A2UI client-side functions ,Agent 可以告诉 Flutter Client 调用客户端函数做某些逻辑,不需要每件事都问大模型

  • 无障碍(Accessibility) : Android 现在可以自动检测高对比度和颜色反转设置(MediaQueryData.highContrastMediaQueryData.invertColors)。Text.rich 中嵌套的文本 Span 现在会按照实际布局顺序排列在语义树中,同时 BlockSemantics 也新增了阻止键盘焦点的功能。

  • 文本与选择: 在移动端进行轻微滚动时,文本选择手柄现在可以保持稳定,不会随意移动;同时,现在也可以通过键盘快捷键关闭已打开的文本选择菜单。在 Android 上,当选择手柄靠近屏幕顶部时,也不会再遮挡上下文菜单。

以前:

现在:

还修复了一个SelectableRegion 的崩溃问题:当用户从空的可滚动容器中开始选择内容时,不会再导致崩溃。此外,淡化显示的可选文本中出现的高亮视觉异常也已得到解决。

  • 手势与滚动: 改进了通过 Platform View 嵌入的原生 iOS 视图的手势传递机制。EdgeDraggingAutoScroller 现在会遵循当前滚动视图的 ScrollPhysics 设置,从而避免在已锁定滚动的列表上触发自动滚动。
  • ImageIconseOriginalColors: true 可以保留原图片颜色,不会强制按 Icon tint 处理

  • AnimatedCrossFade 可以显式指定 clipping behavior

  • ImageStreamListener 现在可以直接跟踪 image stream error

相关推荐
郭邯1 小时前
一次「字符洁癖」引发的思考:我用 AI 写了个全角半角转换工具
前端
想要成为糕糕手1 小时前
🚀 在浏览器里跑 DeepSeek-R1?WebGPU 端侧推理实战(五)—— 中断、重置、缓存与流式生成
前端·react.js·llm
GuWenyue1 小时前
后端接口又双叒没写好?3 步搭建前端 Mock,从此告别"傻等后端"
前端·mocha
海兰1 小时前
【数据采集】开源的 Web 数据采集与处理Firecrawl(一)
前端·开源
vivo互联网技术1 小时前
从一键检测到 AI 修复:我们如何把无障碍检查做进研发流程
前端·人工智能
liuxiaocheng1 小时前
聊聊 Vercel AI SDK 的流式协议:前后端到底是怎么"边想边说"的
前端·后端
胡萝卜术1 小时前
在浏览器中跑 DeepSeek-R1:WebGPU 推理全流程深度解析
前端·javascript·面试
windliang2 小时前
Claude Code 源码分析(十):MCP 外部工具如何进入下一轮 Agent 调用
前端·算法·面试
Yan_chen6662 小时前
CTFHub XSS反射型实战攻略
前端·网络安全·漏洞·xss·ctfhub web前置技能