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

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

iOS风格网格捏合缩放动效演示

实现逻辑

核心通过监听捏合手势的缩放系数,动态调整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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 22:06:03