欢迎关注微信公众号:FSA全栈行动 👋
BiliBili: www.bilibili.com/video/BV1Qm…
一、背景
上周一尝试从 3.16.9
升级 3.19.3
,主要有两个原因:
一是安卓端有一个疑似造成崩溃率上涨的 bug
在 Flutter 3.16
上出现,相关issue: #138947, 该 bug
在 3.13
不会出现,在 3.17.pre
上得到修复,而 3.16
之后的下个正式版本是 3.19
。
二是苹果的隐私清单审核政策。
在苹果发布的【关于 App Store 提交的隐私更新】新闻中指出
自 3 月 13 日起: 如果你上传新 App 或更新 App 到 App Store Connect,且该 App 使用了需要声明批准原因的 API,但你未在 App 的隐私清单中提供批准原因,我们会通过电子邮件告知你。这是对 App Store Connect 中现有通知的补充。
自 5 月 1 日起: 你需要就你的 App 代码使用的所列 API 提供批准原因,才能将新 App 或更新 App 上传到 App Store Connect。如果你没有合理的原因使用某个 API,请寻找替代的方案。如果你添加了常用第三方 SDK 列表中所列的新版第三方 SDK,那么这些 API、隐私清单和签名要求将应用于该 SDK。请务必使用包含其隐私清单的 SDK 版本,并注意在将该 SDK 添加为二进制依赖项时也需要提供签名。
在苹果的【即将发布的第三方SDK要求】一文中,列举出需要隐私清单和签名的 SDK
,其中就包含了 Flutter
。为了符合该审核要求,Flutter
从 3.19
开始包含了 PrivacyInfo.xcprivacy
这个隐私清单文件。
文件位于: github.com/flutter/eng…
二、踩坑
升到到 3.19.3
后发现,从 页面A
跳转到 页面B
和返回 页面A
时,页面A
的 build
方法都会被执行,降回 3.16.9
则不会,这就很奇怪。后来发现是因为 页面A
间接使用了 ModalRoute.of
。
以下是可复现问题的代码
1 | diff复制代码class PageA extends StatefulWidget { |
三、探索
在经过一番摸索后,发现 ModalRoute
在 3.19
上面有一个小修改~
相关 issue
是: #112567 。
该 issue
主要是涉及在 Web
端上按 Tab
键切换焦点的问题,后续有个 PR
: #130841 解决了该问题。
该
PR
因内部测试原因进行了回滚,后再重新登陆,现PR: #134554
而在该 PR
中就对 ModalRoute
加了如下代码:
1 | diff复制代码// packages/flutter/lib/src/widgets/routes.dart |
didChangeNext
和 didPopNext
这两个方法对应的就是页面的 push
和 pop
,现在在该 PR
中重写并调用了 changedInternalState
方法,在 changedInternalState
方法中调用了 setState
。
下面将以高亮的方式标出重点代码(不是新增代码)。
1 | diff复制代码abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T> { |
这个 ModalRoute
内的 setState
会使 _ModalScopeStatus
的 _routeSetState
被调用,然后触发 _ModalScopeState
的 setState
,接着其 child: _ModalScopeStatus
就开始 rebuild
了。
1 | diff复制代码class _ModalScopeState<T> extends State<_ModalScope<T>> { |
如下代码所示,_ModalScopeStatus
是一个 InheritedWidget
,在经过一系列的处理后最终会走到其 InheritedElement
的 update
方法,在 update
方法中通过调用 updateShouldNotify
来判断数据是否发生变化,进而决定是否通知相关依赖。
1 | diff复制代码+ class _ModalScopeStatus extends InheritedWidget { |
1 | diff复制代码class InheritedElement extends ProxyElement { |
_ModalScopeStatus
的 isCurrent
表示当前页面是否处于最上层,所以在打开和关闭下一个页面时,其值必定切换,也就是 updateShouldNotify
必定返回 true
,既而通知依赖(实际上就是找出一个个依赖进行标脏,然后等待 build
方法的重新调用)。
而我们在使用 ModalRoute.of
的时候,内部就是将当前页的 BuildContext
添加到依赖中,所以他这个改动就会影响到使用 ModalRoute.of
的 Widget
,使其多次 rebuild
。
1 | dart复制代码@optionalTypeArgs |
四、解决方案
方案一:调整 ModalRoute.of
在当前版本中,of
的用意就是找到相应的 ModalRoute
并且创建依赖关系,当数据改变时会重新 build
,这是符合它期望用意的。
但是有些场景下我们并不希望有这个 “特性”,比如,我打开新页面后,通过 ModalRoute.of(context)?.settings.arguments
取路由参数,当前页面的取参,与跳转和关闭下个页面是没有任何关系的,所以这种场景下触发 rebuild
将毫无意义。
所以我提了个 PR
: #145389, 给 ModalRoute.of
添加了 createDependency
参数,为开发者提供了是否创建依赖的选择。目前还在审核中~
1 | dart复制代码 static ModalRoute<T>? of<T extends Object?>( |
方案二:魔改源码
原 PR
是对 Tab
键切换焦点问题的修复,但对于移动端来说根本不算问题,因为用不上~ 😅 (当然,如果你们的用户有使用无障碍功能的,还需要自行斟酌一下)
如果这个问题到时还未解决(原 PR
的作者还在休假),那我们也可以先注释掉相关代码对 changedInternalState
的调用来应对
1 | diff复制代码// packages/flutter/lib/src/widgets/routes.dart |
提供一个补丁
1 | shell复制代码# 进入你的 flutter 目录,比如我用的是 fvm 下载的 3.19.3 |
五、最后
总而言之,距离5月1日(苹果强制要求添加隐私清单文件的期限)还有一个月,我们现在大可保持在 3.13
版本先用着,免得折腾,同时也祈祷快点修复该问题,然后顺利升级上去~
如果文章对您有所帮助, 请不吝点击关注一下我的微信公众号:FSA全栈行动, 这将是对我最大的激励. 公众号不仅有
iOS
技术,还有Android
,Flutter
,Python
等文章, 可能有你想要了解的技能知识点哦~
本文转载自: 掘金