原文链接:Do Not User BuildContext in Async Gaps: Why and How to Handle Flutter Context Corretly - 原文作者 Ashish
本文采用意译的方式
在本文中,我们将探讨一个在 Flutter
开发中经常被忽略的问题:在异步间隙中使用 BuildContext
。我们将调查 Flutter
中这一关键部分的原因和方法,强调合适 context
处理的必要性,以避免潜在的错误和内存泄露。理解基础和最佳实践将帮助我们掌握 Flutter
异步编程的难点,确保我们的应用顺畅且快速运行。
这意味着什么?
别在异步间隙中使用 BuildContext
是一个重要的提示,提醒 Flutter
开发人员在执行异步操作时不要使用 BuildContext
。在 Flutter
中,BuildContext
是一个重要的参数,用来获取在挂件树中一个挂件位置信息,然后执行一个任务,比如导航到其他屏幕,展示对话框,获取主题数据等等。
然而,当开发者跨越异步边界传递 BuildContext
时,比如在 Future Methods
,StreamBuilder
或者脱离,它可能会导致问题。此告警反对这么做,因为这可能导致我们应用程序出现意外和错误的行为。
当在异步间隙中使用 BuildContext
,它可能指向一个不存在的挂件,然后导致下面的问题:
- 过时数据:如果在异步操作正在进行时重建或者处置小部件,
BuildContext
引用可能会指向过时或者不存在的小挂件。这可能导致在应用中展示错误或者展示过时的数据。 - 内存溢出:持有应用被释放的
BuildContext
的引用可能会导致内存泄漏,因为框架不能对其进行垃圾回收。 - 应用崩溃:在某些情况下,如果在操作完成前释放了引用的挂件,在异步间隙中使用
BuildContext
可能导致应用崩溃。
本质上,这告警就是要开发者认真考虑在异步操作中如何处理 BuildContext
,强调明白挂件生命周期管理的重要性,避免可能影响我们 Flutter
引用程序可靠性和性能的常见陷阱。
我们应该怎么做?
方法1:使用 GlobalKey
和 Keyed Subtrees
为了解决不在异步间隙中使用 BuildContext
的问题,我们可以使用 GlobalKey
和键控子树 keyed subtrees
。这个方法保证在异步操作中关联正确的 BuildContext
,即使该挂件被处置并重建。
我们可以参考下面的例子,实现这个解决方案:
步骤1:创建一个 GlobalKey
在我们的 Widget State
中创建一个 GlobalKey
开始,然后附加在我们异步操作的父挂件上。这样就确保从这个 GlobalKey
获取的 BuildContext
是有效的。
final GlobalKey<_MyWidgetState> myWidgetKey = GlobalKey();
步骤2:取回 BuildContext
在我们的异步操作中,我们可以通过 GlobalKey
取回 BuildContext
。
Future<void> fetchData() async {
/// 在挂件树中获取当前挂件的上下文 context
final context = myWidgetKey.currentContext;
var result = await navigator.of(context).pushNamed("/user_selection_page");
if(context != null && context.mounted) {
/// 在异步间隙后的声明不会告警
Scaffold.of(context).showSnackBar(SnackBar(
content: Text(result.name),
));
}
}
通过这个方法,我们确保 BuildContext
在异步操作的过程中依旧是有效的,防止了与陈旧和无效上下文相关的问题。
好处:
- 可靠的上下文
context
:使用GlobalKey
保证关联的BuildContext
总是最新和准确的。 - 可预测的行为:挂件子树依旧正确的和它各自的
BuildContext
关联,即使在异步操作的过程中。 - 不易出错:这个方法减少了由于过时的
BuildContext
引用而导致错误和崩溃的可能。
然而,我们可以通过另一种方法处理同样的事情...
方法2:在 Future
中使用 then
方法
then
方法是处理需要使用有效 BuildContext
的异步操作的直接方法。它要确保我们的代码仅在异步操作成功后执行,因此其能获取正确的 BuildContext
。下面例子展示我们如何应用该解决方法:
Future<void> fetchData() async {
await navigator.of(context).pushNamed('/user_selection_page')
.then((result) {
/// 在异步间隙后的声明不会告警
Scaffold.of(context).showSnackBar(SnackBar(
content: Text(result.name),
));
});
}
好处:
- 一致上下文:使用
then
方法确保代码在与异步操作相同的执行上下文中执行,从而提供对BuildContext
的正确访问。 - 清晰的工作流:代码保持有序且直观,逻辑遵循顺序的模式,使其更容易理解和维护。
题外话,如果你想在异步间隙中使用 BuildContext
,但是这些告警让你很烦,那么,我们可以在文件 analysis_options.yaml
文件中添加 use_build_context_synchronously
规则:
linter:
rules:
- use_build_context_synchronously
总结
在 Flutter
开发中,拥有一个清晰且健壮的方法来处理异步操作很重要,以免因使用不正确的 BuildContext
而导致的问题。告警 "Do not user BuildContext in async gaps"
有助于提醒人们注意危险。
请记住,吸取最佳实践,比如上面提到的,能够引导我们编写更加健壮和有序的代码,最终带来更好的用户体验。因此,吸取经验,然后构建更高效和用户友好的 Flutter
应用程序。