Do not use BuildContexts across async gaps

2,556

Don't stock context directly into custom classes, and don't use context after async if you're not sure your widget is mounted.

Do something like this:

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),
    );
  }
}
Share:
2,556
Bermjly Team
Author by

Bermjly Team

Updated on December 31, 2022

Comments

  • Bermjly Team
    Bermjly Team over 1 year

    I have noticed a new lint issue in my project.

    Long story short:

    I need to use BuildContext in my custom classes

    flutter lint tool is not happy when this being used with aysnc method.

    Example:

       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
          }
       }
    
    • Mozes Ong
      Mozes Ong over 2 years
      doesn't seem wise to pass context to objects like that for the purpose of navigation. If your navigation stack changes after you have passed the context to MyCustomClass and you try to navigate again using the old context, you will get errors.
    • Bermjly Team
      Bermjly Team over 2 years
      I agree, then how this scenario should be approached?
    • Mozes Ong
      Mozes Ong over 2 years
      Use some state management like BloC, where you can trigger navigation when a state changes. So long as you do not store your context, but instead, use the context for navigation purposes without storing the instance.
  • Bermjly Team
    Bermjly Team over 2 years
    Unfortunately you cannot use mounted attribute in custom class ;(
  • L Uttama
    L Uttama over 2 years
    This error is caused by if (!mounted) return; check for this variable/attribute mounted. It is giving error because you can not use that mounted here inside your custom class.
  • Bermjly Team
    Bermjly Team over 2 years
    I am not using mounted property because clearly you cannot use it in custom class.
  • Bermjly Team
    Bermjly Team over 2 years
    No be careful of doing that! context might not be mounted so absolutely this does not guarantee to fix the issue and might cause the app to crash instead!
  • Sxndrome
    Sxndrome about 2 years
    @BermjlyTeam I do not understand how this might not fix the issue. The BuildContext was used to fetch the Navigator instance. The resulting instance is now fetched. After the await, we must not use the BuildContext. We don't. We only use the Navigator instance. How does this not fix the issue in at least a StatelessWidget ?
  • stk
    stk about 2 years
    How can you do it in a StatelessWidget?
  • Guildem
    Guildem about 2 years
    @stk I don't think you can, a StatelessWidget don't really have a lifecycle (no state). But if you want an async method, then you have a long-ish thing to do, and you want the app user to see something is happening, so you need a state. If you look at your app, you should see where you need to handle this state and create a Stateful widget.
  • Guildem
    Guildem about 2 years
    @Sxndrome imagine that your someFuture() waits 5 seconds (biiiiig task). During this time, you go back with Android back button or another implemented way. What //2 will do when someFuture() is finished ? Context before and after the future won't always be the same.
  • stk
    stk about 2 years
    Good point in general!
  • jaredbaszler
    jaredbaszler about 2 years
    You can't just copy and paste directly from documentation and not at least cite your source: dart-lang.github.io/linter/lints/…
  • Jim Gomes
    Jim Gomes almost 2 years
    This is very close to being the correct answer. It just needs a check before calling navigator.pop(). Change to if (navigator.mounted) navigator.pop(); I would also store the NavigatorState instead of BuildContext: MyCustomClass { final NavigatorState navigator; const MyCustomClass(this.navigator); } Example instantiation: final obj = MyCustomClass(Navigator.of(context));
  • M Karimi
    M Karimi almost 2 years
    @stk In stateless widget, you can use this: stackoverflow.com/a/69512692/11675817