csells / go_router

The purpose of the go_router for Flutter is to use declarative routes to reduce complexity, regardless of the platform you're targeting (mobile, web, desktop), handling deep linking from Android, iOS and the web while still allowing an easy-to-use developer experience.
https://gorouter.dev
441 stars 97 forks source link

Exception when using MaterialApp.router builder function #85

Closed noga-dev closed 3 years ago

noga-dev commented 3 years ago

If the MaterialApp's builder function wraps the child widget with a Scaffold that has a drawer that calls showBottomSheet then this error occurs:

The context used to push or pop routes from the Navigator must be that of a widget that is a descendant of a Navigator widget.)

Or

Null operator used on non null value

If it returns a Dialog instead.

MaterialApp:

Widget build(BuildContext context) => MaterialApp.router(
        routeInformationParser: _router.routeInformationParser,
        routerDelegate: _router.routerDelegate,
        theme: ThemeData.dark(),
        builder: (context, child) => Scaffold(
          drawer: const MainDrawer(),
          body: child,
        ),
      );

MainDrawer:

Widget build(BuildContext context) {
    return Drawer(
      child: Column(
        children: [
          DrawerHeader(
            child: IconButton(
              onPressed: () => showModalBottomSheet<void>(
                context: context,
                builder: (context) => Container(),
              ),
            ),
          ),
        ],
      ),
    );
  }

But if I remove the builder function and have the child itself return a widget wrapped in Scaffold that has the drawer then everything works OK.

It may not be unique to this routing package; I'm just experimenting.

csells commented 3 years ago

@Agondev can you post a complete (but minimal) set of repro code so that I can understand what's going on here?

noga-dev commented 3 years ago

Had to Frankenstein this together. Hopefully it does the trick.

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  runApp(
    const ProviderScope(child: TestScreen()),
  );
}

final _testRouter = GoRouter(
  initialLocation: '/1',
  debugLogDiagnostics: true,
  errorPageBuilder: (BuildContext context, GoRouterState state) =>
      MaterialPage<void>(
    key: state.pageKey,
    child: const CircularProgressIndicator.adaptive(
      valueColor: AlwaysStoppedAnimation(Colors.red),
    ),
  ),
  routes: [
    GoRoute(
      path: '/1',
      pageBuilder: (context, state) => MaterialPage<void>(
        key: state.pageKey,
        child: const Placeholder(
          color: Colors.green,
        ),
      ),
    ),
    GoRoute(
      path: '/2',
      pageBuilder: (context, state) => MaterialPage<void>(
        key: state.pageKey,
        child: const Placeholder(
          color: Colors.blue,
        ),
      ),
    ),
  ],
);

class TestScreen extends StatelessWidget {
  const TestScreen({Key? key}) : super(key: key);

  @override
  Widget build(context) {
    return MaterialApp.router(
      routeInformationParser: _testRouter.routeInformationParser,
      routerDelegate: _testRouter.routerDelegate,
      theme: ThemeData.dark(),
      builder: (context, child) => SafeArea(
        child: Scaffold(
          drawerEdgeDragWidth: 50,
          drawer: const TestHeader(),
          body: child,
        ),
      ),
    );
  }
}

class TestHeader extends StatelessWidget {
  const TestHeader({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Drawer(
      child: Column(
        children: [
          DrawerHeader(
            child: IconButton(
              icon: const Icon(
                Icons.accessible_rounded,
                color: Colors.red,
              ),
              onPressed: () => showBottomSheet<void>(
                context: context,
                builder: (context) => BottomSheet(
                  onClosing: () {},
                  builder: (context) => MaterialBanner(
                    content: const Text('test'),
                    actions: [
                      IconButton(
                        onPressed: () {},
                        icon: const Icon(Icons.check),
                      ),
                      IconButton(
                        onPressed: () {},
                        icon: const Icon(Icons.cancel),
                      ),
                    ],
                  ),
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

showBottomSheet can be swapped with showDialog for the other aforementioned issue.

noga-dev commented 3 years ago

So I still don't know why this occurs, but I can confirm it's definitely unrelated to this package, so I'll just close this issue. Sorry for the trouble.

csells commented 3 years ago

No worries.