I had previously posted a question on showing main content/segment on my Flutter Web App that uses Riverpod and GoRouter. The answer to my question as one has commented is using ShellRoute. I tried to implement it and it did what I intended to do but I did encounter a bug as well as an exception caught by the library. I noticed that if I clicked the same link twice in the (left) menu panel, the first time it will go to the page, but the second time it will produce a blank page. I think it is related to the exception caught:
══╡ 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:
- [GlobalObjectKey int#ab910]
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:
- Expanded(flex: 4)
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:
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 296:3 throw_
packages/flutter/src/widgets/framework.dart 3235:15 <fn>
packages/flutter/src/widgets/framework.dart 3259:16 finalizeTree
packages/flutter/src/widgets/binding.dart 1143:7 drawFrame
packages/flutter/src/rendering/binding.dart 443:5 [_handlePersistentFrameCallback]
packages/flutter/src/scheduler/binding.dart 1392:7 [_invokeFrameCallback]
packages/flutter/src/scheduler/binding.dart 1313:9 handleDrawFrame
packages/flutter/src/scheduler/binding.dart 1171:5 [_handleDrawFrame]
lib/_engine/engine/platform_dispatcher.dart 1404:5 invoke
lib/_engine/engine/platform_dispatcher.dart 307:5 invokeOnDrawFrame
lib/_engine/engine/initialization.dart 187:36 <fn>
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 426:37 _checkAndCall
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 431:39 dcall
How should I fixed this, I just followed several tutorials on ShellRoute and it has 2 GlobalKeys. Somehow, the issue is pointing to the 2nd Expanded widget which is the main content in Desktop view.
Here are my code:
Router Provider
final _rootkey = GlobalKey<NavigatorState>(debugLabel: 'root');
final _shellKey = GlobalKey<NavigatorState>(debugLabel: 'shell');
final routerProvider = Provider<GoRouter>((ref) {
final authState = ref.watch(authProvider);
return GoRouter(
navigatorKey: _rootkey,
debugLogDiagnostics: false,
initialLocation: '/',
refreshListenable: authState,
routes: [
GoRoute(
path: '/login',
name: LoginPage.routeName,
builder: (context, state) {
return const LoginPage();
}),
GoRoute(
path: '/cover',
name: CoverPage.routeName,
builder: (context, state) {
return const CoverPage();
}),
ShellRoute(
navigatorKey: _shellKey,
builder: (context, state, child) => ShellPage(child: child),
routes: [
GoRoute(
path: '/',
name: HomePage.routeName,
parentNavigatorKey: _shellKey,
builder: (context, state) {
return const HomePage();
},
),
GoRoute(
path: '/viewUsers',
name: ViewUsers.routeName,
parentNavigatorKey: _shellKey,
builder: (context, state) {
return const ViewUsers();
}),
GoRoute(
path: '/addUser',
name: AddUser.routeName,
parentNavigatorKey: _shellKey,
builder: (context, state) {
return const AddUser();
}),
],
),
],
redirect: (context, state) {
final isAuthenticated = authState.isLoggedIn;
if (state.fullPath == '/login') {
return isAuthenticated ? null : '/login';
}
return isAuthenticated ? null : '/cover';
});
});
Shell Page
class ShellPage extends ConsumerStatefulWidget {
final Widget child;
const ShellPage({required this.child, super.key});
@override
ConsumerState createState() => _ShellPageState();
}
class _ShellPageState extends ConsumerState<ShellPage> {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: SizedBox(
child: Image.asset('images/logo_dswd.png'),
),
actions: <Widget>[
IconButton(
onPressed: () {
ref.read(authProvider).signOut();
},
icon: const Icon(Icons.logout))
],
leading: constraints.maxWidth > 600
? null
: IconButton(
onPressed: () {
if (_scaffoldKey.currentState!.isDrawerOpen) {
_scaffoldKey.currentState!.openEndDrawer();
} else {
_scaffoldKey.currentState!.openDrawer();
}
},
icon: const Icon(Icons.menu)),
),
body: constraints.maxWidth > 600
? Row(
children: [
const Expanded(flex: 1, child: MenuPanel()),
Expanded(flex: 4, child: widget.child),
],
)
: widget.child,
drawer: constraints.maxWidth > 600 ? null : const MenuPanel(),
);
},
);
}
}
Menu Panel
class MenuPanel extends ConsumerWidget {
const MenuPanel({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final String userAccountType =
ref.watch(userProvider.notifier).state!.accountType;
final router = ref.watch(routerProvider);
return Drawer(
child: ListView(
children: <Widget>[
if (userAccountType == 'admin') ...[
ListTile(
title: const Text("View Users"),
onTap: () {
router.goNamed(ViewUsers.routeName);
Navigator.pop(context);
},
),
ListTile(
title: const Text("Add Users"),
onTap: () {
router.goNamed(AddUser.routeName);
Navigator.pop(context);
},
),
...
] else ...[
ListTile(
title: const Text("Menu Item"),
onTap: () {},
),
]
],
),
);
}
}
Running the web app in debug mode in Visual Studio Code, after Login, it will correctly display the Menu depending on the status of the user. However as mentioned, there is an exception caught in the debug console, and if I clicked the link twice, it will show a blank page. How should I correct this issue?
I have tried asking Bing/Co-pilot but its answer is vague nor does not show how to fix the error. I also checked Stackoverflow on similar issues but there are no concrete answers.