Collectives™ on Stack Overflow
Find centralized, trusted content and collaborate around the technologies you use most.
Learn more about Collectives
Teams
Q&A for work
Connect and share knowledge within a single location that is structured and easy to search.
Learn more about Teams
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
UPDATE: 17/September/2022
It appears that BuildContext will soon have a "mounted" property
So you can do:
if (context.mounted)
It basically allows StatelessWidgets to check "mounted" too.
Reference: Remi Rousselet Tweet
–
–
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),
–
–
Use context.mounted
*
In StatefulWidget
/StatelessWidget
or in any class that has BuildContext
:
void foo(BuildContext context) async {
await someFuture();
if (!context.mounted) return;
Navigator.pop(context); // No warnings now
* If you're in a StatefulWidget
, you can also use just mounted
instead of context.mounted
–
–
–
would work!
I had this issue again and again and here's the trick - use or declare variables using context
before using async methods like so:
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();
EDIT END
As per Guildem's answer, he still uses
if (!mounted) return;
so what's the point of adding more spaghetti code with callbacks? What if this async
method will have to pass some data to the methods you're also passing context
? Then my friend, you will have even more spaghetti on the table and another extra issue.
The core concept is to not use context
after async bloc is triggered ;)
–
–
–
–
–
If you want to use mounted check in a stateless widget its possible by making an extension on BuildContext
extension ContextExtensions on BuildContext {
bool get mounted {
try {
widget;
return true;
} catch (e) {
return false;
and then you can use it like this
if (context.mounted)
Inspiration taken from GitHub PR for this feature and it passes the same tests in the merged PR
–
In Flutter 3.7.0 BuildContext
has the property mounted
. It can be used both in StatelessWidget and StatefulWidgets like this:
void bar(BuildContext context) async {
await yourFuture();
if (!context.mounted) return;
Navigator.pop(context);
–
–
This help for me.
/// transition to the main page after a pause of 3 seconds
Future<void> _navigateToMainScreen(BuildContext context) async {
await Future.delayed(const Duration(seconds: 1));
if (context.mounted) {
Navigator.of(context)
.push(MaterialPageRoute(builder: (context) => const MainScreen()));
To avoid this in StatelessWidget you can refer to this example
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),
Just simplify create a function to call the navigation
void onButtonTapped(BuildContext context) {
Navigator.of(context).pop();
–
I handle it with converting the function become not async
and using then
Future<void> myAsyncMethod(BuildContext context) {
Navigator.of(context).push(/*waiting dialog */);
Future.delayed(const Duration(seconds: 2)).then(_) {
Navigator.of(context).pop();
–
just save your navigator or whatever needs a context to a variable at the beginning of the function
myAsyncMethod() async {
final navigator = Navigator.of(context); // 1
await someFuture();
navigator.pop(); // 2
–
–
–
–
DO NOT use BuildContext across asynchronous gaps.
Storing BuildContext for later usage can easily lead to difficult to diagnose crashes. Asynchronous gaps are implicitly storing BuildContext and are some of the easiest to overlook when writing code.
When a BuildContext is used from a StatefulWidget, the mounted property must be checked after an asynchronous gap.
So, I think, you can use like this:
GOOD:
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();
–
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.