参阅链接:medium.com/flutter/wha…

新年假日的尾巴,Flutter 迎来了 3.19 更新,该版别带来了 Gemini(Google’s most capable AI ) 的 Dart SDK,更好操控动画颗粒度的 Widget ,Impeller 的功用增强和 Android 优化支撑,deep links 工具支撑,Android 和 iOS 上的特定渠道新支撑,Windows Arm64 支撑等等。

普遍优化修正居多。

Gemini Dart SDK

Google 的 AI Dart SDK Gemini 现在已经发布,pub 上的 google_generative_ai 将 Gemini 的生成式 AI 功用支撑到 Dart 或 Flutter 运用里,Google Generative AI SDK 能够更方便地让 Dart 开发人员在 App 里集成 LLM 的 AI 才能。

import 'dart:io';
import 'package:google_generative_ai/google_generative_ai.dart';
void main() async {
  // Access your API key as an environment variable (see first step above)
  final apiKey = Platform.environment['API_KEY'];
  if (apiKey == null) {
    print('No $API_KEY environment variable');
    exit(1);
  }
  // For text-and-image input (multimodal), use the gemini-pro-vision model
  final model = GenerativeModel(model: 'gemini-pro-vision', apiKey: apiKey);
  final (firstImage, secondImage) = await (
    File('image0.jpg').readAsBytes(),
    File('image1.jpg').readAsBytes()
  ).wait;
  final prompt = TextPart("What's different between these pictures?");
  final imageParts = [
    DataPart('image/jpeg', firstImage),
    DataPart('image/jpeg', secondImage),
  ];
  final response = await model.generateContent([
    Content.multi([prompt, ...imageParts])
  ]);
  print(response.text);
}

Flutter 3.19 发布,快来看看有什么更新吧?

Flutter 3.19 发布,快来看看有什么更新吧?

Framework

翻滚优化

在 3.19 之前,运用两根手指在 Flutter 列表进步行滑动时,Flutter 的翻滚速度会加快到两倍,这一直是一个饱受争议的问题,现在,从 3.19 开端,开发者能够运用 MultiTouchDragStrategy.latestPointer 来装备默许的 ScrollBehavior ,然后让滑动作用与手指数量无关。

ScrollBehavior.multitouchDragStrategy 默许情况下会避免多个手指一起与可翻滚目标进行交互,然后影响翻滚速度,假如之前你已经依靠老板本这个多指滑动才能,那么能够经过 MaterialApp.scrollBehavior / CupertinoApp.scrollBehavior 去恢复:

class MyCustomScrollBehavior extends MaterialScrollBehavior {
  // Override behavior methods and getters like multitouchDragStrategy
  @override
  MultitouchDragStrategy get multitouchDragStrategy => MultitouchDragStrategy.sumAllPointers;
}
// Set ScrollBehavior for an entire application.
MaterialApp(
  scrollBehavior: MyCustomScrollBehavior(),
  // ...
);

或者经过 ScrollConfiguration 进行部分装备:

class MyCustomScrollBehavior extends MaterialScrollBehavior {
  // Override behavior methods and getters like multitouchDragStrategy
  @override
  MultitouchDragStrategy get multitouchDragStrategy => MultitouchDragStrategy.sumAllPointers;
}
// ScrollBehavior can be set for a specific widget.
final ScrollController controller = ScrollController();
ScrollConfiguration(
  behavior: MyCustomScrollBehavior(),
  child: ListView.builder(
    controller: controller,
    itemBuilder: (BuildContext context, int index) {
      return Text('Item $index');
    },
  ),
);

具体可参阅:docs.flutter.dev/release/bre…

别的,本次 3.19 还修正了 SingleChildScrollView#136871ReorderableList#136828 相关的溃散问题,一起 two_dimensional_scrollables 也修正了一些问题,比方在任一方向上正在进行翻滚时呈现拖动或者点击,scroll activity 将按预期中止。

最终,two_dimensional_scrollables 上的 TableView 控件也进行了多次更新,供给了需求改善,例如增加了对合并单元格的支撑,并在上一个安稳版别 3.16 之后适配了更多 2D foundation。

AnimationStyle

来自社区 @TahaTesser 的奉献,现在 Flutter 开发者运用 AnimationStyle ,能够让用户快速掩盖 Widget 中的默许动画行为,就像 MaterialAppExpansionTilePopupMenuButton

   popUpAnimationStyle: AnimationStyle(
            curve: Easing.emphasizedAccelerate,
            duration: Durations.medium4,
          ),
