Skip to content

[go_router]: Bad state: Future already completed on go or goNamed after pop #123369 #146938

@eli1stark

Description

@eli1stark

Steps to reproduce

  1. go_router: v12.1.3
  2. Launch sample
  3. Press 'Go to page B' button
  4. Error is thrown

This issue is very inconsistent. When I wait longer before calling goNamed, for example, 1000ms, no error is thrown. However, when I wait for a shorter duration, such as 100ms, an error is thrown. We can't go lower than 100ms because of the issue here: #145573.

This issue is also reproducible when showing dialogs and bottom sheets instead of calling go or goNamed.

Can be related to:

  1. [go_router]: Bad state: Future already completed on context.pop() #123369
  2. [go_router]: dialogs and bottom sheets are not shown after calling go or goNamed. #146578
  3. [go_router]: "There is nothing to pop" when popped fast #145573

I created a separate issue because the error thrown is different and the use case is slightly different as well.

At this point, go_router fails to deliver on the basic promise of 'Declarative routing.' We're forced to implement workarounds in our project, adding unintuitive artificial delays here and there to prevent navigation from breaking which reminds spaghetti code.

Expected results

Page "B" should be shown without death screen errors.

Actual results

The following StateError was thrown building InheritedGoRouter(goRouter: Instance of 'GoRouter'):
Bad state: Future already completed

Code sample

Code sample
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

final router = GoRouter(
  routes: [
    GoRoute(
      name: 'A',
      path: '/',
      builder: (_, __) => const PageA(),
    ),
    GoRoute(
      name: 'B',
      path: '/B',
      builder: (_, __) => const PageB(),
    ),
  ],
);

void main() => runApp(const App());

class App extends StatelessWidget {
  const App({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      routerConfig: router,
    );
  }
}

class PageA extends StatelessWidget {
  const PageA({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Center(
            child: TextButton(
              onPressed: () async {
                final ctx = router.routerDelegate.navigatorKey.currentContext!;
                unawaited(ctx.pushNamed('A'));
                await Future.delayed(const Duration(milliseconds: 100));
                ctx.pop();
                ctx.goNamed('B');
              },
              child: const Text('Go to page B'),
            ),
          ),
        ],
      ),
    );
  }
}

class PageB extends StatelessWidget {
  const PageB({super.key});

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      backgroundColor: Colors.brown,
    );
  }
}

Screenshots or Video

Screenshots / Video demonstration

[Upload media here]

Logs

Logs
Restarted application in 850ms.
Reloaded 1 of 1406 libraries in 238ms (compile: 24 ms, reload: 91 ms, reassemble: 90 ms).

════════ Exception caught by widgets library ═══════════════════════════════════
The following StateError was thrown building InheritedGoRouter(goRouter: Instance of 'GoRouter'):
Bad state: Future already completed

The relevant error-causing widget was:
    MaterialApp MaterialApp:file:///path_to_lib/lib/main_development.dart:28:24

