如何在expandedHeight为0时实现SliverAppBar标题从左到居中的滚动动画
实现方案说明
你要的效果不需要自定义AnimatedWidget,也不需要设置非0的expandedHeight,核心逻辑是通过滚动监听获取SliverAppBar的折叠进度,用系统自带的插值方法计算标题的对齐位置,即可实现平滑的移动过渡效果。
完整基础实现示例
import 'package:flutter/material.dart'; class SliverTitleMoveDemo extends StatefulWidget { const SliverTitleMoveDemo({super.key}); @override State<SliverTitleMoveDemo> createState() => _SliverTitleMoveDemoState(); } class _SliverTitleMoveDemoState extends State<SliverTitleMoveDemo> { final ScrollController _scrollController = ScrollController(); // 滚动触发标题移动的阈值,和AppBar高度保持一致即可 final double _scrollThreshold = kToolbarHeight; @override void initState() { super.initState(); _scrollController.addListener(() { // 滚动时触发刷新 setState(() {}); }); } @override void dispose() { _scrollController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { // 计算滚动进度:0为完全未滚动,1为滚动到阈值位置 double progress = (_scrollController.offset / _scrollThreshold).clamp(0, 1); // 插值计算当前标题的对齐位置 Alignment titleAlignment = Alignment.lerp(Alignment.centerLeft, Alignment.center, progress)!; return Scaffold( body: CustomScrollView( controller: _scrollController, slivers: [ SliverAppBar( pinned: true, // expandedHeight和toolbarHeight保持一致,无需设置非0的额外高度 expandedHeight: kToolbarHeight, toolbarHeight: kToolbarHeight, flexibleSpace: FlexibleSpaceBar( titlePadding: const EdgeInsets.symmetric(horizontal: 16), title: Align( alignment: titleAlignment, child: const Text("测试标题"), ), ), ), SliverList( delegate: SliverChildBuilderDelegate( (context, index) => ListTile(title: Text("列表项 $index")), childCount: 50, ), ) ], ), ); } }
性能优化方案
如果不想每次滚动都调用setState刷新整个页面,可以用ValueNotifier做局部刷新,性能开销更低:
// 替换进度变量定义 final ValueNotifier<double> _progress = ValueNotifier(0.0); @override void initState() { super.initState(); _scrollController.addListener(() { // 只更新进度值,不触发全页面刷新 _progress.value = (_scrollController.offset / _scrollThreshold).clamp(0, 1); }); } // FlexibleSpaceBar部分修改为局部刷新逻辑 flexibleSpace: FlexibleSpaceBar( titlePadding: const EdgeInsets.symmetric(horizontal: 16), title: ValueListenableBuilder<double>( valueListenable: _progress, builder: (context, progress, child) { Alignment titleAlignment = Alignment.lerp(Alignment.centerLeft, Alignment.center, progress)!; return Align( alignment: titleAlignment, child: child, ); }, child: const Text("测试标题"), ), )
内容的提问来源于stack exchange,提问作者H Wong
相关产品推荐
相关产品推荐

