正如大家所知,最近又是召开了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_ui和cupertino_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.dart 和 package: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设置成falseinInfo.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,直接获取底层原生窗口(HWND、NSWindow 或 GtkWindow)的指针。借助这一功能,你可以使用更高级的原生特性,例如 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_aflutter 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.highContrast和MediaQueryData.invertColors)。Text.rich中嵌套的文本 Span 现在会按照实际布局顺序排列在语义树中,同时BlockSemantics也新增了阻止键盘焦点的功能。

- 文本与选择: 在移动端进行轻微滚动时,文本选择手柄现在可以保持稳定,不会随意移动;同时,现在也可以通过键盘快捷键关闭已打开的文本选择菜单。在 Android 上,当选择手柄靠近屏幕顶部时,也不会再遮挡上下文菜单。
以前: 
现在: 
还修复了一个SelectableRegion 的崩溃问题:当用户从空的可滚动容器中开始选择内容时,不会再导致崩溃。此外,淡化显示的可选文本中出现的高亮视觉异常也已得到解决。

- 手势与滚动: 改进了通过 Platform View 嵌入的原生 iOS 视图的手势传递机制。
EdgeDraggingAutoScroller现在会遵循当前滚动视图的ScrollPhysics设置,从而避免在已锁定滚动的列表上触发自动滚动。

-
ImageIcon的seOriginalColors: true可以保留原图片颜色,不会强制按 Icon tint 处理 -
AnimatedCrossFade可以显式指定 clipping behavior -
ImageStreamListener现在可以直接跟踪 image stream error