flutter 不要跨异步间隙使用BuildContexts

vu8f3i0k  于 2022-11-17  发布在  Flutter
关注(0)|答案(8)|浏览(606)

我注意到我的项目中有一个新的lint问题。
长话短说:

我需要在自定义类中使用BuildContext

flutter lint工具在与aysnc方法一起使用时不太满意。
示例:

MyCustomClass{

      final buildContext context;
      const MyCustomClass({required this.context});

      myAsyncMethod() async {
        await someFuture();
        # if (!mounted) return;          << has no effect even if i pass state to constructor
        Navigator.of(context).pop(); #   << example
      }
   }

更新日期:2022年9月17日
似乎BuildContext很快就会有一个“mounted”属性
因此您可以执行:

if (context.mounted)

它基本上也允许StatelessWidgets检查“已安装”。
参考:Remi Rousselet Tweet

vq8itlhq

vq8itlhq1#

不要将上下文直接存储到自定义类中,如果不确定小部件是否已装载,也不要在异步后使用上下文。
请执行以下操作:

class MyCustomClass {
  const MyCustomClass();

  Future<void> myAsyncMethod(BuildContext context, VoidCallback onSuccess) async {
    await Future.delayed(const Duration(seconds: 2));
    onSuccess.call();
  }
}

class MyWidget extends StatefulWidget {
  @override
  _MyWidgetState createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  @override
  Widget build(BuildContext context) {
    return IconButton(
      onPressed: () => const MyCustomClass().myAsyncMethod(context, () {
        if (!mounted) return;
        Navigator.of(context).pop();
      }),
      icon: const Icon(Icons.bug_report),
    );
  }
}
wfypjpf4

wfypjpf42#

StatefulWidget中,用途:
void bar(BuildContext context) async {
  await yourFuture();
  if (!mounted) return;
  Navigator.pop(context);
}
StatelessWidget或任何其他类中,尝试以下方法:
class Foo {
  void bar(BuildContext context, [bool mounted = true]) async {
    await yourFuture();
    if (!mounted) return;
    Navigator.pop(context);
  }
}
58wvjzkj

58wvjzkj3#

如果类可以从StatefulWidget扩展,则添加

if (!mounted) return;

会起作用!

编辑

我一次又一次地遇到这个问题,这里有一个窍门--在使用异步方法之前,使用context来使用或声明变量,如下所示:

MyCustomClass{
  const MyCustomClass({ required this.context });

  final buildContext context;
  
  myAsyncMethod() async {
    // Declare navigator instance (or other context using methods/classes)
    // before async method is called to use it later in code
    final navigator = Navigator.of(context);
    await someFuture();
    
    // Now use the navigator without the warning
    navigator.pop();
  }
}

编辑结束

根据Guildem的回答,他仍然使用

if (!mounted) return;

那么,通过回调添加更多的意大利面代码有什么意义呢?如果这个async方法必须将一些数据传递给您也在传递context的方法,那该怎么办?那么,我的朋友,您将有更多的意大利面在桌面上,并出现另一个额外的问题。
核心概念是在触发异步阻塞后不使用context;)

93ze6v8z

93ze6v8z4#

您可以使用此方法

myAsyncMethod() async {
    await someFuture().then((_){
    if (!mounted) return;          
    Navigator.of(context).pop(); 
  }
});
jfewjypa

jfewjypa5#

只需创建一个函数来调用导航

void onButtonTapped(BuildContext context) {

pop();}

g6ll5ycj

g6ll5ycj6#

要在StatelessWidget中避免这种情况,您可以参考以下示例

class ButtonWidget extends StatelessWidget {
  final String title;
  final Future<String>? onPressed;

  final bool mounted;

  const ButtonWidget({
    super.key,
    required this.title,
    required this.mounted,
    this.onPressed,
  });

  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        const SizedBox(height: 20),
        Expanded(
            child: ElevatedButton(
          onPressed: () async {
            final errorMessage = await onPressed;
            if (errorMessage != null) {
              // This to fix: 'Do not use BuildContexts across async gaps'
              if (!mounted) return;
              snackBar(context, errorMessage);
            }
          },
          child: Text(title),
        ))
      ],
    );
  }
}
1zmg4dgp

1zmg4dgp7#

只需将导航器或任何需要上下文的东西保存到函数开头的变量中

myAsyncMethod() async {
        final navigator = Navigator.of(context); // 1
        await someFuture();
        navigator.pop();  // 2
      }
efzxgjgh

efzxgjgh8#

请勿跨异步间隙使用BuildContext。
存储BuildContext以供以后使用很容易导致难以诊断的崩溃。异步间隙隐式存储BuildContext,是编写代码时最容易忽略的一些间隙。
从StatefulWidget使用BuildContext时,必须在异步间隔之后检查已挂载的属性。
所以,我想,你可以这样用:
良好:

class _MyWidgetState extends State<MyWidget> {
  ...

  void onButtonTapped() async {
    await Future.delayed(const Duration(seconds: 1));

    if (!mounted) return;
    Navigator.of(context).pop();
  }
}

坏消息:

void onButtonTapped(BuildContext context) async {
  await Future.delayed(const Duration(seconds: 1));
  Navigator.of(context).pop();
}

相关问题