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

如何在Flutter中实现Horizontal GridView的吸附滚动(Snap Scroll)效果?

Flutter 实现吸附滚动(Snap Scroll)效果

根据你提供的动图效果,以下是几种常用的实现方案:

方法一:全屏项吸附(ListView + PageScrollPhysics)

如果每个滚动项占满整个视口,直接给 ListView 添加 PageScrollPhysics 即可实现页面切换式的吸附:

ListView(
  // 横向滚动可设置 scrollDirection: Axis.horizontal
  scrollDirection: Axis.vertical,
  physics: const PageScrollPhysics(), // 核心配置
  children: [
    Container(height: MediaQuery.of(context).size.height, color: Colors.red),
    Container(height: MediaQuery.of(context).size.height, color: Colors.blue),
    Container(height: MediaQuery.of(context).size.height, color: Colors.green),
  ],
)

方法二:原生页面切换(PageView)

如果是全屏页面切换场景,PageView 是更贴合需求的原生组件,自带吸附特性:

PageView(
  scrollDirection: Axis.vertical, // 默认横向,可改为纵向
  children: [
    Container(color: Colors.red),
    Container(color: Colors.blue),
    Container(color: Colors.green),
  ],
)

方法三:非全屏项自定义吸附

若滚动项不是全屏,需要吸附到每个item的特定位置,可选择以下两种方式:

方式1:使用第三方包 scroll_snap_list

先在 pubspec.yaml 中添加依赖:

dependencies:
  scroll_snap_list: ^0.9.0

实现代码:

ScrollSnapList(
  itemCount: 5,
  itemSize: 200, // 每个item的高度(纵向)或宽度(横向)
  onItemFocus: (index) {
    // 吸附到目标item时的回调
  },
  itemBuilder: (context, index) {
    return Container(
      height: 200,
      color: Colors.primaries[index % Colors.primaries.length],
      alignment: Alignment.center,
      child: Text('Item $index', style: const TextStyle(fontSize: 24)),
    );
  },
)

方式2:自定义滚动物理(无依赖)

如果不想依赖第三方包,可自定义 ScrollPhysics 控制吸附逻辑:

class CustomSnapPhysics extends ScrollPhysics {
  final double snapInterval; // 吸附间隔(比如每个item的高度)

  const CustomSnapPhysics({required this.snapInterval, ScrollPhysics? parent}) : super(parent: parent);

  @override
  CustomSnapPhysics applyTo(ScrollPhysics? ancestor) {
    return CustomSnapPhysics(snapInterval: snapInterval, parent: buildParent(ancestor));
  }

  @override
  Simulation? createBallisticSimulation(ScrollMetrics position, double velocity) {
    // 计算最近的吸附目标位置
    final target = (position.pixels / snapInterval).round() * snapInterval;
    if (target != position.pixels) {
      return ScrollSpringSimulation(
        spring,
        position.pixels,
        target,
        velocity,
        tolerance: tolerance,
      );
    }
    return super.createBallisticSimulation(position, velocity);
  }
}

// 使用示例
ListView(
  physics: CustomSnapPhysics(snapInterval: 200), // 每隔200px吸附一次
  children: List.generate(5, (index) => Container(
    height: 200,
    color: Colors.primaries[index % Colors.primaries.length],
  )),
)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 13:08:09