slovnicki / beamer

A routing package built on top of Router and Navigator's pages API, supporting arbitrary nested navigation, guards and more.
MIT License
591 stars 129 forks source link

Duplicate GlobalKey detected in widget tree Error with NavigationBar #613

Closed STRENCH0 closed 1 year ago

STRENCH0 commented 1 year ago

Describe the bug Have strange errors with NavigationBar. When I click button I got the error "Duplicate GlobalKey detected in widget tree". Found only 1 workaround: to use NoAnimationTransitionDelegate. My code:

class _BottomNavigationBarWidgetState extends State<BottomNavigationBarWidget> {
  late BeamerDelegate _beamerDelegate;
  int _currentIndex = 0;
  bool _isVisible = true;

  void _setStateListener() => setState(() {});

  @override
  void initState() {
    super.initState();
    _beamerDelegate = widget.beamerKey.currentState!.routerDelegate;
    _beamerDelegate.addListener(_setStateListener);
  }

  @override
  Widget build(BuildContext context) {
    var location = _beamerDelegate.currentBeamLocation;
    if (location is MyLocation) {
      _currentIndex = 0;
      _isVisible = location.state.uri.pathSegments.length <= 1;
   } else if 
    ...
   }
    return Visibility(
      visible: _isVisible,
      child: NavigationBar(
        selectedIndex: _currentIndex,
        destinations: [
          NavigationDestination(
            label: label,
            icon: const Icon(Icons.view_list_outlined),
            selectedIcon: const Icon(Icons.view_list),
          ),
        ...
        ],
        onDestinationSelected: (index) {
          String url;
          switch (index) {
            case 0:
              url = '/someUrl';
              break;
              ...            
            default:
              url = '/404';
          }
          context.beamToReplacementNamed(url);
        },
      ),
    );
  }
 ...
}

In main file have 2 top-level delegates:

final _routerDelegate = BeamerDelegate(
  initialPath: '/someUrl',
  notFoundRedirect: NotFoundErrorLocation(),
  locationBuilder: BeamerLocationBuilder(
    beamLocations: [
      ...
    ],
  ),
);

final _routerDelegateHome = BeamerDelegate(
  locationBuilder: RoutesLocationBuilder(
    routes: {
      '*': (context, state, data) => HomeScreen(),
    },
  ),
);

final _beamerKey = GlobalKey<BeamerState>(debugLabel: "beamer_global_key");
 return MaterialApp.router(
                  scrollBehavior: CustomScrollBehavior(),
                  debugShowCheckedModeBanner: false,
                  routeInformationParser: BeamerParser(),
                  routerDelegate: _routerDelegateHome,
                  backButtonDispatcher: BeamerBackButtonDispatcher(delegate: _routerDelegate, alwaysBeamBack: true),
                  localizationsDelegates: context.localizationDelegates,
                  supportedLocales: context.supportedLocales,
                  locale: context.locale)

And HomeScreen:

class HomeScreen extends StatefulWidget {
  HomeScreen({Key? key}) : super(key: key);

  @override
  State<StatefulWidget> createState() {
    return HomeScreenState();
  }
}

class HomeScreenState extends State<HomeScreen> {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Beamer(
          key: _beamerKey,
          routerDelegate: _routerDelegate,
        ),
        bottomNavigationBar: BottomNavigationBarWidget(
          beamerKey: _beamerKey,
        ),
      );
  }
}

Beamer version: 1.5.3

Smartphone:

Additional context Full error:

======== 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<BeamerState>#0a75e beamer_global_key]
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:
- KeyedSubtree-[GlobalKey#2a673]
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)
====================================================================================================
STRENCH0 commented 1 year ago

Ok, I solved this. The problem was on line context.beamToReplacementNamed(url); Replaced it with _beamerDelegate.beamToReplacementNamed(url); Now it works like a charm