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

Flutter中如何让堆叠粘性头部被最新头部替换?

解决日历粘性头部堆叠问题

问题分析

默认的position: sticky会让每个日期头部滚动到顶部时固定,但多个粘性元素会依次堆叠在顶部,无法自动替换。要实现"最新头部替换顶部旧头部"的效果,需要通过监听元素交叉状态来控制头部的显示逻辑。

解决方案:使用Intersection Observer API

通过一个单独的占位容器作为固定粘性头部,配合Intersection Observer监听每个日期头部的滚动位置,实时更新占位头部的内容,避免堆叠。

修改后的完整代码

HTML

<div class="calendar-container">
  <div class="day-section">
    <div class="day-header" data-date="2024-05-20">2024年5月20日</div>
    <div class="event">团队周会</div>
    <div class="event">项目进度评审</div>
  </div>
  <div class="day-section">
    <div class="day-header" data-date="2024-05-21">2024年5月21日</div>
    <div class="event">客户需求沟通</div>
    <div class="event">代码提交</div>
    <div class="event">版本发布准备</div>
  </div>
  <div class="day-section">
    <div class="day-header" data-date="2024-05-22">2024年5月22日</div>
    <div class="event">测试用例评审</div>
    <div class="event">线上问题排查</div>
  </div>
</div>
<div class="sticky-header-placeholder"></div>

CSS

.calendar-container {
  max-height: 400px;
  overflow-y: auto;
  position: relative;
  width: 300px;
  margin: 20px;
}

.day-header {
  background-color: #f8f9fa;
  padding: 10px 15px;
  border-bottom: 1px solid #dee2e6;
  font-weight: 600;
}

.sticky-header-placeholder {
  position: sticky;
  top: 0;
  background-color: #f8f9fa;
  padding: 10px 15px;
  border-bottom: 1px solid #dee2e6;
  font-weight: 600;
  display: none;
  z-index: 10;
  width: 300px;
  margin-left: 20px;
}

.day-section {
  margin-bottom: 15px;
}

.event {
  padding: 8px 15px;
  border: 1px solid #e9ecef;
  border-radius: 4px;
  margin: 5px 0;
}

JavaScript

const stickyPlaceholder = document.querySelector('.sticky-header-placeholder');
const dayHeaders = document.querySelectorAll('.day-header');
const calendarContainer = document.querySelector('.calendar-container');

// 初始化交叉观察器
const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    const header = entry.target;
    const rect = entry.boundingClientRect;
    const containerRect = calendarContainer.getBoundingClientRect();

    // 当前头部进入视口顶部区域时,更新占位头部内容
    if (entry.isIntersecting && rect.top <= containerRect.top) {
      stickyPlaceholder.textContent = header.textContent;
      stickyPlaceholder.style.display = 'block';
    }

    // 当前头部完全离开视口顶部时,切换为下一个头部内容
    if (!entry.isIntersecting && rect.bottom <= containerRect.top) {
      const nextSection = header.parentElement.nextElementSibling;
      if (nextSection) {
        const nextHeader = nextSection.querySelector('.day-header');
        if (nextHeader) {
          stickyPlaceholder.textContent = nextHeader.textContent;
        }
      }
    }

    // 最后一个头部完全离开视口底部时,隐藏占位头部
    if (!entry.isIntersecting && rect.bottom <= 0 && header === dayHeaders[dayHeaders.length - 1]) {
      stickyPlaceholder.style.display = 'none';
    }
  });
}, {
  root: calendarContainer,
  threshold: 0.01,
  rootMargin: '-1px 0px 0px 0px' // 微调触发时机,避免切换闪烁
});

// 监听所有日期头部
dayHeaders.forEach(header => observer.observe(header));

关键逻辑说明

  1. 替换单个粘性容器:移除每个日期头部的position: sticky,改用单独的sticky-header-placeholder作为唯一的粘性头部,从根源避免堆叠。
  2. 交叉状态监听:通过Intersection Observer实时检测每个日期头部与容器的交叉位置,动态更新占位头部的显示内容。
  3. 边界处理:针对头部进入顶部、离开顶部、最后一个头部离开底部三种场景做了逻辑处理,确保头部切换流畅且符合预期。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 02:26:21