Flutter 3.10 发布之后,我们或许注意到,在它的 release note 里提了一句: Window singleton 相关将被弃用,并且这个改动是为了支撑未来多窗口的相关完成

所以这是一个为了支撑多窗口的相关改善,多窗口更多是在 PC 场景下更常见,但是又需要兼容 Mobile 场景,故而有此次改动作为提早铺垫。

如下图所示,假如详细到对应的 API 场景,首要便是涉及 WidgetsBinding.instance.windowMediaQueryData.fromWindow 等接口的适配,由于 WidgetsBinding.instance.window 即将被弃用。

Flutter 小技巧之 3.10 适配之单例 Window 弃用,一起来了解 View.of 和 PlatformDispatcher

你能够不适配,还能跑,只是晋级的技能债务往后累计罢了。

那首要或许就有一个疑问,为什么会有需要直接运用 WidgetsBinding.instance.window 的运用场景?简略来说,详细能够总结为:

  • 没有 BuildContext ,不想引进 BuildContext
  • 不期望获取到的 MediaQueryData 遭到地点 BuildContext 的影响,例如键盘弹起导致 padding 改动重构和遭到 Scaffold 下的参数影响等

这部分详细可见:《MediaQuery 和 build 优化你不知道的秘密》 。

那么从 3.10 开始,针对 WidgetsBinding.instance.window 能够经过新的 API 方式进行兼容:

  • 假如存在 BuildContex , 能够经过 View.of 获取 FlutterView,这是官方最引荐的替代方式
  • 假如没有 BuildContex 能够经过 PlatformDispatcherviews 目标去获取

这里注意到没有,现在用的是 View.of ,获取的是 FlutterView ,目标都称呼为 View 而不是 「Window」,对应的 MediaQueryData.fromWindow API 也被弃用,修正为 MediaQueryData.fromView ,这个修正的依据在于:

起先 Flutter 假定了它只支撑一个 Window 的场景,所以会有 SingletonFlutterWindow 这样的 instance window 目标存在,一起 window 特点又供给了许多和窗口自身无关的功用,在多窗口逻辑下会显得很另类。

那么接下来就让咱们用「长篇大论」来简略介绍下这两个场景的特别之处。

存在 BuildContext

回归到本次的调整,首要是存在 BuildContext 的场景,如下代码所示,关于存在 BuildContex 的场景, View.of 相关的调整为:

/// 3.10 之前
doubledpr=WidgetsBinding.instance.window.devicePixelRatio;
Localelocale=WidgetsBinding.instance.window.locale;
doublewidth=
 MediaQueryData.fromWindow(WidgetsBinding.instance.window).size.width;
​
​
/// 3.10 之后
doubledpr=View.of(context).devicePixelRatio;
Localelocale=View.of(context).platformDispatcher.locale;
doublewidth=
 MediaQueryData.fromView(View.of(context)).size.width;

能够看到,这里的 View 内部完成肯定是有一个 InheritedWidget ,它将 FlutterView 经过 BuildContext 往下共享,从而供给相似 「window」 的参数才能,而经过 View.of 获取的参数:

  • FlutterView 自身的特点值发生改动时,是不会告诉绑定的 context 更新,这个行为相似于之前的 WidgetsBinding.instance.window
  • 只有当 FlutterView 自身发生改动时,比如 context 绘制到不同的 FlutterView 时,才会触发对应绑定的 context 更新

能够看到 View.of 这个行为考虑的是「多 FlutterView」 下的更新场景,假如是需要绑定到详细对应参数的变动更新,如 size 等,仍是要经过以前的 MediaQuery.of / MediaQuery.maybeOf 来完成。

而关于 View 来说,每个 FlutterView 都必须是独立且唯一的,在一个 Widget Tree 里,一个 FlutterView 只能和一个 View 相关联,这个首要体现在 FlutterView 标识 GlobalObjectKey 的完成上。

Flutter 小技巧之 3.10 适配之单例 Window 弃用,一起来了解 View.of 和 PlatformDispatcher

简略总结一下:在存在 BuildContex 的场景,能够简略将 WidgetsBinding.instance.window 替换为 View.of(context) ,不必担心绑定了 context 导致重构,由于 View.of 只对 FlutterView 切换的场景生效

不存在 BuildContext

