Kinda Code
Home/Flutter/Flutter: Don’t use BuildContexts across async gaps

Flutter: Don’t use BuildContexts across async gaps

Last updated: May 03, 2023

The Problem

When working with one of the recent versions of Flutter, you might be annoyed by the following warning:

Do not use BuildContexts across async gaps

This warning means that you should not use BuildContext after an async operation because the widget that provided the context may no longer be in the widget tree. This can lead to potential errors or crashes if you try to access the context or use it for navigation. This article will show you a couple of different ways to get rid of this warning as well as make your code safer.

Solutions

Check the mounted property

The quickest approach is to check the mounted property in StatefulWidget or StatelessWidget before using the context like so:

void doSomething(BuildContext context) async {
  await someFuture();

  // check "mounted" property
  if (!context.mounted) return;
  // then do what you want with "context"
  ScaffoldMessenger.of(context).showSnackBar(/*...*/);
  Navigator.pop(context);
}

Write code like this is fine, too:

void doSomething(BuildContext context) async {
  await someFuture();

  // check "mounted" property
  if (context.mounted){
     ScaffoldMessenger.of(context).showSnackBar(/*...*/);
     Navigator.pop(context);
  }
}

If you’re dealing with a StatefulWidget, you can simply do as follows:

void doSomething(BuildContext context) async {
  await someFuture();

  // simply check "mounted" instead of "context.mounted"
  if (!mounted) return;
  
  // then do what you want with "context"
  ScaffoldMessenger.of(context).showSnackBar(/*...*/);
  Navigator.pop(context);
}

This tutorial applies this technique in its second example.

Using a callback function

You can also use a callback function instead of directly using the context to extinguish the warning. This allows you to handle the async result in your widget and use the context safely.

A minimal example:

class MyCustomClass {
  const MyCustomClass();

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

// In your widget
class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return IconButton(
      onPressed: () => const MyCustomClass().myAsyncMethod(context, () {
        Navigator.of(context).pop();
      }),
      icon: const Icon(Icons.bug_report),
    );
  }
}

Using a state management solution

By using a state management solution like Provider, GetX, or Bloc to separate your UI logic from your business logic, you don’t need to pass the context to your custom class or method, and you can trigger navigation based on state changes.

An example of using Bloc:

// Using Bloc as an example
class MyBloc extends Bloc<MyEvent, MyState> {
  final Api api;

  MyBloc(this.api) : super(MyInitial());

  @override
  Stream<MyState> mapEventToState(MyEvent event) async* {
    if (event is MyFetchEvent) {
      yield MyLoading();
      try {
        final authors = await api.getAuthors();
        yield MySuccess(authors);
      } catch (e) {
        yield MyFailure(e.toString());
      }
    }
  }
}

// In your widget
class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (context) => MyBloc(Api()),
      child: BlocListener<MyBloc, MyState>(
        listener: (context, state) {
          if (state is MyFailure) {
            Navigator.of(context).pushNamed(RoutePaths.login);
          }
        },
        child: BlocBuilder<MyBloc, MyState>(
          builder: (context, state) {
            // Render your UI based on state
          },
        ),
      ),
    );
  }
}

See also:

Conclusion

We’ve walked through 3 different methods to make Flutter happy and stop yelling “Do not use BuildContexts across async gaps”. In my opinion, the first approach is way more convenient than the other ones. However, the decision is totally up to you.

If you’d like to explore more new and interesting stuff about modern Flutter, take a look at the following articles:

You can also tour around our Flutter topic page or Dart topic page for the most recent tutorials and examples.