介绍

Flutter 3.29版本优化了开发流程,提升了性能,并更新了 Impeller、Cupertino、DevTools 等功能

之所以说带着“大坑”,主要是 3.29 更新带来了太多“意料之外”的东西,例如:

  • Dart 代码会直接在 Android/iOS 的主 UI 线程上运行,而不是单独的 Dart UI 线程,此时 Dart 和平台调用直接可以同步执行
  • iOS skia 正式被移除
  • 没有 Vulkan 驱动的 Android 设备将回退到在 OpenGLES 上运行的 Impeller,而不是使用 Skia
  • 移除了 Flutter Gradle 插件,之前没迁移的需要手动迁移适配
  • Web 平台 HTML renderer 正式移除
  • 全新的 DevTools
  • ···

框架

Cupertino updates

CupertinoNavigationBarCupertinoSliverNavigationBar导航栏可以配置底部组件(搜索框、分段组件)

  • bottomWidget 可以展示搜索框、分段组件
  • bottomMode 设置底部组件的显示状态
    1. NavigationBarBottomMode.automatic 跟随导航栏滚动状态变化,自动隐藏和出现
    2. NavigationBarBottomMode.always 在导航栏滚动的时候,都是显示的状态

导航栏支持配置大标题
CupertinoNavigationBar.large

设置底部组件

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
const CupertinoSliverNavigationBar(
leading: Icon(CupertinoIcons.person_2),
// This title is visible in both collapsed and expanded states.
// When the "middle" parameter is omitted, the widget provided
// in the "largeTitle" parameter is used instead in the collapsed state.
largeTitle: Text('Contacts'),
bottom: PreferredSize(
preferredSize: Size.fromHeight(100),
child: ColoredBox(color: Color(0xff191970), child: Text('Bottom Widget')),
),
trailing: Icon(CupertinoIcons.add_circled),
),

iOS模态非全屏弹出风格

模态弹出

文本选择

  • 当反转时,Flutter 的文本选择手柄在 iOS 上交换它们的顺序,且文本选择放大镜的边框颜色现在与当前主题匹配。

文本选择

Material

FadeForwardsPageTransitionsBuilder全新的 Material 3 (M3) 页面过渡构建器

  • FadeForwardsPageTransitionsBuilder旨在与 Android 最新的页面过渡行为相契合。
  • 过渡过程中,传入页面从右向左滑入并同时淡入
  • 传出页面从右向左滑出并同时淡出
  • 此全新过渡还解决了之前由 ZoomPageTransitionsBuilder 引起的性能问题。

页面过渡构建器

更新CircularProgressIndicatorLinearProgressIndicator Slider使其符合最新的 Material Design 3 规范

image.png|500

image.png|500

image.png|500

如果要使用更新的样式,需要将 year2023 属性设置为 false,或者将 ProgressIndicatorThemeData.year2023设置为 false
year2023 已经被==废弃==

  • 键盘导航现在可以正确触发 DropdownMenu.onSelected 回调
  • 改进了 TabBar 弹性标签动画
  • 改进了 RangeSlider 拇指对齐方式,包括分区、拇指填充和圆角
  • 增强了多个 Material 组件的可 Chip 性。Chip、 TooltipReorderableListView 新增了 mouseCursor 属性,允许在鼠标悬停时自定义鼠标光标

文本选择

Flutter 还通过继承自 SelectableRegionSelectionStatusScope 的 Widget 向您提供有关 SelectionAreaSelectableRegion 状态的信息。您可以使用 SelectableRegionSelectionStatusScope.maybeOf(context) 并检查 SelectableRegionSelectionStatus 来检查父级 SelectionAreaSelectableRegion 是否正在主动更改或已完成选择。

文本选择

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class MySelectableText extends StatefulWidget {  
  const MySelectableText({super.key, required this.selectionNotifier, required this.onChanged});

  final SelectionListenerNotifier selectionNotifier;
  final ValueChanged<SelectableRegionSelectionStatus> onChanged;

  @override
  State<MySelectableText> createState() => _MySelectableTextState();
}

class _MySelectableTextState extends State<MySelectableText{
  ValueListenable<SelectableRegionSelectionStatus>? _selectableRegionScope;

  void _handleOnSelectableRegionChanged() {
    if (_selectableRegionScope == null) {
      return;
    }
    widget.onChanged.call(_selectableRegionScope!.value);
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    _selectableRegionScope?.removeListener(_handleOnSelectableRegionChanged);
    _selectableRegionScope = SelectableRegionSelectionStatusScope.maybeOf(context);
    _selectableRegionScope?.addListener(_handleOnSelectableRegionChanged);
  }

  @override
  void dispose() {
    _selectableRegionScope?.removeListener(_handleOnSelectableRegionChanged);
    _selectableRegionScope = null;
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return SelectionListener(
      selectionNotifier: widget.selectionNotifier,
      child: const Text('This is some text under a SelectionArea that can be selected.'),
    );
  }
}

引擎

Impeller Vulkan 稳定性

  • 修复了许多用户在支持 Vulkan 的旧版设备上出现的可重现闪烁和视觉抖动问题
  • 禁用了 Android Hardware Buffer swapchains
  • 由于在 MediaTek/PowerVR SoC 上使用 Vulkan 会导致的大量黑屏和崩溃报告,目前这些设备现在仅使用 Impeller OpenGLES,注意不是 skia
  • Android 模拟器已更新为使用 Impeller GLES 后端,注意不是 skia

Impeller OpenGLES

在 3.29 里,没有 Vulkan 驱动的 Android 设备将回退到在 OpenGLES 上运行的 Impeller,而不是使用 Skia,默认情况下该行为处于启用状态,无需配置

tip:这样 Flutter 将实现 100% 支持 Android 上的 Impeller。

Impeller iOS

和去年提到的 roadmap 一样,现在 iOS 后端已删除 Skia 支持,并且 FLTEnableImpeller 选择退出标志不再有效,随着 Flutter 开始从 iOS 版本中删除 Skia ,预计在未来版本中会进一步减小二进制文件大小。

新功能

Backdrop filter优化

