Flutter中SliverAppBar搜索栏滚动切换效果实现请求
实现搜索栏滚动迁移至SliverAppBar的方案
核心思路
通过ScrollController监听滚动偏移量,配合状态控制/动画组件,实现页面初始搜索栏与SliverAppBar内搜索栏的平滑切换:滚动到指定阈值时,隐藏页面顶部的搜索栏,同时在SliverAppBar中显示搜索栏;反向滚动则恢复初始状态。
完整代码示例
import 'package:flutter/material.dart'; class SearchBarScrollDemo extends StatefulWidget { const SearchBarScrollDemo({super.key}); @override State<SearchBarScrollDemo> createState() => _SearchBarScrollDemoState(); } class _SearchBarScrollDemoState extends State<SearchBarScrollDemo> with SingleTickerProviderStateMixin { final ScrollController _scrollController = ScrollController(); // 触发切换的滚动阈值,与SliverAppBar展开高度一致 final double _offsetThreshold = 100.0; late AnimationController _animationController; late Animation<double> _pageSearchOpacity; late Animation<double> _appBarSearchOpacity; @override void initState() { super.initState(); _scrollController.addListener(_onScroll); // 初始化动画控制器,实现平滑过渡 _animationController = AnimationController( duration: const Duration(milliseconds: 300), vsync: this, ); _pageSearchOpacity = Tween<double>(begin: 1, end: 0).animate(_animationController); _appBarSearchOpacity = Tween<double>(begin: 0, end: 1).animate(_animationController); } void _onScroll() { if (_scrollController.offset >= _offsetThreshold) { _animationController.forward(); } else { _animationController.reverse(); } } @override void dispose() { _scrollController.dispose(); _animationController.dispose(); super.dispose(); } // 复用搜索栏组件,减少冗余代码 Widget _buildSearchBar() { return Container( margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), boxShadow: const [ BoxShadow(color: Colors.black12, blurRadius: 4, offset: Offset(0, 2)) ], ), child: const TextField( decoration: InputDecoration( hintText: "搜索...", prefixIcon: Icon(Icons.search), border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 12), ), ), ); } @override Widget build(BuildContext context) { return Scaffold( body: CustomScrollView( controller: _scrollController, slivers: [ SliverAppBar( pinned: true, backgroundColor: Colors.blue, // AppBar内的搜索栏,随动画渐显 title: AnimatedOpacity( opacity: _appBarSearchOpacity.value, duration: const Duration(milliseconds: 300), child: _appBarSearchOpacity.value > 0 ? _buildSearchBar() : const Text("首页"), ), expandedHeight: _offsetThreshold, flexibleSpace: FlexibleSpaceBar( background: Container(color: Colors.blue), ), ), // 页面初始显示的搜索栏,随动画渐隐 SliverToBoxAdapter( child: AnimatedOpacity( opacity: _pageSearchOpacity.value, duration: const Duration(milliseconds: 300), child: _buildSearchBar(), ), ), // 页面内容列表 SliverList( delegate: SliverChildBuilderDelegate( (context, index) => ListTile( title: Text("列表项 $index"), subtitle: const Text("测试内容"), ), childCount: 50, ), ), ], ), ); } }
关键细节说明
- 滚动阈值设置:
_offsetThreshold需与SliverAppBar的expandedHeight保持一致,确保AppBar完全收起时完成搜索栏的切换。 - 动画优化:使用
AnimationController配合AnimatedOpacity实现搜索栏的平滑显隐,避免生硬切换。 - 组件复用:封装
_buildSearchBar确保两个搜索栏样式统一,减少代码冗余。
内容的提问来源于stack exchange,提问作者Nupur Rawat
相关产品推荐
相关产品推荐

