Duplicate GlobalKey detected in widget tree

Flutter

Flutter Problem Overview


I am running into a globalKey error after I navigate from Screen A to Screen B and click a "Cancel" button to go back to Screen A.

It seems like the issue is that Screen B is either

  • A) Not being disposed of correctly
  • B) Is not doing something that it otherwise could

And I don't actually know:

  • What bad things are happening if I just remove the use of a globalKey? (as to get a better understanding of the fundamentals)
  • How can I correctly resolve this issue?

StatefulWidget documentation states:enter link description here

> A StatefulWidget keeps the same State object when moving from one > location in the tree to another if its creator used a GlobalKey for > its key. Because a widget with a GlobalKey can be used in at most one > location in the tree, a widget that uses a GlobalKey has at most one > associated element. The framework takes advantage of this property > when moving a widget with a global key from one location in the tree > to another by grafting the (unique) subtree associated with that > widget from the old location to the new location (instead of > recreating the subtree at the new location). The State objects > associated with StatefulWidget are grafted along with the rest of the > subtree, which means the State object is reused (instead of being > recreated) in the new location. However, in order to be eligible for > grafting, the widget must be inserted into the new location in the > same animation frame in which it was removed from the old location.

Console Error Output:

══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
The following assertion was thrown while finalizing the widget tree:
Duplicate GlobalKey detected in widget tree.
The following GlobalKey was specified multiple times in the widget tree. This will lead to parts of
the widget tree being truncated unexpectedly, because the second time a key is seen, the previous
instance is moved to the new location. The key was:
- [LabeledGlobalKey<FormFieldState<String>>#3c76d]
This was determined by noticing that after the widget with the above global key was moved out of its
previous parent, that previous parent never updated during this frame, meaning that it either did
not update at all or updated before the widget was moved, in either case implying that it still
thinks that it should have a child with that global key.
The specific parent that did not update after having one or more children forcibly removed due to
GlobalKey reparenting is:
- Column(direction: vertical, mainAxisAlignment: start, crossAxisAlignment: center, renderObject:
RenderFlex#7595c relayoutBoundary=up1 NEEDS-PAINT)
A GlobalKey can only be specified on one widget at a time in the widget tree.

So this part of the error output:

> previous parent never updated during this frame, meaning that it > either did not update at all or updated before the widget was moved

makes me think there was some opportunity for my old Stateful widget to do something (either reposition itself or release something as to be disposed correctly.

This seems to be failing in framework.dart on assert(_children.contains(child)):

  @override
  void forgetChild(Element child) {
    assert(_children.contains(child));
    assert(!_forgottenChildren.contains(child));
    _forgottenChildren.add(child);
  }

Flutter Solutions


Solution 1 - Flutter

In my case, it likes a hot reload bug. Just restart debugging works for me.

Solution 2 - Flutter

Remove the static and final type from the key variable so if

static final GlobalKey<FormState> _abcKey = GlobalKey<FormState>();

change it to

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

Solution 3 - Flutter

Thanks to Gunter's commments, I determined that this is because the Screens are not being properly disposed.

Flutter's pushReplacement makes a call to Route.dispose which will ultimately dispose the screen.

I am still unsure as to this comes into play:

> widget must be inserted into the new location in the same animation > frame

I'm not sure what situation would benefit from such trickery. However, my problem is solved. I just need to make a call to pop or replace.

Here are the available options:

  • Use push from A to B and just Navigator.pop from B
  • Use pushReplacement from A to B and from B to A

I've recently started playing with Fluro for routing and there are a few more ways to to handle these situations (Note the optional argument replace):

  • Use router.navigateTo(context, route, replace: false) from A to B and Navigator.pop from B

  • Use router.navigateTo(context, route, replace: true) from A to B the same from B to A (the key is replace: true)

Solution 4 - Flutter

Make sure that you don't have a Form parent and a Form child with the same key

Solution 5 - Flutter

Best way to solve that, which worked for me:

class _HomeScreenState extends State<HomeScreen> {
   GlobalKey<FormState> _homeKey = GlobalKey<FormState>(debugLabel: '_homeScreenkey');
   @override
   Widget build(BuildContext context) {
     return Container(
       key: _homeKey,
     );
   }
}

Solution 6 - Flutter

I had this issue too. I had a four screen bottom tabbed application and a 'logout' method. However, that logout method was calling a pushReplacementNamed. This prevented the class that held the global keys (different from the logout function) from calling dispose.

The resolution was to change pushReplacementNamed with popAndPushNamed to get back to my 'login' screen.

Solution 7 - Flutter

In my case I wanted to use the static GlobalKey<ScaffoldState> _scaffoldKey but when I used the same widget multiple times it gave this duplicate error.

I wanted to give it a unique string and still use this scaffold state. So I ended up using:

static GlobalObjectKey<ScaffoldState> _scaffoldKey

and in the initState:

_scaffoldKey = new GlobalObjectKey<ScaffoldState>(id);

Edit: Actually, silly me. I just simply removed the static and made it GlobalKey again :)

Solution 8 - Flutter

please take SingleChildScrollview:

and after if you use the bloc pettern then use strem with broadcast

code is here:

        body: Container(
        decoration: BoxDecoration(
          image: DecorationImage(
            image: AssetImage('assets/images/abcd.jpg'),
            fit: BoxFit.cover,
          ),
        ),
        child: Container(child:Form(key: _key,
          child: Padding(
            padding: EdgeInsets.symmetric(vertical: 100.0, horizontal: 20.0),
              child: SingleChildScrollView(child:Column(
                children: <Widget>[
                  Padding(
                    padding: const EdgeInsets.all(10.0),
                    child: Image.asset('assets/images/logo.png', height: 80, width:80,),
                  ),
                  email(),
                  Padding(
                    padding: EdgeInsets.all(5.0),
                  ),
                  password(),
                  row(context),
                ],
              ),
            ),
          ),
        ),
      ),
      ),
      resizeToAvoidBottomPadding: false,
    );
  }

