如何根据NestedScrollView滚动动态显示/隐藏SliverAppBar?
问题解决:根据NestedScrollView内部滚动状态动态显隐SliverAppBar
问题核心分析
你当前代码存在三个关键问题导致无法实现预期效果:
- 滚动控制器冲突:NestedScrollView与内部ListView共用同一个
ScrollController,破坏了NestedScrollView的内部滚动协调机制,导致innerBoxIsScrolled状态异常。 - 控制器被覆盖:在
MainScreen的initState中重新赋值widget.controller,直接覆盖了外部传入的控制器,导致滚动状态监听完全混乱。 - 显隐逻辑未绑定:SliverAppBar仅设置了
floating: true,未关联滚动状态驱动的显隐逻辑。
解决方案与修改后代码
步骤1:新增状态Provider管理AppBar可见性
final appBarVisibleProvider = StateProvider<bool>((ref) => true);
步骤2:修改MainScreen,移除共用控制器并监听滚动通知
class MainScreen extends ConsumerStatefulWidget { const MainScreen({super.key}); @override ConsumerState<ConsumerStatefulWidget> createState() => _MainScreenState(); } class _MainScreenState extends ConsumerState<MainScreen> { @override Widget build(BuildContext context) { final poketmonPageIndex = ref.watch(poketmonPaginationProvider); final poketmonsProviderRef = ref.watch(poketmonsProvider(poketmonPageIndex)); // 通过滚动通知监听滚动方向与分页触发 return NotificationListener<ScrollUpdateNotification>( onNotification: (notification) { final scrollDirection = notification.metrics.userScrollDirection; final appBarNotifier = ref.read(appBarVisibleProvider.notifier); // 向下滚动隐藏AppBar,向上滚动显示 if (scrollDirection == ScrollDirection.reverse) { appBarNotifier.state = false; } else if (scrollDirection == ScrollDirection.forward) { appBarNotifier.state = true; } // 分页加载逻辑 if (notification.metrics.offset >= notification.metrics.maxScrollExtent && !notification.metrics.outOfRange) { ref.read(poketmonPaginationProvider.notifier).state++; } return true; }, child: ListView.builder( itemBuilder: (context, index) { return PoketmonTile(poketmon: poketmonsProviderRef[index]); }, itemCount: poketmonsProviderRef.length, ), ); } }
步骤3:修改MyApp,实现AppBar平滑显隐
class MyApp extends ConsumerWidget { const MyApp({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final isAppBarVisible = ref.watch(appBarVisibleProvider); return MaterialApp( debugShowCheckedModeBanner: false, theme: ThemeData( fontFamily: 'NotoSansKR', brightness: Brightness.light, primaryColor: Colors.white, appBarTheme: const AppBarTheme( backgroundColor: Colors.white, titleTextStyle: TextStyle( color: Colors.black, fontSize: 16, fontWeight: FontWeight.w500, ), ), ), home: Scaffold( body: NestedScrollView( floatHeaderSlivers: true, headerSliverBuilder: (context, innerBoxIsScrolled) { return [ // 用动画实现平滑显隐 SliverAnimatedOpacity( opacity: isAppBarVisible ? 1.0 : 0.0, duration: const Duration(milliseconds: 200), sliver: const SliverAppBar( title: Text("HELLOWORLD"), backgroundColor: Colors.transparent, floating: true, elevation: 0, toolbarHeight: 56, ), ), ]; }, body: const MainScreen(), ), ), ); } }
关键修改说明
- 移除共用控制器:NestedScrollView和内部ListView不再共享控制器,避免滚动状态冲突。
- 滚动通知监听:通过
NotificationListener捕获内部滚动事件,替代原有的Controller监听,更符合NestedScrollView的设计逻辑。 - 动画显隐:使用
SliverAnimatedOpacity实现AppBar的平滑过渡,提升交互体验。 - 状态解耦:通过Riverpod的
StateProvider管理AppBar可见性,实现UI与业务逻辑的分离。
内容的提问来源于stack exchange,提问作者하욱현
相关产品推荐
相关产品推荐

