字节笔记本
2026年3月22日
Flutter SnapList - 列表滑动 Snapping 效果组件
本文介绍 flutter_snaplist,一个轻量级的 Flutter 库,用于创建具有吸附效果的可滚动列表视图。在日常的移动应用开发中,我们经常需要实现类似轮播图、卡片选择器等交互效果——用户滑动列表后,列表会自动吸附到最近的某一项上。flutter_snaplist 正是为解决这一需求而生的,它提供了简洁的 API 和流畅的手势检测,让开发者能够轻松实现各种吸附式列表。
项目简介
flutter_snaplist 是一个由开发者 ariedov 维护的开源 Flutter 库,已获得 Awesome Flutter 认证。它的核心理念是"small and cozy"——小巧而舒适。该库通过 SnapList 这个 StatefulWidget,封装了复杂的吸附计算逻辑,让开发者只需关注界面渲染和数据绑定。
与 Flutter 自带的 PageView 或 ListView 相比,flutter_snaplist 的最大区别在于:它支持不同尺寸的子项,并且能够对每个子项进行精确的吸附计算。这意味着你可以在同一个列表中放置大小不一的卡片或组件,而吸附行为依然能够正确工作。
该项目在 GitHub 上积极欢迎社区贡献,Issue 和 Pull Request 都受到鼓励。
核心特性
- 吸附式滚动:用户滑动列表后,列表会自动吸附到最近的一项,提供流畅的交互体验
- 动态尺寸支持:支持列表中每个子项具有不同的大小,吸附计算会根据实际尺寸进行精确调整
- 进度回调:提供
center、next、progress三个关键数据,让开发者可以实时感知滚动和吸附状态 - 灵活的方向控制:默认水平滚动,也支持垂直方向滚动
- 自定义分隔符:通过
separatorProvider可以自定义子项之间的间距 - 手势检测驱动:基于手势检测实现滑动,确保用户交互的流畅性
技术栈
- Flutter/Dart:基于 Flutter 框架开发,纯 Dart 实现
- 手势检测:使用 Flutter 原生的手势系统进行滑动检测和吸附计算
- 发布平台:通过 pub.dev 发布,版本号为 0.1.8
安装指南
在 Flutter 项目的 pubspec.yaml 文件中添加依赖:
dependencies:
snaplist: ^0.1.8然后在需要使用的 Dart 文件中导入:
import 'package:snaplist/snaplist.dart';安装完成后,运行 flutter pub get 命令获取依赖包。
快速开始
SnapList 组件有 4 个必填参数,理解它们是使用该库的关键:
sizeProvider:提供每个子项的尺寸。库会根据此尺寸将每个子项包裹在指定大小的SizedBox中,这是吸附计算正确工作的基础separatorProvider:提供子项之间的分隔间距大小,与sizeProvider类似builder:构建子项的函数,类似于 Flutter 中常见的 builder 模式,会传入 context、当前索引和附加数据count:列表子项的总数
使用示例
基础用法
以下是一个最基本的 SnapList 使用示例:
import 'package:flutter/material.dart';
import 'package:snaplist/snaplist.dart';
class SnapListExample extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('SnapList 示例')),
body: SnapList(
sizeProvider: (index, data) => Size(100.0, 160.0),
separatorProvider: (index, data) => Size(10.0, 0.0),
builder: (context, index, data) => Container(
color: Colors.blue.withOpacity(0.5 + (index % 5) * 0.1),
child: Center(
child: Text('Item $index'),
),
),
count: 10,
),
);
}
}利用进度数据实现动态效果
SnapList 的 builder 和 provider 函数都会接收一个 data 参数,其中包含三个字段:
center:当前显示在中心位置的子项索引next:用户正在滑向的目标位置索引,空闲时为 -1progress:滚动和吸附的进度值,范围为 0 到 100
利用这些数据,可以实现丰富的动态视觉效果:
SnapList(
sizeProvider: (index, data) => Size(120.0, 160.0),
separatorProvider: (index, data) => Size(12.0, 0.0),
count: 20,
builder: (context, index, data) {
// 根据是否为中心项来调整透明度和缩放
final isCenter = index == data.center;
final progress = data.progress / 100.0;
return Transform.scale(
scale: isCenter ? 1.0 : 0.85,
child: Opacity(
opacity: isCenter ? 1.0 : 0.7,
child: Container(
decoration: BoxDecoration(
color: Colors.primaries[index % Colors.primaries.length],
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(isCenter ? 0.3 : 0.1),
blurRadius: isCenter ? 12 : 4,
),
],
),
child: Center(
child: Text(
'Card $index',
style: TextStyle(
color: Colors.white,
fontSize: isCenter ? 18 : 14,
fontWeight: isCenter ? FontWeight.bold : FontWeight.normal,
),
),
),
),
),
);
},
)垂直方向滚动
通过设置 axis 参数为 Axis.vertical,可以切换为垂直方向的吸附列表:
SnapList(
axis: Axis.vertical,
sizeProvider: (index, data) => Size(300.0, 80.0),
separatorProvider: (index, data) => Size(0.0, 8.0),
builder: (context, index, data) => Container(
color: Colors.teal.withOpacity(0.6 + (index % 3) * 0.15),
child: Center(child: Text('Vertical Item $index')),
),
count: 15,
)动态尺寸子项
SnapList 支持列表中每个子项具有不同的大小,只需在 sizeProvider 中根据索引返回不同尺寸即可:
SnapList(
sizeProvider: (index, data) {
// 每隔 3 个项目设置一个较大尺寸
if (index % 3 == 0) {
return Size(160.0, 200.0);
}
return Size(100.0, 140.0);
},
separatorProvider: (index, data) => Size(8.0, 0.0),
builder: (context, index, data) => Container(
color: Colors.orange.withOpacity(0.5 + (index % 4) * 0.12),
child: Center(child: Text('Item $index')),
),
count: 12,
)注意事项
- 手势冲突:flutter_snaplist 使用手势检测来实现滑动吸附,因此需要确保列表内部子组件的手势操作不会与之冲突,以获得最佳的用户体验
- 性能考量:虽然库本身足够轻量,但在子项数量较大且每个子项较为复杂时,建议结合
builder中的progress数据进行条件渲染优化 - 尺寸准确性:
sizeProvider返回的尺寸必须与实际渲染的子项尺寸一致,否则吸附效果会出现偏差
项目链接
- GitHub 仓库:https://github.com/ariedov/flutter_snaplist
- pub.dev 包:https://pub.dev/packages/snaplist