如何自定义Flutter的SliverAppBar实现三星笔记滚动效果?
实现三星笔记风格顶部栏(SliverAppBar方案+替代方案)
一、SliverAppBar实现方案
1. 反向滚动效果实现
三星笔记的反向滚动指列表向下滚动时,顶部栏内部元素产生反向偏移的视觉效果。你可以通过ScrollController监听滚动偏移,在flexibleSpace中动态调整组件位置实现:
final ScrollController _scrollController = ScrollController(); double _offset = 0.0; @override void initState() { super.initState(); _scrollController.addListener(() { setState(() { // 限制偏移范围,避免过度移动 _offset = _scrollController.offset.clamp(0.0, 60.0); }); }); } @override void dispose() { _scrollController.dispose(); super.dispose(); }
在SliverAppBar的flexibleSpace中使用Transform实现反向位移:
SliverAppBar( expandedHeight: 120.0, pinned: true, flexibleSpace: FlexibleSpaceBar( background: Transform.translate( offset: Offset(0, -_offset * 0.5), // 反向滚动系数可自行调整 child: Container( padding: const EdgeInsets.only(left: 20, bottom: 20), alignment: Alignment.bottomLeft, child: const Text( 'All notes', style: TextStyle(fontSize: 16), ), ), ), title: AnimatedOpacity( opacity: _offset > 30 ? 1.0 : 0.0, // 滚动到指定偏移时显示大标题 duration: const Duration(milliseconds: 200), child: const Text( 'All notes', style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold), ), ), centerTitle: false, ), ),
2. 标题切换逻辑
上述代码已包含标题切换逻辑:通过滚动偏移_offset控制两个标题的透明度与位置。当滚动偏移超过30时,小字号标题随反向滚动移出视野,大字号标题平滑显示。你可以根据需求调整阈值、动画时长和反向滚动系数,让过渡更贴合三星笔记的效果。
二、非SliverAppBar替代方案
如果不想依赖SliverAppBar,可采用Stack + ListView的组合,手动监听滚动来控制顶部栏行为,自由度更高:
final ScrollController _scrollController = ScrollController(); double _offset = 0.0; @override void initState() { super.initState(); _scrollController.addListener(() { setState(() { _offset = _scrollController.offset.clamp(0.0, 80.0); }); }); } @override Widget build(BuildContext context) { return Scaffold( body: Stack( children: [ // 列表内容 ListView.builder( controller: _scrollController, padding: const EdgeInsets.only(top: 120), // 给顶部栏预留空间 itemCount: 50, itemBuilder: (context, index) => ListTile(title: Text('Note $index')), ), // 自定义顶部栏 Positioned( top: -_offset * 0.3, // 反向滚动效果 left: 0, right: 0, child: Container( height: 120 - _offset, // 随滚动收缩高度 color: Theme.of(context).scaffoldBackgroundColor, padding: const EdgeInsets.only(left: 20, bottom: 20), alignment: Alignment.bottomLeft, child: Stack( children: [ // 小字号标题,随滚动移出 Transform.translate( offset: Offset(0, _offset), child: const Text('All notes', style: TextStyle(fontSize: 16)), ), // 大字号标题,随滚动显示 Opacity( opacity: _offset / 80, // 0到1的平滑过渡 child: const Text('All notes', style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)), ), ], ), ), ), ], ), ); }
这种方案完全脱离Sliver组件的约束,适合需要自定义更复杂交互逻辑的场景。
内容的提问来源于stack exchange,提问作者arash shakibaee
相关产品推荐
相关产品推荐

