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);
–
–
–
–
–
–
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>();
–
–
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
)
–
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,
–
–
–
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.
–
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.