使用底部导航栏颤动保存 PageView 内每个页面的状态

Posted

技术标签:

【中文标题】使用底部导航栏颤动保存 PageView 内每个页面的状态【英文标题】:Save state of each page inside of PageView with Bottom Navigation bar flutter 【发布时间】:2020-12-01 20:47:57 【问题描述】:

我在堆栈内有两个主要小部件,它们放置在主路由(屏幕)内:

页面浏览量 底部导航

pageView 的每一页都包裹在 CustomNavigator 小部件内,导航器的键列表放置在主路由(屏幕)内。使用底部导航栏导航可以正常工作,但是当推送到页面内的另一个死记硬背并转到另一个页面并返回时,推送路由的页面状态变为默认状态,我该怎么办?这是我的主屏幕状态:

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],),
  ];

这是自定义导航器类:

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);
    ;
  

我已经阅读了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 和http://bizz84.github.io/2018/07/07/Multiple-Navigators-BottomNavigationBar.html,但无论如何这些主题都太难理解了。

另外找到了这个解决方案,但是不行:https://github.com/indatawetrust/nested-navigation-demo-flutter

帮助!谢谢))

【问题讨论】:

【参考方案1】:

PageView 卸载不可见的选项卡以节省内存,这就是您的状态丢失的原因。但是你可以通过使用KeepAlive 机器来强迫他们留下来。请参阅AutomaticKeepAliveClientMixin 了解最简单的方法。这应该适用于与标签一起使用的 PageView。您可以使用 IndexedStack 而不是 PageView,因为它不会卸载小部件,它只会在其他小部件之上显示当前活动的小部件。 IndexedStack 中的每个 Widget 都返回自己的 Navigator,就像您目前正在做的那样。所以您可以这样做

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

然后点击

setState(() 
      _pressedPosition = index;
    );

您可以使用BottomNavigationBar,它是 Scaffold 小部件的一部分,专门为此目的而设计。检查此link 以供参考(导航部分)。

【讨论】:

非常感谢,我在自定义导航器中使用了 AutomaticKeepAliveClientMixin 您能谈谈优化吗?最好的方法是什么? 我会说,这取决于您要保留在 PageView 中的小部件的数量。尽管 AutomaticKeepAliveClientMixin 很昂贵,但对您的用例来说并不重要。使用 IndexedStack,您也可以将小部件保存在内存中,这就是预期的行为。查看此讨论以获取更多信息。 github.com/flutter/flutter/issues/21023 IndexedStack 的另一个问题是它无论如何都会构建看不见的孩子。因此,如果您对其中一个孩子进行 api 调用或昂贵的东西,您将失去性能。我已经在这个解决方案中处理了这个问题:***.com/questions/49439047/…

以上是关于使用底部导航栏颤动保存 PageView 内每个页面的状态的主要内容,如果未能解决你的问题,请参考以下文章

使用底部导航栏导航回页面时颤动通过底部导航栏选择的索引

使用底部应用栏标签导航 - 颤动

如何使用颤动的底部导航栏导航到几个新页面?

使用底部导航时删除应用栏的后退按钮 - 颤动

如何将底部导航栏添加到此颤动代码

移动到其他页面时无法保持底部导航栏颤动