Search code examples
flutterdartflutter-dependenciesflutter-stateflutter-build

Do not use BuildContexts across async gaps


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
      }
   }

Solution

  • Update Flutter 3.7+ :

    mounted property is now officially added to BuildContext, so you can check it from everywhere, whether it comes from a StatefulWidget State, or from a Stateless widget.

    While storing context into external classes stays a bad practice, you can now check it safely after an async call like this :

    class MyCustomClass {
      const MyCustomClass();
    
      Future<void> myAsyncMethod(BuildContext context) async {
        Navigator.of(context).push(/*waiting dialog */);
        await Future.delayed(const Duration(seconds: 2));
        if (context.mounted) Navigator.of(context).pop();
      }
    }
    
    // Into widget
      @override
      Widget build(BuildContext context) {
        return IconButton(
          onPressed: () => const MyCustomClass().myAsyncMethod(context),
          icon: const Icon(Icons.bug_report),
        );
      }
    // Into widget
    

    Original answer

    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),
        );
      }
    }