Milad-Akarie / auto_route_library

Flutter route generator
MIT License
1.58k stars 400 forks source link

pop/push detection with AutoRouteObserver and navigateTo #1774

Open herna opened 11 months ago

herna commented 11 months ago

Hello,

I have the following routes:

I am trying to detect every time the "list" page is visible so I can refresh some data (basically invalidate a Riverpod provider to get some data from the database)

I use AutoRouteObserver and the corresponding "with AutoRouteAwareStateMixin" in the list page like this:

@RoutePage()
class ListPage extends ConsumerStatefulWidget {

  class _ListPageState extends ConsumerState<ListPage>
      with AutoRouteAwareStateMixin {
@override
  void didPop() {
    print('didPop');
  }

  @override
  void didPopNext() {
    print('didPopNext');
  }

  @override
  void didPush() {
    print('didPush');
  }

  @override
  void didPushNext() {
    print('didPushNext');
  }
 }
}

From an initial state I navigate from "home" to "list" with "context.navigateTo(const ListRoute())" and I can correctly see the "didPush" text.

No I navigate from "list" to "detail" the same way with "context.navigateTo(DetailRoute(id: id.id))" and I can correctly see the "didPushNext" message

Now from the "detail" page if I click the "back" button then I can correctly see again the "didPopNext" message.

My problem comes now. In the detail page I have a "save" button than when clicked I do a "context.navigateTo(const ListRoute())" to go to the "list" page again. The thing here is I cannot see any of the didPop/didPush messages and I expected to see the "didPopNext" message the same way it's displayed through the "back" button. According to the docs, navigateTo "pops until provided route, if it already exists in stack"

Also I tried to add a custom observer to the main() just to log all the transitions:

class MyObserver extends AutoRouterObserver {
  @override
  void didPush(Route route, Route? previousRoute) {
    print('New route pushed: ${route.settings.name}');
    print('Previous route pushed: ${previousRoute?.settings.name}');
  }

  @override
  void didPop(Route route, Route? previousRoute) {
    print('New route popped: ${route.settings.name}');
    print('Previous route popped: ${previousRoute?.settings.name}');
  }

}

And this observer prints "null" in this case when navigating from "detail" to "list" when "list" already exists in the stack:

flutter: New route popped: null flutter: Previous route popped: EditRoute

If I change the navigation to "pushRoute" instead of "navigateTo", then I can detect everything but since my app will be a a Web app, navigateTo is far more convenient and I don't want to do this modification.

At this moment I am just invalidating the Riverpod provider just before calling "context.navigateTo(const ListRoute())" in the "edit" page but this is a manual process and I'd like to do it automatically every time the "list" page is on top of the stack and visible for the user.

Why "pop" events are not being caught by the observers when using navigateTo to navigate to a route that already exists in the stack? How to deal with this?

Thank you.

herna commented 11 months ago

Ok after further testing, I can see it's related to a Dialog.

I have a button to display a Dialog like this:

showDialog<bool>(
                context: context,
                builder: (BuildContext context) => AlertDialog(
                actions: <Widget>[
                  FilledButton(
                    onPressed: () async {
                      await context.popRoute();
                      // this is a Riverpod call to delete an item from the database
                      ref
                          .read(installationEditControllerProvider(
                                  widget.installationId)
                              .notifier)
                          .deleteInstallation(widget.installationId);
                    }),
                  child: Text('Delete'),
                ],
              ),
);

And in another part of the widget I detect the Riverpod response after the item is deleted to redirect to another page. At this point, if I just do a context.navigateTo(const InstallationsRoute()); it doesn't work but if I do a small pause like the following, then it works:

Future.delayed(const Duration(milliseconds: 200), () => context.navigateTo(const InstallationsRoute())); It seems related to the time it takes to close the Dialog. The problem is even if I wait like this await context.popRoute(); it doesn't work, I must put a small delay anyway.

Future<void> doInstallationDeleted(BuildContext context) async {
    await Future(() async {
      // IT WORKS
      // Future.delayed(const Duration(milliseconds: 200),
      //     () => context.navigateTo(const InstallationsRoute()));

      // IT DOESN'T WORK
      // context.navigateTo(const InstallationsRoute());
    });
  }

What's the correct way to deal with showDialog() and auto_route?

Thanks.

tomlezen commented 11 months ago

I also found this issue; this is because navigateTo is asynchronous !

herna commented 11 months ago

Hi @tomlezen

So how to deal with it? I realised that and tried to await context.navigateTo(const InstallationsRoute()); but nothing changes

Thanks.

tomlezen commented 11 months ago

Hi @tomlezen

So how to deal with it? I realised that and tried to await context.navigateTo(const InstallationsRoute()); but nothing changes

Thanks.

You can try to use:context.popForced

herna commented 11 months ago

Thanks @tomlezen

This seems to work for the Dialog. Anyways I am also facing some other issues when combining navigateTo() with didPopNext(), I was trying to just invalidate a Riverpod provider in the didPopNext() method to refresh some data but it's not always working the same way so I have to manually call ref.invalidate() before navigateTo() to ensure it works as expected.

Thanks again.

BenjiFarquhar commented 1 month ago

@Milad-Akarie is this something planned?

BenjiFarquhar commented 1 month ago

I am only wanting to change the url but also capture it as history. push causes initState to rerun even if i use a constant key. So i am using navigate, but the history is wiped.