从 3.29 开始,显示多个背景滤镜的应用现在可以使用新的 BackdropGroup 和新的 BackdropFilter.grouped,通过这些 Widget 可以提高多个模糊的性能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Widget build(BuildContext context) {  
  return BackdropGroup(
    child: ListView.builder(
      itemCount: 60,
      itemBuilder: (BuildContext context, int index) {
        return ClipRect(
          child: BackdropFilter.grouped(
            filter: ui.ImageFilter.blur(
              sigmaX: 40,
              sigmaY: 40,
            ),
            child: Container(
              color: Colors.black.withOpacity(0.2),
              height: 200,
              child: const Text('Blur item'),
            ),
          ),
        );
     }
   ),
 );

在 3.29 里,如果这些 Backdrop filter 控件都共享一个共同的 BackdropKey,那么 Flutter 引擎就可以将多个背景过滤器组合到一个渲染操作中

Backdrop Key 可以唯一标识背景过滤器的输入,当共享时表示执行一次过滤,这可以显著减少在场景中使用多个背景滤镜的开销。

对应的 Key 可以通过 backdropKey 构造函数参数手动提供,也可以通过 .grouped 构造函数从[BackdropGroup] 中查找。

ImageFilter.shader

新的 ImageFilter 构造函数允许将自定义着色器应用于任何子 Widget ,这和 package:flutter_shaders 中的 AnimatedSampler 功能类似,不同之处在于它还适用于 backdrop filters 。

Android/iOS 上的 Dart 线程更改

一直以来 Flutter 的 Dart UI Thread 和 Android/iOS 平台的 UI Thread 是不同线程,而独立的 Dart UI 线程的主要目的之一防止阻塞平台 UI 线程。

但是由于 Flutter 是在与 native 主线程不同的 Thread(UI 线程)上执行 Dart 代码,所以会出现 Dart 和平台互相调用时需要序列化和异步消息传递,这意味着:

Flutter 和 Native 之间需要使用 platform channels 来封装对平台线程的调用,而不是能够直接从 Dart 调用 API(即通过 FFI),这让一些简单的同步调用增加了性能损耗和无意义的异步行为。

而从 3.29 开始,Android 和 iOS 上的 Flutter 将在应用的主线程上执行 Dart 代码,并且不再有单独的 Dart UI 线程

这是改进移动平台上 Native 和 Dart 互操作系列调整中的第一部分,因为它将允许对平台进行同步调用和从平台进行同步调用,并且不会产生序列化和消息传递的开销

而当双方处于同一个线程下时,同步响应和调用可以更好处理一些平台事件处理、文本输入、插件调用和辅助功能等。

特别是在对于 PlatformView 混合渲染等场景,如果处于同一线程之上,那么一些场景下的 PlatformView 由于不同线程导致的闪烁或者同步问题或者也可以得到改善

当然, Eric 也在 150525#issuecomment-2652547816 提到合并线程后的忧虑,比如 Dart 和 Native 平台同一线程之后,那么「滚动/动画」是否会因此出现相互影响,特别是第三方插件处理不当的时候,反而更加卡顿的情况。

当然,在整个 Flutter 团队的目标里,完全剔除 platform/message channels 是必然的方向,未来整个异步 channel 肯定会被彻底“消灭”

DevTools and IDEs

默认情况下,3.29 下所有用户都启用了新的 DevTools inspector,新的 inspector 具有一个精简的 Widget Tree 和一个全新的 Widget 属性视图,以及一个自动更新以响应热重载和导航事件的选项。
image.png|500

通过全新的 inspector ,开发者可以更直观地调试布局问题和诊断布局问题:
image.png|500

更多内容点击

日志记录工具改进

DevTools 中的 Logging 工具在 3.29 更新中得到了一些改进:

  • 日志包含并显示更多元的数据,例如日志严重性、类别、区域和 isolate
  • 添加了对按日志严重性级别进行筛选的支持
  • 性能和初始加载时间的显著改进
    image.png|500

更改和废弃

package 将停止支持

  • ios_platform_images planned to be discontinued #162961
  • css_colors planned to be discontinued #162962
  • palette_generator planned to be discontinued #162963
  • flutter_image discontinued #162964
  • flutter_adaptive_scaffold planned to be discontinued #162965
  • flutter_markdown planned to be discontinued #162966

删除 Flutter Gradle 插件

3.29 移除了 Flutter Gradle 插件,这个在很久之前就提到了,该插件其实自 3.19 起已被弃用,后续将把 Flutter Gradle 插件从 Groovy 转换为 Kotlin,并将其迁移到使用 AGP 公共 API,这个改动有望降低发布新 AGP 版本时损坏的频率,并减少基于构建的回归。

不得不说,现在 Android 平台自己的 AGP 兼容问题越来越麻烦,坑越来越多。

如果是在 3.16 之前创建但尚未迁移的项目可能会受到影响,比如 Flutter 工具在构建项目时会有警告:“You are applying Flutter's main Gradle plugin imperatively”,则基本可以确定会受到这个变动的影响,开发者需要根据 docs.flutter.dev 上提供的方式进行迁移。
更多内容点击

Material changes

作为 Material 中正在进行的主题规范化项目的一部分,3.29 弃用 ThemeData.dialogBackgroundColor 并迁移到了 DialogThemeData.backgroundColor ,开发者可以使用 dart fix 命令迁移受影响的代码。

同样在 Material 中,ButtonStyleButton iconAlignment 属性在添加到 ButtonStyle 和关联的 styleFrom方法后已被弃用。

PC多窗口

最后,不得不提提及 PC 端多窗口支持的进展,在去年的 《Flutter PC 多窗口新进展,已在 Ubuntu/Canonical 展示》官方已经向我们展示了多窗口的可行性,而从 #142845 看目前推进的进度还可以

参考链接

medium