如何让ShellRoute子路由滚动时控制SliverAppBar的显示/隐藏/浮动?
解决Go Router ShellRoute中SliverAppBar随子路由滚动联动的问题
问题原因
当前你的DashboardScreen中,NestedScrollView直接将子路由(widget.child)作为body,但子路由的滚动控件是独立的,NestedScrollView无法感知其滚动状态,导致SliverAppBar的浮动、隐藏效果无法触发。
解决方案
要实现联动,需要让子路由的滚动内容与NestedScrollView的滚动系统关联,具体分两步:
1. 修改DashboardScreen的NestedScrollView配置
在headerSliverBuilder中添加SliverOverlapAbsorber,用于处理SliverAppBar的重叠区域,确保滚动状态能被正确传递:
DefaultTabController( length: 6, child: Scaffold( bottomNavigationBar: bottomMenuBuilder(context), padding: EdgeInsets.zero, body: NestedScrollView( floatHeaderSlivers: true, headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) { return <Widget>[ SliverOverlapAbsorber( // 关联NestedScrollView的handle handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context), sliver: const SliverAppBar( title: Text("floating appbar"), centerTitle: false, automaticallyImplyLeading: false, floating: true, // 如果需要收缩隐藏效果,添加以下配置 // pinned: true, // expandedHeight: 200, // flexibleSpace: FlexibleSpaceBar(background: FlutterLogo()), actions: [ Icon(Icons.search, size: 30, color: Colors.white), ], elevation: 0.0, ), ), ]; }, body: widget.child, ), ), )
2. 改造子路由页面的滚动结构
子路由页面需要使用CustomScrollView包裹内容,并添加SliverOverlapInjector来接收DashboardScreen传递的滚动状态:
方式一:使用Sliver组件(推荐)
直接用SliverList/SliverGrid构建滚动内容:
class ChildRoutePage extends StatelessWidget { const ChildRoutePage({super.key}); @override Widget build(BuildContext context) { return CustomScrollView( slivers: [ // 注入重叠处理,关联父级NestedScrollView SliverOverlapInjector( handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context), ), // 滚动内容 SliverList( delegate: SliverChildBuilderDelegate( (context, index) => ListTile(title: Text("列表项 $index")), childCount: 50, ), ), ], ); } }
方式二:适配普通ListView
如果要保留原有的ListView,需要将其放入SliverToBoxAdapter,并禁用自身滚动:
class ChildRoutePage extends StatelessWidget { const ChildRoutePage({super.key}); @override Widget build(BuildContext context) { return CustomScrollView( slivers: [ SliverOverlapInjector( handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context), ), SliverToBoxAdapter( child: ListView.builder( shrinkWrap: true, // 禁止ListView自身滚动,交由CustomScrollView处理 physics: const NeverScrollableScrollPhysics(), itemCount: 50, itemBuilder: (context, index) => ListTile(title: Text("列表项 $index")), ), ), ], ); } }
效果说明
- 开启
floating: true:子路由向下滚动时,AppBar会自动隐藏;向上滚动时,AppBar会立即浮动显示。 - 配合
pinned: true和expandedHeight:AppBar会在滚动时收缩至仅保留标题栏,向上滚动时展开恢复。
内容的提问来源于stack exchange,提问作者RedsVision
相关产品推荐
相关产品推荐

