getx
getx copied to clipboard
Router issue after clicking too fast on navigation buttons
Hello everyone! We have a bottomNavigationbar in our RootPage() which changes the route on click for example to /home or to /psf.
Everything works fine if you click slowly but whenever I click faster, the navigation stops completely with the following error:
======== Exception caught by widgets library =======================================================
The following assertion was thrown building GetRouterOutlet(state: _RouterOutletState<GetDelegate, GetNavConfig>#357e4):
'package:flutter/src/widgets/navigator.dart': Failed assertion: line 3630 pos 14: '!pageKeyToOldEntry.containsKey(page.key)': 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=2_bug.md
The relevant error-causing widget was:
GetRouterOutlet GetRouterOutlet:file:///Users/.../lib/pages/root/root_page.dart:42:15
When the exception was thrown, this was the stack:
#2 NavigatorState._updatePages (package:flutter/src/widgets/navigator.dart:3630:14)
#3 NavigatorState.didUpdateWidget (package:flutter/src/widgets/navigator.dart:3433:7)
...
followed by:
======== Exception caught by rendering library =====================================================
The following assertion was thrown during performLayout():
Each child must be laid out exactly once.
The _ScaffoldLayout custom multichild layout delegate forgot to lay out the following child:
_ScaffoldSlot.body: RenderErrorBox#6d3fa NEEDS-LAYOUT NEEDS-PAINT
parentData: offset=Offset(0.0, 0.0); id=_ScaffoldSlot.body
constraints: MISSING
size: MISSING
The relevant error-causing widget was:
Scaffold Scaffold:file:///Users/alexanderthiele/projects/hermes/mobile-app/lib/pages/root/root_page.dart:41:14
When the exception was thrown, this was the stack:
#0 MultiChildLayoutDelegate._callPerformLayout.<anonymous closure> (package:flutter/src/rendering/custom_layout.dart:243:11)
#1 MultiChildLayoutDelegate._callPerformLayout (package:flutter/src/rendering/custom_layout.dart:255:8)
#2 RenderCustomMultiChildLayoutBox.performLayout (package:flutter/src/rendering/custom_layout.dart:403:14)
#3 RenderObject._layoutWithoutResize (package:flutter/src/rendering/object.dart:1707:7)
...
The navigation stops completely and after a hot reload the following error appears:
======== Exception caught by widgets library =======================================================
The following assertion was thrown building GetRouterOutlet(state: _RouterOutletState<GetDelegate, GetNavConfig>#357e4):
A GlobalKey was used multiple times inside one widget's child list.
The offending GlobalKey was: [LabeledGlobalKey<NavigatorState>#9688d Getx nested key: /home]
The parent of the widgets with that key was: GetRouterOutlet
state: _RouterOutletState<GetDelegate, GetNavConfig>#357e4
The first child to get instantiated with that key became: GetNavigator-[LabeledGlobalKey<NavigatorState>#9688d Getx nested key: /home]
dirty
dependencies: [_EffectiveTickerMode, HeroControllerScope, UnmanagedRestorationScope]
state: NavigatorState#7e95a(tickers: tracking 3 tickers)
The second child that was to be instantiated with that key was: GetRouterOutlet
A GlobalKey can only be specified on one widget at a time in the widget tree.
The relevant error-causing widget was:
GetRouterOutlet GetRouterOutlet:file:///Users/.../lib/pages/root/root_page.dart:42:15
When the exception was thrown, this was the stack:
#0 Element._retakeInactiveElement.<anonymous closure> (package:flutter/src/widgets/framework.dart:3568:11)
#1 Element._retakeInactiveElement (package:flutter/src/widgets/framework.dart:3582:8)
#2 Element.inflateWidget (package:flutter/src/widgets/framework.dart:3613:33)
followed by:
======== Exception caught by widgets library =======================================================
The following assertion was thrown building GetRouterOutlet(state: _RouterOutletState<GetDelegate, GetNavConfig>#357e4):
A GlobalKey was used multiple times inside one widget's child list.
The offending GlobalKey was: [LabeledGlobalKey<NavigatorState>#9688d Getx nested key: /home]
The parent of the widgets with that key was: GetRouterOutlet
state: _RouterOutletState<GetDelegate, GetNavConfig>#357e4
The first child to get instantiated with that key became: GetNavigator-[LabeledGlobalKey<NavigatorState>#9688d Getx nested key: /home]
dirty
dependencies: [_EffectiveTickerMode, HeroControllerScope, UnmanagedRestorationScope]
state: NavigatorState#7e95a(tickers: tracking 3 tickers)
The second child that was to be instantiated with that key was: GetRouterOutlet
A GlobalKey can only be specified on one widget at a time in the widget tree.
The relevant error-causing widget was:
GetRouterOutlet GetRouterOutlet:file:///Users/.../lib/pages/root/root_page.dart:42:15
When the exception was thrown, this was the stack:
#0 Element._retakeInactiveElement.<anonymous closure> (package:flutter/src/widgets/framework.dart:3568:11)
#1 Element._retakeInactiveElement (package:flutter/src/widgets/framework.dart:3582:8)
#2 Element.inflateWidget (package:flutter/src/widgets/framework.dart:3613:33)
Our Implementation
The bottom navigation always stays inside the RootPage() and has a GetRouterOutlet
.
@override
Widget build(BuildContext context) {
return GetRouterOutlet.builder(builder: (context, delegate, currentRoute) {
//This router outlet handles the the bottom navigation bar
final currentLocation = currentRoute?.location;
var currentIndex = _determineCurrentIndex(currentLocation);
return Scaffold(
body: GetRouterOutlet(
initialRoute: Routes.HOME,
key: Get.nestedKey(Routes.HOME),
),
bottomNavigationBar: CustomBottomNavigation(
width: MediaQuery.of(context).size.width,
currentIndex: currentIndex,
onTap: (value) => _onTap(value, delegate),
),
);
});
}
void _onTap(int value, GetDelegate delegate) {
String? route = RoutesIndex.values
.firstWhere((routesIndex) => routesIndex.index == value)
.route;
if (route != null) {
delegate.toNamed(route);
}
}
The Routes are defined as follows:
static final routes = [
GetPage(
name: _Paths.ROOT,
page: () => const RootPage(),
binding: RootBinding(),
participatesInRootNavigator: true,
preventDuplicates: true,
children: [
GetPage(
preventDuplicates: true,
name: _Paths.HOME,
page: () => const HomePage(),
binding: HomeBinding(),
),
GetPage(
preventDuplicates: true,
name: _Paths.PSF,
page: () => const PsfPage(),
),
],
)
];
and the HomeBinding:
class HomeBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut(() => HomeController());
}
}
Main App is:
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return GetMaterialApp.router(
title: "MyApp",
initialBinding: RootBinding(),
getPages: AppPages.routes,
defaultTransition: Transition.noTransition,
theme: MyThemes.myTheme,
debugShowCheckedModeBanner: false,
);
}
}
The Controller log before the issue appears is:
[GETX] "HomeController" onDelete() called
[GETX] "HomeController" deleted from memory
[GETX] Instance "HomeController" has been created
[GETX] Instance "HomeController" has been initialized
[GETX] "HomeController" onDelete() called
[GETX] "HomeController" deleted from memory
[GETX] Instance "HomeController" has been created
[GETX] Instance "HomeController" has been initialized
[GETX] "HomeController" onDelete() called
[GETX] "HomeController" deleted from memory
[GETX] Instance "HomeController" has been created
[GETX] Instance "HomeController" has been initialized
I have a feeling that the navigation is trying to use the controller that is about to get destroyed and then produces the crash.
I'm not sure if it's an implementation issue or a general routing issue.
Flutter Version: 2.8.0
Getx Version: 4.6.1
Describe on which device you found the bug: Android Pixel 3, ios Simulator iPhone 13
A GlobalKey was used multiple times inside one widget's child list.
It means more than 1 widget uses the same GlobalKey which is Get.nestedKey(Routes.HOME) in your case. I will check this first if I were you.
Hey @XuanTung95 Yes, but i'm not managing the lifecycle of the controllers when routing, GetX is managing them afaik and i think that the controller is still in the process of destroying.
What does the controller have to do with globalkey error? I think your case is not about the controller, delegate.toNamed(route) may open 2 pages that use the same globalKey.
Anyway, we know controller error in some rare cases as #1880.
Hey @XuanTung95 ok (i had a similar issue somewhere else where it brought up the controllers) but the GlobalKey is only used on a single page. That's why I said moving fast back and forth to the same page.
getX official demo example_nav2 have a similar issue, not solved yet
part of 'app_pages.dart';
// DO NOT EDIT. This is code generated via package:get_cli/get_cli.dart
abstract class Routes {
static const HOME = _Paths.HOME;
static const PROFILE = _Paths.HOME + _Paths.PROFILE; // try with nested _Paths
static const SETTINGS = _Paths.SETTINGS;
static const PRODUCTS = _Paths.HOME + _Paths.PRODUCTS; // try with nested _Paths
static const LOGIN = _Paths.LOGIN;
static const DASHBOARD = _Paths.HOME + _Paths.DASHBOARD; // try with nested _Paths
Routes._();
static String LOGIN_THEN(String afterSuccessfulLogin) =>
'$LOGIN?then=${Uri.encodeQueryComponent(afterSuccessfulLogin)}';
static String PRODUCT_DETAILS(String productId) => '$PRODUCTS/$productId';
}
abstract class _Paths {
static const HOME = '/home';
static const PRODUCTS = '/products';
static const PROFILE = '/profile';
static const SETTINGS = '/settings';
static const PRODUCT_DETAILS = '/:productId';
static const LOGIN = '/login';
static const DASHBOARD = '/dashboard';
}`
is there any solutions?