在小部件树中检测到重复 GlobalKey

在我从 Screen A导航到 Screen B并单击“取消”按钮返回到 Screen A之后,我遇到了 globalKey错误。

看起来问题在于 Screen B要么是

  • A)没有正确处理
  • B)没有做本来可以做的事情

我也不知道:

  • 如果我只是删除 globalKey的使用,会发生什么坏事情?(以便更好地理解基本原理)
  • 我如何正确地解决这个问题?

StatateWidget 文档状态: 在这里输入链接描述

当从一个状态对象移动时,状态小部件保持相同的状态对象 如果其创建者使用 GlobalKey 用于 因为一个具有 GlobalKey 的小部件最多只能在一个 位置,使用 GlobalKey 的小部件最多只有一个 GlobalKey 框架利用了这个属性 当从树中的一个位置移动具有全局键的小部件时 通过嫁接与之关联的(唯一的)子树来实现 Widget 从旧位置到新位置(而不是 在新位置重新创建子树) 关联的语句与其他语句一起嫁接 子树,这意味着 State 对象被重用(而不是 然而,为了符合下列条件: 中的新位置插入小部件 同样的动画框 ,它从旧的位置被移除

控制台错误输出:

══╡ 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.

这部分的错误输出:

前一个父级在此帧期间从未更新,这意味着它 要么根本没有更新,要么在移动小部件之前就更新了

让我觉得我的旧 Stateful 小部件有机会做一些事情(或者重新定位自己或者释放一些东西以便正确处理。

这似乎在 assert(_children.contains(child))台的 framework.dart节目中失败了:

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

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)

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.

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

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

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

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

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

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.

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.

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.

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

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 had similar issue on a StatelessWidget class, Converted it to StatefulWidget and error is gone.

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