Flutter自定义布局与动画实现可折叠AppBar问题求助
解决可折叠SliverAppBar的自定义布局与动画问题
核心思路
放弃FlexibleSpaceBar的默认title逻辑,通过监听滚动偏移计算折叠进度,自定义flexibleSpace内的布局与动画,实现图片渐隐、文本样式过渡的效果。
实现代码
import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:cached_network_image/cached_network_image.dart'; class CollapsibleAppBarPage extends StatefulWidget { final YourModel _model; // 替换成你的数据模型类 const CollapsibleAppBarPage(this._model, {super.key}); @override State<CollapsibleAppBarPage> createState() => _CollapsibleAppBarPageState(); } class _CollapsibleAppBarPageState extends State<CollapsibleAppBarPage> { final ScrollController _scrollController = ScrollController(); double _collapseProgress = 0.0; final double _expandedHeight = Get.height * 0.2; final double _toolbarHeight = kToolbarHeight; @override void initState() { super.initState(); _scrollController.addListener(_updateCollapseProgress); } void _updateCollapseProgress() { // 计算折叠进度:0=完全展开,1=完全折叠 final scrollOffset = _scrollController.offset; final maxScroll = _expandedHeight - _toolbarHeight; setState(() { _collapseProgress = (scrollOffset / maxScroll).clamp(0.0, 1.0); }); } @override void dispose() { _scrollController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( body: CustomScrollView( controller: _scrollController, slivers: [ SliverAppBar( pinned: true, leading: GestureDetector( onTap: () => Get.back(), child: const Icon(Icons.arrow_back, color: Colors.black), ), expandedHeight: _expandedHeight, backgroundColor: const Color(0xFFFFF4F4), // 自定义flexibleSpace,替代FlexibleSpaceBar的默认布局 flexibleSpace: FlexibleSpaceBar( background: Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), child: Column( mainAxisAlignment: MainAxisAlignment.end, children: [ Row( children: [ // 可动画过渡的文本区域 Expanded( child: AnimatedDefaultTextStyle( duration: const Duration(milliseconds: 200), style: TextStyle( color: Colors.black, fontSize: 14 - (4 * _collapseProgress), // 从14过渡到10 height: 1.2, ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Buffet Deal"), Opacity( opacity: 1 - _collapseProgress, // 副标题随折叠渐隐 child: Text("Flash Deal", style: TextStyle(fontSize: 10)), ), ], ), ), ), // 渐隐的图片 AnimatedOpacity( opacity: 1 - _collapseProgress, duration: const Duration(milliseconds: 200), child: widget._model.imgUrl != null ? CachedNetworkImage( imageUrl: widget._model.imgUrl!, width: 80, // 固定图片宽度,可按需调整 height: 60, fit: BoxFit.cover, ) : const SizedBox.shrink(), ), const SizedBox(width: 18), ], ), const SizedBox(height: 16), // 底部内边距,避免内容贴底 ], ), ), // 折叠后的标题,随进度渐显 title: Opacity( opacity: _collapseProgress, child: const Text( "Buffet Deal", style: TextStyle(color: Colors.black, fontSize: 16), ), ), centerTitle: true, ), ), // 页面内容占位 SliverToBoxAdapter( child: Container( height: 1000, color: Colors.white, ), ), ], ), ); } } // 替换成你的实际数据模型 class YourModel { final String? imgUrl; YourModel({this.imgUrl}); }
关键细节说明
- 折叠进度计算:通过滚动偏移与
expandedHeight - kToolbarHeight的比值,得到0-1之间的进度值,统一控制所有动画参数。 - 文本过渡:使用
AnimatedDefaultTextStyle实现字体大小平滑变化,副标题通过Opacity随折叠进度渐隐;折叠后的独立标题通过Opacity渐显,保证折叠后标题样式符合AppBar常规逻辑。 - 图片渐隐:通过
AnimatedOpacity绑定折叠进度,实现图片从完全显示到隐藏的平滑过渡。 - 布局对齐:展开时文本左对齐,折叠后标题居中,兼顾展开时的布局需求和折叠后的交互习惯。
内容的提问来源于stack exchange,提问作者Fardeen Khan
相关产品推荐
相关产品推荐