return MaterialApp(
      themeAnimationStyle: AnimationStyle.noAnimation,

SegmentedButton.styleFrom

来自社区成员 @AcarFurkan 的奉献,该静态方式就像其他按钮类型供给的办法一样。能够快速创立分段按钮的按钮款式,能够与其他分段按钮同享或用于装备运用的分段按钮主题。

Flutter 3.19 发布,快来看看有什么更新吧?

Flutter 3.19 发布,快来看看有什么更新吧?

Adaptive Switch

Adaptive Switch 能够让 Widget 在 macOS 和 iOS 上看起来和感觉是原生的作用,而且在其他地方具有 Material Design 的外观和感觉,它不依靠于 Cupertino 库,因而它的 API 在一切渠道上都彻底相同。

import 'package:flutter/material.dart';
/// Flutter code sample for [Switch.adaptive].
void main() => runApp(const SwitchApp());
class SwitchApp extends StatefulWidget {
  const SwitchApp({super.key});
  @override
  State<SwitchApp> createState() => _SwitchAppState();
}
class _SwitchAppState extends State<SwitchApp> {
  bool isMaterial = true;
  bool isCustomized = false;
  @override
  Widget build(BuildContext context) {
    final ThemeData theme = ThemeData(
        platform: isMaterial ? TargetPlatform.android : TargetPlatform.iOS,
        adaptations: <Adaptation<Object>>[
          if (isCustomized) const _SwitchThemeAdaptation()
        ]);
    final ButtonStyle style = OutlinedButton.styleFrom(
      fixedSize: const Size(220, 40),
    );
    return MaterialApp(
      theme: theme,
      home: Scaffold(
        appBar: AppBar(title: const Text('Adaptive Switches')),
        body: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            OutlinedButton(
              style: style,
              onPressed: () {
                setState(() {
                  isMaterial = !isMaterial;
                });
              },
              child: isMaterial
                  ? const Text('Show cupertino style')
                  : const Text('Show material style'),
            ),
            OutlinedButton(
              style: style,
              onPressed: () {
                setState(() {
                  isCustomized = !isCustomized;
                });
              },
              child: isCustomized
                  ? const Text('Remove customization')
                  : const Text('Add customization'),
            ),
            const SizedBox(height: 20),
            const SwitchWithLabel(label: 'enabled', enabled: true),
            const SwitchWithLabel(label: 'disabled', enabled: false),
          ],
        ),
      ),
    );
  }
}
class SwitchWithLabel extends StatefulWidget {
  const SwitchWithLabel({
    super.key,
    required this.enabled,
    required this.label,
  });
  final bool enabled;
  final String label;
  @override
  State<SwitchWithLabel> createState() => _SwitchWithLabelState();
}
class _SwitchWithLabelState extends State<SwitchWithLabel> {
  bool active = true;
  @override
  Widget build(BuildContext context) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        Container(
            width: 150,
            padding: const EdgeInsets.only(right: 20),
            child: Text(widget.label)),
        Switch.adaptive(
          value: active,
          onChanged: !widget.enabled
              ? null
              : (bool value) {
                  setState(() {
                    active = value;
                  });
                },
        ),
      ],
    );
  }
}
class _SwitchThemeAdaptation extends Adaptation<SwitchThemeData> {
  const _SwitchThemeAdaptation();
  @override
  SwitchThemeData adapt(ThemeData theme, SwitchThemeData defaultValue) {
    switch (theme.platform) {
      case TargetPlatform.android:
      case TargetPlatform.fuchsia:
      case TargetPlatform.linux:
      case TargetPlatform.windows:
        return defaultValue;
      case TargetPlatform.iOS:
      case TargetPlatform.macOS:
        return SwitchThemeData(
          thumbColor: MaterialStateProperty.resolveWith<Color?>(
              (Set<MaterialState> states) {
            if (states.contains(MaterialState.selected)) {
              return Colors.yellow;
            }
            return null; // Use the default.
          }),
          trackColor: const MaterialStatePropertyAll<Color>(Colors.brown),
        );
    }
  }
}

具体可见:main-api.flutter.dev/flutter/mat…

SemanticsProperties 可访问性标识符

