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 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);
                    You probably want to use popAndPushNamed() or similar instead of just pushNamed() to navigate between pages to avoid pages being added multiple times
    – Günter Zöchbauer
                    Mar 19, 2018 at 19:51
                    Thanks for the reply. Two things here 1) As luck would have it, I just started using fluro (github.com/goposse/fluro) and I don't yet know how pop work with that library.. 2) If this error is implying that Screen A is still around, is there a way I can just use that instance? (I think the error is all from Screen A. Perhaps because there is the original instance of Screen A and it's trying to create a new one
    – Ashton Thomas
                    Mar 19, 2018 at 19:57
                    That was my assumption as well. I don't know Fluro though. I think you need top pop the active route to go back to the previous one, or when you push a new route, replace the previous one instead of just adding one of top of the other. popAndPushNamed() is how the Flutter router does that, no idea about Fluro.
    – Günter Zöchbauer
                    Mar 19, 2018 at 20:04
                    @GunterZochbauer yep that was it! I didn't know Widget disposal was tied to 'popping' a route. I guess that makes sense, though. I stopped being lazy and looked at fluro's doc: github.com/goposse/fluro/blob/master/lib/src/… and setting replace: true triggered a call to dispose() so we are all good. Thanks!
    – Ashton Thomas
                    Mar 19, 2018 at 20:11
                    Flutter pro-tip: If you get an error, don't assume you actually have error. Restart first.
    – Michael Peterson
                    Feb 21, 2020 at 1:10
                    Ufffff, after having spent half a day debugging, this is the kind of stuff that makes me chew on my keyboard ...
    – Wecherowski
                    May 17, 2022 at 23:00
    

    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>();
                    When I use GlobalKey<FormState> _abcKey = GlobalKey<FormState>();, then I can't use the keyboard anymore, when I am using a smartphone. I just can use the keyboard when I have a static final GlobalKey. Does someone have a solution?
    – Hans Sama
                    Jul 4, 2022 at 13:21
                    I must to use static final GlobalKey due to I call some function from another pages like Page.staticGlobalKey.currentState.methodName(). Is there other way to call function from other class without static GlobalKey ?
    – Edimar Martins
                    Nov 30, 2022 at 13:03
    

    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)
  • This solves my problem, but I would like to keep the back button functional. Any way to do this? – JAgüero Dec 29, 2022 at 22:53

    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,
                    What is debugLabel: '_homeScreenkey', Kindly explain that what actually you have did here.
    – mufazmi
                    Jan 17, 2022 at 5:37
                    debugLabel - it's used to define a individual label(a.k.a. name, id) for multiple GlobalKey in app.
    – Dumitru Boaghi
                    Jan 27, 2022 at 8:18
                    @DumitruBoaghi are you sure it is "a.k.a id"? Documentation states the opposite. The label is purely for debugging and not used for comparing the identity of the key.
    – mirkancal
                    Feb 28 at 3:51
    

    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.

    I had a similar setup, but with the GetX package. Get.offAllNamed() was needed to route to and from the auth screen to ensure that all previous routes are disposed. github.com/jonataslaw/getx/blob/master/documentation/en_US/… – King Leon Aug 12, 2021 at 14:06

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

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

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

    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,
    

    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.

    I was getting this error for using multiple loader in a single page for every API response delay. Applied above all solution but no luck.

    finally, used separate Globalkey for every loader calling

    GlobalKey<State> _loaderDialog = GlobalKey<State>(); 
    GlobalKey<State> _loaderDialogForSubmit = GlobalKey<State>();
    

    And solved my issue.

    The simple example of my solution:

    // STEP 1
    late GlobalKey<NavigatorState> productPageNavigatorKey;
    class TestScreen extends StatefulWidget {
      const TestScreen({
        Key? key,
      }) : super(key: key);
      @override
      State<TestScreen> createState() => _ProductCardState();
    class _ProductCardState extends State<TestScreen> {
      @override
      void initState() {
        // STEP 2
        productPageNavigatorKey = GlobalKey<NavigatorState>();
        super.initState();
      @override
      void dispose() {
        // STEP 3
        productPageNavigatorKey.currentState?.dispose();
        super.dispose();
      @override
      Widget build(BuildContext context) {
        return CupertinoPageScaffold(
          key: productPageNavigatorKey,
          child: Text('Test'),
    

    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.