Skip to content

[go_router] EXCEPTION CAUGHT BY WIDGETS LIBRARY #149237

@anasmano

Description

@anasmano

Steps to reproduce

  1. Click Tab Section B
  2. Click On the Floating Action Button (It will result EXCEPTION being printed in the debug console like the Logs report I included below)
  3. Click Tab Section A
  4. Click the Back Button of Details Screen - A page (not working, stuck to go back to the Root Of Section A page.)

Note :
This issue just appeared in debugging mode. In release mode it runs well.

Expected results

The Back Button of Details Screen - A (Steps to reproduce #4) works properly, so I can go to the Root Of Section A page.

Actual results

Getting stuck, The Back Button of Details Screen - A (Steps to reproduce #4) doesn't work.

Code sample

Code sample
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

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

final GlobalKey<NavigatorState> _rootNavigatorKey =
    GlobalKey<NavigatorState>(debugLabel: 'root');
final GlobalKey<NavigatorState> _sectionANavigatorKey =
    GlobalKey<NavigatorState>(debugLabel: 'sectionANav');

// This example demonstrates how to setup nested navigation using a
// BottomNavigationBar, where each bar item uses its own persistent navigator,
// i.e. navigation state is maintained separately for each item. This setup also
// enables deep linking into nested pages.

class CounterCubit extends Cubit<int> {
  CounterCubit() : super(0);

  void increment() => emit(state + 1);

  void decrement() => emit(state - 1);
}

void main() {
  runApp(BlocProvider(
      create: (_) => CounterCubit(),
      lazy: false,
      child: NestedTabNavigationExampleApp()));
}

/// An example demonstrating how to use nested navigators
class NestedTabNavigationExampleApp extends StatelessWidget {
  /// Creates a NestedTabNavigationExampleApp
  NestedTabNavigationExampleApp({super.key});

  final GoRouter _router = GoRouter(
    navigatorKey: _rootNavigatorKey,
    initialLocation: '/a/details',
    routes: <RouteBase>[
      StatefulShellRoute.indexedStack(
        builder: (BuildContext context, GoRouterState state,
            StatefulNavigationShell navigationShell) {
          // Return the widget that implements the custom shell (in this case
          // using a BottomNavigationBar). The StatefulNavigationShell is passed
          // to be able access the state of the shell and to navigate to other
          // branches in a stateful way.
          return ScaffoldWithNavBar(navigationShell: navigationShell);
        },
        branches: <StatefulShellBranch>[
          // The route branch for the first tab of the bottom navigation bar.
          StatefulShellBranch(
            navigatorKey: _sectionANavigatorKey,
            routes: <RouteBase>[
              GoRoute(
                // The screen to display as the root in the first tab of the
                // bottom navigation bar.
                path: '/a',
                builder: (BuildContext context, GoRouterState state) =>
                    const RootScreen(label: 'A', detailsPath: '/a/details'),
                routes: <RouteBase>[
                  // The details screen to display stacked on navigator of the
                  // first tab. This will cover screen A but not the application
                  // shell (bottom navigation bar).
                  GoRoute(
                    path: 'details',
                    builder: (BuildContext context, GoRouterState state) =>
                        const DetailsScreen(label: 'A'),
                  ),
                ],
              ),
            ],
          ),

          // The route branch for the second tab of the bottom navigation bar.
          StatefulShellBranch(
            // It's not necessary to provide a navigatorKey if it isn't also
            // needed elsewhere. If not provided, a default key will be used.
            routes: <RouteBase>[
              GoRoute(
                // The screen to display as the root in the second tab of the
                // bottom navigation bar.
                path: '/b',
                builder: (BuildContext context, GoRouterState state) =>
                    const RootScreen(
                  label: 'B',
                  detailsPath: '/b/details/1',
                  secondDetailsPath: '/b/details/2',
                ),
                routes: <RouteBase>[
                  GoRoute(
                    path: 'details/:param',
                    builder: (BuildContext context, GoRouterState state) =>
                        DetailsScreen(
                      label: 'B',
                      param: state.pathParameters['param'],
                    ),
                  ),
                ],
              ),
            ],
          ),

          // The route branch for the third tab of the bottom navigation bar.
          StatefulShellBranch(
            routes: <RouteBase>[
              GoRoute(
                // The screen to display as the root in the third tab of the
                // bottom navigation bar.
                path: '/c',
                builder: (BuildContext context, GoRouterState state) =>
                    const RootScreen(
                  label: 'C',
                  detailsPath: '/c/details',
                ),
                routes: <RouteBase>[
                  GoRoute(
                    path: 'details',
                    builder: (BuildContext context, GoRouterState state) =>
                        DetailsScreen(
                      label: 'C',
                      extra: state.extra,
                    ),
                  ),
                ],
              ),
            ],
          ),
        ],
      ),
    ],
  );

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      routerConfig: _router,
    );
  }
}

