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

如何在Flutter中实现Formulia应用式可折叠AppBar(类似SliverAppBar)?

实现类似Formulia的可折叠AppBar(Flutter)

Formulia的可折叠AppBar核心是滚动时动态调整栏内元素的大小、透明度和位置,单纯依赖SliverAppBar的默认属性无法实现这种精细化的过渡效果,需要结合滚动监听+自定义布局来完成。以下是完整的实现方案:

核心实现思路

  1. 使用CustomScrollView作为滚动容器,搭配SliverAppBar作为基础栏
  2. 通过ScrollController监听滚动偏移量,计算折叠进度(0=完全展开,1=完全收缩)
  3. 在SliverAppBar的flexibleSpace中自定义布局,根据折叠进度动态更新子组件的样式

完整代码示例

import 'package:flutter/material.dart';

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

  @override
  State<FormuliaStyleAppBar> createState() => _FormuliaStyleAppBarState();
}

class _FormuliaStyleAppBarState extends State<FormuliaStyleAppBar> {
  final ScrollController _scrollController = ScrollController();
  double _collapseProgress = 0.0;
  
  // 定义展开/收缩状态的高度
  final double _expandedHeight = 220.0;
  final double _collapsedHeight = kToolbarHeight;

  @override
  void initState() {
    super.initState();
    _scrollController.addListener(_onScroll);
  }

  void _onScroll() {
    // 计算折叠进度:(当前偏移量) / (展开高度与收缩高度的差值)
    final double scrollOffset = _scrollController.offset;
    final double maxScrollOffset = _expandedHeight - _collapsedHeight;
    
    setState(() {
      _collapseProgress = (scrollOffset / maxScrollOffset).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,
            floating: false,
            expandedHeight: _expandedHeight,
            toolbarHeight: _collapsedHeight,
            backgroundColor: Colors.blue,
            // 自定义flexibleSpace实现过渡效果
            flexibleSpace: FlexibleSpaceBar(
              background: Stack(
                fit: StackFit.expand,
                children: [
                  // 展开时的背景(可选)
                  Container(color: Colors.blue.shade300),
                  // 动态调整的内容区域
                  Padding(
                    padding: const EdgeInsets.symmetric(horizontal: 16.0),
                    child: Column(
                      mainAxisAlignment: MainAxisAlignment.end,
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        // 大标题:展开时显示,收缩时缩小并移到顶部
                        AnimatedBuilder(
                          animation: _scrollController,
                          builder: (context, child) {
                            return Transform.translate(
                              offset: Offset(0, -_collapseProgress * 80),
                              child: Opacity(
                                opacity: 1 - _collapseProgress,
                                child: Text(
                                  'Formulia',
                                  style: TextStyle(
                                    fontSize: 32 - _collapseProgress * 14,
                                    fontWeight: FontWeight.bold,
                                    color: Colors.white,
                                  ),
                                ),
                              ),
                            );
                          },
                        ),
                        // 副标题/额外内容:收缩时隐藏
                        AnimatedOpacity(
                          opacity: 1 - _collapseProgress,
                          duration: Duration.zero, // 跟随滚动实时变化
                          child: const Padding(
                            padding: EdgeInsets.symmetric(vertical: 8.0),
                            child: Text(
                              '专业数学工具集',
                              style: TextStyle(color: Colors.white70),
                            ),
                          ),
                        ),
                        // 搜索栏:收缩时缩小并移到导航栏内
                        AnimatedContainer(
                          duration: Duration.zero,
                          height: 40 - _collapseProgress * 8,
                          margin: EdgeInsets.only(bottom: 16 - _collapseProgress * 16),
                          child: TextField(
                            decoration: InputDecoration(
                              filled: true,
                              fillColor: Colors.white.withOpacity(0.9),
                              hintText: '搜索公式...',
                              border: OutlineInputBorder(
                                borderRadius: BorderRadius.circular(20),
                                borderSide: BorderSide.none,
                              ),
                              contentPadding: const EdgeInsets.symmetric(horizontal: 16),
                            ),
                          ),
                        ),
                      ],
                    ),
                  ),
                  // 收缩时的导航栏标题(默认隐藏,折叠到一定程度显示)
                  Align(
                    alignment: Alignment.topCenter,
                    child: Opacity(
                      opacity: _collapseProgress,
                      child: SizedBox(
                        height: _collapsedHeight,
                        child: const Center(
                          child: Text(
                            'Formulia',
                            style: TextStyle(
                              fontSize: 18,
                              fontWeight: FontWeight.bold,
                              color: Colors.white,
                            ),
                          ),
                        ),
                      ),
                    ),
                  ),
                ],
              ),
            ),
            // 导航栏操作按钮
            actions: const [
              Padding(
                padding: EdgeInsets.only(right: 16.0),
                child: Icon(Icons.more_vert),
              ),
            ],
          ),
          // 主体内容
          SliverList(
            delegate: SliverChildBuilderDelegate(
              (context, index) => ListTile(
                title: Text('列表项 $index'),
                subtitle: Text('这是第${index+1}个内容项'),
              ),
              childCount: 50,
            ),
          ),
        ],
      ),
    );
  }
}

关键细节说明

  • 折叠进度计算:通过scrollOffset / (_expandedHeight - _collapsedHeight)得到0到1的比例,控制所有元素的过渡
  • AnimatedBuilder/AnimatedOpacity/AnimatedContainer:利用这些组件实现滚动时的实时UI变化,避免手动 setState 带来的性能问题
  • Stack布局:同时承载展开状态的内容和收缩状态的导航栏标题,通过透明度切换显示
  • 边界处理:用clamp(0.0,1.0)确保进度值不会超出0-1范围,避免UI异常

你可以根据实际需求调整_expandedHeight、_collapsedHeight以及各个元素的过渡参数,完全复刻Formulia的效果。

内容的提问来源于stack exchange,提问作者user21766581

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 20:13:19