I have a screen with list of widgets which is scrolling to selected the element on tap. To achieve this I use Scrollable.ensureVisible and GlobalObjectKeys. It usually works well except one case. If I am at my scroll screen and pushing this screen again by Navigator.pushReplacement occurs "Duplicate GlobalKeys detected in widget tree" error (if I push some other screen and then my scroll screen - everything is OK). But everything works well - app isn't crashing, scroll works as expected. Can't find a way to resolve this problem.
Full description of the exception:
======== Exception caught by widgets library =======================================================
The following assertion was thrown while finalizing the widget tree:
Duplicate GlobalKeys detected in widget tree.
The following GlobalKeys were 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 keys were:
- [GlobalObjectKey String#bd95f]
[GlobalObjectKey String#392a1]
[GlobalObjectKey String#b1172]
[GlobalObjectKey String#65986]
[GlobalObjectKey String#47dd0]
[GlobalObjectKey String#5a612]
[GlobalObjectKey String#4cad0]
[GlobalObjectKey String#9344c]
This was determined by noticing that after widgets with the above global keys were moved out of their respective previous parents, those previous parents never updated during this frame, meaning that they either did not update at all or updated before the widgets were moved, in either case implying that they still think that they should have a child with those global keys.
The specific parents that did not update after having one or more children forcibly removed due to GlobalKey reparenting are:
- RepaintBoundary(renderObject: RenderRepaintBoundary#c9ecc relayoutBoundary=up4)
RepaintBoundary(renderObject: RenderRepaintBoundary#6838c relayoutBoundary=up4)
RepaintBoundary(renderObject: RenderRepaintBoundary#a8b6b relayoutBoundary=up4)
RepaintBoundary(renderObject: RenderRepaintBoundary#0ed26 relayoutBoundary=up4)
RepaintBoundary(renderObject: RenderRepaintBoundary#6b21e relayoutBoundary=up4)
RepaintBoundary(renderObject: RenderRepaintBoundary#96952 relayoutBoundary=up4)
RepaintBoundary(renderObject: RenderRepaintBoundary#59325 relayoutBoundary=up4)
RepaintBoundary(renderObject: RenderRepaintBoundary#bf00e relayoutBoundary=up4)
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:
#0 BuildOwner.finalizeTree.<anonymous closure> (package:flutter/src/widgets/framework.dart:3083:15)
#1 BuildOwner.finalizeTree (package:flutter/src/widgets/framework.dart:3108:8)
#2 WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:866:19)
#3 RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:381:5)
#4 SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1289:15)
#5 SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1218:9)
#6 SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:1076:5)
#7 _invoke (dart:ui/hooks.dart:145:13)
#8 PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:338:5)
#9 _drawFrame (dart:ui/hooks.dart:112:31)
====================================================================================================
Minimal reproducible example (just tap FAB to reproduce the problem):
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final ScrollController _chipsScrollController = ScrollController();
String _currentChip = 'Chip 1';
static const _chips = [
'Chip 1',
'Chip 2',
'Chip 3',
'Chip 4',
'Chip 5',
'Chip 6',
'Chip 7',
'Chip 8',
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: SizedBox(
height: 70,
child: ListView.separated(
controller: _chipsScrollController,
physics: const ClampingScrollPhysics(),
padding: const EdgeInsets.fromLTRB(16, 12, 16, 12),
scrollDirection: Axis.horizontal,
itemCount: _chips.length,
itemBuilder: (final BuildContext context, final int index) {
return ChoiceChip(
key: GlobalObjectKey(_chips[index]),
label: Text(_chips[index]),
selected: _chips[index] == _currentChip,
onSelected: (final bool value) {
_currentChip = _chips[index];
Scrollable.ensureVisible(
GlobalObjectKey(_chips[index]).currentContext!,
alignment: 0.5,
);
});
},
separatorBuilder: (final BuildContext context, final int index) =>
const SizedBox(width: 12),
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (_) => const MyHomePage(
title: 'Flutter Demo Home Page',
),
),
),
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
Flutter doctor summary:
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.7.10, on macOS 13.2.1 22D68 darwin-arm64, locale ru-RU)
[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.1)
[✓] Xcode - develop for iOS and macOS (Xcode 14.1)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2022.1)
[✓] IntelliJ IDEA Ultimate Edition (version 2023.1)
[✓] IntelliJ IDEA Community Edition (version 2022.2)
[✓] VS Code (version 1.74.3)
[✓] Connected device (3 available)
[✓] HTTP Host Availability
• No issues found!
I have a screen with list of widgets which is scrolling to selected the element on tap. To achieve this I use
Scrollable.ensureVisibleandGlobalObjectKeys. It usually works well except one case. If I am at my scroll screen and pushing this screen again by Navigator.pushReplacement occurs "Duplicate GlobalKeys detected in widget tree" error (if I push some other screen and then my scroll screen - everything is OK). But everything works well - app isn't crashing, scroll works as expected. Can't find a way to resolve this problem.Full description of the exception:
Minimal reproducible example (just tap FAB to reproduce the problem):
Flutter doctor summary: