新年假日的尾巴,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);
}
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');
},
),
);
别的,本次 3.19 还修正了 SingleChildScrollView#136871 和 ReorderableList#136828 相关的溃散问题,一起 two_dimensional_scrollables 也修正了一些问题,比方在任一方向上正在进行翻滚时呈现拖动或者点击,scroll activity 将按预期中止。
最终,two_dimensional_scrollables 上的 TableView 控件也进行了多次更新,供给了需求改善,例如增加了对合并单元格的支撑,并在上一个安稳版别 3.16 之后适配了更多 2D foundation。
AnimationStyle
来自社区 @TahaTesser 的奉献,现在 Flutter 开发者运用 AnimationStyle ,能够让用户快速掩盖 Widget 中的默许动画行为,就像 MaterialApp
、 ExpansionTile
和 PopupMenuButton
:
popUpAnimationStyle: AnimationStyle(
curve: Easing.emphasizedAccelerate,
duration: Durations.medium4,
),
return MaterialApp(
themeAnimationStyle: AnimationStyle.noAnimation,
SegmentedButton.styleFrom
来自社区成员 @AcarFurkan 的奉献,该静态方式就像其他按钮类型供给的办法一样。能够快速创立分段按钮的按钮款式,能够与其他分段按钮同享或用于装备运用的分段按钮主题。
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),
);
}
}
}
SemanticsProperties 可访问性标识符
3.19 里SemanticsProperties
增加了新的可访问性标识符,为 native 可访问性层次结构中的语义节点供给标识符。
-
在 Android 上,它在辅佐功用层次结构中显示为 “resource-id”
-
在 iOS 上是设置里
UIAccessibilityElement.accessibilityIdentifier
MaterialStatesController
TextField
和 TextFormField
增加了 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
的 getClosestGlyphInfoForOffset
和 getGlyphInfoAt
,这两个方式都会返回一个新类型的目标字形信息,包含段落内字符(或视觉上相连的字符序列)的尺寸。
- Paragraph.getGlyphInfoAt,查找与文本中的代码单元相关的 GlyphInfo
- Paragraph.getClosestGlyphInfoForOffset,查找屏幕上最接近给定 Offset 的字形的 GlyphInfo
GPU追踪
Metal 下的 Impeller(iOS、macOS、模拟器) 和在支撑 Vulkan 的 Android 设备上,Flutter 引擎现在将在调试和 profile 构建中报告时刻线中每个帧的 GPU 时刻,能够在 DevTools 中的 “GPUTracer” 下检查 GPU 帧时序。
请注意,因为非 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 在每个背景滤镜上不再无条件存储模板缓冲区,相反,任何影响操作的编排都会被记录下来,并在恢复背景滤镜的保存层时重播到新的模板缓冲区中。
经过这一更改,在运行具有 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 文件的设置。
开发者能够打开DevTools,单击 “Deep Links” 选项,然后导入包含 Deeplinking 的 Flutter 项目,Deeplinking 验证器将告诉你装备是否正确。
Flutter 期望这个工具能够成为简化的 Deeplinking ,后续将继续补全 iOS 上的 Web 检查以及 iOS 和 Android 上的运用检查的支撑。
更多能够查阅 :docs.google.com/document/d/…
支撑 Share.invoke
Android 渠道上 Flutter 之前缺少默许 “share” 按钮,而本次 3.19 里将开端支撑它,作为 Flutter 继续尽力的一部分,以保证一切默许上下文菜单按钮在每个渠道上都可用。
更多相关发展可见:github.com/flutter/flu…
Native assets
假如需求 Flutter 代码中与其他言语的其他函数进行互操作,现在能够在 Android 上经过履行 FFI 来处理 Native assets 。
简略来说便是,在此之前, Dart interop 一直在全面支撑与 Java 和 Kotlin 和 Objective 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-assets
和flutter create --template=package_ffi [package name]
启用。
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.');
}
纹理层混合合成 (THLC) 形式
现在使 Google 地图 SDK 和文本输入框的放大镜功用时,他们都是工作在 TLHC 形式下,这会让 App 的功用得到不错的提升。
自定义 system-wide text selection toolbar 按键
Android 运用能够增加呈现在一切文本挑选菜单(长按文本时呈现的菜单)中的自定义文本挑选菜单项, Flutter 的 TextField 挑选菜单现在包含了这些项目。
在 Android 上,一般能够编写一个运用,将自定义按钮增加到系统规模的文本挑选工具栏上。例如上图这儿 Android 运用 AnkiDroid 在文本挑选工具栏中增加了 “Anki Card ”按钮,而且它能够呈现在任何运用中。
iOS
Flutter iOS 原生字体
Flutter 文本现在在 iOS 上看起来更紧凑、更像 native,因为依据苹果设计攻略,iOS 上较小的字体应该愈加分散,以便在移动设备上更容易阅读,而较大的字体应该愈加紧凑,避免占用太多空间。
在此之前,咱们在一切情况下都过错地运用了更小、间距更大的字体,现在默许情况下 Flutter 将为较大的文本运用紧凑字体。
开发工具
开发工具更新
3.19 版别的 DevTools 的一些亮点包含:
● 在 DevTools 中增加了新功用以进行验证 Deeplinking
● 在 “Enhance Tracing” 菜单中增加了一个选项,用于跟踪渠道 channel activity,这关于带有插件的运用很有用
● 当没有衔接的运用时,功用和 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 名,
弃用和严重改变
放弃 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 版别。
最终,新年快乐~预备开工咯。