a2x
a2x

Reputation: 1411

Looking up a deactivated widget's ancestor is unsafe

I am new in Flutter and I am trying to receive data with a Dialog. When i click in textField the error of image2 appears...

Layout's Image Error's Image

show(BuildContext context){

    var dialog = Dialog(
      child: Container(
        margin: EdgeInsets.all(8.0),
        child: Form(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: <Widget>[
              TextFormField(
                decoration: InputDecoration(
                    labelText: "Insira o número de telefone",
                    border: OutlineInputBorder(
                        borderRadius: BorderRadius.all(Radius.circular(2.0)))),
              ),
              Row(
                mainAxisAlignment: MainAxisAlignment.end,
                children: <Widget>[
                  FlatButton(
                      onPressed: () {
                        Navigator.of(context).pop();
                      },
                      child: Text("Cancelar")),
                  FlatButton(
                      onPressed: () {
                        Navigator.of(context).pop();
                      },
                      child: Text("Aceitar"))
                ],
              )
            ],
          ),
        ),
      ),
    );

    showDialog(context: context,builder: (context){
      return dialog;
    });
  }

This is my code.

I/flutter (31032): Looking up a deactivated widget's ancestor is unsafe.
I/flutter (31032): At this point the state of the widget's element tree is no longer stable. To safely refer to a
I/flutter (31032): widget's ancestor in its dispose() method, save a reference to the ancestor by calling
I/flutter (31032): inheritFromWidgetOfExactType() in the widget's didChangeDependencies() method.
I/flutter (31032): 

Upvotes: 131

Views: 258710

Answers (24)

Compaq LE2202x
Compaq LE2202x

Reputation: 2386

in my case, i popped it before invoking callback:

    onPressed: {
        Navigator.pop(context);
        widget.onAccept();
    }

the fix:

    onPressed: {
        widget.onAccept();

        // Call logic above before popping
        Navigator.pop(context);
    }

Upvotes: 0

Mr J
Mr J

Reputation: 1

you can work with it:

 final GlobalKey<NavigatorState> navigatorKey =GlobalKey<NavigatorState();
Future<void> main() async {...}

put it all over the method hand. and then add it in the materialApp

return MaterialApp(
        navigatorKey: navigatorKey,

and then use it from anywhere in the app

Upvotes: 0

Nilanshu sharma
Nilanshu sharma

Reputation: 1

I encountered the error 'Looking up a deactivated widget's ancestor is unsafe.' while working with GetX. Initially, I utilized the 'GetMaterialApp' widget in the main function to launch the app:

void main() => runApp(
  DevicePreview(
    enabled: true,
    builder: (context) => GetMaterialApp(home: const MyApp()), // Wrap your app
  ),
);

However, after switching to the conventional MaterialApp, the issue was resolved:

void main() => runApp(
  DevicePreview(
    enabled: true,
    builder: (context) => MaterialApp(home: const MyApp()), // Wrap your app
  ),
);

Upvotes: 0

Esmaeil Ahmadipour
Esmaeil Ahmadipour

Reputation: 1192

i get answer with this snippet :

  @override
  void didChangeDependencies() {
    Navigator.of(context);
    super.didChangeDependencies();
  }

Upvotes: 2

O&#39;Prime
O&#39;Prime

Reputation: 101

First let's understand what the framework is saying:

Point - 1:

Looking up a deactivated widget's ancestor is unsafe. At this point the state of the widget's element tree is no longer stable.

This simply means that the widget(parent of Dialog) is no longer present in the widget tree and it can be confirmed by checking the result of context.mounted property. This can be solved by providing a valid BuildContext to the child just by wrapping the Dialog with a Builder class. You can refer this answer to understand the significance of Builder.

show(BuildContext context){
var dialog = Dialog(
  child: Container(
    margin: EdgeInsets.all(8.0),
    child: Form(
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: <Widget>[
          TextFormField(
            decoration: InputDecoration(
                labelText: "Insira o número de telefone",
                border: OutlineInputBorder(
                    borderRadius: BorderRadius.all(Radius.circular(2.0)))),
          ),
          Row(
            mainAxisAlignment: MainAxisAlignment.end,
            children: <Widget>[
              FlatButton(
                  onPressed: () {
                    Navigator.of(context).pop();
                  },
                  child: Text("Cancelar")),
              FlatButton(
                  onPressed: () {
                    Navigator.of(context).pop();
                  },
                  child: Text("Aceitar"))
            ],
          )
        ],
      ),
    ),
  ),
);

showDialog(
   context: context,
   // Wrapping the clild widget (dialog) within a Builder class.
   builder: (context) => Builder(
     builder: (context) {
      return dialog;
      },
    ),
  );
 }

Point - 2:

To safely refer to a widget's ancestor in its dispose() method, save a reference to the ancestor by calling inheritFromWidgetOfExactType() in the widget's didChangeDependencies() method.

