auto_route_library icon indicating copy to clipboard operation
auto_route_library copied to clipboard

How do I go back to the previous tab in the BottomNavigationBar?

Open moxajuine opened this issue 3 years ago • 11 comments

I used the example (https://autoroute.vercel.app/advanced/bottom_navigation_bar_routing) and it works great except for one detail: if I go from the first tab to the second, and then press the "Back" button, then I exit the application, instead of going back to the previous tab. As far as I know, this behavior is not recommended and all popular applications return to the previous tab in such scenarios. How can I achieve this behavior so that when the back button is clicked, it will return to the previous tab in the bottom nav bar?

moxajuine avatar Nov 15 '21 04:11 moxajuine

@moxajuine if you just want to exit the App if you're on HOME_TAB all you have to do is set homeIndex: MY_HOME_TAB_INDEX in AutoTabsRouter or AutoTabsScaffold

Milad-Akarie avatar Nov 16 '21 07:11 Milad-Akarie

@Milad-Akarie No, I do not want to exit the application. I am using AutoTabsScaffold and I have a bottom navigation bar with two tabs - the first tab and the second tab. When I switch from the first tab to the second, and then press the back button (being on the second tab) - I do not get back to the first tab. How can I make it return to the first tab?

moxajuine avatar Nov 16 '21 08:11 moxajuine

@moxajuine alright but what happens if you're already on the first tab and you hit the backbutton?

Milad-Akarie avatar Nov 16 '21 08:11 Milad-Akarie

@Milad-Akarie If I am on the first tab and press the back button, the application exits, as it should be. But the exit from the application occurs not only on the first tab, on the second tab the application will also exit. If you go to any popular application - for example youtube, go from the first tab to any other, and then press the back button, you will return to the previous tab instead of exiting the application. I would like to achieve the same behavior.

moxajuine avatar Nov 16 '21 08:11 moxajuine

@moxajuine your goal is possible using navigation history, navigation history on mobile keeps the last 20 records ( hard coded for now ) a simple implementation would be

WillPopScope(
            onWillPop: () {
              final router = context.router;
              if (router.canNavigateBack) {
                router.navigateBack();
              }
              // if can't navigate back go with normal behavior
              return SynchronousFuture(!router.canNavigateBack);
            },
            child: AutoTabsScaffold(...)
);

I just implemented this logic recently, and I don't think it's prefect yet, so if you spend some time on this and come up with good algorithm on when to reset or remove some history entires or if you need any simple Api changes/improvements let me know.

Milad-Akarie avatar Nov 16 '21 09:11 Milad-Akarie

@Milad-Akarie

Thanks for the sample code.

I created a simple example with three tabs: first, second, third.

If, after launching the application, go to the second tab, and then to the third tab, then the first press of the button back will return us from the third tab to the second (as we expect), but the second press of the button back will exit the application instead of returning us to the first tab.

However, I found that if, after launching the application, we first go to the FirstDetailsPage page inside the first tab, and only then go to the second tab, and then to the third, then if we press the back button twice, we will return to the first tab. But now we will face another problem: returning to the first tab after having pressed the back button twice, we will see the FirstDetailsPage open in front of us (and this is correct, because we left it open before moving to other tabs), and if we press the back button, then FirstDetailsPage will close as it should, if we then press the back button again, nothing will happen, but if we press the back button a second time, then FirstDetailsPage will open again.

Perhaps I am doing something wrong or do not understand it well, so I would be grateful if you would look at my example. For the sake of simplicity, I've added everything to one file main.dart:

import 'package:auto_route/auto_route.dart';
import 'package:auto_route_example/routes.gr.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(App());
}

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

  final _appRouter = AppRouter();

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      routeInformationParser: _appRouter.defaultRouteParser(),
      routerDelegate: _appRouter.delegate(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () {
        final router = context.router;
        if (router.canNavigateBack) {
          router.navigateBack();
        }
        return SynchronousFuture(!router.canNavigateBack);
      },
      child: AutoTabsScaffold(
        routes: const [
          FirstRouter(),
          SecondRouter(),
          ThirdRouter(),
        ],
        bottomNavigationBuilder: (_, tabsRouter) {
          return BottomNavigationBar(
            currentIndex: tabsRouter.activeIndex,
            onTap: tabsRouter.setActiveIndex,
            items: const [
              BottomNavigationBarItem(icon: Icon(Icons.dynamic_feed), label: 'First'),
              BottomNavigationBarItem(icon: Icon(Icons.family_restroom), label: 'Second'),
              BottomNavigationBarItem(icon: Icon(Icons.face), label: 'Third'),
            ],
          );
        },
      ),
    );
  }
}

