0

I have two main widgets inside of stack, which placed inside of main route(screen):

  • PageView
  • BottomNavigation

Each page of pageView is wrapped inside of CustomNavigator widget, and list of keys for navigators are placed inside of main route(screen). Navigation by using bottom navigation bar works fine, but when pushing to another rote inside of page and going to another page and back, the state of page with pushed route become to default state, what can I do with it? Here is my main screen's state:

const String MainRoute = '/';
const String AccountRoute = '/account';
const String CategoriesRoute = '/categories';
const String HomeRoute = '/home';
const String WishListRoute = '/wish_list';
const String ProductRoute = '/product';
class _MainState extends State<Main> {

  int _pressedPosition = 0;

  final List _screenStates = [
    GlobalKey<NavigatorState>(),
    GlobalKey<NavigatorState>(),
    GlobalKey<NavigatorState>(),
    GlobalKey<NavigatorState>(),
    GlobalKey<NavigatorState>(), 
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        fit: StackFit.expand,
        children: <Widget>[
          PageView(
            physics:new NeverScrollableScrollPhysics(),
            controller: _pageController,
            children: _mainScreens(),
          ),
          Align(
            alignment: Alignment.bottomCenter,
            child: CurvedNavigationBar(
              animationDuration: Duration(milliseconds: 200),
              backgroundColor: Colors.transparent,
              onTap: (int position) {
                _bottomTapped(position);
              },
              height: 60.0,
              items: <Widget>[...],
            ),
          )
        ],
      ),
    );
  }

  PageController _pageController = PageController(
    initialPage: 0,
    keepPage: true,
  );

  void _bottomTapped(int index) {
    setState(() {
      _pressedPosition = index;
      _pageController.animateToPage(index,
          duration: Duration(milliseconds: 300), curve: Curves.ease);
    });
  }
  
 List<Widget> _mainScreens() {
  return <Widget>[
    CustomNavigator(initialRoute: HomeView(), navigatorKey: _screenStates[0],),
    CustomNavigator(initialRoute: CategoriesView(), navigatorKey: _screenStates[1],),
    CustomNavigator(initialRoute: ShoppingCartView(), navigatorKey: _screenStates[2],),
    CustomNavigator(initialRoute: WishListView(), navigatorKey: _screenStates[3],),
    CustomNavigator(initialRoute: AccountView(), navigatorKey: _screenStates[4],),
  ];
}

Here is Custom navigator class:

class CustomNavigator extends StatelessWidget {
  final Widget initialRoute;
  final GlobalKey<NavigatorState> navigatorKey;

  CustomNavigator({@required this.initialRoute, @required this.navigatorKey});

  @override
  Widget build(BuildContext context) {
    return Navigator(
      key: navigatorKey,
      onGenerateRoute: _routes(initialRoute),
    );
  }

  //routing
  RouteFactory _routes(Widget initialRoute) {
    return (settings) {
      final Map<String, dynamic> arguments = settings.arguments;
      Widget screen;
      switch (settings.name) {
        case MainRoute:
          screen = initialRoute;
          break;

        case ProductRoute:
          screen = ProductView();
          break;

        default:
          return null;
      }
      return MaterialPageRoute(builder: (BuildContext context) => screen);
    };
  }
}

I already read https://medium.com/flutter/getting-to-the-bottom-of-navigation-in-flutter-b3e440b9386, https://medium.com/@Mr_Pepe/nested-navigation-with-a-bottom-navigation-bar-using-flutter-d3c5086fbcdc and http://bizz84.github.io/2018/07/07/Multiple-Navigators-BottomNavigationBar.html but anyway these topics are too difficult to understand.

In addition found this solution, but it doesn't work: https://github.com/indatawetrust/nested-navigation-demo-flutter

HELP! Thanks))

Atamyrat Babayev
  • 778
  • 1
  • 10
  • 23

1 Answers1

2

PageView unloads tabs that aren't visible to save memory, that's why your state is getting lost. But you can force them to stay around by using the KeepAlive machinery. See AutomaticKeepAliveClientMixin for the simplest way to do that. That should work with the PageView used with tabs.You can use IndexedStack instead of PageView because it doesn't unload the widgets, it just displays the current active widget on top of others. Each Widget in IndexedStack returns its own Navigator like you are doing currently.So you can do something like this

IndexedStack(
      index: _pressedPosition,
      children: _mainScreens(), //return Views Here
    ),
  )

and then on click

setState(() {
      _pressedPosition = index;
    });

You can use BottomNavigationBar which is a part of Scaffold widget and made for just this purpose. Check this link for reference (the navigation part).

  • Thank you so much, I used AutomaticKeepAliveClientMixin inside of Custom navigator – Atamyrat Babayev Aug 12 '20 at 09:24
  • Could you say anything about optimization? What is the best way? – Atamyrat Babayev Aug 12 '20 at 09:36
  • I would say, it depends on the number of widgets you are going to keep in PageView. Though AutomaticKeepAliveClientMixin is expensive, it won't matter much for your use case. With IndexedStack also, you are keeping your Widgets in the memory, so that is the intended behavior. Check out this discussion for more insight. https://github.com/flutter/flutter/issues/21023 – Subhendu Pratap Singh Aug 12 '20 at 10:55
  • Another problem with IndexedStack is that it builds the unseen children anyway. So if you have an api call or something expensive on one of those children, you will be losing performance. I've dealt with this in this solution: https://stackoverflow.com/questions/49439047/how-to-preserve-widget-states-in-flutter-when-navigating-using-bottomnavigation – filipetrm Feb 28 '21 at 00:43