This simply asks us to save the context(reference to the parent widget) before it gets de-activated. The suggestions to use GlobalKey in the above answers simply does the same. As GlobalKey is unique throughout the application, the context is preserved. However, one can also save the context using state management.

Upvotes: 1

PrzemekTom
PrzemekTom

Reputation: 1994

I had the same bug, but in totally different context. I use Riverpod as a state manager, and I wanted to cancel a stream in StatefulWidget dispose method. It turned out that it caused this bug. What I had to do is to use Riverpod's onDispose API.

ref.onDispose(() {
    batteryStateSubscription.cancel();
});

A full example is shown in the documentation

Upvotes: 2

anas
anas

Reputation: 39

calling Navigator.pop(context) at the beginning of async function worked for me.

Upvotes: 1

Bhavy Gajjar
Bhavy Gajjar

Reputation: 41

Use this if You are using Stack in AlertDialog Not Closing on Navigator.of(context).pop();

late NavigatorState _navigator;

   @override
  void didChangeDependencies() {

    _navigator = Navigator.of(context);

    super.didChangeDependencies();
}

Use This

 Positioned(right: 10.0,child: GestureDetector(

                  // behavior: HitTestBehavior.translucent,

                  onTap: () {
                    _navigator.pop(context);
                    },
                  child: Align(
                    alignment: Alignment.topRight,
                    child: CircleAvatar(
                      radius: 14.0,
                      backgroundColor: Colors.white,
                      child: Icon(Icons.close, color: black),
                    ),
                  ),
                ),
              ),

Upvotes: 4

In my case I was using a provider where I used a context as an argument to a function, the thing was that when I passed that page I did it with pushnamedAndRemove Until then on the next page I was trying to use a function where I required the above context, so the error was mine because it was trying to get a parameter that I destroyed earlier, for that reason it didn't work. So be careful if you are deleting old pages.

Upvotes: 0

Mimu Saha Tishan
Mimu Saha Tishan

Reputation: 2633

Though you got desired answer, just for better clarification for others I put my opinion here.

Reason : It is happend due to context mismatch issue. Your passing context to Navigator.of(context).pop() is not matching with your MainApp BuildContext.

Solution : There has 2 way

  1. U can use Global key
  2. pass actual context to your Navigator

Below link I already mentioned how to solve this by passing actual context

https://stackoverflow.com/a/73543251/6109034

Upvotes: 1

Komolafe Ezekiel dare
Komolafe Ezekiel dare

Reputation: 115

before calling a dialog when a page is just loading, call it by adding SchedulerBinding to it, call it like this

SchedulerBinding.instance?.addPostFrameCallback((_) => showDialog( context: context, barrierDismissible: false, builder: (context) { return dialogBox(context, "Fetching account data", 'Profile page', DialogType.processing, function: () {}, dismissText: "", ); }));

Upvotes: 1

Muhammad Kashif
Muhammad Kashif

Reputation: 331

In my case i was calling

setState(() {
   Navigator.pop(context);
});

Upvotes: -3

mayfall
mayfall

Reputation: 41

removing application from emulator and run below commands

flutter clean
flutter pub get 

works for me

Upvotes: 2

Ahmed Taha
Ahmed Taha

Reputation: 1

first : declare a FormKey.

 GlobalKey<FormState>myFormKey=GlobalKey<FormState>();

second : add the FormKey to your Form widget.

Form( 

 key:myFormKey,

 child:child

 )

Upvotes: -1

Linar
Linar

Reputation: 1354

declare dialog and set in initState

  late Dialog dialog;

  @override
  void initState() {
    super.initState();

    dialog = Dialog(
      ...
    );
  }

Upvotes: 0

Komolafe Ezekiel dare
Komolafe Ezekiel dare

Reputation: 115

I simply solved this by wrapping the showDialog with a Builder widget, though for me the error came from a stream builder I simply wrap the stream builder with a builder widget and the remove the notify listeners from the a stream am calling in the stream builder, but in your case wrap the showDialog with a Builder widget and it will use the context from the builder, problem solved

Upvotes: -1

Ray Rojas
Ray Rojas

Reputation: 323

My problem was that I was using hot reload for pretty long time, I think at some point everything got messed up, doing a normal run of the app fixed the problem.

Upvotes: 8

Biruk Telelew
Biruk Telelew

Reputation: 1193

use this:

Navigator.of(context,rootNavigator: true).pop();

instead of

Navigator.of(context).pop();

Upvotes: 20

enumerator
enumerator

Reputation: 197

This might happen while you are popping from the context and trying to open new content on the context you are popping.

()async{
    Navigator.of(context).pop();
    _alertPopUp(); // shows a dialog
    // might do some work after
}

if alert dialog is created on current context then it throws an error because context doesn't exist anymore