3.19 里SemanticsProperties 增加了新的可访问性标识符,为 native 可访问性层次结构中的语义节点供给标识符。

  • 在 Android 上,它在辅佐功用层次结构中显示为 “resource-id”

  • 在 iOS 上是设置里 UIAccessibilityElement.accessibilityIdentifier

MaterialStatesController

TextFieldTextFormField 增加了 MaterialStatesController ,因为在此之前,开发者无法确认 TextFormField 当时是否处于过错状态,例如:

  • 它显示过错消息并运用了errorBorder
  • 确认它是否 foucs,但条件是供给自己的 FocusNode

而现在答应开发者供给自己的 MaterialStatesController(类似于ElevatedButton),以便开发者能够彻底访问有关这些控件的状态信息。

final MaterialStatesController statesController = MaterialStatesController();
statesController.addListener(valueChanged);
TextField(
  statesController: statesController,
  controller: textEditingController,
)

UndoHistory stack

修正了 undo/redo 前史在日语键盘上或许消失的问题,并使其现在能够在将条目推送到 UndoHistory 堆栈之前对其进行修正。

UndoHistory 是一个供给吊销/重做功用的 Widget,它还具有绑定到特定于渠道的完成的底层接口,监听了键盘事情以完成 undo/redo 操作。

从 Flutter 3.0.0 开端,能够将 UndoHistoryController 传递给 TextField它附带了 UndoHistoryValue

关于一个十分简略的展示,我将创立一个 UndoHistoryController 实例,将其传递给 TextField,运用 ValueListenableBuilder 监听该实例,并在构建器中的按钮上返回一行以履行吊销/重做操作。

import 'package:flutter/material.dart';
/// Flutter code sample for [UndoHistoryController].
void main() {
  runApp(const UndoHistoryControllerExampleApp());
}
class UndoHistoryControllerExampleApp extends StatelessWidget {
  const UndoHistoryControllerExampleApp({super.key});
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: MyHomePage(),
    );
  }
}
class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});
  @override
  State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
  final TextEditingController _controller = TextEditingController();
  final FocusNode _focusNode = FocusNode();
  final UndoHistoryController _undoController = UndoHistoryController();
  TextStyle? get enabledStyle => Theme.of(context).textTheme.bodyMedium;
  TextStyle? get disabledStyle =>
      Theme.of(context).textTheme.bodyMedium?.copyWith(color: Colors.grey);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            TextField(
              maxLines: 4,
              controller: _controller,
              focusNode: _focusNode,
              undoController: _undoController,
            ),
            ValueListenableBuilder<UndoHistoryValue>(
              valueListenable: _undoController,
              builder: (BuildContext context, UndoHistoryValue value,
                  Widget? child) {
                return Row(
                  children: <Widget>[
                    TextButton(
                      child: Text('Undo',
                          style: value.canUndo ? enabledStyle : disabledStyle),
                      onPressed: () {
                        _undoController.undo();
                      },
                    ),
                    TextButton(
                      child: Text('Redo',
                          style: value.canRedo ? enabledStyle : disabledStyle),
                      onPressed: () {
                        _undoController.redo();
                      },
                    ),
                  ],
                );
              },
            ),
          ],
        ),
      ),
    );
  }
}

Engine

Impeller 发展

Android OpenGL 预览

在 3.16 安稳版别中,Flutter 官方邀请了用户在支撑 Vulkan 的 Android 设备上试用 Impeller,掩盖了该范畴 77% 的 Android 设备,而在过去的几个月里,Flutter 官方团队让 Impeller 的 OpenGL 达到了与 Vulkan 平等的功用,例如增加支撑 MSAA

这意味着简直一切 Android 设备上的 Flutter 运用都有望支撑 Impeller 烘托,除了少量即将推出的剩下功用除外,例如自定义着色器和对外部纹理的彻底支撑,现在官方团队表示在今年晚些时候 Androd 也会将 Impeller 作为默许烘托器。

别的,Impeller 的 Vulkan 在“调试”构建中启用了超出 Skia 的附加调试功用,而且这些功用会发生额定的运行时开支。因而,有关 Impeller 功用的反馈你许来自 profile 或 release 版别,而且需求包含 DevTools 的时刻表以及与同一设备上的 Skia 后端的比较。

路线

在完成了烘托保真度之后,在 Impeller Android 预览期间的首要关注点是功用,别的一些更大的改善也正在进行,例如能够运用 Vulkan subpasses 大大提高高档混合形式的功用。

