You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter中如何实现非SliverAppBar的下拉展开式AppBar?

Flutter 下拉展开式 AppBar 实现方案

核心思路

放弃SliverAppBar的滚动联动逻辑,自定义可切换高度的AppBar组件:

  • 初始为收起的常规高度,仅显示核心导航元素
  • 支持下拉手势或点击触发,通过动画展开隐藏内容
  • 展开时AppBar悬浮在页面内容上方,不干扰下方内容布局

完整代码实现

import 'package:flutter/material.dart';

class ExpandableAppBarPage extends StatefulWidget {
  const ExpandableAppBarPage({super.key});

  @override
  State<ExpandableAppBarPage> createState() => _ExpandableAppBarPageState();
}

class _ExpandableAppBarPageState extends State<ExpandableAppBarPage> with SingleTickerProviderStateMixin {
  bool _isExpanded = false;
  late AnimationController _animationController;
  late Animation<double> _heightAnimation;

  // 定义收起/展开状态的高度
  final double _collapsedHeight = 60;
  final double _expandedHeight = 200;

  @override
  void initState() {
    super.initState();
    _animationController = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 300),
    );
    _heightAnimation = Tween<double>(
      begin: _collapsedHeight,
      end: _expandedHeight,
    ).animate(CurvedAnimation(
      parent: _animationController,
      curve: Curves.easeInOut,
    ));
  }

  @override
  void dispose() {
    _animationController.dispose();
    super.dispose();
  }

  // 切换展开/收起状态
  void _toggleExpansion() {
    setState(() {
      _isExpanded = !_isExpanded;
      _isExpanded ? _animationController.forward() : _animationController.reverse();
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: [
          // 页面主体内容
          Positioned(
            top: _collapsedHeight,
            left: 0,
            right: 0,
            bottom: 0,
            child: ListView.builder(
              padding: EdgeInsets.zero,
              itemCount: 20,
              itemBuilder: (context, index) => ListTile(title: Text('列表项 $index')),
            ),
          ),
          // 可展开的AppBar
          GestureDetector(
            onVerticalDragUpdate: (details) {
              // 下拉超过20像素触发展开
              if (details.delta.dy > 2 && !_isExpanded) _toggleExpansion();
              // 上拉超过20像素触发收起
              else if (details.delta.dy < -2 && _isExpanded) _toggleExpansion();
            },
            onTap: _toggleExpansion, // 点击也可切换状态
            child: AnimatedBuilder(
              animation: _heightAnimation,
              builder: (context, child) {
                return Container(
                  height: _heightAnimation.value,
                  color: Theme.of(context).primaryColor,
                  padding: const EdgeInsets.symmetric(horizontal: 16),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      // 收起状态的导航栏内容
                      SizedBox(
                        height: _collapsedHeight,
                        child: Row(
                          mainAxisAlignment: MainAxisAlignment.spaceBetween,
                          children: [
                            const Text(
                              '首页',
                              style: TextStyle(color: Colors.white, fontSize: 20, fontWeight: FontWeight.bold),
                            ),
                            IconButton(
                              icon: Icon(_isExpanded ? Icons.expand_less : Icons.expand_more, color: Colors.white),
                              onPressed: _toggleExpansion,
                            ),
                          ],
                        ),
                      ),
                      // 展开状态的隐藏内容
                      if (_isExpanded)
                        Expanded(
                          child: Padding(
                            padding: const EdgeInsets.only(top: 8),
                            child: Column(
                              crossAxisAlignment: CrossAxisAlignment.start,
                              children: const [
                                Text('隐藏内容1', style: TextStyle(color: Colors.white70)),
                                SizedBox(height: 8),
                                Text('隐藏内容2', style: TextStyle(color: Colors.white70)),
                                SizedBox(height: 8),
                                Text('隐藏内容3', style: TextStyle(color: Colors.white70)),
                              ],
                            ),
                          ),
                        ),
                    ],
                  ),
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}

关键细节说明

  • 平滑动画:通过AnimationController和Tween实现高度过渡,避免生硬的尺寸变化
  • 手势兼容:同时支持拖拽和点击触发,适配不同操作习惯
  • 层级布局:用Stack将AppBar置于内容上方,主体内容固定在收起高度下方,展开时AppBar覆盖内容
  • 状态同步:通过_isExpanded变量统一控制动画和内容显示逻辑

内容的提问来源于stack exchange,提问作者Rudransh Singh Mahra

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 11:31:01