4

Refer to this Duplicate GlobalKey detected in widget tree , i still have struggle with duplicate key while navigate from Screen A , to Screen B and Navigatie again to Screen A

LoginPage

class LoginPage extends StatefulWidget {
  @override
  _LoginPageState createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {
  @override
  void initState() {
    super.initState();
//UsingSharedPreference , If Already Login , GoToHomePage
    Provider.of<LoginChangeNotifier>(context, listen: false)
        .checkLogin(context); 
  }

  @override
  void dispose() {
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    var _size = Screen(MediaQuery.of(context).size);
    return Consumer<LoginChangeNotifier>(
      builder: (context, login, _) {
        return Scaffold(
          key:Provider.of<LoginChangeNotifier>(context, listen: false)
              .scaffoldLoginKey,
          backgroundColor: Colors.black,
          body: Container(
              child: SingleChildScrollView(
            child: Column(
              mainAxisSize: MainAxisSize.max,
              children: <Widget>[
                Container(
                ),
                SizedBox(height: _size.hp(2)),
                Form(
                  key: Provider.of<LoginChangeNotifier>(context, listen: false)
                      .loginKey,
                  child:Container(
                    margin: EdgeInsets.symmetric(horizontal: _size.wp(8)),
                    child: Column(
                      children: <Widget>[
                        TextFormField(
                          controller: login.controllerEmail,
                          ),
                          keyboardType: TextInputType.emailAddress,
                          autovalidate: login.autoValidate,
                          validator: (e) {
                            if (e.isEmpty) {
                              return "Cant Be Empty";
                            } else {
                              return null;
                            }
                          },
                        ),
                        SizedBox(height: 16),
                        TextFormField(
                          controller: login.controllerPassword,
                            labelText: 'Password',
                          ),
                          keyboardType: TextInputType.emailAddress,
                          obscureText: login.secureText,
                          autovalidate: login.autoValidate,
                          validator: (e) {
                            if (e.isEmpty) {
                              return "Cant Be Empty";
                            } else {
                              return null;
                            }
                          },
                        ),
RaisedButton(
                                color: myPrimaryColor,
                                  children: <Widget>[
                                    Text("Login",
                                        style: TextStyle(
                                            color: Colors.black,
                                            fontFamily: "NunitoSansBold")),
                                  ],
                                ),
                                padding:
                                    EdgeInsets.only(top: 16.0, bottom: 16.0),
                                onPressed: () {
                                  print("clicked Button Login");
//HereHandleFromProvider
                                  login.authenticationUser(context);
                                },
                              ),
                        SizedBox(height: _size.hp(3)),
                        ),
                      ],
                    ),
                  ),
                ),
              ],
            ),
          )),
        );
      },
    );
  }

Login_Provider

class LoginChangeNotifier with ChangeNotifier {
  GlobalKey<ScaffoldState> _scaffoldLoginKey = GlobalKey<ScaffoldState>(
      debugLabel: '_scaffoldLoginkey');
  GlobalKey<FormState> _loginKey = new GlobalKey<FormState>();

