别在异步间隙中使用 BuildContext:为什么且如何正确处理 Flutter Context

2024-06-26 08:52:21 浏览数 (2)

原文链接:Do Not User BuildContext in Async Gaps: Why and How to Handle Flutter Context Corretly - 原文作者 Ashish

本文采用意译的方式

在本文中,我们将探讨一个在 Flutter 开发中经常被忽略的问题:在异步间隙中使用 BuildContext。我们将调查 Flutter 中这一关键部分的原因和方法,强调合适 context 处理的必要性,以避免潜在的错误和内存泄露。理解基础和最佳实践将帮助我们掌握 Flutter 异步编程的难点,确保我们的应用顺畅且快速运行。

banner.webpbanner.webp

这意味着什么?

别在异步间隙中使用 BuildContext 是一个重要的提示,提醒 Flutter 开发人员在执行异步操作时不要使用 BuildContext。在 Flutter 中,BuildContext 是一个重要的参数,用来获取在挂件树中一个挂件位置信息,然后执行一个任务,比如导航到其他屏幕,展示对话框,获取主题数据等等。

然而,当开发者跨越异步边界传递 BuildContext 时,比如在 Future MethodsStreamBuilder 或者脱离,它可能会导致问题。此告警反对这么做,因为这可能导致我们应用程序出现意外和错误的行为。

当在异步间隙中使用 BuildContext,它可能指向一个不存在的挂件,然后导致下面的问题:

  1. 过时数据:如果在异步操作正在进行时重建或者处置小部件,BuildContext 引用可能会指向过时或者不存在的小挂件。这可能导致在应用中展示错误或者展示过时的数据。
  2. 内存溢出:持有应用被释放的 BuildContext 的引用可能会导致内存泄漏,因为框架不能对其进行垃圾回收。
  3. 应用崩溃:在某些情况下,如果在操作完成前释放了引用的挂件,在异步间隙中使用 BuildContext 可能导致应用崩溃。

本质上,这告警就是要开发者认真考虑在异步操作中如何处理 BuildContext,强调明白挂件生命周期管理的重要性,避免可能影响我们 Flutter 引用程序可靠性和性能的常见陷阱。

我们应该怎么做?

方法1:使用 GlobalKeyKeyed Subtrees

为了解决不在异步间隙中使用 BuildContext 的问题,我们可以使用 GlobalKey 和键控子树 keyed subtrees。这个方法保证在异步操作中关联正确的 BuildContext,即使该挂件被处置并重建。

我们可以参考下面的例子,实现这个解决方案:

步骤1:创建一个 GlobalKey

在我们的 Widget State 中创建一个 GlobalKey 开始,然后附加在我们异步操作的父挂件上。这样就确保从这个 GlobalKey 获取的 BuildContext 是有效的。

代码语言:javascript复制
final GlobalKey<_MyWidgetState> myWidgetKey = GlobalKey();

步骤2:取回 BuildContext

在我们的异步操作中,我们可以通过 GlobalKey 取回 BuildContext

代码语言:javascript复制
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。下面例子展示我们如何应用该解决方法:

代码语言:javascript复制
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 规则:

代码语言:javascript复制
linter:
  rules:
    - use_build_context_synchronously

总结

Flutter 开发中,拥有一个清晰且健壮的方法来处理异步操作很重要,以免因使用不正确的 BuildContext 而导致的问题。告警 "Do not user BuildContext in async gaps" 有助于提醒人们注意危险。

请记住,吸取最佳实践,比如上面提到的,能够引导我们编写更加健壮和有序的代码,最终带来更好的用户体验。因此,吸取经验,然后构建更高效和用户友好的 Flutter 应用程序。

0 人点赞