Vingtoft
Vingtoft

Reputation: 14656

Flutter | How to listen for navigation event?

In Flutter, is there any way to listen for navigation changes?

Example: Navigation is triggered in Widget A:

Navigator.of(context).pushNamed('/chat');

When the above code is executed, I want an event to fire in a child of Widget A. Is this possible?

Upvotes: 11

Views: 13088

Answers (3)

Dom Hoàn
Dom Hoàn

Reputation: 320

If you using GoRouter, you can create class extends NavigatorObserver, then put it into observer of GoRouter's constructor, and put it into StatefulShellBranch if you have.

Example:

final router = GoRouter(
  initialLocation: '/',
  navigatorKey: rootNavigatorKey,
  observers: [GoRouterObserver()], // <== add here
  routes: <RouteBase>[
    GoRoute(
      path: pathOnboarding,
      pageBuilder: (context, state) {
        return const NoTransitionPage(
          child: Scaffold(
            body: SafeArea(
              child: Center(
                child: Text('Onboard Page'),
              ),
            ),
          ),
        );
      },
    ),
    StatefulShellRoute.indexedStack(
      builder: (context, state, navigationShell) {
        return MainPage(
          navigationShell: navigationShell,
          currentPath: state.fullPath,
        );
      },
      branches: [
        StatefulShellBranch(
          navigatorKey: homeShellNavigatorKey,
          observers:  [GoRouterObserver()], // <=== add here
          routes: [
            GoRoute(
              path: pathHome,
              pageBuilder: (context, state) =>
              const NoTransitionPage(
                child: HomePage(),
              ),
            ),
          ],
        ),
      ],
    ),
  ],
  debugLogDiagnostics: true,
);


class GoRouterObserver extends NavigatorObserver {
  @override
  void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) {
    debugPrint('GoRouterObserver didPush: $route');
  }

  @override
  void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) {
    debugPrint('GoRouterObserver didPop: $route');
  }

  @override
  void didRemove(Route<dynamic> route, Route<dynamic>? previousRoute) {
    debugPrint('GoRouterObserver didRemove: $route');
  }

  @override
  void didReplace({Route<dynamic>? newRoute, Route<dynamic>? oldRoute}) {
    debugPrint('GoRouterObserver didReplace: $newRoute');
  }
}

Upvotes: 1

Sure
Sure

Reputation: 113

I came across a similar problem and in my case, the best solution was using MaterialApp's onNavigationNotification, like this:

MaterialApp(
  onNavigationNotification: (notification) {
    print('something changed');
    // your logic to notify your widget
    return notification.canHandlePop;
  },
);

This way, you'll get notified when a navigation event occurs and you can add your own logic for notifying Widget A that a navigation event happened. To notify Widget A, or any widgets inside your app, you could use a ChangeNotifier or flutter_bloc's Bloc, for example.

This solution worked great for me because I just needed to notify a widget when something happened in the navigation stack, and I used ModalRoute's isCurrent to check if the widget was still part of the current route after the navigation event occurred.

Upvotes: 2

Mojtaba Ghiasi
Mojtaba Ghiasi

Reputation: 983

Use RouteObserver :

final RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>();

then add this to root materialApp widget :

MaterialApp(
      theme: ThemeData(),
      navigatorObservers: [routeObserver],
)

we need to implement RouteAware in every widget that might be push or pop into the routes stack.

class Screen extends State<Screen3> with RouteAware{
...

 @override
 void didChangeDependencies() {
   super.didChangeDependencies();
   routeObserver.subscribe(this, ModalRoute.of(context) as PageRoute<dynamic>);
 }

  @override
  void dispose() {
    routeObserver.unsubscribe(this);
    super.dispose();
  }

  @override
  void didPush() {
    final route = ModalRoute.of(context)?.settings.name;
    print('didPush route: $route');
  }

  @override
  void didPopNext() {
    final route = ModalRoute.of(context)?.settings.name;
    print('didPopNext route: $route');
  }

  @override
  void didPushNext() {
    final route = ModalRoute.of(context)?.settings.name;
    print('didPushNext route: $route');
  }

  @override
  void didPop() {
    final route = ModalRoute.of(context)?.settings.name;
    print('didPop route: $route');
  }
...
}

Upvotes: 16

Related Questions