此外,Flutter 官方还期望烘托策略发生变化,不再总是将 CPU 上的每条途径细分为先模板后掩盖 ,这样的完成将大大下降 Android 和 iOS 上 Impeller 的 CPU 运用率。

最终,Flutter 还期望新的高斯含糊施行能匹配 Skia 完成的吞吐量,并改善 iOS 上含糊的惯用用法。

API 改善

字形信息

3.19 版别包含两个新的 dart:ui 办法:Paragraph getClosestGlyphInfoForOffsetgetGlyphInfoAt,这两个方式都会返回一个新类型的目标字形信息,包含段落内字符(或视觉上相连的字符序列)的尺寸。

GPU追踪

Metal 下的 Impeller(iOS、macOS、模拟器) 和在支撑 Vulkan 的 Android 设备上,Flutter 引擎现在将在调试和 profile 构建中报告时刻线中每个帧的 GPU 时刻,能够在 DevTools 中的 “GPUTracer” 下检查 GPU 帧时序。

Flutter 3.19 发布,快来看看有什么更新吧?

请注意,因为非 Vulkan Android 设备或许会误报其对查询 GPU 计时的支撑,因而只能经过在 AndroidManifest.xml 设置标志来启用 Impeller 的 GPU 跟踪:

<meta-data
    android:name="io.flutter.embedding.android.EnableOpenGLGPUTracing"
    android:value="true" /> 

功用优化

Specialization Constants

Impeller 增加支撑 Specialization Constants ,运用 Impeller 着色器中的这一功用,减少了 Flutter 引擎的未紧缩二进制巨细 350KB。

Backdrop Filter 加快

3.19 版别包含一些不错的功用改善,其中就包含了 Impeller 的 Backdrop Filter 和含糊优化,特别是开源奉献者 knopp noticed 注意到 Impeller 过错地恳求了读取屏幕纹理的功用,删去这个功用支撑,在基准测试中,依据复杂程度,将包含多个背景滤镜的场景改善了 20-70%。

一起,Impeller 在每个背景滤镜上不再无条件存储模板缓冲区,相反,任何影响操作的编排都会被记录下来,并在恢复背景滤镜的保存层时重播到新的模板缓冲区中。

Flutter 3.19 发布,快来看看有什么更新吧?

经过这一更改,在运行具有 Vulkan 的 Impeller 的 Pixel 7 Pro 进步行动画高档混合形式基准测试时,将平均 GPU 帧时刻从 55 毫秒改善到 16 毫秒,并将 90% 的光栅线程 CPU 时刻从大约 110 毫秒改善到 22 毫秒。

Android

Deeplinking web 验证器

3.19 开端,Flutter 的 Deeplinking web 验证器的早期版别将被推出运用。

在该版别中,Flutter Deeplinking 验证器支撑 Android 上的 Web 检查,这意味着能够验证 assetlinks.json 文件的设置。

Flutter 3.19 发布,快来看看有什么更新吧?

开发者能够打开DevTools,单击 “Deep Links” 选项,然后导入包含 Deeplinking 的 Flutter 项目,Deeplinking 验证器将告诉你装备是否正确。

Flutter 期望这个工具能够成为简化的 Deeplinking ,后续将继续补全 iOS 上的 Web 检查以及 iOS 和 Android 上的运用检查的支撑。

Flutter 3.19 发布,快来看看有什么更新吧?

更多能够查阅 :docs.google.com/document/d/…

支撑 Share.invoke

Android 渠道上 Flutter 之前缺少默许 “share” 按钮,而本次 3.19 里将开端支撑它,作为 Flutter 继续尽力的一部分,以保证一切默许上下文菜单按钮在每个渠道上都可用。

Flutter 3.19 发布,快来看看有什么更新吧?

更多相关发展可见:github.com/flutter/flu…

Native assets

假如需求 Flutter 代码中与其他言语的其他函数进行互操作,现在能够在 Android 上经过履行 FFI 来处理 Native assets 。

简略来说便是,在此之前, Dart interop 一直在全面支撑与 Java 和 KotlinObjective C 和 Swift 的直接调用支撑,例如在 Dart 3.2 开端,Native assets 就作为实验性测试支撑,一直在解决与依靠于 Native 代码的 Dart 包分发相关的许多问题,它经过供给统一的钩子来与构建 Flutter 和独立 Dart 运用所触及的各种构建需求。

