Flutter实现折叠状态下Sliver App Bar的颜色切换
实现带轮播的渐变切换SliverAppBar
核心实现思路
- 用
StatefulWidget监听滚动状态,动态控制背景样式与轮播的显示/隐藏 - 不在
SliverAppBar的title中嵌套AppBar,直接通过leading、actions实现logo和操作项,避免出现1px分割线 - 利用
ScrollController获取滚动偏移,计算SliverAppBar的展开比例,以此切换渐变/纯色背景 - 轮播组件放在
flexibleSpace区域,随滚动收起自动隐藏
修改后的完整代码
import 'package:carousel_slider/carousel_slider.dart'; import 'package:flutter/material.dart'; class HomePage extends StatefulWidget { const HomePage({super.key}); @override State<HomePage> createState() => _HomePageState(); } class _HomePageState extends State<HomePage> { final ScrollController _scrollController = ScrollController(); double _expansionRatio = 1.0; final double _toolbarHeight = kToolbarHeight; final double _carouselHeight = 112; final double _topPadding = 38; @override void initState() { super.initState(); _scrollController.addListener(_onScroll); } void _onScroll() { final maxScroll = _scrollController.position.maxScrollExtent; final currentScroll = _scrollController.position.pixels; setState(() { _expansionRatio = 1.0 - (currentScroll / maxScroll).clamp(0.0, 1.0); }); } @override void dispose() { _scrollController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final expandedHeight = _toolbarHeight + _topPadding + _carouselHeight; return Scaffold( backgroundColor: Colors.white, body: CustomScrollView( controller: _scrollController, slivers: [ SliverAppBar( leading: const Icon(Icons.menu, color: Colors.white), // 替换为你的Logo actions: const [ Padding( padding: EdgeInsets.only(right: 16), child: Icon(Icons.search, color: Colors.white), // 操作项示例 ) ], title: const Text("Sliver App Bar Demo", style: TextStyle(color: Colors.white)), titleSpacing: 0, centerTitle: false, pinned: true, floating: false, expandedHeight: expandedHeight, elevation: 0, // 动态切换背景:展开时渐变,折叠时纯色 backgroundColor: _expansionRatio > 0.01 ? Colors.transparent : Colors.blue, surfaceTintColor: Colors.transparent, // 滚动时隐藏阴影 shadowColor: _expansionRatio > 0.01 ? Colors.transparent : null, flexibleSpace: FlexibleSpaceBar( background: Container( decoration: BoxDecoration( gradient: _expansionRatio > 0.01 ? const LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Colors.blue, Colors.white], stops: [0, 0.65], ) : null, color: _expansionRatio <= 0.01 ? Colors.blue : null, ), child: Column( children: [ SizedBox(height: _toolbarHeight + _topPadding), // 根据展开比例控制轮播的显示与隐藏 if (_expansionRatio > 0.01) CarouselSlider( items: [ Container( decoration: BoxDecoration( border: Border.all(color: Colors.red), ), height: _carouselHeight, child: const Center(child: Text('Item 1')), ), Container( decoration: BoxDecoration( border: Border.all(color: Colors.red), ), height: _carouselHeight, child: const Center(child: Text('Item 2')), ), Container( decoration: BoxDecoration( border: Border.all(color: Colors.red), ), height: _carouselHeight, child: const Center(child: Text('Item 3')), ), ], options: CarouselOptions( height: _carouselHeight, autoPlay: true, enlargeCenterPage: true, viewportFraction: 1.0, ), ), ], ), ), ), ), SliverList( delegate: SliverChildBuilderDelegate( (context, index) => ListTile(title: Text('Item #$index')), childCount: 20, ), ), ], ), ); } }
关键细节说明
- 滚动状态监听:通过
ScrollController监听滚动偏移,计算_expansionRatio(0=完全折叠,1=完全展开),以此作为UI切换的依据 - 背景平滑切换:利用
_expansionRatio判断,展开时显示渐变背景,折叠时切换为纯色背景,同时通过shadowColor控制阴影的显示/隐藏,避免出现1px分割线 - 轮播组件控制:当
_expansionRatio趋近于0时,隐藏轮播组件,实现滚动收起的效果 - 顶部操作栏优化:直接在
SliverAppBar上配置leading、title、actions,替代嵌套AppBar的方式,彻底解决1px线的问题
内容的提问来源于stack exchange,提问作者ayush
相关产品推荐
相关产品推荐