/// Builds the "shell" for the app by building a Scaffold with a
/// BottomNavigationBar, where [child] is placed in the body of the Scaffold.
class ScaffoldWithNavBar extends StatelessWidget {
  /// Constructs an [ScaffoldWithNavBar].
  const ScaffoldWithNavBar({
    required this.navigationShell,
    Key? key,
  }) : super(key: key ?? const ValueKey<String>('ScaffoldWithNavBar'));

  /// The navigation shell and container for the branch Navigators.
  final StatefulNavigationShell navigationShell;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          context.read<CounterCubit>().increment();
        },
        backgroundColor: Colors.green,
        child: const Icon(Icons.navigation),
      ),
      body: navigationShell,
      bottomNavigationBar: BottomNavigationBar(
        // Here, the items of BottomNavigationBar are hard coded. In a real
        // world scenario, the items would most likely be generated from the
        // branches of the shell route, which can be fetched using
        // `navigationShell.route.branches`.
        items: const <BottomNavigationBarItem>[
          BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Section A'),
          BottomNavigationBarItem(icon: Icon(Icons.work), label: 'Section B'),
          BottomNavigationBarItem(icon: Icon(Icons.tab), label: 'Section C'),
        ],
        currentIndex: navigationShell.currentIndex,
        onTap: (int index) => _onTap(context, index),
      ),
    );
  }

  /// Navigate to the current location of the branch at the provided index when
  /// tapping an item in the BottomNavigationBar.
  void _onTap(BuildContext context, int index) {
    // When navigating to a new branch, it's recommended to use the goBranch
    // method, as doing so makes sure the last navigation state of the
    // Navigator for the branch is restored.
    navigationShell.goBranch(
      index,
      // A common pattern when using bottom navigation bars is to support
      // navigating to the initial location when tapping the item that is
      // already active. This example demonstrates how to support this behavior,
      // using the initialLocation parameter of goBranch.
      initialLocation: index == navigationShell.currentIndex,
    );
  }
}

/// Widget for the root/initial pages in the bottom navigation bar.
class RootScreen extends StatelessWidget {
  /// Creates a RootScreen
  const RootScreen({
    required this.label,
    required this.detailsPath,
    this.secondDetailsPath,
    super.key,
  });

  /// The label
  final String label;

  /// The path to the detail page
  final String detailsPath;

  /// The path to another detail page
  final String? secondDetailsPath;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: BlocBuilder<CounterCubit, int>(
          builder: (context, state) {
            return Text('Root of section $label, Counter From Bloc/Cubit $state');
          },
        ),
      ),
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: <Widget>[
            Text('Screen $label',
                style: Theme.of(context).textTheme.titleLarge),
            const Padding(padding: EdgeInsets.all(4)),
            TextButton(
              onPressed: () {
                GoRouter.of(context).go(detailsPath, extra: '$label-XYZ');
              },
              child: const Text('View details'),
            ),
          ],
        ),
      ),
    );
  }
}

/// The details screen for either the A or B screen.
class DetailsScreen extends StatefulWidget {
  /// Constructs a [DetailsScreen].
  const DetailsScreen({
    required this.label,
    this.param,
    this.extra,
    this.withScaffold = true,
    super.key,
  });

  /// The label to display in the center of the screen.
  final String label;

  /// Optional param
  final String? param;

  /// Optional extra object
  final Object? extra;

  /// Wrap in scaffold
  final bool withScaffold;

  @override
  State<StatefulWidget> createState() => DetailsScreenState();
}

/// The state for DetailsScreen
class DetailsScreenState extends State<DetailsScreen> {
  @override
  Widget build(BuildContext context) {
    if (widget.withScaffold) {
      return Scaffold(
        appBar: AppBar(
          title: Text('Details Screen - ${widget.label}'),
        ),
        body: _build(context),
      );
    } else {
      return ColoredBox(
        color: Theme.of(context).scaffoldBackgroundColor,
        child: _build(context),
      );
    }
  }

  Widget _build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: <Widget>[
          Text('Details for ${widget.label}',
              style: Theme.of(context).textTheme.titleLarge),
          const Padding(padding: EdgeInsets.all(4)),
          const Padding(padding: EdgeInsets.all(8)),
          if (widget.param != null)
            Text('Parameter: ${widget.param!}',
                style: Theme.of(context).textTheme.titleMedium),
          const Padding(padding: EdgeInsets.all(8)),
          if (widget.extra != null)
            Text('Extra: ${widget.extra!}',
                style: Theme.of(context).textTheme.titleMedium),
          if (!widget.withScaffold) ...<Widget>[
            const Padding(padding: EdgeInsets.all(16)),
            TextButton(
              onPressed: () {
                GoRouter.of(context).pop();
              },
              child: const Text('< Back',
                  style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18)),
            ),
          ]
        ],
      ),
    );
  }
}

