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

Flutter如何实现滚动向下隐藏顶部卡片、滚动向上显示?

Hey there! 作为Flutter新手碰到这个滚动交互的需求太常见啦,我来给你一步步拆解怎么实现这个效果~

实现滚动时隐藏/显示顶部筛选卡片的方案

核心思路

我们可以通过ScrollController监听滚动事件,判断滚动方向(向上/向下),再结合动画组件让筛选卡片的显示状态平滑切换,避免生硬的跳变。

完整代码示例

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(
      home: Scaffold(
        appBar: AppBar(title: const Text('滚动隐藏筛选卡片示例')),
        body: const ScrollHideDemo(),
      ),
    );
  }
}

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

  @override
  State<ScrollHideDemo> createState() => _ScrollHideDemoState();
}

class _ScrollHideDemoState extends State<ScrollHideDemo> {
  // 滚动控制器,用来监听滚动偏移
  final ScrollController _scrollController = ScrollController();
  // 筛选卡片的高度,默认完全显示
  double _filterCardHeight = 80.0;
  // 记录上一次的滚动位置,用来判断滚动方向
  double _previousScrollOffset = 0.0;

  @override
  void initState() {
    super.initState();
    // 给滚动控制器添加监听事件
    _scrollController.addListener(_handleScroll);
  }

  void _handleScroll() {
    final currentOffset = _scrollController.offset;
    // 向下滚动:当前偏移 > 上一次偏移,且已经滚动过顶部
    if (currentOffset > _previousScrollOffset && currentOffset > 0) {
      setState(() {
        _filterCardHeight = 0.0; // 隐藏卡片
      });
    } else {
      setState(() {
        _filterCardHeight = 80.0; // 显示卡片
      });
    }
    // 更新上一次的滚动位置
    _previousScrollOffset = currentOffset;
  }

  @override
  void dispose() {
    // 销毁控制器,避免内存泄漏
    _scrollController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        // 用AnimatedContainer实现平滑过渡动画
        AnimatedContainer(
          duration: const Duration(milliseconds: 300),
          height: _filterCardHeight,
          child: Card(
            margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
            child: const Center(
              child: Text('筛选卡片:地区 | 价格 | 类型'),
            ),
          ),
        ),
        // 可滚动的列表内容
        Expanded(
          child: ListView.builder(
            controller: _scrollController,
            itemCount: 50,
            itemBuilder: (context, index) {
              return ListTile(
                title: Text('列表项 $index'),
                subtitle: const Text('这里是列表的具体内容'),
              );
            },
          ),
        ),
      ],
    );
  }
}

关键代码说明

  • ScrollController:核心用来捕捉滚动的偏移量,通过对比当前偏移和上一次偏移,判断用户是向上还是向下滚动。
  • AnimatedContainer:代替普通Container,在高度变化时自动生成300毫秒的平滑过渡动画,让隐藏/显示的过程更自然。
  • dispose方法:一定要记得在组件销毁时释放ScrollController,防止内存泄漏。

进阶优化小技巧

如果觉得改变高度的效果不够丝滑,也可以用Transform.translate控制卡片的垂直偏移,比如向下滚动时让卡片向上移出屏幕,向上滚动时再滑回来:

// 替换原有的AnimatedContainer部分
AnimatedContainer(
  duration: const Duration(milliseconds: 300),
  transform: Matrix4.translationValues(0, _filterCardOffset, 0),
  child: Card(
    // 卡片内容保持不变
    margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
    child: const Center(
      child: Text('筛选卡片:地区 | 价格 | 类型'),
    ),
  ),
)

// 同时修改_handleScroll逻辑:
double _filterCardOffset = 0.0; // 替换原来的_height变量

void _handleScroll() {
  final currentOffset = _scrollController.offset;
  if (currentOffset > _previousScrollOffset && currentOffset > 0) {
    setState(() {
      _filterCardOffset = -80.0; // 向上偏移隐藏
    });
  } else {
    setState(() {
      _filterCardOffset = 0.0; // 回到原位显示
    });
  }
  _previousScrollOffset = currentOffset;
}

这样的交互会更贴近常见的App体验,你可以根据自己的UI风格选择合适的实现方式~

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 16:35:13