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

Flutter中AppBar无法自动完全展开/折叠问题求助

解决Flutter滚动时AppBar自动完全展开/折叠的问题

你的代码主要存在两个核心问题:条件判断逻辑错误,以及未正确管理ScrollController生命周期,导致无法实现预期的吸附效果。以下是修正后的代码及说明:

修正后的完整代码

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

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

  @override
  State<CustomAppBar> createState() => _CustomAppBarState();
}

class _CustomAppBarState extends State<CustomAppBar> {
  final _controller = ScrollController();

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: NotificationListener<ScrollNotification>(
        onNotification: (scrollNotification) {
          if (scrollNotification is ScrollEndNotification &&
              scrollNotification.depth == 0) {
            final minExtent = scrollNotification.metrics.minScrollExtent;
            final maxExtent = scrollNotification.metrics.maxScrollExtent;
            final middle = (maxExtent - minExtent) / 2;
            final pos = scrollNotification.metrics.pixels;

            if (kDebugMode) {
              print("pos : $pos, maxExtent : $maxExtent, minExtent : $minExtent");
            }

            if (pos <= middle) {
              // 滚动到完全展开状态
              _controller.animateTo(
                minExtent,
                duration: const Duration(milliseconds: 300),
                curve: Curves.ease,
              );
            } else {
              // 滚动到完全折叠状态
              _controller.animateTo(
                maxExtent,
                duration: const Duration(milliseconds: 300),
                curve: Curves.ease,
              );
            }
          }
          return false;
        },
        child: NestedScrollView(
          controller: _controller,
          headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
            return <Widget>[
              SliverOverlapAbsorber(
                handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
                sliver: const SliverAppBar(
                  expandedHeight: 200.0,
                  pinned: true,
                  flexibleSpace: FlexibleSpaceBar(title: Text("Hello world")),
                ),
              ),
            ];
          },
          body: Builder(builder: (BuildContext context) {
            return CustomScrollView(
              slivers: [
                SliverOverlapInjector(
                  handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
                ),
                // 增加足够多的列表项确保滚动正常触发
                SliverList(
                  delegate: SliverChildBuilderDelegate(
                    (context, index) => ListTile(title: Text("Item $index")),
                    childCount: 50,
                  ),
                ),
              ],
            );
          }),
        ),
      ),
    );
  }
}

关键修改点

  • 修复条件判断逻辑:原代码中第二个else if的条件middle > pos与第一个if的pos <= middle完全重复,导致折叠动画永远无法触发。改为else分支直接处理pos > middle的场景,确保滚动位置超过中间值时自动折叠AppBar。
  • 改为StatefulWidget:原代码使用StatelessWidget管理ScrollController,无法在组件销毁时释放控制器,存在内存泄漏风险。改为StatefulWidget后,在dispose方法中释放控制器,符合Flutter的生命周期规范。
  • 增加可滚动内容:原代码中仅存在一个高度200的容器,无法触发足够的滚动距离来测试吸附效果。添加50个列表项后,滚动操作可以正常触发。
  • 简化代码结构:移除冗余的条件判断,让逻辑更清晰易懂。

效果说明

现在滚动列表时,当滚动结束后:

  • 如果滚动位置未超过展开/折叠状态的中间值,AppBar会自动动画展开至完全状态
  • 如果滚动位置超过中间值,AppBar会自动动画折叠至最小状态
    完全符合联系人、设置类App的常见交互效果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 14:04:54