Upvotes: 10

Andy King
Andy King

Reputation: 1572

I got the same error when attempting to open a dialog and I found a solution here: github flutter issues. Specifically, I followed the poster's recommendation, which was to create a GlobalKey and associate it with the Scaffold widget, and use the context from that key when creating the dialog. In my case, I have a globally accessible object which holds the GlobalKey:

MyGlobals myGlobals = MyGlobals();
class MyGlobals {
  GlobalKey _scaffoldKey;
  MyGlobals() {
    _scaffoldKey = GlobalKey();
  }
  GlobalKey get scaffoldKey => _scaffoldKey;
}

In the Scaffold widget constructor call:

Scaffold(
  appBar: ...,
  body: ...,
  drawer: ...,
  key: myGlobals.scaffoldKey,
)

And in the showDialog call:

showDialog<String>(
  barrierDismissible: ...,
  builder: ...,
  context: myGlobals.scaffoldKey.currentContext,
);

Upvotes: 28

Felix Runye
Felix Runye

Reputation: 2551

Declare a global variable

    final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();

then register the key on your widget build's scaffold eg

    @override
    Widget build(BuildContext context) {
     return Scaffold(
       key: _scaffoldKey,
       ...

then on the dialog

show(BuildContext context){

var dialog = Dialog(
  child: Container(
    margin: EdgeInsets.all(8.0),
    child: Form(
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: <Widget>[
          TextFormField(
            decoration: InputDecoration(
                labelText: "Insira o número de telefone",
                border: OutlineInputBorder(
                    borderRadius: BorderRadius.all(Radius.circular(2.0)))),
          ),
          Row(
            mainAxisAlignment: MainAxisAlignment.end,
            children: <Widget>[
              FlatButton(
                  onPressed: () {
                    Navigator.of(context).pop();
                  },
                  child: Text("Cancelar")),
              FlatButton(
                  onPressed: () {
                    Navigator.of(context).pop();
                  },
                  child: Text("Aceitar"))
            ],
          )
        ],
      ),
    ),
  ),
);

Pass that scaffold context to the showDialog method

showDialog(context: _scaffoldKey.currentContext ,builder: (context){
  return dialog;
 });
}

Upvotes: 127

Savad
Savad

Reputation: 1622

Try This

Give different context name for dialog

 showDialog(context: context,builder: (dialogContex){
              return Dialog(
                child: Container(
                  margin: EdgeInsets.all(8.0),
                  child: Form(
                    child: Column(
                      mainAxisSize: MainAxisSize.min,
                      children: <Widget>[
                        TextFormField(
                          decoration: InputDecoration(
                              labelText: "Insira o número de telefone",
                              border: OutlineInputBorder(
                                  borderRadius: BorderRadius.all(Radius.circular(2.0)))),
                        ),
                        Row(
                          mainAxisAlignment: MainAxisAlignment.end,
                          children: <Widget>[
                            FlatButton(
                                onPressed: () {
                                  Navigator.of(dialogContex).pop();
                                },
                                child: Text("Cancelar")),
                            FlatButton(
                                onPressed: () {
                                  Navigator.of(context).pop();
                                },
                                child: Text("Aceitar"))
                          ],
                        )
                      ],
                    ),
                  ),
                ),
              );
            });

Upvotes: 77

Fellipe Malta
Fellipe Malta

Reputation: 3530

Try this:

    Future<AlertDialog> myDialog(BuildContext context) {
    return showDialog<AlertDialog>(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          content: Container(
            margin: EdgeInsets.all(8.0),
            child: Form(
              child: Column(
                mainAxisSize: MainAxisSize.min,
                children: <Widget>[
                  TextFormField(
                    decoration: InputDecoration(
                        labelText: "Insira o número de telefone",
                        border: OutlineInputBorder(
                            borderRadius:
                                BorderRadius.all(Radius.circular(2.0)))),
                  ),
                ],
              ),
            ),
          ),
          actions: <Widget>[
            FlatButton(
                onPressed: () {
                  Navigator.of(context).pop();
                },
                child: Text("Cancelar")),
            FlatButton(
                onPressed: () {
                  Navigator.of(context).pop();
                },
                child: Text("Aceitar"))
          ],
        );
      },
    );
  }

Upvotes: 0

Miguel Ruivo
Miguel Ruivo

Reputation: 17776

You’re trying to access a context that isn’t probably available. That happens because you’ve assigned your Dialog to a var and afterwards use a different context (the one from your dialog builder).

Either create your dialog directly after your return in the builder or make it a method instead that returns a Dialog and pass it a BuildContext parameter.

Widget myDialog(BuildContext context) => Dialog(/*your dialog here*/);

This is also a more convenient Flutter practice. You should use methods that return widgets instead of assigning it to variables.

Upvotes: 21

Related Questions