Native Assets 能够让 Dart 包更无缝依靠和运用 Native 代码,经过 flutter run/flutter build dart run/dart build 构建并绑缚 Native 代码 。

补白:可经过 flutter config --enable-native-assetsflutter create --template=package_ffi [package name] 启用。

Flutter 3.19 发布,快来看看有什么更新吧?

Demo native_add_library 就展示了相关运用,当 Flutter 项目依靠 package:native_add_library 时, 脚本会自动在 build.dart 指令上调用:

import 'package:native_add_library/native_add_library.dart';
void main() {
  print('Invoking a native function to calculate 1 + 2.');
  final result = add(1, 2);
  print('Invocation success: 1 + 2 = $result.');
}

更多可见:github.com/flutter/flu…

纹理层混合合成 (THLC) 形式

现在使 Google 地图 SDK 和文本输入框的放大镜功用时,他们都是工作在 TLHC 形式下,这会让 App 的功用得到不错的提升。

自定义 system-wide text selection toolbar 按键

Android 运用能够增加呈现在一切文本挑选菜单(长按文本时呈现的菜单)中的自定义文本挑选菜单项, Flutter 的 TextField 挑选菜单现在包含了这些项目。

Flutter 3.19 发布,快来看看有什么更新吧?

在 Android 上,一般能够编写一个运用,将自定义按钮增加到系统规模的文本挑选工具栏上。例如上图这儿 Android 运用 AnkiDroid 在文本挑选工具栏中增加了 “Anki Card ”按钮,而且它能够呈现在任何运用中。

iOS

Flutter iOS 原生字体

Flutter 文本现在在 iOS 上看起来更紧凑、更像 native,因为依据苹果设计攻略,iOS 上较小的字体应该愈加分散,以便在移动设备上更容易阅读,而较大的字体应该愈加紧凑,避免占用太多空间。

在此之前,咱们在一切情况下都过错地运用了更小、间距更大的字体,现在默许情况下 Flutter 将为较大的文本运用紧凑字体。

Flutter 3.19 发布,快来看看有什么更新吧?

开发工具

开发工具更新

3.19 版别的 DevTools 的一些亮点包含:

● 在 DevTools 中增加了新功用以进行验证 Deeplinking

● 在 “Enhance Tracing” 菜单中增加了一个选项,用于跟踪渠道 channel activity,这关于带有插件的运用很有用

Flutter 3.19 发布,快来看看有什么更新吧?

● 当没有衔接的运用时,功用和 CPU 分析器现在也能够运用,能够从头加载之前从 DevTools 保存的功用数据或 CPU 装备文件

● VS Code 中的 Flutter 侧边栏现在能够在当时项目未启用的情况下启用新渠道,而且侧边栏中的 DevTools 菜单现在有一个在外部浏览器窗口中运用 DevTools 的选项

桌面

Windows Arm64 支撑

Windows 上的 Flutter 现在开端开始支撑 Arm64 架构,现在仍处于开发阶段, 能够在GitHub #62597 上检查发展,虽然现在关于 Flutter 开发者来说或许用途不是特别明显,但是也算是一个可贵的桌面增强。

生态系统

隐私清单

Flutter 现在包含 iOS 上的隐私清单以满足即将推出的 Apple 要求 ,所以看来一般情况下,这个 Flutter 3.19 非升不可

包生态的发展

2023 年,pub package 生态增长了 26%,从 1 月份的 38,000 个 package 增加到 12 月底的 48,000 个,截至 2024 年 1 月,Pub.dev 现在每月活泼用户超过 700,000 名,

Flutter 3.19 发布,快来看看有什么更新吧?

弃用和严重改变

放弃 Windows 7 和 8 支撑

Dart 3.3 和 Flutter 3.19 版别中止对 Windows 7 和 8 的支撑

Impeller Dithering flag

正如 3.16 发布时所说的,现在全局标志 Paint.enableDithering 已经删去

弃用 iOS 11

Flutter 不再支撑 iOS 11,因为调用某些网络 API 时会发生运行时溃散, Flutter 3.16.6 及更高版别构建的运用将不再支撑 iOS11 ,具体可见 :juejin.cn/post/732141…

最终

现在看来 Flutter 3.19 并没有什么大更新,属于季度正常迭代,首要是问题修正和功用高优化的版别,仍是老规矩,坐等 3.19.6 版别。

最终,新年快乐~预备开工咯。