and the bloc pettern code is here:

    final _email = StreamController<String>.broadcast();
    final _password = StreamController<String>.broadcast();

    Stream<String> get email => _email.stream.transform(validateEmail);
    Stream<String> get password=> _password.stream.transform(validatepassword);

    Function(String) get changeEmail=> _email.sink.add;
    Function(String) get changePassword => _password.sink.add;

    dispose(){

      _email.close();
      _password.close();
    }

}

final bloc=Bloc();

Solution 9 - Flutter

I also had a similar error. My answer was that after I updated Flutter some widgets no longer had child or children properties. In my case it was the CircleAvatar. The build doesn't error out initially, but when navigating back and forth through the app it will fail.

*Please review all widgets that require a child then review the updated documentation and make sure you're parameters are still correct.

Solution 10 - Flutter

I had a similar problem. in my case the problem was that I had a function in dispose method of screen B and it wouldn't execute properly. I just removed it and the problem was fixed.

so be sure your dispose method is executed properly in all screens.

Solution 11 - Flutter

This happened to me, what I did was enclosed the whole view into a navigator using an extension I made

Widget addNavigator() => Navigator(
    onGenerateRoute: (_) => MaterialPageRoute(
      builder: (context2) => Builder(
        builder: (context) => this,
      ),
    ),
  );

Solution 12 - Flutter

I also got this error. There was a static bloc object in a class and I removed the static keyword which fixed the error.

Events should be added by using the BlocProvider anyway.

Solution 13 - Flutter

I had similar issue on a StatelessWidget class, Converted it to StatefulWidget and error is gone.

Solution 14 - Flutter

If you have multiple forms with different widgets, you must use separate GlobalKey for each form. Like I have two forms, one with Company signup & one with Profile. So, I declared

GlobalKey<FormState> signUpCompanyKey = GlobalKey<FormState>();
GlobalKey<FormState> signUpProfileKey = GlobalKey<FormState>();

Solution 15 - Flutter

I am not sure why no one has mentioned this yet but, in my case, I simply changed a widget from Stateful to Stateless. To fix the error, you have to restart the application instead of doing a hot reload.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAshton ThomasView Question on Stackoverflow
Solution 1 - FlutterflaughView Answer on Stackoverflow
Solution 2 - FlutterMindBrainView Answer on Stackoverflow
Solution 3 - FlutterAshton ThomasView Answer on Stackoverflow
Solution 4 - Flutteryoussef ahmdView Answer on Stackoverflow
Solution 5 - FlutterDumitru BoaghiView Answer on Stackoverflow
Solution 6 - FlutterDaniel ChapmanView Answer on Stackoverflow
Solution 7 - FlutterIdoView Answer on Stackoverflow
Solution 8 - FlutterHarsh ChovatiyaView Answer on Stackoverflow
Solution 9 - FlutterCharles JrView Answer on Stackoverflow
Solution 10 - FlutterBarzan SaeedpourView Answer on Stackoverflow
Solution 11 - FlutterKetan ChoyalView Answer on Stackoverflow
Solution 12 - FlutterColibriView Answer on Stackoverflow
Solution 13 - FlutterAzizView Answer on Stackoverflow
Solution 14 - FlutterShihab UddinView Answer on Stackoverflow
Solution 15 - FlutterBeast77View Answer on Stackoverflow