Flutter -在微件树中检测到重复的GlobalKey

6ss1mwsb  于 2023-01-02  发布在  Flutter
关注(0)|答案(3)|浏览(576)

参考此Duplicate GlobalKey detected in widget tree,当从屏幕A导航到屏幕B并再次导航到屏幕A时,我仍在与重复密钥斗争
登录页面

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)),
                        ),
                      ],
                    ),
                  ),
                ),
              ],
            ),
          )),
        );
      },
    );
  }

登录_提供者

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

更改密码页面

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",)
                      ],
                    ));
              },
            )),
      ),
    );
  }
}

更改密码提供程序

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

}

点击导航至屏幕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=====对于Temp Solution,我发现使用重复的弹出上下文(1个用于关闭警报对话框,1个用于返回到页面A),但我猜这不是一个最佳实践。

Navigator.pop(context); 
Navigator.pop(context);
gg0vcinb

gg0vcinb1#

您可能需要考虑将GlobalKeys移动到页中,以便在不再使用它们时将其处理掉,而不是在ChangeNotifier中初始化它们。然后,您只需在authenticationUser()上传递导航键,以便在需要时访问导航器。

final navigatorKey = GlobalKey<NavigatorState>();

要从键访问Navigator,可以使用类似的方法。

navigatorKey.currentState!.pushAndRemoveUntil(context,
    MaterialPageRoute(builder: (context) => LoginPage()),
        (Route<dynamic> route) => false);
fhity93d

fhity93d2#

你在弹出对话框之前推掉你的对话框。事实上你从来没有弹出对话框。弹出对话框,然后你可以导航到新的页面。同样,你可以推到第二个屏幕,然后弹回到原来的屏幕。

nxagd54h

nxagd54h3#

尝试通过重写dispose方法释放global key

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

相关问题