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

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. – Mozes Ong Aug 21, 2021 at 11:25 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. – Mozes Ong Aug 21, 2021 at 12:59

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),
                @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
                Mar 5, 2022 at 9:12
                I get error the getter 'mounted' isn't defined for the type 'BuildContext' . I am using stateless widget
– Aseem
                Feb 23 at 22:55

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

@BeniaminoBaggins StatelessWidget is always mounted, so you never have to pass mounted: false in them, but to get away with the warning, it's better to have a field named mounted set to true. – iDecode Sep 18, 2022 at 9:02 @BeniaminoBaggins This entire solution/concept is nowhere in the docs (as far as I know). Docs may suggest you to convert your StatelessWidget to StatefulWidget and then use mounted flag to check if the widget is in the tree. – iDecode Sep 19, 2022 at 5:40 the getter 'mounted' isn't defined for the type 'BuildContext' I get this error on my stateless widget – Aseem Feb 23 at 22:59

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

This solution does not work. It has exactly the same problem as if you had called Navigator.of(context).pop() directly from the async method. If this hides the related analyzer warning, it is only a bug in the analyzer. – kine Jun 28, 2022 at 13:47 what about if I don't have a stateful widget and I have that navigator code inside a normal class? – Dani Jul 24, 2022 at 14:34 Hey guys, please see my answer after edit. I have found the solution for this problem after many tries myself as this issue was sometimes visiting me back and I said enough :) – mcgtrt Jul 27, 2022 at 17:41 As it is, your answer is wrong. Context can't be used without checking mounted before use on async method. On the custom class, without StatefulWidget implementation, you must let the caller of myAsyncMethod check a context it may not have itself here (because you gave context at the wrong step and it may have changed since constructor). There's no spaghetti code in the accepted answer. Only adaptation for the "Flutter isn't happy when I use context in my custom async method of my custom class". And there's no reason a service class do UI stuff itself. Not its role. – Guildem Jul 28, 2022 at 10:26 As a confirmation of my solution, you can even see the comments under your own answer, which states similarly, here you have also richer explanation why this solution is correct and is not used in any case only to silence the warning: stackoverflow.com/a/69512692/11675817. Your answer is not being denied, your answer is correct. Sometimes there are more complex functions which requires handling additional parameters on return and that makes it problematic - more code, and then resulting in spaghetti code. Using context before async gap is solution, also with connection with things like BLOC. – mcgtrt Jul 28, 2022 at 19:26

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

Yes, i started using the solution with 3.3 i think, unsure if there are any issues with using it prior to that – Markusrut Apr 26 at 8:26

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);
                what if class is neither stateful not statless? e.g. any class with static methods and with no ui
– Muhammad Umair Saqib
                Jan 31 at 13:24
                @MuhammadUmairSaqib It is not about the widget. If you have a context, you can access the property mounted no matter if you are in a static class or not
– Yayo Arellano
                Feb 2 at 8:49

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();
                Does not answer the question, which is about context in async calls. (also, formatting issues)
– Guildem
                Sep 16, 2022 at 8:35

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();
                You can't do that without checking the context state (mounted or not). If context is unmounted when delayed future ends, your app will be instable.
– Guildem
                May 18 at 12:23

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
                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!
– Bermjly Team
                Oct 15, 2021 at 20:41
                @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 ?
– Sxndrome
                Feb 10, 2022 at 23:41
                @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.
– Guildem
                Mar 5, 2022 at 10:17
                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));
– Jim Gomes
                May 13, 2022 at 18:48

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();
                You can't just copy and paste directly from documentation and not at least cite your source: dart-lang.github.io/linter/lints/…
– jaredbaszler
                Mar 28, 2022 at 18:35
        

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.