关于不存在或许不方便运用 BuildContext 的场景,官方供给了 PlatformDispatcher.views API 来进行支撑,不过由于 get views 对应的是 Mapvalues ,它是一个 Iterable 目标,那么关于 3.10 咱们需要如何运用 PlatformDispatcher.views 来适配没有 BuildContextWidgetsBinding.instance.window 场面

Flutter 小技巧之 3.10 适配之单例 Window 弃用,一起来了解 View.of 和 PlatformDispatcher

PlatformDispatcher 内部的views 保护了中所有可用 FlutterView 的列表,用于供给在没有 BuildContext 的情况下拜访视图的支撑。

你说什么情况下会有没有 BuildContext ?比如 Flutter 里 的 runApp ,如下图所示,3.10 目前在 runApp 时会经过 platformDispatcher.implicitView 来塞进去一个默许的 FlutterView

Flutter 小技巧之 3.10 适配之单例 Window 弃用,一起来了解 View.of 和 PlatformDispatcher

implicitView 又是什么?其实 implicitView 便是 PlatformDispatcher._views 里 id 为 0 的 FlutterView ,默许也是 views 这个 Iterable 里的 first 目标。

Flutter 小技巧之 3.10 适配之单例 Window 弃用,一起来了解 View.of 和 PlatformDispatcher

也便是在没有 BuildContext 的场景, 能够经过 platformDispatcher.views.first 的完成迁移对应的 instance.window 完成。

/// 3.10 之前
MediaQueryData.fromWindow(WidgetsBinding.instance.window)
/// 3.10 之后
MediaQueryData.fromView(WidgetsBinding.instance.platformDispatcher.views.first)

为什么不直接运用 implicitView 目标? 由于 implicitView 目前是一个过渡性计划,官方期望在多视图的场景下不应该一直存在 implicit view 的概念,而是运用自己应该自动恳求创立一个窗口,去供给一个视图进行绘制。

Flutter 小技巧之 3.10 适配之单例 Window 弃用,一起来了解 View.of 和 PlatformDispatcher

所以关于 implicitView 目前官方供给了 _implicitViewEnabled 函数,后续能够经过可装备位来操控引擎是否支撑 implicitView ,也便是 implicitView 在后续更新随时或许为 null ,这也是咱们不应该在外部去运用它的理由,一起它是在 runApp 时装备的,所以它在运用启动运转后永久不会改动,假如它在启动时为空,则它永久都会是 null。

PlatformDispatcher.instance.views[0] 在之前的单视图场景中,不管是否有窗口存在,相似的 implicitView 会一直存在;而在多窗口场景下,PlatformDispatcher.instance.views 将会跟随窗口改动。

别的咱们是经过 WidgetsBinding.instance.platformDispatcher.views 去拜访 views ,而不是直接经过 PlatformDispatcher.instance.views ,由于通常官方更主张在 Binding 的依赖关系下去拜访 PlatformDispatcher

除了需要在 runApp() 或许 ensureInitialized() 之前拜访 PlatformDispatcher 的场景。

别的,如下图所示,经过 Engine 里关于 window 部分代码的完成,能够看到咱们所需的默许FlutterView 是 id 为 0 的相关依据,所以这也是咱们经过 WidgetsBinding.instance.platformDispatcher.views 去兼容支撑的逻辑地点。

Flutter 小技巧之 3.10 适配之单例 Window 弃用,一起来了解 View.of 和 PlatformDispatcher
Flutter 小技巧之 3.10 适配之单例 Window 弃用,一起来了解 View.of 和 PlatformDispatcher
Flutter 小技巧之 3.10 适配之单例 Window 弃用,一起来了解 View.of 和 PlatformDispatcher
Flutter 小技巧之 3.10 适配之单例 Window 弃用,一起来了解 View.of 和 PlatformDispatcher

最后

最后总结一下,说了那么多,其实不外乎便是将 WidgetsBinding.instance.window 替换为 View.of(context) ,假如还有一些骚操作场景,能够运用 WidgetsBinding.instance.platformDispatcher.views ,假如不怕后续又坑,乃至能够直接运用 WidgetsBinding.instance.platformDispatcher.implicitView

整体上解释那么多,首要仍是给我们对这次变动有一个布景认知,一起也对未来多窗口完成进展有进一步的了解,信任下一个版本多窗口应该就能够和我们碰头了。

更多评论可见:

  • github.com/flutter/flu…
  • github.com/flutter/eng…
  • github.com/flutter/flu…
  • github.com/flutter/flu…
  • github.com/flutter/eng…