Flutter实现iOS相册风格捏合缩放调整列数的动态GridView.builder

实现逻辑
核心通过监听捏合手势的缩放系数,动态调整GridView的交叉轴列数,配合隐式动画实现平滑的布局切换效果,完全对齐iOS照片应用的交互逻辑。
关键实现步骤
- 定义状态变量限制列数范围:设置最小列数、最大列数,避免列数过多或过少导致布局异常,核心变量如下:
// 初始列数 int _crossAxisCount = 3; // 列数上下限 static const int _minColumns = 2; static const int _maxColumns = 5; // 捏合临时缩放系数 double _tmpScale = 1.0; // 上次手势结束后的基准缩放系数 double _baseScale = 1.0;
- 用
GestureDetector包裹GridView,监听缩放手势回调:- onScaleStart:记录当前基准缩放系数
- onScaleUpdate:计算实时缩放系数,换算对应的目标列数,限制在上下限范围内
- onScaleEnd:重置临时系数,保存新的基准系数
- GridView使用
AnimatedSwitcher包裹,绑定列数为唯一key,实现布局切换时的平滑过渡动画
完整示例代码
import 'package:flutter/material.dart'; class PinchableGridView extends StatefulWidget { const PinchableGridView({super.key}); @override State<PinchableGridView> createState() => _PinchableGridViewState(); } class _PinchableGridViewState extends State<PinchableGridView> { int _crossAxisCount = 3; static const int _minColumns = 2; static const int _maxColumns = 5; double _tmpScale = 1.0; double _baseScale = 1.0; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text("捏合缩放网格")), body: GestureDetector( onScaleStart: (details) { _baseScale = _tmpScale; }, onScaleUpdate: (details) { setState(() { _tmpScale = _baseScale * details.scale; // 缩放系数换算列数:系数越大列数越多 final newCount = (3 / _tmpScale).round(); _crossAxisCount = newCount.clamp(_minColumns, _maxColumns); }); }, onScaleEnd: (details) { _baseScale = 3 / _crossAxisCount; _tmpScale = _baseScale; }, child: AnimatedSwitcher( duration: const Duration(milliseconds: 200), child: GridView.builder( key: ValueKey(_crossAxisCount), padding: const EdgeInsets.all(4), gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: _crossAxisCount, crossAxisSpacing: 4, mainAxisSpacing: 4, childAspectRatio: 1, ), itemCount: 100, itemBuilder: (context, index) { return Container( color: Colors.primaries[index % Colors.primaries.length], child: Center(child: Text(index.toString())), ); }, ), ), ), ); } } void main() => runApp(const MaterialApp(home: PinchableGridView()));
优化建议
- 可以添加捏合时的实时预览效果,不用等手势结束再切换布局,上述代码已经实现了实时更新
- 如果需要和iOS照片完全一致的单元格缩放过渡,可以替换AnimatedSwitcher为自定义的布局动画,控制每个单元格的大小变化
- 加入触觉反馈,列数切换时触发轻微震动,提升交互质感
内容的提问来源于stack exchange,提问作者Yodart
相关产品推荐
相关产品推荐

