Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AutoRoute Aware with nested tab navigation #1028

Closed
martiina opened this issue Apr 8, 2022 · 7 comments
Closed

AutoRoute Aware with nested tab navigation #1028

martiina opened this issue Apr 8, 2022 · 7 comments

Comments

@martiina
Copy link

martiina commented Apr 8, 2022

Hi.

Love the auto_route plugin and really appreciate your work with this.
Makes navigation a lot easier :)

However, I'm encountering the problem with AutoRouteAware screens.
I'm using flutter hooks and thus made a custom hook with auto route observer. (rrousselGit/flutter_hooks#166 (comment)) from this snippet

import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';

import '../services/logger.dart';

final log = logger.get(useRouteObserver);

class _RouteCallbacks with AutoRouteAware {
  const _RouteCallbacks({
    this.handleDidPopNext,
    this.handleDidPush,
    this.handleDidPop,
    this.handleDidPushNext,
    this.handleDidChangeTabRoute,
    this.handleDidInitTabRoute,
  });

  final VoidCallback? handleDidPopNext;
  final VoidCallback? handleDidPush;
  final VoidCallback? handleDidPop;
  final VoidCallback? handleDidPushNext;
  final ValueChanged<TabPageRoute>? handleDidChangeTabRoute;
  final ValueChanged<TabPageRoute?>? handleDidInitTabRoute;

  @override
  void didPopNext() {
    handleDidPopNext?.call();
  }

  @override
  void didPush() {
    handleDidPush?.call();
  }

  @override
  void didPop() {
    handleDidPop?.call();
  }

  @override
  void didPushNext() {
    handleDidPushNext?.call();
  }

  @override
  void didChangeTabRoute(TabPageRoute previousRoute) {
    handleDidChangeTabRoute?.call(previousRoute);
  }

  @override
  void didInitTabRoute(TabPageRoute? previousRoute) {
    handleDidInitTabRoute?.call(previousRoute);
  }
}

void useRouteObserver({
  required BuildContext context,
  VoidCallback? didPopNext,
  VoidCallback? didPush,
  VoidCallback? didPop,
  VoidCallback? didPushNext,
  ValueChanged<TabPageRoute>? didChangeTabRoute,
  ValueChanged<TabPageRoute?>? didInitTabRoute,
  List<Object?> keys = const [],
}) {
  final route = context.watchTabsRouter.parent()?.routeData;
  final observer = RouterScope.of(context).firstObserverOfType<AutoRouteObserver>();

  final callbacks = _RouteCallbacks(
    handleDidPop: didPop,
    handleDidPopNext: didPopNext,
    handleDidPush: didPush,
    handleDidPushNext: didPushNext,
    handleDidChangeTabRoute: didChangeTabRoute,
    handleDidInitTabRoute: didInitTabRoute,
  );

  useEffect(
    () {
      if (route == null) {
        log.info('router not found skipping observer');

        return () {};
      }

      if (observer != null) {
        // we subscribe to the observer by passing our
        // AutoRouteAware state and the scoped routeData
        observer.subscribe(callbacks, route);
      }

      return () => observer?.unsubscribe(callbacks);
    },
    [route, observer, ...keys],
  );
}

This is my current observer now with AutoRouteAware.

The thing is, I have put this hook on one screen. (course details screen)

useRouteObserver(
      context: context,
      didPop: () => print('didPop'),
      didPopNext: () => print('didPopNext'),
      didPush: () => print('didPush'),
      didPushNext: () => print('didPushNext'),
      didChangeTabRoute: (previousRoute) => print('didChangeTabRoute :: ${previousRoute.name}'),
      didInitTabRoute: (previousRoute) => print('didInitTabRoute :: ${previousRoute?.name}'),
    );

If I navigate to this course details screen I get "didPush", but nothing else.
If I trying to navigate back or trying to push next screen, I do not get any events.

Getting didPush, if popping and then navigating back to details screen again.

I'm using tab navigation, this is my rough routes file with courses details screen

AutoRoute(
      path: 'main',
      page: MainMenuScreen,
      name: 'MainRouter',
      children: [
          ....
          // hthis is one of the bottom navigation router
          AutoRoute(
          path: 'more',
          name: 'MoreRouter',
          page: MoreScreen,
          children: [
              ...
              AutoRoute(path: 'courses/:articleId', page: CoursesDetailsScreen),
          ]
      ]
)

I even tried with stateful widget and the result is same, is it because I'm using wrong routeData?

@Milad-Akarie
Copy link
Owner

That might be the reason.
You should subscribe to context.routeData

Also you should pass type to findFirstObservorOfType

@martiina
Copy link
Author

martiina commented Apr 8, 2022

Hi.

Thanks for your reply.

The type exists already. :)
Tried context.routeData;

Nothing changed. The result still persists.
tried also context.router.root.push instead of context.router.push() to navigate another screen but still does not manage to get it work.

For back button I'm using this: context.tabsRouter.parent()?.pop(), which pops the current tabscreen

@martiina
Copy link
Author

martiina commented Apr 8, 2022

Don't know if related but after some digging I found something:

@override
  void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) {
    print(previousRoute?.settings);

    if (route.settings is CustomPage && previousRoute?.settings is CustomPage) {
      final previousKey = (previousRoute!.settings as CustomPage).routeKey;

      final Set<AutoRouteAware>? previousSubscribers = _listeners[previousKey];

      print('LISTENER :: $previousKey :: $previousSubscribers');

      if (previousSubscribers != null) {
        for (final routeAware in previousSubscribers) {
          routeAware.didPushNext();
        }
      }
    }
  }

Created duplicate observer with the code provided with package.

route.settings type was CustomPage. Added this to if statement

If I navigate from "course Item" screen to video player screen, the didPush $previousRoute reports CustomPage<dynamic>("MainRouter", null, null), which is the root route, but should be the screen you navigating from?

I'm using @CustomAutoRouter

@CustomAutoRouter(
  // replaces *Screen() to *Route()
  // e.g: LoadingScreen() to LoadingRoute()
  replaceInRouteName: 'Screen,Route',
  transitionsBuilder: TransitionsBuilders.fadeIn,
  durationInMilliseconds: AppAnimation.fast,
  reverseDurationInMilliseconds: AppAnimation.fast,
  routes: [])

@martiina
Copy link
Author

martiina commented Apr 8, 2022

Eddit:

I think I've got somewhere.

with: context.tabsRouter.routeData; and above didPush override i get the events now.

@martiina
Copy link
Author

THe issue remains, if I do above, using: tabsrouter.routeData, then I get pushNext/popNext/pop/push events, but I'm losing the didInitTab and didTabChange events, if I use context.routeData, then I can get the didInitTab and didChangeTab events, but others are not firing for some reason.

the previousRoute fro didPush remains always "MainRouter" for some reason.

@dastein1
Copy link

dastein1 commented Jun 29, 2022

Hm, I'm also struggling with that. I can't get handleDidChangeTabRoute() and didChangeTabRoute() to get called.
Need to dig deeper on how to get routeData in such a way that those get called. In the meantime
adding a listener like suggested in #518 (comment) works.

@github-actions
Copy link

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants