Flutter实现SliverAppBar展开时FlexibleSpace带底部圆角、折叠时无圆角
可切换圆角状态的SliverAppBar实现方案
核心逻辑:不要直接给SliverAppBar外层包裹固定圆角装饰,否则折叠后圆角会永久保留,不符合需求,正确做法是监听滚动进度,动态调整背景容器的圆角半径。
实现步骤
- 给关联的
CustomScrollView绑定ScrollController,实时获取滚动偏移量 - 计算AppBar的可折叠区间:最大可滚动偏移量为
expandedHeight - 状态栏高度 - AppBar固定高度,超出这个区间说明AppBar已经完全折叠为固定状态 - 根据折叠进度计算当前圆角半径:完全展开时为目标圆角值,完全折叠时为0,中间状态按比例平滑过渡
- 带圆角的装饰仅作用于
FlexibleSpaceBar的background容器,不要覆盖到折叠后的固定AppBar区域
完整可运行代码
class RoundedSliverAppBarPage extends StatefulWidget { const RoundedSliverAppBarPage({super.key}); @override State<RoundedSliverAppBarPage> createState() => _RoundedSliverAppBarPageState(); } class _RoundedSliverAppBarPageState extends State<RoundedSliverAppBarPage> { late final ScrollController _scrollController; // 和SliverAppBar配置保持一致的参数 final double _expandedHeight = 250; final double _maxCornerRadius = 20; // 动态变化的圆角值 double _currentRadius = 20; @override void initState() { super.initState(); _scrollController = ScrollController(); _scrollController.addListener(_calcCornerRadius); } @override void dispose() { _scrollController.removeListener(_calcCornerRadius); _scrollController.dispose(); super.dispose(); } void _calcCornerRadius() { // 获取状态栏高度,避免计算偏移量偏差 final double statusBarHeight = MediaQuery.of(context).padding.top; // 可折叠的最大滚动距离:滚到这个值时AppBar完全折叠为固定状态 final double maxFoldOffset = _expandedHeight - statusBarHeight - kToolbarHeight; // 限制偏移量在合法区间,避免顶部越界下拉导致的负值 final double clampedOffset = _scrollController.offset.clamp(0.0, maxFoldOffset); // 计算折叠进度比例 final double foldProgress = clampedOffset / maxFoldOffset; setState(() { _currentRadius = _maxCornerRadius * (1 - foldProgress); }); } @override Widget build(BuildContext context) { return Scaffold( body: CustomScrollView( controller: _scrollController, slivers: [ SliverAppBar( leading: const BackButton(color: Colors.black), pinned: true, snap: false, floating: false, expandedHeight: _expandedHeight, backgroundColor: const Color(0xffEBEBEB), flexibleSpace: FlexibleSpaceBar( centerTitle: true, title: const Text( "Collapsing Toolbar", style: TextStyle( color: Colors.red, fontSize: 16.0, ), ), // 动态圆角仅作用于背景容器 background: Container( decoration: BoxDecoration( color: const Color(0xffEBEBEB), borderRadius: BorderRadius.only( bottomLeft: Radius.circular(_currentRadius), bottomRight: Radius.circular(_currentRadius), ), ), child: const FlutterLogo(), ), ), ), // 替换为实际业务列表内容即可 SliverList( delegate: SliverChildBuilderDelegate( (context, index) => ListTile(title: Text("列表项 $index")), childCount: 30, ), ), ], ), ); } }
适配说明
- 如果不需要下拉越界的弹性效果,可以把
CustomScrollView的physics设置为ClampingScrollPhysics - 圆角值可以根据设计需求调整
_maxCornerRadius的数值即可 - 如果需要圆角过渡更丝滑,可以把包裹背景的
Container替换为AnimatedContainer,设置较短的duration(比如100毫秒)
内容的提问来源于stack exchange,提问作者Abu Taha
相关产品推荐
相关产品推荐