Screenshots or Video

Screenshots / Video demonstration

[Upload media here]

Logs

Logs
══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
The following assertion was thrown building Text("Root of section A, Counter From Bloc/Cubit 1",
dependencies: [DefaultSelectionStyle, DefaultTextStyle, MediaQuery]):
Assertion failed: file:///C:/flutter/flutter/packages/flutter/lib/src/rendering/object.dart:2283:14
_debugSubtreeRelayoutRootAlreadyMarkedNeedsLayout()
is not true

The relevant error-causing widget was:
  Text Text:file:///C:/Users/User/Desktop/stateful_books-main/lib/main.dart:229:20

When the exception was thrown, this was the stack:
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 297:3       throw_
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 38:3        assertFailed
packages/flutter/src/rendering/object.dart 2283:14                                markNeedsLayout
packages/flutter/src/rendering/box.dart 2379:11                                   markNeedsLayout
packages/flutter/src/rendering/paragraph.dart 443:11                              markNeedsLayout
packages/flutter/src/rendering/paragraph.dart 336:9                               set text
packages/flutter/src/widgets/basic.dart 5898:9                                    <fn>
packages/flutter/src/widgets/basic.dart 5910:26                                   updateRenderObject
packages/flutter/src/widgets/framework.dart 6491:6                                [_performRebuild]
packages/flutter/src/widgets/framework.dart 6470:5                                update
packages/flutter/src/widgets/framework.dart 6914:11                               update
packages/flutter/src/widgets/framework.dart 3824:14                               updateChild
packages/flutter/src/widgets/framework.dart 5505:16                               performRebuild
packages/flutter/src/widgets/framework.dart 5196:7                                rebuild
packages/flutter/src/widgets/framework.dart 5556:5                                update
packages/flutter/src/widgets/framework.dart 3824:14                               updateChild
packages/flutter/src/widgets/framework.dart 5505:16                               performRebuild
packages/flutter/src/widgets/framework.dart 5643:11                               performRebuild
packages/flutter/src/widgets/framework.dart 5196:7                                rebuild
packages/flutter/src/widgets/framework.dart 5666:5                                update
packages/flutter/src/widgets/framework.dart 3824:14                               updateChild
packages/flutter/src/widgets/framework.dart 5505:16                               performRebuild
packages/flutter/src/widgets/framework.dart 5643:11                               performRebuild
packages/flutter/src/widgets/framework.dart 5196:7                                rebuild
packages/flutter/src/widgets/framework.dart 2904:18                               buildScope
packages/flutter/src/widgets/binding.dart 989:9                                   drawFrame
packages/flutter/src/rendering/binding.dart 448:5                                 [_handlePersistentFrameCallback]
packages/flutter/src/scheduler/binding.dart 1386:7                                [_invokeFrameCallback]
packages/flutter/src/scheduler/binding.dart 1311:9                                handleDrawFrame
packages/flutter/src/scheduler/binding.dart 1169:5                                [_handleDrawFrame]
lib/_engine/engine/platform_dispatcher.dart 1346:5                                invoke
lib/_engine/engine/platform_dispatcher.dart 260:5                                 invokeOnDrawFrame
lib/_engine/engine/initialization.dart 185:36                                     <fn>
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 550:37  _checkAndCall
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 555:39  dcall

════════════════════════════════════════════════════════════════════════════════════════════════════
Another exception was thrown: Assertion failed: file:///C:/flutter/flutter/packages/flutter/lib/src/rendering/object.dart:2283:14
Another exception was thrown: Assertion failed: file:///C:/flutter/flutter/packages/flutter/lib/src/rendering/object.dart:1853:12
Another exception was thrown: Assertion failed: file:///C:/flutter/flutter/packages/flutter/lib/src/rendering/object.dart:1853:12

Flutter Doctor output

Doctor output
[√] Flutter (Channel stable, 3.19.6, on Microsoft Windows [Version 6.3.9600], locale en-US)
[X] Windows Version (Unable to determine Windows version (command `ver` returned Microsoft Windows [Version
    6.3.9600]))
[!] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
    X Could not determine java version
[√] Chrome - develop for the web
[X] Visual Studio - develop Windows apps
    X Visual Studio not installed; this is necessary to develop Windows apps.
      Download at https://visualstudio.microsoft.com/downloads/.
      Please install the "Desktop development with C++" workload, including all of its default components
[√] Android Studio (version 4.1)
[√] VS Code (version 1.79.2)
[√] Connected device (3 available)
[√] Network resources

! Doctor found issues in 3 categories.

Metadata

Metadata

Assignees

No one assigned

    Labels

    in triagePresently being triaged by the triage teamwaiting for customer responseThe Flutter team cannot make further progress on this issue until the original reporter responds

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions