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:
- Using Provider for State Management in Flutter
- Using GetX (Get) for State Management in Flutter
- Flutter: ValueListenableBuilder Example
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:
- Flutter: Global, Unique, Value, Object, and PageStorage Keys
- 2 Ways to Get a Random Item from a List in Dart (and Flutter)
- How to Create a Countdown Timer in Flutter
- 3 Ways to Cancel a Future in Flutter and Dart
- Flutter: Showing SnackBar with ScaffoldMessenger
- Flutter: Create a Button with a Loading Indicator Inside
You can also tour around our Flutter topic page or Dart topic page for the most recent tutorials and examples.