Flutter实现基于分类的滚动显示AppBar功能
Flutter 滚动时动态切换顶部固定Toolbar的实现方案
要实现滚动ListView时根据分类切换顶部固定的Toolbar,核心是利用Flutter的Sliver组件体系,通过CustomScrollView结合自定义的SliverPersistentHeaderDelegate实现分类头部的粘性效果,同时监听滚动位置动态更新顶部AppBar内容。以下是具体实现步骤:
1. 定义分类数据结构
先创建包含分类标题和对应内容的数据模型:
class CategorySection { final String title; final List<String> items; CategorySection(this.title, this.items); }
2. 自定义粘性分类头部Delegate
实现一个SliverPersistentHeaderDelegate,用于渲染每个分类的头部并控制粘性行为:
class CategoryHeaderDelegate extends SliverPersistentHeaderDelegate { final String title; final double minHeight; final double maxHeight; CategoryHeaderDelegate({ required this.title, this.minHeight = 50, this.maxHeight = 50, }); @override Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) { return Container( color: Colors.grey[100], alignment: Alignment.centerLeft, padding: const EdgeInsets.symmetric(horizontal: 16), child: Text( title, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), ); } @override double get maxExtent => maxHeight; @override double get minExtent => minHeight; @override bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) { return oldDelegate != this; } }
3. 完整实现代码
通过ScrollController监听滚动位置,动态更新AppBar标题,结合CustomScrollView组织布局:
import 'package:flutter/material.dart'; void main() => runApp(const MyApp()); class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'Dynamic Toolbar Demo', theme: ThemeData(primarySwatch: Colors.blue), home: const DynamicToolbarPage(), ); } } class DynamicToolbarPage extends StatefulWidget { const DynamicToolbarPage({super.key}); @override State<DynamicToolbarPage> createState() => _DynamicToolbarPageState(); } class _DynamicToolbarPageState extends State<DynamicToolbarPage> { final ScrollController _scrollController = ScrollController(); final List<CategorySection> _sections = [ CategorySection('分类A', List.generate(20, (index) => 'A类内容$index')), CategorySection('分类B', List.generate(20, (index) => 'B类内容$index')), CategorySection('分类C', List.generate(20, (index) => 'C类内容$index')), CategorySection('分类D', List.generate(20, (index) => 'D类内容$index')), ]; String _currentToolbarTitle = '分类A'; @override void initState() { super.initState(); _scrollController.addListener(_onScroll); } void _onScroll() { double currentOffset = _scrollController.offset; double accumulatedOffset = 0; for (var section in _sections) { // 计算当前分类总高度:头部高度 + 所有item高度(假设item高度固定为50) double sectionHeight = 50 + section.items.length * 50; if (currentOffset < accumulatedOffset + sectionHeight) { if (_currentToolbarTitle != section.title) { setState(() { _currentToolbarTitle = section.title; }); } break; } accumulatedOffset += sectionHeight; } } @override void dispose() { _scrollController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(_currentToolbarTitle), centerTitle: true, ), body: CustomScrollView( controller: _scrollController, slivers: [ for (var section in _sections) ...[ SliverPersistentHeader( pinned: true, // 开启头部固定效果 delegate: CategoryHeaderDelegate(title: section.title), ), SliverList( delegate: SliverChildBuilderDelegate( (context, index) { return ListTile( title: Text(section.items[index]), leading: const Icon(Icons.list), ); }, childCount: section.items.length, ), ), ], ], ), ); } }
关键细节说明
SliverPersistentHeader的pinned: true属性确保分类头部滚动到顶部时固定,配合滚动监听同步更新AppBar标题。- 示例中假设列表项高度固定为50,若你的item高度不固定,可使用
GlobalKey获取实际渲染高度,或通过LayoutBuilder动态计算。 - 若需要更流畅的标题切换效果,可给AppBar标题添加
AnimatedSwitcher实现过渡动画。
内容的提问来源于stack exchange,提问作者newcomer
相关产品推荐
相关产品推荐