// First Page

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text("First Page", style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
            ElevatedButton(child: const Text("Go to First Details"), onPressed: () => context.pushRoute(FirstDetailsRoute(id: "some id"))),
          ],
        ),
      ),
    );
  }
}

class FirstDetailsPage extends StatelessWidget {
  const FirstDetailsPage({Key? key, @PathParam("id") this.id = ""}) : super(key: key);

  final String id;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text("First Details Page, id = $id", style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
          ],
        ),
      ),
    );
  }
}

// Second page

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text("Second Page", style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
            ElevatedButton(child: const Text("Go to Second Details"), onPressed: () => context.pushRoute(SecondDetailsRoute(id: "some id"))),
          ],
        ),
      ),
    );
  }
}

class SecondDetailsPage extends StatelessWidget {
  const SecondDetailsPage({Key? key, @PathParam("id") this.id = ""}) : super(key: key);

  final String id;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text("Second Details Page, id = $id", style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
            ElevatedButton(child: const Text("Go to Second Details Info"), onPressed: () => context.pushRoute(const SecondDetailsInfoRoute())),
          ],
        ),
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: const [
            Text("Second Details Info Page", style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
          ],
        ),
      ),
    );
  }
}

// Third Page

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text("Third Page", style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
            ElevatedButton(child: const Text("Go to Third Details"), onPressed: () => context.pushRoute(ThirdDetailsRoute(id: "some id"))),
          ],
        ),
      ),
    );
  }
}

class ThirdDetailsPage extends StatelessWidget {
  const ThirdDetailsPage({Key? key, @PathParam("id") this.id = ""}) : super(key: key);

  final String id;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text("Third Details Page, id = $id", style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
          ],
        ),
      ),
    );
  }
}

And routes.dart:

@MaterialAutoRouter(
  replaceInRouteName: 'Page,Route',
  routes: <AutoRoute>[
    AutoRoute(
      path: "/",
      name: "HomeRouter",
      page: HomePage,
      children: [
        AutoRoute(
          path: "first",
          name: "FirstRouter",
          page: EmptyRouterPage,
          children: [
            AutoRoute(path: '', page: FirstPage),
            AutoRoute(path: ':id', page: FirstDetailsPage),
          ],
        ),
        AutoRoute(
          path: "second",
          name: "SecondRouter",
          page: EmptyRouterPage,
          children: [
            AutoRoute(path: '', page: SecondPage),
            AutoRoute(path: ':id', page: SecondDetailsPage),
            AutoRoute(path: 'info', page: SecondDetailsInfoPage),
          ],
        ),
        AutoRoute(
          path: "third",
          name: "ThirdRouter",
          page: EmptyRouterPage,
          children: [
            AutoRoute(path: '', page: ThirdPage),
            AutoRoute(path: ':id', page: ThirdDetailsPage),
          ],
        ),
      ],
    ),
  ],
)
class $AppRouter {}

moxajuine avatar Nov 18 '21 08:11 moxajuine

@Milad-Akarie : Any update on this? We have the same issue in our project. The route definition is not the same, but the structure is almost the same. After a little investigation here is what we have in the navigation stack after pushing a detail page(SpaceBrowserRoute) from a StackRouter under the TabRouter (RootNavigationRoute)

image

Sounds weird that we have the HomeRoute in the last position after pushing the Detail Page.

wer-mathurin avatar Jan 25 '22 03:01 wer-mathurin

If the nesting after the transition is more than 2 routes, NavigationBack does not return to the previous tab.

I also note that sometimes ~ 1 out of 10 works ... it looks like a bug

Screen Shot 2022-04-10 at 01 33 50

bronjham-code avatar Apr 09 '22 22:04 bronjham-code

I've also encountered weird behavior when navigating into a child route of a root route of the TabsRouter. Then occasionally when the navigationhistory has length 1 and I expect the app to close, instead it returns to a previously opened tab and it seems the history is reverted to an older state. For example one time the history entries were of length 3 after it was of length 1 before using navigateBack.

weidenbach avatar May 17 '22 09:05 weidenbach

Im also facing this issue and i solve this by doing some changes in this code

WillPopScope(
            onWillPop: () {
              final router = context.router;
              if (!router.canNavigateBack) {
                router.navigateBack();
              }
              // if can't navigate back go with normal behavior
              return SynchronousFuture(router.canNavigateBack);
            },
            child: AutoTabsScaffold(...)
);

just inverse not "!" condition

mehul425 avatar Jun 10 '22 04:06 mehul425

@moxajuine same behavior here. Did you find a solution for this? Thanks

manueltruepill avatar Jun 25 '22 03:06 manueltruepill

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

github-actions[bot] avatar Aug 29 '22 08:08 github-actions[bot]

Still nothing?

minhnguyenandpad avatar Dec 02 '22 15:12 minhnguyenandpad