 GlobalKey<ScaffoldState> get scaffoldLoginKey => _scaffoldLoginKey;
  GlobalKey<FormState> get loginKey => _loginKey;

authenticationUser(BuildContext context) async {
showDialog(
              context: context,
              builder: (myContext) =>
                  AlertDialog(
                    title: new Text('Warning'),
                    content: new Text('Your Password Expired'),
                    actions: [
                      new FlatButton(
                          onPressed: () {
                            Navigator.of(context).pushReplacement(
                                MaterialPageRoute(builder: (context) =>
//MoveToScreenB
                                    ChangePasswordPage()));
                          },
                          child: new Text(
                            'Change Password',
                            style: TextStyle(color: Colors.lightBlue),
                          ))
                    ],
                    shape: RoundedRectangleBorder(
                      side: BorderSide(color: Colors.white70, width: 1),
                      borderRadius: BorderRadius.circular(10),
                    ),
                  ));
}

ChangePasswordPage

class ChangePasswordPage extends StatelessWidget {
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Change Password", style: TextStyle(color: Colors.black)),
        centerTitle: true,
        iconTheme: IconThemeData(color: Colors.white),
      ),
      key: Provider.of<ChangePasswordNotifier>(context, listen: false)
          .scaffoldChangePasswordKey,
      body: Consumer<ChangePasswordNotifier>(
        builder: (context, changePassword, _) {
          return changePassword.changePasswordProcess
              ? Center(child: CircularProgressIndicator())
              : Padding(
                  padding: EdgeInsets.symmetric(
                  ),
                  child: Form(
                    key: Provider.of<ChangePasswordNotifier>(context,
                            listen: false)
                        .changePasswordkey,
                    child: Column(
                      children: [
                        TextFormField(),
                        SizedBox(
                            height: MediaQuery.of(context).size.height / 47),
                        TextFormField(),
                        SizedBox(
                            height: MediaQuery.of(context).size.height / 47),
                        TextFormField(),
                      ],
                    ),
                  ),
                );
        },
      ),
      bottomNavigationBar: BottomAppBar(
        elevation: 0.0,
        child: Padding(
            padding: const EdgeInsets.all(8.0),
            child: Consumer<ChangePasswordNotifier>(
              builder: (context, changePassword, _) {
                return RaisedButton(
                    onPressed: () {
                      print("Submit ChangePassword Clicked");
                      changePassword.changePasswordProcess
                          ? null
//HereHandleOnProvider
                          : changePassword.check(context);
                    },
                    child: Row(
                      children: <Widget>[
                        Text("SUBMIT",)
                      ],
                    ));
              },
            )),
      ),
    );
  }
}

ChangePasswordProvider

class ChangePasswordNotifier with ChangeNotifier {
  GlobalKey<ScaffoldState> _scaffoldChangePasswordKey = new GlobalKey<ScaffoldState>();
  GlobalKey<FormState> _changePasswordkey =new GlobalKey<FormState>();

  GlobalKey<FormState> get changePasswordkey => _changePasswordkey;
  GlobalKey<ScaffoldState> get scaffoldChangePasswordKey =>
      _scaffoldChangePasswordKey;

      check(BuildContext context) async {
confirmationPassword(context, "Success");
        }

        confirmationPassword(BuildContext context, result)async{
           var _providerLogin =
        Provider.of<LoginChangeNotifier>(context, listen: false);
          showDialog(
        context: context,
        builder: (myContext) => AlertDialog(
              title: new Text("Confirmation"),
              content: new Text(result),
              actions: [
                new FlatButton(
                    onPressed: () {
                      print("Clicked on Confirmation Password");
                      Navigator.pushAndRemoveUntil(
                          context,
                          //Here i Want to navigate back to Screen A
                          MaterialPageRoute(builder: (context) => LoginPage()),
                              (Route<dynamic> route) => false);
                    },
                    child: new Text(
                      'OK',
                      style: TextStyle(color: Colors.lightBlue),
                    ))
              ],
              shape: RoundedRectangleBorder(
                side: BorderSide(color: Colors.white70, width: 1),
                borderRadius: BorderRadius.circular(10),
              ),
            ));
        }

}

The Result when click navigate to Screen A

