Use widgets with GlobalKeys in navigation
When you are using pushNamed new route is added on top of existing one.
Instead of pushNamed you can use pushReplacementNamed, so existing widget will be removed from navigation stack and replaced by new.
kikap
By day: do the operations engineering and manage the devops team. AWS EC2/RDS/ElasticSearch/CloudFormation, Packer, Docker, CI/CD, Python and other nice tools. By night: working on the next generation infrastructure as a code tooling in Purescript. Maintain physical infrastructure visualization service at https://www.rackmaze.com (CoffeeScript/Purescript/Python/NoSQL)
Updated on December 12, 2022Comments
-
kikap 14 minutes
My "main" widget has to have the Global key. If I navigate to it with a
pushNamedor equivalent it generates the exception about duplicate key in the widget tree. I can onlypopto this widget, but that severely reduces my navigation options and reusability of widgets.I've included the source of a small repro case, run the app, click
Loginbutton on the main page, enter 3 chars of login and 3 chars of password and thenLogin.Any thoughts? Redesigning without the
GlobalKeyis a major undertaking.flutter: ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════ flutter: The following assertion was thrown while finalizing the widget tree: flutter: Duplicate GlobalKey detected in widget tree. flutter: The following GlobalKey was specified multiple times in the widget tree. This will lead to parts of flutter: the widget tree being truncated unexpectedly, because the second time a key is seen, the previous flutter: instance is moved to the new location. The key was: flutter: - [GlobalKey#1c91e navKey] flutter: This was determined by noticing that after the widget with the above global key was moved out of its flutter: previous parent, that previous parent never updated during this frame, meaning that it either did flutter: not update at all or updated before the widget was moved, in either case implying that it still flutter: thinks that it should have a child with that global key. flutter: The specific parent that did not update after having one or more children forcibly removed due to flutter: GlobalKey reparenting is: flutter: - Semantics(container: false, properties: SemanticsProperties, label: null, value: null, hint: null, flutter: hintOverrides: null, renderObject: RenderSemanticsAnnotations#147f5 NEEDS-PAINT) flutter: A GlobalKey can only be specified on one widget at a time in the widget tree. flutter: flutter: When the exception was thrown, this was the stack: flutter: #0 BuildOwner.finalizeTree.<anonymous closure> flutter: #1 BuildOwner.finalizeTree flutter: #2 _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&SemanticsBinding&RendererBinding&WidgetsBinding.drawFrame flutter: #3 _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&SemanticsBinding&RendererBinding._handlePersistentFrameCallback flutter: #4 _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding._invokeFrameCallback flutter: #5 _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding.handleDrawFrame flutter: #6 _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding._handleDrawFrame flutter: #10 _invoke (dart:ui/hooks.dart:236:10) flutter: #11 _drawFrame (dart:ui/hooks.dart:194:3) flutter: (elided 3 frames from package dart:async) flutter: ════════════════════════════════════════════════════════════════════════════════════════════════════ flutter: Another exception was thrown: Multiple widgets used the same GlobalKey.import 'package:flutter/material.dart'; class MyKeys { static final GlobalKey navKey = GlobalKey<NavigatorState>(debugLabel: 'navKey'); } void main() => runApp(MyApp()); class MyApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, ), routes: { "/": (_) => MyHomePage(key: MyKeys.navKey, title: 'Home Page'), '/auth': (_) => MyAuth(), }, ); } } class MyHomePage extends StatefulWidget { MyHomePage({Key key, this.title}) : super(key: key); final String title; @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { int _counter = 0; void _incrementCounter() { setState(() { _counter++; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ FlatButton( child: Text('Login'), onPressed: () => Navigator.of(context).pushNamed('/auth'), ), Text( 'You have pushed the button this many times:', ), Text( '$_counter', style: Theme.of(context).textTheme.display1, ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: _incrementCounter, tooltip: 'Increment', child: Icon(Icons.add), ), ); } } class MyAuth extends StatefulWidget { @override _MyAuthState createState() => _MyAuthState(); } class _MyAuthState extends State<MyAuth> { String login; String password; final _formKey = GlobalKey<FormState>(); void _doLogin() { final form = _formKey.currentState; if(form.validate()) { form.save(); Navigator.of(context).pushNamed('/'); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Login')), body: Padding( padding: const EdgeInsets.all(24.0), child: Form( key: _formKey, child: Column( children: <Widget>[ TextFormField( decoration: InputDecoration( labelText: 'Enter login', ), autocorrect: false, autofocus: true, validator: (v) => v.trim().length < 3 ? 'Enter more than 3 chars': null, onSaved: (v) => login = v.trim(), ), TextFormField( decoration: InputDecoration( labelText: 'Enter password', ), obscureText: true, autocorrect: false, validator: (v) => v.trim().length < 3 ? 'Password should be no less than 3 chars': null, onSaved: (v) => password = v.trim(), ), FlatButton( child: Text('Login'), onPressed: () => _doLogin(), ) ], ), ), ), ); } }