Flutter切换底部导航页如何保留状态及滚动位置避免重复请求数据
问题根因
- 你用到的
PageStorageKey仅用于存储页面的临时状态(比如滚动偏移量),并不能阻止页面State对象的销毁重建,所以切换Tab时子页面State重新初始化,initState自然会重复执行触发数据拉取 - 子页面
build方法中额外写了setState(() {}),会触发无意义的重绘,需要删除
解决方案1:用IndexedStack实现页面保活(最简单)
IndexedStack会同时初始化所有子页面,切换时仅控制显示隐藏,不会触发State重建,initState只会在首次加载时执行一次,适合Tab数量≤5的场景,仅需修改主页面代码即可:
class _AppHomePageState extends State<AppHomePage> { int _currentIndex = 0; onTap(index) => { setState(() => {_currentIndex = index}) }; @override Widget build(BuildContext context) { return Scaffold( /* appbar ... */ // 替换原来的 body: _navs.elementAt(_currentIndex) body: IndexedStack( index: _currentIndex, children: _navs, ), bottomNavigationBar: BottomNavigationBar( items: [ /* nav items */ ], currentIndex: _currentIndex, onTap: onTap, ), ); } }
解决方案2:子页面混入AutomaticKeepAliveClientMixin(按需保活)
如果不想一开始就加载全部三个页面的请求,可以用这个方案,页面第一次激活时才加载,之后切换不会销毁,适合Tab数量较多的场景:
需要给每个子页面的State类做如下修改:
// 混入 AutomaticKeepAliveClientMixin class _HotPostsRouteState extends State<HotPostsRoute> with AutomaticKeepAliveClientMixin { late PostInstance postInstance; List<Post> _posts = []; bool _loaded = false; // 重写该方法返回true,开启页面保活 @override bool get wantKeepAlive => true; fetchPosts(String? after) async { var stream = postInstance.front.hot(limit: 10, after: after); await for (UserContent post in stream) { Submission submission = post as Submission; Post pPost = Post(submission); pPost.parse().then((value) => setState(() { _posts.add(pPost); })); } setState(() { _loaded = true; }); } @override void initState() { super.initState(); if (mounted) { // 这里不需要套setState,直接赋值即可 postInstance = Provider.of<PostInstanceState>(context, listen: false) .getInstance; fetchPosts(""); } } // _buildPosts、_buildPost原有逻辑不变 @override Widget build(BuildContext context) { // 必须调用super.build,是混入的强制要求 super.build(context); // 删掉原来多余的setState(() {}) return Container( child: _loaded ? _buildPosts() : SpinKitDualRing(color: Colors.white), ); } }
内容的提问来源于stack exchange,提问作者SupremeDeity
相关产品推荐
相关产品推荐

