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 小时前
移动端滑动手势冲突:overflow: auto导致外层横向滑动失效
前端·javascript·css·vue.js·html
默_笙2 小时前
🎃 前端学了 Next.js,后端该学啥?NestJS 就是 Node 版的蜜雪冰城
前端·javascript
前端snow3 小时前
ai agent --- 实现 openclaw 定时间效果
前端
码云之上4 小时前
换模型之后,Chatbot 为什么要自己做 compact?
前端·agent·前端工程化
星栈4 小时前
自定义 UI-UX-Pro-Max 的 CSV 知识库,把 AI 生成的后台拉回行业该有的样子
前端·agent·weui
研☆香4 小时前
简单的图片上传 删除 预览
前端
ITresearchGuest4 小时前
我用 AI 一小时写了一个世界杯数据可视化平台|前端 VibeCoding 初体验
前端·人工智能·信息可视化
慧一居士6 小时前
Vite项目中使用Less步骤,详细使用示例
前端·css
花间相见6 小时前
【LangChain组件03】—— LangChain Agents 执行与状态:工作流程与状态管理实战
java·前端·langchain
kyriewen6 小时前
面试官让我用 AI 重构一个 8 年陈的 React 组件——他说他不看代码,只看我会不会拆
前端·javascript·面试