Flutter:阻止底部导航栏切换时重复重建页面类
嘿,我看你已经用了PageStorage但还是遇到页面重复重建的问题,其实PageStorage主要是用来保存页面的滚动位置这类状态,没法直接阻止页面Widget本身被重建。咱们来一步步解决这个问题:
问题根源
你当前的实现里,每次切换tab时都会通过setState把currentPage替换成pages[index],如果pages里的页面是每次调用都新建的(比如直接写pages = [HomePage(), SettingsPage()]),或者Stack在替换子Widget时会销毁旧的页面实例,这就导致每次切换都会重建页面。
最优解决方案:用IndexedStack管理页面
IndexedStack是Flutter专门用来保留多个页面状态的组件,它会把所有子页面都保存在内存里,只显示当前索引对应的页面,不会销毁其他页面,完美解决重建问题。
1. 预存页面实例
首先在你的State类里提前初始化好所有页面的实例,只初始化一次,不要每次切换都新建:
// 在你的State类中定义,只初始化一次 final List<Widget> _pages = [ HomePage(), ExplorePage(), ProfilePage(), ]; int _currentTab = 0;
2. 替换Stack为IndexedStack
修改build方法里的页面容器,把原来的Stack换成IndexedStack,不用再维护currentPage变量了:
@override Widget build(BuildContext context) { return SafeArea( top: false, child: Scaffold( body: PageStorage( bucket: bucket, // 用IndexedStack替代Stack,index对应当前选中的tab child: IndexedStack( index: _currentTab, children: _pages, ), ), // 直接把底部导航栏放在Scaffold的bottomNavigationBar里,更规范 bottomNavigationBar: _buildBottomBar(), ), ); }
3. 简化底部导航栏代码
原来的bottomBar用Column+Expanded来固定在底部,其实Scaffold的bottomNavigationBar已经帮你做了这个工作,直接返回BottomBarView就行:
Widget _buildBottomBar() { return BottomBarView( tabIconsList: tabIconsList, addClick: () {}, changeIndex: (int index) { setState(() { _currentTab = index; // 不用再更新currentPage,IndexedStack会自动切换显示 }); }, ); }
额外优化:保留滚动位置
如果你的页面有滚动控件(比如ListView),想要切换回来时保留之前的滚动位置,给每个页面添加唯一的PageStorageKey就行:
final List<Widget> _pages = [ HomePage(key: const PageStorageKey('home_page')), ExplorePage(key: const PageStorageKey('explore_page')), ProfilePage(key: const PageStorageKey('profile_page')), ];
这样修改后,切换底部导航栏时页面完全不会重建,状态也能完整保留,PageStorage也能正常发挥作用保存滚动位置啦!
内容的提问来源于stack exchange,提问作者Wail Hayaly