When the exception was thrown, this was the stack:
#1      Route.didComplete (package:flutter/src/widgets/navigator.dart:418:19)
navigator.dart:418
#2      _RouteEntry.handleComplete (package:flutter/src/widgets/navigator.dart:3052:11)
navigator.dart:3052
#3      NavigatorState._flushHistoryUpdates (package:flutter/src/widgets/navigator.dart:4257:17)
navigator.dart:4257
#4      NavigatorState._updatePages (package:flutter/src/widgets/navigator.dart:4161:5)
navigator.dart:4161
#5      NavigatorState.didUpdateWidget (package:flutter/src/widgets/navigator.dart:3803:7)
navigator.dart:3803
#6      StatefulElement.update (package:flutter/src/widgets/framework.dart:5643:55)
framework.dart:5643
#7      Element.updateChild (package:flutter/src/widgets/framework.dart:3815:15)
framework.dart:3815
#8      ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5496:16)
framework.dart:5496
#9      Element.rebuild (package:flutter/src/widgets/framework.dart:5187:7)
framework.dart:5187
#10     ProxyElement.update (package:flutter/src/widgets/framework.dart:5800:5)
framework.dart:5800
#11     Element.updateChild (package:flutter/src/widgets/framework.dart:3815:15)
framework.dart:3815
#12     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5496:16)
framework.dart:5496
#13     Element.rebuild (package:flutter/src/widgets/framework.dart:5187:7)
framework.dart:5187
#14     ProxyElement.update (package:flutter/src/widgets/framework.dart:5800:5)
framework.dart:5800
#15     _InheritedNotifierElement.update (package:flutter/src/widgets/inherited_notifier.dart:105:11)
inherited_notifier.dart:105
#16     Element.updateChild (package:flutter/src/widgets/framework.dart:3815:15)
framework.dart:3815
#17     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5496:16)
framework.dart:5496
#18     Element.rebuild (package:flutter/src/widgets/framework.dart:5187:7)
framework.dart:5187
#19     StatelessElement.update (package:flutter/src/widgets/framework.dart:5547:5)
framework.dart:5547
#20     Element.updateChild (package:flutter/src/widgets/framework.dart:3815:15)
framework.dart:3815
#21     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5496:16)
framework.dart:5496
#22     Element.rebuild (package:flutter/src/widgets/framework.dart:5187:7)
framework.dart:5187
#23     ProxyElement.update (package:flutter/src/widgets/framework.dart:5800:5)
framework.dart:5800
#24     Element.updateChild (package:flutter/src/widgets/framework.dart:3815:15)
framework.dart:3815
#25     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5496:16)
framework.dart:5496
#26     Element.rebuild (package:flutter/src/widgets/framework.dart:5187:7)
framework.dart:5187
#27     StatelessElement.update (package:flutter/src/widgets/framework.dart:5547:5)
framework.dart:5547
#28     Element.updateChild (package:flutter/src/widgets/framework.dart:3815:15)
framework.dart:3815
#29     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5496:16)
framework.dart:5496
#30     Element.rebuild (package:flutter/src/widgets/framework.dart:5187:7)
framework.dart:5187
#31     ProxyElement.update (package:flutter/src/widgets/framework.dart:5800:5)
framework.dart:5800
#32     Element.updateChild (package:flutter/src/widgets/framework.dart:3815:15)
framework.dart:3815
#33     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5496:16)
framework.dart:5496
#34     Element.rebuild (package:flutter/src/widgets/framework.dart:5187:7)
framework.dart:5187
#35     ProxyElement.update (package:flutter/src/widgets/framework.dart:5800:5)
framework.dart:5800
#36     Element.updateChild (package:flutter/src/widgets/framework.dart:3815:15)
framework.dart:3815
#37     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5496:16)
framework.dart:5496
#38     StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5634:11)
framework.dart:5634
#39     Element.rebuild (package:flutter/src/widgets/framework.dart:5187:7)
framework.dart:5187
#40     BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2895:19)
framework.dart:2895
#41     WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:984:21)
binding.dart:984
#42     RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:457:5)
binding.dart:457
#43     SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1325:15)
binding.dart:1325
#44     SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1255:9)
binding.dart:1255
#45     SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:1113:5)
binding.dart:1113
#46     _invoke (dart:ui/hooks.dart:312:13)
hooks.dart:312
#47     PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:383:5)
platform_dispatcher.dart:383
#48     _drawFrame (dart:ui/hooks.dart:283:31)
hooks.dart:283
(elided one frame from dart:async)

════════════════════════════════════════════════════════════════════════════════

════════ Exception caught by widgets library ═══════════════════════════════════
'package:flutter/src/widgets/navigator.dart': Failed assertion: line 5495 pos 12: '!_debugLocked': is not true.
The relevant error-causing widget was:
════════════════════════════════════════════════════════════════════════════════

════════ Exception caught by widgets library ═══════════════════════════════════
'package:flutter/src/widgets/framework.dart': Failed assertion: line 6764 pos 12: 'renderObject.child == child': is not true.
The relevant error-causing widget was:
════════════════════════════════════════════════════════════════════════════════

════════ Exception caught by scheduler library ═════════════════════════════════
Tried to build dirty widget in the wrong build scope.
════════════════════════════════════════════════════════════════════════════════
[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: 'package:flutter/src/widgets/navigator.dart': Failed assertion: line 2993 pos 18: '!navigator._debugLocked': is not true.
#0      _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:51:61)
#1      _AssertionError._throwNew (dart:core-patch/errors_patch.dart:40:5)
#2      _RouteEntry.handlePush.<anonymous closure> (package:flutter/src/widgets/navigator.dart:2993:18)
#3      TickerFuture.whenCompleteOrCancel.thunk (package:flutter/src/scheduler/ticker.dart:420:15)
#4      Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:846:45)
#5      Future._propagateToListeners (dart:async/future_impl.dart:875:13)
#6      Future._completeWithValue (dart:async/future_impl.dart:647:5)
#7      Future._asyncCompleteWithValue.<anonymous closure> (dart:async/future_impl.dart:721:7)
#8      _microtaskLoop (dart:async/schedule_microtask.dart:40:21)
#9      _startMicrotaskLoop (dart:async/schedule_microtask.dart:49:5)

Flutter Doctor output

Doctor output
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.16.7, on macOS 14.2.1 23C71 darwin-arm64, locale en-US)
[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.0)
[✓] Xcode - develop for iOS and macOS (Xcode 15.3)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2022.3)
[✓] VS Code (version 1.87.2)
[✓] Connected device (4 available)
[✓] Network resources

• No issues found!

Metadata

Metadata

Assignees

Labels

P2Important issues not at the top of the work lista: error messageError messages from the Flutter frameworkfound in release: 3.19Found to occur in 3.19found in release: 3.22Found to occur in 3.22has reproducible stepsThe issue has been confirmed reproducible and is ready to work onp: go_routerThe go_router packagepackageflutter/packages repository. See also p: labels.r: fixedIssue is closed as already fixed in a newer version

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions