GSYFlutterBook/Flutter-319.md

596 lines
24 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Flutter 3.19 发布,快来看看有什么更新吧?
> 参考链接https://medium.com/flutter/whats-new-in-flutter-3-19-58b1aae242d2
新年假期的尾巴Flutter 迎来了 3.19 更新,该版本带来了 **GeminiGoogle'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](https://pub.dev/packages/google_generative_ai) 将 Gemini 的生成式 AI 功能支持到 Dart 或 Flutter 应用里Google Generative AI SDK 可以更方便地让 Dart 开发人员在 App 里集成 LLM 的 AI 能力。
```dart
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);
}
```
![](http://img.cdn.guoshuyu.cn/20240216_Flutter-319/image1.png)
![](http://img.cdn.guoshuyu.cn/20240216_Flutter-319/image2.png)
# Framework
## 滚动优化
在 3.19 之前,使用两根手指在 Flutter 列表上进行滑动时Flutter 的滚动速度会加快到两倍,这一直是一个饱受争议的问题,现在,从 3.19 开始,**开发者可以使用 `MultiTouchDragStrategy.latestPointer` 来配置默认的 `ScrollBehavior`** ,从而让滑动效果与手指数量无关。
`ScrollBehavior.multitouchDragStrategy` 默认情况下会防止多个手指同时与可滚动对象进行交互,从而影响滚动速度,如果之前你已经依赖老板本这个多指滑动能力,那么可以通过 `MaterialApp.scrollBehavior` / `CupertinoApp.scrollBehavior` 去恢复:
```dart
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` 进行局部配置:
```dart
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');
},
),
);
```
> 详细可参考https://docs.flutter.dev/release/breaking-changes/multi-touch-scrolling
另外,本次 3.19 还修复了 [SingleChildScrollView#136871](https://github.com/flutter/flutter/pull/136871) 和 [ReorderableList#136828](https://github.com/flutter/flutter/pull/136828) 相关的崩溃问题,同时 [two_dimensional_scrollables](https://pub-web.flutter-io.cn/packages/two_dimensional_scrollables) 也修复了一些问题比如在任一方向上正在进行滚动时出现拖动或者点击scroll activity 将按预期停止。
最后two_dimensional_scrollables 上的 TableView 控件也进行了多次更新,提供了需要改进,例如添加了对合并单元格的支持,并在上一个稳定版本 3.16 之后适配了更多 2D foundation。
## AnimationStyle
来自社区 [@TahaTesser](https://github.com/TahaTesser) 的贡献,现在 Flutter 开发者使用 AnimationStyle ,可以让用户快速覆盖 Widget 中的默认动画行为,就像 `MaterialApp``ExpansionTile``PopupMenuButton`
```dart
popUpAnimationStyle: AnimationStyle(
curve: Easing.emphasizedAccelerate,
duration: Durations.medium4,
),
```
```dart
return MaterialApp(
themeAnimationStyle: AnimationStyle.noAnimation,
```
## SegmentedButton.styleFrom
来自社区成员 [@AcarFurkan](https://github.com/AcarFurkan) 的贡献,该静态方式就像其他按钮类型提供的方法一样。可以快速创建分段按钮的按钮样式,可以与其他分段按钮共享或用于配置应用的分段按钮主题。
![](http://img.cdn.guoshuyu.cn/20240216_Flutter-319/image3.png)
![](http://img.cdn.guoshuyu.cn/20240216_Flutter-319/image4.png)
## Adaptive Switch
Adaptive Switch 可以让 Widget 在 macOS 和 iOS 上看起来和感觉是原生的效果,并且在其他地方具有 Material Design 的外观和感觉,它不依赖于 Cupertino 库,因此它的 API 在所有平台上都完全相同。
```dart
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),
);
}
}
}
```
> 详细可见https://main-api.flutter.dev/flutter/material/Switch/Switch.adaptive.html
### SemanticsProperties 可访问性标识符
3.19 里`SemanticsProperties` 添加了新的可访问性标识符,为 native 可访问性层次结构中的语义节点提供标识符。
- 在 Android 上,它在辅助功能层次结构中显示为 “resource-id”
- 在 iOS 上是设置里 `UIAccessibilityElement.accessibilityIdentifier`
## MaterialStatesController
`TextField``TextFormField` 添加了 `MaterialStatesController` ,因为在此之前,开发者无法确定 `TextFormField` 当前是否处于错误状态,例如:
- 它显示错误消息并使用了`errorBorder`
- 确定它是否 foucs但前提是提供自己的 `FocusNode`
而现在允许开发者提供自己的 `MaterialStatesController`(类似于`ElevatedButton`),以便开发者可以完全访问有关这些控件的状态信息。
```dart
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 监听该实例,并在构建器中的按钮上返回一行以执行撤消/重做操作。
```dart
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](https://github.com/flutter/engine/pull/47030)。
这意味着几乎所有 Android 设备上的 Flutter 应用都有望支持 Impeller 渲染,除了少数即将推出的剩余功能除外,例如自定义着色器和对外部纹理的完全支持,目前官方团队表示在今年晚些时候 Androd 也会将 Impeller 作为默认渲染器。
另外Impeller 的 Vulkan 在“调试”构建中启用了超出 Skia 的附加调试功能,并且这些功能会产生额外的运行时开销。因此,有关 Impeller 性能的反馈你许来自 profile 或 release 版本,并且需要包括 DevTools 的时间表以及与同一设备上的 Skia 后端的比较。
### 路线
在实现了渲染保真度之后,在 Impeller Android 预览期间的主要关注点是性能,另外一些更大的改进也正在进行,例如能够利用 [Vulkan subpasses](https://github.com/flutter/flutter/issues/128911) 大大提高高级混合模式的性能。
此外Flutter 官方还期望渲染策略发生变化,不再总是将 CPU 上的每条路径细分为[先模板后覆盖](https://github.com/flutter/flutter/issues/137714) ,这样的实现将大大降低 Android 和 iOS 上 Impeller 的 CPU 利用率。
最后Flutter 还期望新的[高斯模糊](https://github.com/flutter/flutter/issues/131580)实施能匹配 Skia 实现的吞吐量,并改进 iOS 上模糊的惯用用法。
## API 改进
### 字形信息
3.19 版本包括两个新的 dart:ui 方法:`Paragraph ` 的 `getClosestGlyphInfoForOffset` 和 `getGlyphInfoAt`,这两个方式都会返回一个新类型的对象字形信息,包含段落内字符(或视觉上相连的字符序列)的尺寸。
- [Paragraph.getGlyphInfoAt](https://main-api.flutter.dev/flutter/dart-ui/Paragraph/getGlyphInfoAt.html),查找与文本中的代码单元关联的 [GlyphInfo ](https://main-api.flutter.dev/flutter/dart-ui/GlyphInfo-class.html)
- [Paragraph.getClosestGlyphInfoForOffset](https://main-api.flutter.dev/flutter/dart-ui/Paragraph/getClosestGlyphInfoForOffset.html),查找屏幕上最接近给定 Offset 的字形的 GlyphInfo
### GPU追踪
Metal 下的 ImpelleriOS、macOS、模拟器 和在支持 Vulkan 的 Android 设备上Flutter 引擎现在将在调试和 profile 构建中报告时间线中每个帧的 GPU 时间,可以在 DevTools 中的 “GPUTracer” 下检查 GPU 帧时序。
![](http://img.cdn.guoshuyu.cn/20240216_Flutter-319/image5.png)
请注意,由于非 Vulkan Android 设备可能会误报其对查询 GPU 计时的支持,因此只能通过在 AndroidManifest.xml 设置标志来启用 Impeller 的 GPU 跟踪:
```xml
<meta-data
android:name="io.flutter.embedding.android.EnableOpenGLGPUTracing"
android:value="true" />
```
## 性能优化
### Specialization Constants
Impeller 添加支持 [Specialization Constants](https://github.com/flutter/flutter/issues/119357) ,利用 Impeller 着色器中的这一功能,减少了 Flutter 引擎的未压缩二进制大小 350KB。
### Backdrop Filter 加速
3.19 版本包含一些不错的性能改进,其中就包括了 Impeller 的 Backdrop Filter 和模糊优化,特别是开源贡献者 [knopp](https://github.com/knopp) [noticed](https://github.com/flutter/flutter/issues/131567#issuecomment-1678210475) 注意到 Impeller 错误地请求了读取屏幕纹理的功能,[删除这个](https://github.com/flutter/engine/pull/47808)功能支持,在基准测试中,根据复杂程度,将包含多个背景滤镜的场景改进了 20-70%。
同时Impeller 在每个背景滤镜上[不再无条件存储模板缓冲区](https://github.com/flutter/engine/pull/47397),相反,任何影响操作的剪辑都会被记录下来,并在恢复背景滤镜的保存层时重播到新的模板缓冲区中。
![](http://img.cdn.guoshuyu.cn/20240216_Flutter-319/image6.png)
通过这一更改,在运行具有 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 文件的设置。
![](http://img.cdn.guoshuyu.cn/20240216_Flutter-319/image7.png)
开发者可以打开DevTools单击 “Deep Links” 选项,然后导入包含 Deeplinking 的 Flutter 项目Deeplinking 验证器将告诉你配置是否正确。
Flutter 希望这个工具能够成为简化的 Deeplinking ,后续将继续补全 iOS 上的 Web 检查以及 iOS 和 Android 上的应用检查的支持。
![](http://img.cdn.guoshuyu.cn/20240216_Flutter-319/image8.png)
> 更多可以查阅 https://docs.google.com/document/d/1fnWe8EpZleMtSmP0rFm2iulqS3-gA86z8u9IsnXjJak/edit
## 支持 Share.invoke
Android 平台上 Flutter 之前缺少默认 “share” 按钮,而本次 3.19 里将开始支持它,作为 Flutter 持续努力的一部分,以确保所有默认上下文菜单按钮在每个平台上都可用。
![](http://img.cdn.guoshuyu.cn/20240216_Flutter-319/image9.png)
> 更多相关进度可见https://github.com/flutter/flutter/issues/107578
## Native assets
如果需要 Flutter 代码中与其他语言的其他函数进行互操作,现在可以在 Android 上通过执行 FFI 来处理 Native assets 。
简单来说就是,在此之前, Dart interop 一直在全面支持与 [Java 和 Kotlin](https://link.juejin.cn/?target=https%3A%2F%2Fdart.dev%2Fguides%2Flibraries%2Fjava-interop) 和 [Objective C 和 Swift](https://link.juejin.cn/?target=https%3A%2F%2Fdart.dev%2Fguides%2Flibraries%2Fobjective-c-interop) 的直接调用支持,例如在 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-assets` 和 `flutter create --template=package_ffi [package name]` 启用。
![](http://img.cdn.guoshuyu.cn/20240216_Flutter-319/image10.png)
Demo [`native_add_library`](https://github.com/dart-lang/native/tree/main/pkgs/native_assets_cli/example/native_add_library) 就展示了相关使用,当 Flutter 项目依赖 `package:native_add_library` 时, 脚本会自动在 `build.dart` 命令上调用:
```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.');
}
```
> 更多可见https://github.com/flutter/flutter/issues/129757
## 纹理层混合合成 (THLC) 模式
现在使 Google 地图 SDK 和文本输入框的放大镜功能时,他们都是工作在 TLHC 模式下,这会让 App 的性能得到不错的提升。
## 自定义 system-wide text selection toolbar 按键
Android 应用可以添加出现在所有文本选择菜单(长按文本时出现的菜单)中的自定义文本选择菜单项, Flutter 的 TextField 选择菜单现在包含了这些项目。
![](http://img.cdn.guoshuyu.cn/20240216_Flutter-319/image11.png)
> 在 Android 上,一般可以编写一个应用,将自定义按钮添加到系统范围的文本选择工具栏上。例如上图这里 Android 应用 AnkiDroid 在文本选择工具栏中添加了 “Anki Card ”按钮,并且它可以出现在任何应用中。
# iOS
## Flutter iOS 原生字体
Flutter 文本现在在 iOS 上看起来更紧凑、更像 native因为根据苹果设计指南iOS 上较小的字体应该更加分散,以便在移动设备上更容易阅读,而较大的字体应该更加紧凑,以免占用太多空间。
在此之前,我们在所有情况下都错误地使用了更小、间距更大的字体,现在默认情况下 Flutter 将为较大的文本使用紧凑字体。
![](http://img.cdn.guoshuyu.cn/20240216_Flutter-319/image12.png)
# 开发工具
## 开发工具更新
3.19 版本的 DevTools 的一些亮点包括:
● 在 DevTools 中添加了新功能以进行验证 Deeplinking
● 在 “Enhance Tracing” 菜单中添加了一个选项,用于跟踪平台 channel activity这对于带有插件的应用很有用
![](http://img.cdn.guoshuyu.cn/20240216_Flutter-319/image13.jpg)
● 当没有连接的应用时,性能和 CPU 分析器现在也可以使用,可以重新加载之前从 DevTools 保存的性能数据或 CPU 配置文件
● VS Code 中的 Flutter 侧边栏现在能够在当前项目未启用的情况下启用新平台,并且侧边栏中的 DevTools 菜单现在有一个在外部浏览器窗口中使用 DevTools 的选项
# 桌面
## Windows Arm64 支持
Windows 上的 Flutter 现在开始初步支持 Arm64 架构,目前仍处于开发阶段, 可以在GitHub [#62597 ](https://github.com/flutter/flutter/issues/62597)上查看进度,虽然目前对于 Flutter 开发者来说可能用处不是特别明显,但是也算是一个难得的桌面增强。
# 生态系统
## 隐私清单
**Flutter 现在包含 iOS 上的隐私清单以满足[即将推出的 Apple 要求](https://juejin.cn/post/7311876701909549065) ,所以看来一般情况下,这个 Flutter 3.19 非升不可**
## 包生态的进展
2023 年pub package 生态增长了 26%,从 1 月份的 38,000 个 package 增加到 12 月底的 48,000 个,截至 2024 年 1 月Pub.dev 现在每月活跃用户超过 700,000 名,
![](http://img.cdn.guoshuyu.cn/20240216_Flutter-319/image14.png)
# 弃用和重大变更
## 放弃 Windows 7 和 8 支持
Dart 3.3 和 Flutter 3.19 版本停止对 Windows 7 和 8 的支持
## Impeller Dithering flag
正如 3.16 发布时所说的,现在全局标志 `Paint.enableDithering` 已经[删除](https://github.com/flutter/engine/pull/46745)。
## 弃用 iOS 11
Flutter 不再支持 iOS 11由于调用某些网络 API 时会发生[运行时崩溃](https://github.com/flutter/flutter/issues/136060) Flutter 3.16.6 及更高版本构建的应用将不再支持 iOS11 ,详细可见 https://juejin.cn/post/7321410906427359258。
# 最后
目前看来 Flutter 3.19 并没有什么大更新,属于季度正常迭代,主要是问题修复和性能高优化的版本,还是老规矩,坐等 3.19.6 版本。
最后,新年快乐~准备开工咯。