This commit is contained in:
Asher 2023-12-11 10:28:05 +08:00
parent 3277bcc146
commit dd5b1ba3ff
8 changed files with 1175 additions and 1 deletions

101
Dart-320.md Normal file
View File

@ -0,0 +1,101 @@
# Dart 3.2 更新Flutter Web 的未来越来越明朗
> 参考原文https://medium.com/dartlang/dart-3-2-c8de8fe1b91f
本次跟随 [Flutter 3.16 发布](https://juejin.cn/post/7301574930869321779) 的 Dart 3.2 ,包含有:私有 final 字段的非空改进、新的 interop 改进、对 DevTools 中的扩展支持、以及对 Web 路线图的更新,包括对 Wasm 的Web 组件支持。
> 最重要的就是 Wasm 的Web 组件支持。
# private final 的非空类型提升
自 Dart 2.12 发布 sound null safety 以来,类型提升一直是空安全的核心部分之一,但仅限于局部变量里,字段和顶级变量无法处理,例如在这样的情况下会报错:
```dart
class Container {
final int? _fillLevel;
Container(this._fillLevel);
check() {
if (_fillLevel != null) {
int i = _fillLevel; // Prior to Dart 3.2, causes an error.
}
}
}
```
这种限制是由于几个复杂的情况造成的在这些情况下flow analysis 无法确定字段何时会发生什么变化,例如,在字段提升的情况下,如果子类使用 getter 覆盖字段(有时会返回 null这就可能会出现问题。
> 在 Dart 3.2 开始Dart 改进了 flow analysis ,现在能够归类出 **private Final fields**
现在 3.2 里上面的代码片段可以顺利通过检测:对于 private & final 的字段,它的值在初始分配后永远不会改变,因此仅检查一次就被认为是安全的。
# 包中的新代码分析选项lints 3.0
3.2 还对 [package:lints](https://pub.dev/packages/lints) 中的标准代码分析规则进行了一些改进package 包含了默认和推荐的静态分析规则集,这些规则随 `dart create``flutter create` 创建的新项目一起提供 (通过 [package:flutter_lints](https://pub.dev/packages/flutter_lints) )。
该 lint 集的主要版本3.0)目前已经发布,其中向核心集添加了 6 个 lint向推荐集添加了 2 个 lint它具有用于验证 pubspec URL、验证是否使用正确参数调用集合方法等相关的 lints 。
> 有关更改的完整列表,请查看 https://github.com/dart-lang/lints/blob/main/CHANGELOG.md#300, 3.0 版本将成为即将发布的新项目的默认版本。
# Dart interop 更新
目前正在努力扩展 Dart interop 以全面支持与 [Java 和 Kotlin](https://dart.dev/guides/libraries/java-interop) 和 [Objective C 和 Swift](https://dart.dev/guides/libraries/objective-c-interop) 的直接调用支持,从 Dart 3.2 开始进行了许多改进:
- 引入了 C FFI 的构造函数 `NativeCallable.isolateLocal` ,它可以从任意 Dart 函数创建一个 C 函数指针,这是 `Pointer.fromFunction` 提供的功能的扩展,它只能从顶级函数创建函数指针。
- 更新了 Objective-C 利用`NativeCallable.listener` 绑定生成器,生成器现在可以自动处理包含异步回调的 API例如 [Core Motion](https://developer.apple.com/documentation/coremotion) 这种以前需要手动绑定的代码。
- 改进 [package:jnigen](https://dart.dev/guides/libraries/java-interop) 实现 Java 和 Kotlin 的直接调用支持,现在我们能够将 [package:cronet_http](https://pub.dev/packages/cronet_http)Android Cronet HTTP 客户端的包装器)从手写绑定代码迁移到[自动生成的](https://github.com/dart-lang/http/blob/master/pkgs/cronet_http/jnigen.yaml)包装器。
- 在 [Native Assets](https://github.com/dart-lang/sdk/issues/50565) 功能上取得了重大进展,该功能旨在解决与依赖于 Native 代码的 Dart 包分发相关的许多问题,它通过提供统一的钩子来与构建 Flutter 和独立 Dart 应用所涉及的各种构建需要,详细可见 http://dart.dev/guides/libraries/c-interop#native-assets
> 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/20231116_Dart32/image1.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.');
}
```
# Dart 包的 DevTools 扩展
在 Dart 3.2 和 Flutter 3.16 中发布了一个新的[扩展框架](https://pub.dev/packages/devtools_extensions),该框架让包作者能够为它的 package 构建自定义工具,并直接在 DevTools 中显示。
它允许包含框架的 pub.dev 包提供特定用例的自定义工具,例如 [Serverpod](https://pub.dev/packages/serverpod) 的作者一直在努力为它的 package 构建开发人员工具,并且很高兴在即将发布的 [1.2 版本](https://github.com/orgs/serverpod/projects/4) 中提供 DevTools 扩展。
![](http://img.cdn.guoshuyu.cn/20231116_Dart32/image2.png)
# Dart Web 和 Wasm 更新
从 Chrome 119 开始Chrome 会默认启用 [Wasm 垃圾收集支持(称为 Wasm-GC](https://developer.chrome.com/blog/wasmgc/) Wasm-GC 支持也出现在 Firefox 120他们的下一个稳定版本中被支持那么 Dart、Flutter 和 Wasm-GC 的现状如何?
Dart-to-Wasm 编译器的功能几乎已经完全实现,团队对性能和兼容性非常满意,现在的重点是边缘情况,以确保在广泛的场景中能同样完美运行。
对于 Flutter Web这里类似于完成了一个全新的 “Skwasm” 渲染引擎。为了最大限度地提高性能Skwasm 通过 wasm-to-wasm 绑定将编译后的应用代码,直接连接到自定义 [CanvasKit Wasm 模块](https://skia.org/docs/user/modules/canvaskit/) ,这也是 Flutter Web 多线程渲染支持的第一次迭代,进一步提高了帧时间。
在 Wasm 的 Flutter web 准备脱离当前的实验状态之前,还有一些事情要做:
- **双编译**:生成 Wasm 和 JavaScript 输出,并在运行时启用功能检测,以支持支持和不支持 Wasm-GC 的浏览器。
- **JavaScript interop**:一种基于[扩展类型](https://github.com/dart-lang/language/issues/2727)的新 JS 互操作机制,当针对 JavaScript 和 Wasm 时,可以在 Dart 代码、浏览器 API 和 JS 库之间进行简洁、类型安全的调用。
- **支持 Wasm 的浏览器 API**:一个新的 `package:web`,基于现代 JS 互操作机制,取代了 dart:html (和相关库),这将提供对浏览器 API 的更轻松访问,并支持 JS 和 Wasm 目标。
目前已经开始将一些内部项目迁移到 `package:web` 和新的 JS 互操作机制,并期望在下一个稳定版本中有更多更新。
> 可以在 https://flutter.dev/wasm 了解更多。
# 最后
本次更新最重要有两个点,第一就是 Dart interop 越来越成熟,相信以后直接通过 flutter run 就可以完成所有 interop 的绑定和编译,第二就是 Web 路线随着 Dart Wasm 支持的进展,越来越值得期待了。

View File

@ -90,6 +90,8 @@
* [Flutter 最优秀动画库「完全商业化」Rive 2 你全面了解过吗?](Flutter-Rive.md)
* [Harmony 开始支持 Flutter ,聊聊 Harmony 和 Flutter 之间的因果](Flutter-HF.md)
* [Flutter 与 Dart 的市场应用](Flutter-WH.md)
* [Flutter 小技巧之不一样的思路实现炫酷 3D 翻页折叠动画](Flutter-GLSL.md)
* [Flutter 小技巧之 3.16 升级最坑 M3 默认适配技巧](Flutter-M3D.md)

416
Flutter-316.md Normal file
View File

@ -0,0 +1,416 @@
# Flutter 3.16 发布,快来看有什么更新吧
> 参考原文https://medium.com/flutter/whats-new-in-flutter-3-16-dba6cb1015d1
Flutter 又又又发布新季度更新啦,同时随着而来的还有 Dart 3.2,本次 3.16 开始 Material 3 会成为新的默认主题,另外 Android 也迎来了 Impeller 的预览支持,另外还有 [Flutter Casual Games Toolkit ](https://medium.com/flutter/building-your-next-casual-game-with-flutter-716ef457e440) 的重大更新。
> **最重要的是Impeller 的 Android 支持来了。**
# Framework
## Material default
现在,从 3.16 开始,`MaterialApp` 里的 `useMaterial3` 默认会是 true如果你还希望使用 M2可以使用 `useMaterial3: false` 来使用 M2 的主题效果,**不过 Material 2 相关的东西未来会被弃用并删除**。
另外在 M3 上其实有的 Widget 和 M2 并不是完全兼容,所以更新到 3.16 后一些 UI 你可以需要做手动迁移适配,例如 [NavigationBar](https://api.flutter.dev/flutter/material/NavigationBar-class.html) 的 UI 效果。
> 更多迁移问题可见https://github.com/flutter/flutter/issues/91605 ,另外通过 https://flutter.github.io/samples/material_3.html 你可以比较两种主题下的不同效果。
M3 下的主题主要由 `ThemeData.colorScheme``ThemeData.textTheme` 来决定,创建 Material 3 配色首选是使用 `ColorScheme.fromSeed()` ,另外也可以通过 `ColorScheme.fromImageProvider` 来从图像下获取配色方案支持。
> 木已成舟,建议大家及早适配。
![](http://img.cdn.guoshuyu.cn/20231116_Flutter316/image1.gif)
另外对于 M3 motion 的改进还包括添加 `Easing``Durations` 类,而 Material 2 的 curves 现在被重命名,会包含 “legacy” 的警告,表示它最终将被弃用和删除。( [#129942](https://github.com/flutter/flutter/pull/129942) )
> 简单来说就是,新增加了 motion.dart 来替代老的 [curves.dart#L26 ](https://github.com/flutter/flutter/blob/main/packages/flutter/lib/src/material/curves.dart#L26)。
![](http://img.cdn.guoshuyu.cn/20231116_Flutter316/image2.png)
![](http://img.cdn.guoshuyu.cn/20231116_Flutter316/image3.png)
![](http://img.cdn.guoshuyu.cn/20231116_Flutter316/image4.gif)
## 在编辑菜单中添加附加选项
在 iOS 上,用户现在可以选择文本并启动提供多种标准服务的共享菜单,在 3.16 的版本中本次添加了查找、搜索和共享选项。
![](http://img.cdn.guoshuyu.cn/20231116_Flutter316/image5.gif)
## 增加 TextScaler
为了支持 Android 14 的[非线性字体缩放功能](https://blog.google/products/android/android-14/#:~:text=Also%2C you can improve readability,rate than smaller font size.)来帮助视力障碍,新 `TextScaler` 类替换了`Text.textScaleFactor ` 属性。( [#128522](https://github.com/flutter/flutter/pull/128522) )
## SelectionArea 更新
`SelectionArea` 现在支持鼠标单击、双击以及长按触摸设备相关的原生手势,默认情况下,这些新手势可通过`SelectionArea ` 和 `SelectableRegion` 来支持:
- 单击:在单击的位置设置折叠选区
- 双击:选择单击位置的单词
- 双击 + 拖动:扩展单词块中的选择范围
![](http://img.cdn.guoshuyu.cn/20231116_Flutter316/image6.gif)
- 长按+拖动:扩展单词块中的选择范围。
![](http://img.cdn.guoshuyu.cn/20231116_Flutter316/image7.gif)
## 对焦点 Widget 进行操作的菜单项
3.16 开始增加清除使用菜单项时焦点更改的功能: `FocusManager``applyFocusChangesIfNeeded` 现在支持恢复菜单焦点 ,当用户单击菜单项时,焦点将返回到打开菜单之前具有焦点的项目。( [#130536](https://github.com/flutter/flutter/pull/130536) )
![](http://img.cdn.guoshuyu.cn/20231116_Flutter316/image8.gif)
## iOS、macOS 的菜单项快捷方式自动重新排序
Mac 平台上的 Flutter 应用现在可以对菜单中的快捷方式修饰符进行排序,以遵循 Apple 人机界面指南。( [#129309](https://github.com/flutter/flutter/pull/129309) )
![](http://img.cdn.guoshuyu.cn/20231116_Flutter316/image9.png)
## MatrixTransition 动画
新的 `MatrixTransition` 允许在创建动画过渡时进行矩阵变换,根据当前动画值,可以提供 child widget 的矩阵变换。( [#131084](https://github.com/flutter/flutter/pull/131084) )
```dart
class MatrixTransitionExampleApp extends StatelessWidget {
const MatrixTransitionExampleApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: MatrixTransitionExample(),
);
}
}
class MatrixTransitionExample extends StatefulWidget {
const MatrixTransitionExample({super.key});
@override
State<MatrixTransitionExample> createState() =>
_MatrixTransitionExampleState();
}
class _MatrixTransitionExampleState extends State<MatrixTransitionExample>
with TickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
)..repeat();
_animation = CurvedAnimation(
parent: _controller,
curve: Curves.linear,
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: MatrixTransition(
animation: _animation,
child: const Padding(
padding: EdgeInsets.all(8.0),
child: FlutterLogo(size: 150.0),
),
onTransform: (double value) {
return Matrix4.identity()
..setEntry(3, 2, 0.004)
..rotateY(pi * 2.0 * value);
},
),
),
);
}
}
```
![](http://img.cdn.guoshuyu.cn/20231116_Flutter316/image10.gif)
## PaintPattern 添加到 flutter_test
`flutter_test `包中,新 `PaintPattern` 类允许开发者验证 `CustomPainter``Decoration` (在单元测试中使用)等 Widget 对画布进行的绘制调用。
以前需要一个文件来验证是否绘制了正确的颜色和矩形,但现在可以使用 `PaintPattern` ,例如以下示例验证了 `MyWidget `在画布上绘制了一个圆圈:
```dart
expect(
find.byType(MyWidget),
paints
..circle(
x: 10,
y: 10,
radius: 20,
color: const Color(0xFFF44336),
),
);
// Multiple paint calls can even be chained together.
expect(
find.byType(MyWidget),
paints
..circle(
x: 10,
y: 10,
radius: 20,
color: const Color(0xFFF44336),
),
..image(
image: MyImage,
x: 20,
y: 20,
),
);
```
## 滚动更新
继 Flutter 3.13 中首次发布二维滚动基础之后3.16 带来了更多功能和完善, 2D foundation 现在支持 `KeepAlive` Widget以及默认焦点遍历和隐式滚动。
3.13 版本发布后不久,[two_Dimension_scrollables](https://pub.dev/packages/two_dimensional_scrollables) 包就发布了,该包由 Flutter 团队维护,包含第一个构建在该框架基础上的 2D 滚动 widget - `TableView`,目前已添加了丰富的装饰和样式支持以及其他错误修复。
# Engine
## Impeller Android
在 3.16 版本中Android 上的 Impeller 已准备好在 stable 上提供预览,该预览版包括有关支持 Vulkan 的设备上的 Impeller 特性。
![](http://img.cdn.guoshuyu.cn/20231116_Flutter316/image11.png)
> 图表显示了过去一年中在 Impeller 的 Vulkan 后端上运行的 Flutter Gallery 转换性能基准测试改进,用户将观察到卡顿更少且稳态帧速率更高。
目前 Impeller 在没有 Vulkan 支持的设备上会表现不佳,单在未来几个月内还会将 Impeller 的 OpenGL 后端功能继续完善。
Flutter 开发者现在可以在支持 Vulkan 的 Android 设备上试用 Impeller方法是将标志 `— enable-impeller 传递``flutter run`,或者将以下设置添加到 `AndroidManiest.xml`文件中的 `<application>`
```xml
<meta-data
android:name="io.flutter.embedding.android.EnableImpeller"
android:value="true" />
```
> 通常Impeller 在运行 Android API 29 或更高版本的 64 位操作系统的设备上会使用 Vulkan 。
目前 Android Vulkan 预览版已知问题有:
- platform view 尚未实现支持,包含 platform view 的框架性能会有些差。
- 自定义着色器还未实现。
因为 Android 硬件生态系统更加多样化,预计 Android 的预览期将比 iOS 更长这是无法避免的另外Impeller 的 Vulkan 在“调试”构建中启用了超出 Skia 使用的功能的额外调试功能,并且这些功能会产生额外的运行时开销,**所以有关 Impeller 性能的反馈来自最好来自 profile 或发布版本**。
## Impeller 性能, 保真度和稳定性
3.16 还对 Impeller 中的文本性能进行了多项改进,这对 Android 和 iOS 都是同样的。特别是改进了 Impeller 字形图集的管理,以及在引擎的 UI 和光栅线程之间划分文本工作负载的方式,所以 3.16 上用户会注意到文本繁重工作负载中的卡顿现象减少。
![](http://img.cdn.guoshuyu.cn/20231116_Flutter316/image12.png)
> 图表显示,在使用 Impeller 的 iPhone 11 上进行的一项文本密集型基准测试中平均帧光栅化时间(以毫秒为单位)有所减少。
本次 3.16 已经对 flutter/engine 存储库做出了 209 个 Impeller 相关承诺,解决了 217 个问题,其中包括 42 个有关保真度、稳定性或性能问题的用户报告。
## Engine 性能
为了在具有异构多处理功能的移动设备上支持更好的性能,本次 [修改了](https://github.com/flutter/engine/pull/45673) Engine 对性能敏感的线程(例如 UI 和光栅线程)与设备更强大的内核的支持。
本次修改在某些情况下改进非常显着,预计在 Android 上的 Skia 和 Impeller 进行本次更改后,用户将注意到卡顿现象减少,而在 iOS 设备上,这种影响不太明显,因为在 iOS 设备上,功能较强大的内核与功能较弱的内核之间的差异较小。
![](http://img.cdn.guoshuyu.cn/20231116_Flutter316/image13.png)
## Impeller 性能 overlay
在之前的版本中Flutter 的[性能 overlay ](https://docs.flutter.dev/perf/ui-performance#the-performance-overlay)功能并未与 Impeller 一起发布,本次版本修复了该问题。现在在启用 Impeller 的情况下,性能 overlay 可以正确显示。
![](http://img.cdn.guoshuyu.cn/20231116_Flutter316/image14.png)
## 现在可以正确显示 Dithering
在 3.16 版本中, `Paint.enableDithering` 属性默认设置为 true不再支持开发人员配置在此之前渐变在所有设备上都有很多色带并且在使用某些动画时看起来也很奇怪而解决方案是使渐变不透明并使用 Skia 的Dithering 渐变。
> 而为了简化迁移过程, Impeller 永远不会支持除梯度之外的任何内容的 Dithering。
- 3.16 之前
![](http://img.cdn.guoshuyu.cn/20231116_Flutter316/image15.png)
- 3.16 之后
![](http://img.cdn.guoshuyu.cn/20231116_Flutter316/image16.png)
# 游戏
## Flutter 游戏工具包
在过去的几年里 Flutter 发布了数以万计的游戏,从简单但有趣的谜题到更复杂的街机游戏,其中包括:
- Etermax 的[Trivia Crack](https://triviacrack.com/)
- Lotum 的[4 Pics 1 Word](https://flutter.dev/showcase/lotum)(猜词游戏)
- Dong Digital 的 [Brick Mania](https://play.google.com/store/apps/details?id=net.countrymania.brick&hl=en)(街机游戏)
- Onrizon 的[StopotS](https://play.google.com/store/apps/details?id=com.gartic.StopotS&hl=en)(类别游戏)
- Flutter for I/O 的 [复古弹球游戏](https://pinball.flutter.dev/)
- [PUBG](https://flutter.dev/showcase/pubg-mobile) 移动版在社交和菜单屏幕中使用 Flutter
- ····
![](http://img.cdn.guoshuyu.cn/20231116_Flutter316/image17.gif)
为了帮助游戏开发者提高工作效率Flutter 今天推出了休闲游戏工具包的重大更新,它是一系列新资源的集合,可帮助开发者从概念转向推出更多特定类型的游戏模板,例如纸牌游戏、无尽跑酷游戏,以及 Play 游戏服务、应用内购买、广告、成就、crashlytics 等服务集成和多人游戏支持。
> 要了解更多信息,可以查看 https://medium.com/flutter/building-your-next-casual-game-with-flutter-716ef457e440
# Web
## Chrome DevTools 上的 Flutter 时间轴事件
Flutter 时间轴事件现在显示在 Chrome DevTools 的性能面板中。( [#130132](https://github.com/flutter/flutter/issues/130132) )
![](http://img.cdn.guoshuyu.cn/20231116_Flutter316/image18.png)
# Android
## 鼠标滚轮支持
为了适配鼠标在平板电脑或可折叠设备的效果3.16 版本中 flutter 支持对鼠标滚动与 Android 设备上的滚动速度相匹配。( [44724](https://github.com/flutter/engine/pull/44724) )
![](http://img.cdn.guoshuyu.cn/20231116_Flutter316/image19.gif)
## 预测性后退导航
Android 14 版本包含预测性后退手势功能3.16 更新为 Flutter 带来了预测性返回手势。
```dart
PopScope(
canPop: _myCondition,
child: ...
),
PopScope(
canPop: true,
onPopInvoked (bool didPop) {
_myHandleOnPopMethod();
},
child: ...
),
NavigatorPopHandler(
onPop: () => _nestedNavigatorKey.currentState!.pop(),
child: Navigator(
key: _nestedNavigatorKey,
),
)
···
```
![](http://img.cdn.guoshuyu.cn/20231116_Flutter316/image20.gif)
# iOS系统
## 应用扩展
Flutter 现在可以支持针对某些 [iOS 应用扩展](https://developer.apple.com/app-extensions/),这意味着可以使用 Flutter Widget 为某些类型的 iOS 应用绘制 UI当然这并不适用于所有类型的应用扩展因为 API例如主屏幕空间或内存可能存在限制。
![](http://img.cdn.guoshuyu.cn/20231116_Flutter316/image21.png)
由于应用扩展的内存限制,仅建议使用 Flutter 为内存限制大于 100MB 的扩展类型构建应用扩展 UI。
此外Flutter 在调试模式下会使用额外的内存,因此当用于构建扩展 UI 时Flutter 并不完全支持在物理设备上以调试模式运行应用扩展。
> 详细可见: https://docs.flutter.dev/platform-integration/ios/app-extensions
# 生态
目前 [Flutter Favorite](https://docs.flutter.dev/packages-and-plugins/favorites) 已经重新启动,在本周期中 Flutter 生态系统委员会将 [Flame](https://pub.dev/packages/flame)、[flutter_animate](https://pub.dev/packages/flutter_animate)、[flutter_rust_bridge](https://pub.dev/packages/flutter_rust_bridge)、[Riverpod](https://pub.dev/packages/riverpod)、[video_player](https://pub.dev/packages/video_player)、[macos_ui](https://pub.dev/packages/macos_ui) 和 [fpdart](https://pub.dev/packages/fpdart) 包指定为新的 Flutter Favorite。
## Camera X 改进
在 3.10 稳定版本中, Flutter 相机插件中添加了对 Camera X 的初步支持,而 CameraX 解决了该插件的 Camera 2 实现中存在的许多问题。
```yaml
dependency:
camera: ^0.10.4
camera_android_camerax: ^0.5.0
```
## macOS 视频播放器
[video_player](https://pub.dev/packages/video_player) 中添加了 macOS 支持。
# 开发工具
## 开发工具扩展
新的 [DevTools 扩展框架](https://pub.dev/packages/devtools_extensions) 支持:
- 包作者为其直接在 DevTools 中显示的包构建自定义工具。
- 软件包作者可以编写强大的工具,利用 DevTools 中的现有框架和实用程序。
- 使用 DevTools 调试应用以访问特定于其用例的工具的 Dart 和 Flutter 开发人员(由应用的依赖项以及哪些依赖项提供 DevTools 扩展决定)。
感谢 [Provider](https://pub.dev/packages/provider)、[Drift](https://pub.dev/packages/drift) 和 [Patrol](https://pub.dev/packages/patrol) 的软件包作者,这个生态系统已经建立起来,现在就可以使用这些软件包的 DevTools 扩展!
| ![](http://img.cdn.guoshuyu.cn/20231116_Flutter316/image22.png) | ![](http://img.cdn.guoshuyu.cn/20231116_Flutter316/image23.png) | ![](http://img.cdn.guoshuyu.cn/20231116_Flutter316/image24.png) |
| ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
## 开发工具更新
此版本的 DevTools 的一些亮点包括:
- 添加了对 DevTools 扩展的支持
- 添加了一个新的“主”屏幕,显示连接的应用的摘要
![](http://img.cdn.guoshuyu.cn/20231116_Flutter316/image25.png)
其他改进包括:
- 整体表现
- 热重启 robustness
- 文本选择和复制行为
- viewer polish 网络分析响应
## VS Code UI 的可发现性
感谢 Flutter 社区成员 [DanTup](https://github.com/DanTup) Flutter VS Code 扩展现在拥有一个 Flutter 侧边栏,可让轻松访问:
- 打开 Flutter DevTools 屏幕
- 查看活动的调试会话
- 查看可用设备
- 创建新项目
- 热重载并重启
- 运行 Flutter Doctor -v
- ····
![](http://img.cdn.guoshuyu.cn/20231116_Flutter316/image26.png)
# 最后
本次更新的还是属于比较“低调”的更新,最大的变化应该就是 M3 的默认主题和 Android Impeller ,其他的其实影响并不是很大,其中 M3 主题还是建议大家及早适配,因为 M2 的控件效果未来确实会慢慢剔除。
另外可以看到本次更新的核心还是集中在 Android 和 iOS PC 更新节奏看起来受到“某些影响”后慢了不少?同时关于 Jetbrains 的插件更新也没体现,核心 IDE 的资源都投入到 VSCode 了,只能说且行且珍惜。
好了,勇敢的少年,开始吃螃蟹了。

364
Flutter-GLSL.md Normal file
View File

@ -0,0 +1,364 @@
# Flutter 小技巧之不一样的思路实现炫酷 3D 翻页折叠动画
今天聊一个比较有意思的 Flutter 动画实现,如果需要实现一个如下图的 3D 折叠动画效果,你会选择通过什么方式?
![](http://img.cdn.guoshuyu.cn/20231031_GLSL/image1.gif)
相信可能很多人第一想法就是:**在 Dart 里通过矩阵变换配合 Canvas 实现**。
因为这个效果其实也算「常见」,在目前的小说阅读器场景里,类似的翻页效果基本都是通过这个思路完成,而这个思路以前我也「折腾」过不少,比如 [《炫酷的 3D 卡片和帅气的 360° 展示效果》](https://juejin.cn/post/7124064789763981326) 和 [用纯代码实现立体 Dash 和 3D 掘金 Logo](https://juejin.cn/post/7129239231473385503) ,就是在 Dart 里利用矩阵变换实现的视觉 3D 效果。
![](http://img.cdn.guoshuyu.cn/20231031_GLSL/image2.gif)
但是今天通过一个叫 [riveo_page_curl](https://github.com/Rahiche/riveo_page_curl) 的项目,提供了不一样的实现方式,**那就是通过自定义 Fragment Shaders 实现动画** ,使用自定义 shaders 可以直接使用 GLSL 语言来进行编程,最终达到通过 GPU 渲染出更丰富图形效果。
![](http://img.cdn.guoshuyu.cn/20231031_GLSL/image3.gif)
解释这个项目之前,我们先聊聊 Fragment Shader **Flutter 在 3.7 开始提供 Fragment Shader API** ,顾名思义,它是一个作用于片段的着色器,也就是通过 Fragment Shader API ,开发者可以直接介入到 Flutter 渲染管道的渲染流程中。
![](http://img.cdn.guoshuyu.cn/20231031_GLSL/image4.png)
**那么直接使用 Fragment Shader 而不使用 Dart 矩阵变换的好处是什么**?简单来说就是可以减少 CPU 的耗时直接通过图形语言GLSL直接给 GPU 发送指令,从性能上无疑可以得到提升,并且实现会更简洁。
> 不过加载着色器这个行为的开销可能会比较大,所以必须在运行时将它编译为适当的特定于平台的着色器。
当然,在 Flutter 里使用 Fragment Shader 也是有条件限制的,例如一般都需要引入 `#include <flutter/runtime_effect.glsl>` 这个头文件,因为在编写着色器代码时,我们都需要知道当前片段的局部坐标的值,而 `flutter/runtime_effect.glsl` 里就提供了 `FlutterFragCoord().xy;` 来支持访问局部坐标,而这并不是标准 GLSL 的 API 。
另外, Fragment Shader 只支持 `.frag` 格式文件, 不支持顶点着色文件 `.vert` ,同时还有以下限制:
- 不支持 UBO 和 SSBO
- sampler2D 是唯一受支持的 sampler 类型
- texture 仅支持( sampler 和 uv的两个参数版本
- 不能声明额外的可变输入
- 不支持无符号整数和布尔值
所以如果需要搬运一些已有的 GLSL 效果,例如 [shadertoy](https://www.shadertoy.com/) 上的代码时,那么一些必要的「代码改造」还是逃不掉的,例如下方代码是一段渐变动画的着色器:
```glsl
void mainImage( out vec4 fragColor, in vec2 fragCoord ){
float strength = 0.4;
float t = iTime/3.0;
vec3 col = vec3(0);
vec2 fC = fragCoord;
for(int i = -1; i <= 1; i++) {
for(int j = -1; j <= 1; j++) {
fC = fragCoord+vec2(i,j)/3.0;
vec2 pos = fC/iResolution.xy;
pos.y /= iResolution.x/iResolution.y;
pos = 4.0*(vec2(0.5) - pos);
for(float k = 1.0; k < 7.0; k+=1.0){
pos.x += strength * sin(2.0*t+k*1.5 * pos.y)+t*0.5;
pos.y += strength * cos(2.0*t+k*1.5 * pos.x);
}
col += 0.5 + 0.5*cos(iTime+pos.xyx+vec3(0,2,4));
}
}
col /= 9.0;
col = pow(col, vec3(0.4545));
fragColor = vec4(col,1.0);
}
```
![](http://img.cdn.guoshuyu.cn/20231031_GLSL/image5.gif)
而在 Flutter 里,就需要转化为如下代码所示:
- 首先就是必不可少的 `flutter/runtime_effect.glsl`
- 其次定义 `main() ` 函数
- 然后我们需要将 `mainImage` 里定义的 `out vec4 fragColor;` 移到全局声明
- 因为在 GLSL 里 iResolution 用于表示画布像素高宽iTime 是程序运行的时间,而这里通过 `uniform` 定义 `resolution``iTime` 直接用于接受 Dart 端的输入,其余逻辑不变
- 对应 `fragCoord` 可以在 Flutter 里通过 `FlutterFragCoord ` 获取坐标
```glsl
#version 460 core
#include <flutter/runtime_effect.glsl>
out vec4 fragColor;
uniform vec2 resolution;
uniform float iTime;
void main(){
float strength = 0.25;
float t = iTime/8.0;
vec3 col = vec3(0);
vec2 pos = FlutterFragCoord().xy/resolution.xy;
pos = 4.0*(vec2(0.5) - pos);
for(float k = 1.0; k < 7.0; k+=1.0){
pos.x += strength * sin(2.0*t+k*1.5 * pos.y)+t*0.5;
pos.y += strength * cos(2.0*t+k*1.5 * pos.x);
}
col += 0.5 + 0.5*cos(iTime+pos.xyx+vec3(0,2,4));
col = pow(col, vec3(0.4545));
fragColor = vec4(col,1.0);
}
```
> 第一行 `#version 460 core` 指定所用的 OpenGL 语言版本。
可以看到转换一段 GLSL 代码并不特别麻烦,主要是坐标和输入参数的变化,而通过这些已有的片段着色器,却可以给我们提供极其丰富的渲染效果,如下代码所示:
- 在 `pubspec.yaml` 里引入上面的 shaders 代码
- 通过 `ShaderBuilder` 加载对应 `'shaders/warp.frag'` 文件,获得 `FragmentShader`
- 利用 `FragmentShader``setFloat` 传递数据
- 通过 `Paint()..shader ` 添加着色器进行绘制,就可以完成渲染
```dart
flutter:
shaders:
- shaders/warp.frag
·············
late Ticker _ticker;
Duration _elapsed = Duration.zero;
@override
void initState() {
super.initState();
_ticker = createTicker((elapsed) {
setState(() {
_elapsed = elapsed;
});
});
_ticker.start();
}
@override
Widget build(BuildContext context) => ShaderBuilder(
assetKey: 'shaders/warp.frag',
(BuildContext context, FragmentShader shader, _) => Scaffold(
appBar: AppBar(
title: const Text('Warp')
),
body: CustomPaint(
size: MediaQuery.of(context).size,
painter: ShaderCustomPainter(shader, _elapsed)
),
),
);
class ShaderCustomPainter extends CustomPainter {
final FragmentShader shader;
final Duration currentTime;
ShaderCustomPainter(this.shader, this.currentTime);
@override
void paint(Canvas canvas, Size size) {
shader.setFloat(0, size.width);
shader.setFloat(1, size.height);
shader.setFloat(2, currentTime.inMilliseconds.toDouble() / 1000.0);
final Paint paint = Paint()..shader = shader;
canvas.drawRect(Offset.zero & size, paint);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}
```
这里唯一需要解释的就是 `shader.setFloat` 流程,因为它其实是通过索引来对应到我们在 `.frag` 文件里的变量,简单来说:
> 这里我们在 GLSL 里定义了 `uniform vec2 resolution;``uniform float iTime;` ,那么 vec2 resolution 就占据了索引 0 和 1 float iTime 就占据了索引 2 。
大概理解就是vec2 就是两个 float 类型的值保存在了一起的意思,所以先声明的 vec2 resolution 就占据了 索引 0 和 1 ,举个例子,如下图所示,此时的 vec2 和 vec3 分了就占据了 0-4 的索引。
![](http://img.cdn.guoshuyu.cn/20231031_GLSL/image6.png)
而通过 `uniform ` 在 GLSL 着色器中定义值,然后在 Dart 中就可以通过 `setFloat` 的索引来传递对应数据过去,从而形成了数据交互的完整闭环。
> 这里的渐变动画在 Flutter 的完整代码可以参考 Github https://github.com/tbuczkowski/flutter_shaders 里的 [warp.frag](https://github.com/tbuczkowski/flutter_shaders/blob/master/shaders/warp.frag)
同时针对前面整个渐变动画,作者在仓库内还提供了对应纯 Dart 代码实现一样效果的对比,通过数据可以看到,利用着色器的实现在性能上得到了巨大的提升。
![image-20231031175152699](http://img.cdn.guoshuyu.cn/20231031_GLSL/image7.png)
那么回过头来, [riveo_page_curl](https://github.com/Rahiche/riveo_page_curl) 的项目里的折叠着色器如下所示,除了一堆不懂的矩阵变化,如 `scale` 缩放、`translate` 平移和 `project` 投影转换之外,就是各种看不明白的三角函数计算,简单的核心就是在矩阵变化时计算弯曲部分的弧度,以及增加阴影投影来提高视觉效果。
```glsl
#include <flutter/runtime_effect.glsl>
uniform vec2 resolution;
uniform float pointer;
uniform float origin;
uniform vec4 container;
uniform float cornerRadius;
uniform sampler2D image;
const float r = 150.0;
const float scaleFactor = 0.2;
#define PI 3.14159265359
#define TRANSPARENT vec4(0.0, 0.0, 0.0, 0.0)
mat3 translate(vec2 p) {
return mat3(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, p.x, p.y, 1.0);
}
mat3 scale(vec2 s, vec2 p) {
return translate(p) * mat3(s.x, 0.0, 0.0, 0.0, s.y, 0.0, 0.0, 0.0, 1.0) * translate(-p);
}
vec2 project(vec2 p, mat3 m) {
return (inverse(m) * vec3(p, 1.0)).xy;
}
struct Paint {
vec4 color;
bool stroke;
float strokeWidth;
int blendMode;
};
struct Context {
vec4 color;
vec2 p;
vec2 resolution;
};
bool inRect(vec2 p, vec4 rct) {
bool inRct = p.x > rct.x && p.x < rct.z && p.y > rct.y && p.y < rct.w;
if (!inRct) {
return false;
}
// Top left corner
if (p.x < rct.x + cornerRadius && p.y < rct.y + cornerRadius) {
return length(p - vec2(rct.x + cornerRadius, rct.y + cornerRadius)) < cornerRadius;
}
// Top right corner
if (p.x > rct.z - cornerRadius && p.y < rct.y + cornerRadius) {
return length(p - vec2(rct.z - cornerRadius, rct.y + cornerRadius)) < cornerRadius;
}
// Bottom left corner
if (p.x < rct.x + cornerRadius && p.y > rct.w - cornerRadius) {
return length(p - vec2(rct.x + cornerRadius, rct.w - cornerRadius)) < cornerRadius;
}
// Bottom right corner
if (p.x > rct.z - cornerRadius && p.y > rct.w - cornerRadius) {
return length(p - vec2(rct.z - cornerRadius, rct.w - cornerRadius)) < cornerRadius;
}
return true;
}
out vec4 fragColor;
void main() {
vec2 xy = FlutterFragCoord().xy;
vec2 center = resolution * 0.5;
float dx = origin - pointer;
float x = container.z - dx;
float d = xy.x - x;
if (d > r) {
fragColor = TRANSPARENT;
if (inRect(xy, container)) {
fragColor.a = mix(0.5, 0.0, (d-r)/r);
}
}
else
if (d > 0.0) {
float theta = asin(d / r);
float d1 = theta * r;
float d2 = (3.14159265 - theta) * r;
vec2 s = vec2(1.0 + (1.0 - sin(3.14159265/2.0 + theta)) * 0.1);
mat3 transform = scale(s, center);
vec2 uv = project(xy, transform);
vec2 p1 = vec2(x + d1, uv.y);
s = vec2(1.1 + sin(3.14159265/2.0 + theta) * 0.1);
transform = scale(s, center);
uv = project(xy, transform);
vec2 p2 = vec2(x + d2, uv.y);
if (inRect(p2, container)) {
fragColor = texture(image, p2 / resolution);
} else if (inRect(p1, container)) {
fragColor = texture(image, p1 / resolution);
fragColor.rgb *= pow(clamp((r - d) / r, 0.0, 1.0), 0.2);
} else if (inRect(xy, container)) {
fragColor = vec4(0.0, 0.0, 0.0, 0.5);
}
}
else {
vec2 s = vec2(1.2);
mat3 transform = scale(s, center);
vec2 uv = project(xy, transform);
vec2 p = vec2(x + abs(d) + 3.14159265 * r, uv.y);
if (inRect(p, container)) {
fragColor = texture(image, p / resolution);
} else {
fragColor = texture(image, xy / resolution);
}
}
}
```
![](http://img.cdn.guoshuyu.cn/20231031_GLSL/image8.png)
其实我知道大家并不关心它的实现逻辑,更多是如何使用,这里有个关键信息就是 `uniform sampler2D image` ,通过引入 `sampler2D` ,我们就可以在 Dart 通过 `setImageSampler(0, image); ``ui.Image` 传递到 GLSL 里,这样就可以对 Flutter 控件实现上述的折叠动画逻辑。
对应在 Dart 层,就是除了 `ShaderBuilder` 之外,还可以通过 [flutter_shaders](https://pub.dev/packages/flutter_shaders) 的 `AnimatedSampler` 来实现更简洁的 `shader` 、`image` 和 `canvas` 的配合,其中 `AnimatedSampler` 的最大作用,就是将整个 child 通过 `PictureRecorder` 进行截图,转化成 `ui.Image` 传递给 GLSL完成 UI 传递交互效果。
```dart
Widget _buildAnimatedCard(BuildContext context, Widget? child) {
return ShaderBuilder(
(context, shader, _) {
return AnimatedSampler(
(image, size, canvas) {
_configureShader(shader, size, image);
_drawShaderRect(shader, size, canvas);
},
child: Padding(
padding: EdgeInsets.symmetric(vertical: cornerRadius),
child: widget.child,
),
);
},
assetKey: 'shaders/page_curl.frag',
);
void _configureShader(FragmentShader shader, Size size, ui.Image image) {
shader
..setFloat(0, size.width) // resolution
..setFloat(1, size.height) // resolution
..setFloat(2, _animationController.value) // pointer
..setFloat(3, 0) // origin
..setFloat(4, 0) // inner container
..setFloat(5, 0) // inner container
..setFloat(6, size.width) // inner container
..setFloat(7, size.height) // inner container
..setFloat(8, cornerRadius) // cornerRadius
..setImageSampler(0, image); // image
}
void _drawShaderRect(FragmentShader shader, Size size, Canvas canvas) {
canvas.drawRect(
Rect.fromCenter(
center: Offset(size.width / 2, size.height / 2),
width: size.width,
height: size.height,
),
Paint()..shader = shader,
);
}
```
> 完整项目可见https://github.com/Rahiche/riveo_page_curl
所以可以看到,**相比起在 Dart 层实现这样的 3D 翻页折叠,利用 `FragmentShader` 实现的代码会更简洁,并且性能体验上会更优于纯 Dart 实现**,最重要的是,类似 [ShaderToy](https://www.shadertoy.com/) 里的一些着色器代码,通过简单的移植适配,就可以在直接被运用到 Flutter 里,这对于 Flutter 在游戏场景的实现来无疑说非常友好。
最后Flutter 3.10 之后, Flutter Web 同样支持了 fragment shaders所以着色器在 Flutter 的实现目前已经相对成熟,那么如果是之前的我通过 Flutter 实现的《[霓虹灯文本的「故障」效果的实现](https://juejin.cn/post/7214858677173289017?searchId=202310311754299E224DB054AADBBC6AE2)》的逻辑转换成 fragment shaders 来完成,是不是性能和代码简洁程度也会更高?

280
Flutter-M3D.md Normal file
View File

@ -0,0 +1,280 @@
# Flutter 小技巧之 3.16 升级最坑 M3 默认适配技巧
如果要说 Flutter 3.16 升级里是最坑的是什么?那我肯定要说是 Material 3 default M3
倒不是说 M3 bug 多,也不是 M3 在 3.16 上使用起来多麻烦,因为**虽然从 3.16 开始,`MaterialApp` 里的 `useMaterial3` 默认会是 true但是你是可以直接 使用 `useMaterial3: false` 来关闭**。
那为什么还收坑?因为未来 **Material 2 相关的东西会被弃用并删除**,所以 Material 3 defaultM3 是一个警告,你可以通过 `useMaterial3: false` 来关闭无视,但是这个技术债未来会很坑。
> 难道你还能一直苟着不更新?
为什么说它很坑因为适配它纯纯是一个体力活而且还是一个细节工作M3 是一套配色方案,**一套和 M2 「毫不相关」的配色方案**
- 配色方案代表着它已经帮你默认确定了什么地方应该用什么颜色
- M2 毫不相干,代表着你之前用这 M2 的 Widget 默认的 UI 效果,用了 M3 会完全不一样
![](http://img.cdn.guoshuyu.cn/20231123_M3/image1.gif)
如上图所示,看起来好像是就是:
- `AppBar` 配色发生了变化
- `FloatingActionButton` 从圆的变成方的,颜色发生变化
- 默认 Button 按照风格发生了变卦
- ······
似乎看起来也没什么,但是你知道有多少地方用了 `FloatingActionButton` ?每个地方的 `AppBar` 难道都要手动去调整?`ElevatedButton` 和 `TextButton` 有没有办法全局配置?本篇就是为了让你少走适配弯路,提供适配思路的角度。
> 核心还是国内的产品有谁愿意使用 Material Design 像这种 M2 到 M3 的变化,对于开发者来说纯粹就是负优化。
# 开始
首先,**官方 Material 3 配色首推是使用 `ColorScheme.fromSeed()` 来生成配色**,当然你也可以通过 `ColorScheme.fromImageProvider` 的图片来生成配色,不过一般人应该不会这么干,另外还有 `ColorScheme.fromSwatch` ,不过这个的灵活适配程度不如 fromSeed所以使用 fromSeed 是比较好的选择。
> 因为 M3 默认从蓝色系列变成紫色系统,所以如果你用的是默认色系,那就更需要配置来恢复,本篇的目的就是,**让 App 在 M3 下恢复到 M2 的 UI 效果,因为它真的不是仅仅一个颜色变化而已。**
![](http://img.cdn.guoshuyu.cn/20231123_M3/image2.png)
如果你以前的 `ThemeData` 是如下所示代码,那么运行之后你会看到,原本应该是 M2 效果的正常列表,现在变成了 M3 那种「无法言喻」的效果,可以看到此时 M3 下 `primarySwatch` 其实并没有起到作用。
```dart
ThemeData(
primarySwatch: Colors.blue,
////
)
```
| M2 | M3 |
| ------------------------------------------------------ | ------------------------------------------------------ |
| ![](http://img.cdn.guoshuyu.cn/20231123_M3/image3.png) | ![](http://img.cdn.guoshuyu.cn/20231123_M3/image4.png) |
那么首先我们要做的就是增加 `colorScheme` ,但是你在加完会发现并没有什么变化,这是因为此时控件还是处于 M3 的色系下,所以接下来我们要首先全局恢复 `Appbar`
```dart
ThemeData(
primarySwatch: Colors.blue,
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
),
```
> Do it。
# AppBar
如下代码所示,我们先添加 `AppBarTheme` ,可以看到 AppBar 的背景这样就变回了蓝色,但是这时候 Appbar 的文本和图标还是黑色。
```dart
ThemeData(
primarySwatch: Colors.blue,
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
appBarTheme: AppBarTheme(
backgroundColor: Colors.blue,
),
),
```
| ![](http://img.cdn.guoshuyu.cn/20231123_M3/image5.png) | ![](http://img.cdn.guoshuyu.cn/20231123_M3/image6.png) |
| ------------------------------------------------------ | ------------------------------------------------------ |
为了让图标和文本恢复到 M2 的白色,我们可以在 `AppBarTheme` 里配置 `iconTheme``titleTextStyle` 可以看到配置后如下图所示UI 上 `AppBar` 已经恢复到 M2 的效果,那么此时你可以会疑惑,为什么修改的配置是 `size: 24.0``Typography.dense2021.titleLarge`
```dart
AppBarTheme(
iconTheme: IconThemeData(
color: Colors.white,
size: 24.0,
),
backgroundColor: Colors.blue,
titleTextStyle: Typography.dense2014.titleLarge,
)
```
![](http://img.cdn.guoshuyu.cn/20231123_M3/image7.png)
其实这就是本篇的核心:**在 M2 控件还没被剔除的时候,通过参考源码将 M3 UI 恢复到 M2** 。
例如在 3.16 的源码里,`theme.useMaterial3 ?` 这样的代码目前随处可见,而此时 `AppBar` 里:
- `_AppBarDefaultsM3` 下 icon 的颜色是通过 `onSurface` 字段,大小是 24
- `_AppBarDefaultsM2` 下 icon 是直接使用 theme 下默认的样式,也就是 size 24 颜色白色。
![](http://img.cdn.guoshuyu.cn/20231123_M3/image8.png)
| M2 | M3 |
| ------------------------------------------------------------ | ------------------------------------------------------- |
| ![](http://img.cdn.guoshuyu.cn/20231123_M3/image9.png)![](http://img.cdn.guoshuyu.cn/20231123_M3/image10.png) | ![](http://img.cdn.guoshuyu.cn/20231123_M3/image11.png) |
所以我们可以在上面的 `IconThemeData` 里可以直接配置 `color: Colors.white, size: 24.0,` 来恢复到 M2 的效果。
> 当然你也可以配置 `ColorScheme``onSurface` 来改变颜色,但是这个影响返回太大,还是推荐配置 `AppBarTheme``IconThemeData`
另外可以看到,此时还有一个 `Typography.dense2014.titleLarge` ,这又是哪里来的?还是回到`_AppBarDefaultsM3` 里,在 M3 下, AppBar 使用的是 `ThemeData `下的 `textTheme.titleLarge` ,而默认字体样式配置,基本来自 `Typography` 对象。
![](http://img.cdn.guoshuyu.cn/20231123_M3/image12.png)
![](http://img.cdn.guoshuyu.cn/20231123_M3/image13.png)
`Typography` 里默认配置了大量字体配置,例如 `Typography.dense2014` 对应就是如下所示配置,从上面代码可以看到**默认情况下 M2 用的是 `Typography.material2014 `,对应就是 `Typography.dense2014`**,也就是在 AppBar 上 `Typography.dense2014.titleLarge` 就可以让 M3 的 AppBar 文本恢复到 M2 的样式。
![](http://img.cdn.guoshuyu.cn/20231123_M3/image14.png)
看到这里你是否已经学会了大概的思路?
**通过 `theme.useMaterial3 ` 去检索控件,然后在源码里找到 M2 的实现,然后将其修改到全局的主题设置里**,比如 AppBar 的就通过 `AppBarTheme` 配置,如果是 M2 的实现又引用了某些默认配置,就去检索这些默认配置的起源,所以说 M3 这个坑是一个体力活。
当然,这个思路下,有一些控件适配起来还是会有坑,因为它的变化确实有点大,例如 Card 控件。
# Card
如图所示,这是 `Card` 控件在 M2 和 M3 下的变化,除了默认弧度之后,最主要就是颜色发生了改变,从默认白色变成了带着浅蓝色的效果,但是这里有个坑,就是,**此时就算你给 Card 设置 `color: Colors.white,` ,它也依旧会带着这个浅蓝色的效果**。
| M2 | M3 |
| ------------------------------------------------------- | ------------------------------------------------------- |
| ![](http://img.cdn.guoshuyu.cn/20231123_M3/image15.png) | ![](http://img.cdn.guoshuyu.cn/20231123_M3/image16.png) |
那么这个颜色如何去除?其实只要 `ColorScheme` 下设置 `surfaceTint` 为透明色就可以了,如下图所示,因为 `Card` 的效果是通过封装 `Material` 控件实现,而 `Material` 在 M3 下会通过 `elevation``surfaceTint` 去合成一个覆盖色。
```dart
ColorScheme.fromSeed(
seedColor: Colors.blue,
///影响 card 的表色,因为 M3 下是 applySurfaceTint ,在 Material 里
surfaceTint: Colors.transparent,
),
```
![](http://img.cdn.guoshuyu.cn/20231123_M3/image17.png)
![](http://img.cdn.guoshuyu.cn/20231123_M3/image18.png)
所以根据判断,**将 `surfaceTint` 设置成透明就可以去除 `Card `这个覆盖色,这个逻辑在 `BottomAppBar` 里同样存在**,所以如果你需要把它们都恢复都 M2 效果,那么就只需要把 `surfaceTint` 设置成透明色即可。
![image-20231123172627998](http://img.cdn.guoshuyu.cn/20231123_M3/image19.png)
所以类似的变动才是 M3 里最坑的点,如果你不了解他们的底层实现,那么在升级之后,发现明明代码给了白色,为什么它还是会有浅蓝色效果?这对于开发者来就是一个找🐛的天坑,所以在这里也用 `Card` 提供一个解决问题的典型思路。
另外还有一个典型的控件,那就是 `FloatingActionButton`(FAB) 。
# FloatingActionButton
从 M2 到 M3 `FloatingActionButton`(FAB) 控件最大的变化就是变成了方形,其次颜色也不跟随之前和主题蓝色,我们不说 M3 这个「优化」如何,就说如何恢复到 M2 的效果。
| M2 | M3 |
| ------------------------------------------------------- | ------------------------------------------------------- |
| ![](http://img.cdn.guoshuyu.cn/20231123_M3/image20.png) | ![](http://img.cdn.guoshuyu.cn/20231123_M3/image21.png) |
首先按照惯例,肯定有一个叫 `floatingActionButtonTheme` 的参数,可以用于配置 `FloatingActionButtonThemeData` ,所以这里我们首先添加上配置,然后通过 `shape` 先变回原形,并且修改 `backgroundColor` 变成蓝色。
```dart
floatingActionButtonTheme: FloatingActionButtonThemeData(
backgroundColor: Colors.blue,
shape: CircleBorder()
),
```
那么此时剩下的就是 `Icon` 的颜色,我们当然可以在用到 `Icon` 的地方手动修改为白色,但是我们希望的是全局配置默认恢复到 M2 时代,所以我们就要去找 FAB 下 `Icon` 是如何获取到颜色的。
> 而寻找这个颜色的实现,居然就让我开启了一段漫长的旅程·····
首先 `Icon` 肯定是通过` IconThemeData` 去获取默认颜色,因为 FAB 的主题下没有 `iconTheme` 可以配置,那么首先就想到配置一个全局的 `iconTheme: IconThemeData` ,但是神奇的问题来了,配置之后居然无效。
那么就开始往上查找,然后依次返现, FAB 内部是通过 `RawMaterialButton` 实现的点击,而 `RawMaterialButton` 内部就有一个 `IconTheme.merge` 的实现,**那么 FAB 里的 `Icon` 默认应该是使用了 `effectiveTextColor` 这个颜色**。
![](http://img.cdn.guoshuyu.cn/20231123_M3/image22.png)
之后开始经历一番漫长检索关联,最终可以看到:
- 这个 `effectiveTextColor` 来自从 FAB 传入的 TextSytle 的 color
- 而 `textSytle` 来自 `extendedTextStyle`
- 而 `extendedTextStyle` 来自 `foregroundColor`
- `foregroundColor ` 默认来自 `floatingActionButtonTheme``foregroundColor`
![image-20231123174431747](http://img.cdn.guoshuyu.cn/20231123_M3/image23.png)
![](http://img.cdn.guoshuyu.cn/20231123_M3/image24.png)
![](http://img.cdn.guoshuyu.cn/20231123_M3/image25.png)
![](http://img.cdn.guoshuyu.cn/20231123_M3/image26.png)
所以破案了,**需要全局设置 FAB 下 ` Icon` 的颜色,是要配置 `FloatingActionButtonThemeData``foregroundColor`** ,这个设定和名称正常情况下谁能想得到呢?
而且这个传递嵌套如此“隐晦”,只能说, FAB 是 Flutter 样式跟踪里很典型的一个代表:**传递深theme 引用复杂,类似 `merge`/`copy` 的局部实现太过隐蔽**。
```dart
floatingActionButtonTheme: FloatingActionButtonThemeData(
backgroundColor: Colors.blue,
foregroundColor: Colors.blue,
shape: CircleBorder()),
```
另外关于 **`IconThemeData` 还有一个冷知识,参数不全的情况下,也就是不满足 `isConcrete` 的情况下,其他的参数在 `ofcontext `的时候是会被 `fallback` 覆盖**,这个对于 M3 - M2 的降级适配里也是一个关键信息。
![](http://img.cdn.guoshuyu.cn/20231123_M3/image27.png)
![](http://img.cdn.guoshuyu.cn/20231123_M3/image28.png)
![](http://img.cdn.guoshuyu.cn/20231123_M3/image29.png)
# primarySwatch
最后在聊一个 `ThemeData``primarySwatch`,为什么聊它,因为如果你的代码里用了 `primaryColorDark``primaryColorLight` 作为配置,那么使用 ` ColorScheme.fromSeed` 之后,它们会发生一些「奇妙的变化」,所以为了它们可以恢复到 M2 模式,那么设置 `primarySwatch` 可以将它们恢复到原有的效果。
![](http://img.cdn.guoshuyu.cn/20231123_M3/image30.png)
![](http://img.cdn.guoshuyu.cn/20231123_M3/image31.png)
![](http://img.cdn.guoshuyu.cn/20231123_M3/image32.png)
# 最后
如下所示是本次升级适配里的示例代码总和,其实 M3 模式下「降级」到 M2 UI 效果真的是一个体力活,类似上面三个典型的例子,都可以看出来跟踪默认 UI 的实现并不轻松,虽然对于 Flutter 团队来说,升级到 M3 可能是一次正向优化,但是对于不喜欢 Material Design 的国区而言M3 只能是一个负优化,不知道大家同意不?
```dart
return ThemeData(
///用来适配 Theme.of(context).primaryColorLight 和 primaryColorDark 的颜色变化,不设置可能会是默认蓝色
primarySwatch: color as MaterialColor,
/// Card 在 M3 下,会有 apply Overlay
colorScheme: ColorScheme.fromSeed(
seedColor: color,
primary: color,
brightness: Brightness.light,
///影响 card 的表色,因为 M3 下是 applySurfaceTint ,在 Material 里
surfaceTint: Colors.transparent,
),
/// 受到 iconThemeData.isConcrete 的印象,需要全参数才不会进入 fallback
iconTheme: IconThemeData(
size: 24.0,
fill: 0.0,
weight: 400.0,
grade: 0.0,
opticalSize: 48.0,
color: Colors.white,
opacity: 0.8,
),
///修改 FloatingActionButton的默认主题行为
floatingActionButtonTheme: FloatingActionButtonThemeData(
foregroundColor: Colors.white,
backgroundColor: color,
shape: CircleBorder()),
appBarTheme: AppBarTheme(
iconTheme: IconThemeData(
color: Colors.white,
size: 24.0,
),
backgroundColor: color,
titleTextStyle: Typography.dense2014.titleLarge,
systemOverlayStyle: SystemUiOverlayStyle.light,
),
```

View File

@ -94,6 +94,7 @@
- [Flutter 3.3 正式发布,快来看看有什么新功能吧](Flutter-330.md)
- [Flutter 3.7 正式发布,快来看看有什么新功能吧](Flutter-370.md)
- [ Flutter 3.10 发布,快来看看有什么更新吧](Flutter-310.md)
- [Flutter 3.16 发布,快来看有什么更新吧](Flutter-316.md)
- **Dart**
- [Dart 2.12 发布稳定空安全声明和FFI版本Dart 未来的计划](Dart-212.md)
- [Dart 2.14 发布,新增语言特性和共享标准 lint](Dart-214.md)
@ -103,6 +104,7 @@
- [Dart 2.18 发布Objective-C 和 Swift interop](Dart-218.md)
- [Flutter - Dart 3α 新特性 Record 和 Patterns 的提前预览讲解](Dart-300a.md)
- [Dart 3 发布,快来看看有什么更新吧](Dart-300.md)
- [Dart 3.2 更新Flutter Web 的未来越来越明朗](Dart-320.md)
* [番外](FWREADME.md)
@ -194,8 +196,9 @@
* [Flutter 小技巧之 3.13 全新生命周期 AppLifecycleListener ](Flutter-N31.md)
* [Flutter 最优秀动画库「完全商业化」Rive 2 你全面了解过吗?](Flutter-Rive.md)
* [Harmony 开始支持 Flutter ,聊聊 Harmony 和 Flutter 之间的因果](Flutter-HF.md)
* [Harmony 开始支持 Flutter ,聊聊 Harmony 和 Flutter 之间的因果](Flutter-HF.md)
* [Flutter 与 Dart 的市场应用](Flutter-WH.md)
* [Flutter 小技巧之不一样的思路实现炫酷 3D 翻页折叠动画](Flutter-GLSL.md)
* [Flutter 小技巧之 3.16 升级最坑 M3 默认适配技巧](Flutter-M3D.md)
[Flutter 工程化选择](GCH.md)

View File

@ -61,6 +61,7 @@
- [Flutter 3.3 正式发布,快来看看有什么新功能吧](Flutter-330.md)
- [Flutter 3.7 正式发布,快来看看有什么新功能吧](Flutter-370.md)
- [ Flutter 3.10 发布,快来看看有什么更新吧](Flutter-310.md)
- [Flutter 3.16 发布,快来看有什么更新吧](Flutter-316.md)
- **Dart**
- [Dart 2.12 发布稳定空安全声明和FFI版本Dart 未来的计划](Dart-212.md)
- [Dart 2.14 发布,新增语言特性和共享标准 lint](Dart-214.md)
@ -70,6 +71,7 @@
- [Dart 2.18 发布Objective-C 和 Swift interop](Dart-218.md)
- [Flutter - Dart 3α 新特性 Record 和 Patterns 的提前预览讲解](Dart-300a.md)
- [Dart 3 发布,快来看看有什么更新吧](Dart-300.md)
- [Dart 3.2 更新Flutter Web 的未来越来越明朗](Dart-320.md)
* [番外](FWREADME.md)
@ -247,6 +249,10 @@
* [Flutter 与 Dart 的市场应用](Flutter-WH.md)
* [Flutter 小技巧之不一样的思路实现炫酷 3D 翻页折叠动画](Flutter-GLSL.md)
* [Flutter 小技巧之 3.16 升级最坑 M3 默认适配技巧](Flutter-M3D.md)
* [Flutter 工程化选择](GCH.md)
* [Flutter 工程化框架选择——搞定 Flutter 动画](Z1.md)
* [Flutter 工程化框架选择 — 搞定 UI 生产力](Z3.md)

View File

@ -14,6 +14,7 @@
- [Flutter 3.3 正式发布,快来看看有什么新功能吧](Flutter-330.md)
- [Flutter 3.7 正式发布,快来看看有什么新功能吧](Flutter-370.md)
- [ Flutter 3.10 发布,快来看看有什么更新吧](Flutter-310.md)
- [Flutter 3.16 发布,快来看有什么更新吧](Flutter-316.md)
@ -29,4 +30,5 @@
- [Dart 2.18 发布Objective-C 和 Swift interop](Dart-218.md)
- [Flutter - Dart 3α 新特性 Record 和 Patterns 的提前预览讲解](Dart-300a.md)
- [Dart 3 发布,快来看看有什么更新吧](Dart-300.md)
- [Dart 3.2 更新Flutter Web 的未来越来越明朗](Dart-320.md)