======== Exception caught by widgets library =======================================================
The following assertion was thrown building Form-[LabeledGlobalKey<FormState>#57f0a](state: FormState#c57b1):
'package:flutter/src/widgets/will_pop_scope.dart': Failed assertion: line 61 pos 12: '_route == ModalRoute.of(context)': is not true.


Either the assertion indicates an error in the framework itself, or we should provide substantially more information in this error message to help you determine and fix the underlying cause.
In either case, please report this assertion by filing a bug on GitHub:
  https://github.com/flutter/flutter/issues/new?template=BUG.md

The relevant error-causing widget was: 
  Form-[LabeledGlobalKey<FormState>#57f0a] file:///urlFile:login_page.dart:37:17
When the exception was thrown, this was the stack: 
#2      _WillPopScopeState.didUpdateWidget (package:flutter/src/widgets/will_pop_scope.dart:61:12)
#3      StatefulElement.update (package:flutter/src/widgets/framework.dart:4815:58)
#4      Element.updateChild (package:flutter/src/widgets/framework.dart:3314:15)
#5      ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4652:16)
#6      StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4800:11)
...
====================================================================================================

======== 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<ScaffoldState>#14011]
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:
- Consumer<LoginChangeNotifier>(dependencies: [_LocalizationsScope-[GlobalKey#f6126], InheritedProvider<LoginChangeNotifier>, _InheritedTheme])
A GlobalKey can only be specified on one widget at a time in the widget tree.
When the exception was thrown, this was the stack: 
#0      BuildOwner.finalizeTree.<anonymous closure> (package:flutter/src/widgets/framework.dart:2881:15)
#1      BuildOwner.finalizeTree (package:flutter/src/widgets/framework.dart:2906:8)
#2      WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:915:18)
#3      RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:302:5)
#4      SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1117:15)
...
====================================================================================================

=====EDITED===== For Temp Solution, I figure out to using duplicate pop context (1 for dismissing the alert dialog, and 1 more for back to page A), but I guess this is not a best practice.

Navigator.pop(context); 
Navigator.pop(context);
Don Key
  • 105
  • 1
  • 2
  • 6
  • 1
    try set your globalkey to final globalkey – Raine Dale Holgado Feb 15 '21 at 02:57
  • still same error , btw error line on The relevant error-causing widget was: Form-[LabeledGlobalKey#06619] reffer to Form() Widget – Don Key Feb 15 '21 at 03:10
  • It's possible that when you call Navigator.pushAndRemoveUntil(..), it's pushing a new LoginPage() widget onto the Navigator stack, without having disposed of the old _LoginPageState (meaning the same keys are getting re-used). Can you just call Navigator.pop() instead? – Nick Fisher Feb 15 '21 at 05:41
  • can't , when change from ```Navigator.pushAndRemoveUntil( context, MaterialPageRoute(builder: (context) => LoginPage()), (Route route) => false);``` to ```Navigator.pop(context);``` , the pop will dissmis the alertdialog but still on ChangePasswordPage. – Don Key Feb 15 '21 at 06:53

4 Answers4

1

You may want to consider moving the GlobalKeys to your Pages so they can be disposed when no longer in use instead of initializing them inside ChangeNotifier. Then you can just pass the navigation keys on authenticationUser() so you can access the Navigator if needed.

final navigatorKey = GlobalKey<NavigatorState>();

To access the Navigator from the key, you can use something similar.

navigatorKey.currentState!.pushAndRemoveUntil(context,
    MaterialPageRoute(builder: (context) => LoginPage()),
        (Route<dynamic> route) => false);
Omatt
  • 8,564
  • 2
  • 42
  • 144
0

You push off your Dialog before popping the dialog. In fact you never pop the dialog. Pop the dialog, then you can Navigate to new page. Also, you can push to a 2nd screen, and just pop back to the original.

jbryanh
  • 1,193
  • 7
  • 17
0

Try disposing the global key by overriding dispose method

class MyWidgetState extends State<MyWidget> {
  final _globalKey = GlobalKey<FormState>();
  @override
  void initState() {
    super.initState();
  }
  @override
  void dispose() {
    _globalKey.currentState!.dispose(); //  dispose the key here
    super.dispose();
  }
}
krishnaacharyaa
  • 14,953
  • 4
  • 49
  • 88
0

Must use separate GlobalKey. I also solved my multiple loader calling issue using separate key, like below

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

Hope it will make a sense for someone.

Mimu Saha Tishan
  • 2,402
  • 1
  • 21
  • 40