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

Flutter交错网格中如何通过拖拽单角实现容器固定对角的尺寸调整?

实现Staggered Grid View容器的单角拖拽调整功能

核心思路

Staggered Grid的布局依赖crossAxisCellCount和mainAxisCellCount两个参数控制容器尺寸,因此拖拽调整本质是动态修改这两个参数。我们选择固定容器左上角,拖拽右下角实现单向扩展,确保对角位置不变,仅拖动方向扩展容器大小。需要完成:

  • 添加拖拽手柄组件,监听触摸手势
  • 计算拖拽位移对应的单元格数量变化
  • 实时更新数据并刷新UI

完整实现代码

1. 拖拽手柄与尺寸调整逻辑

class ResizeHandle extends StatefulWidget {
  final int index;
  final Function(int, int) onResize;
  final double cellWidth;
  final double cellHeight;

  const ResizeHandle({
    super.key,
    required this.index,
    required this.onResize,
    required this.cellWidth,
    required this.cellHeight,
  });

  @override
  State<ResizeHandle> createState() => _ResizeHandleState();
}

class _ResizeHandleState extends State<ResizeHandle> {
  late PanGestureRecognizer _panGesture;
  Offset _startOffset = Offset.zero;
  int _initialColumn = 0;
  int _initialRow = 0;

  @override
  void initState() {
    super.initState();
    _panGesture = PanGestureRecognizer()
      ..onStart = _onPanStart
      ..onUpdate = _onPanUpdate;
  }

  void _onPanStart(DragStartDetails details) {
    _startOffset = details.globalPosition;
    final item = (context.findAncestorStateOfType<_MyHomePageState>())!.value[widget.index];
    _initialColumn = item['column'];
    _initialRow = item['row'];
  }

  void _onPanUpdate(DragUpdateDetails details) {
    final offsetDelta = details.globalPosition - _startOffset;
    // 计算位移对应的单元格数量
    int newColumn = _initialColumn + (offsetDelta.dx / widget.cellWidth).floor();
    int newRow = _initialRow + (offsetDelta.dy / widget.cellHeight).floor();
    // 边界限制:最小1列/行,横向不超过总列数4
    newColumn = newColumn.clamp(1, 4);
    newRow = newRow.clamp(1, double.infinity.toInt());
    // 回调更新数据
    widget.onResize(newColumn, newRow);
  }

  @override
  Widget build(BuildContext context) {
    return Positioned(
      right: 4,
      bottom: 4,
      child: GestureDetector(
        behavior: HitTestBehavior.opaque,
        recognizer: _panGesture,
        child: Container(
          width: 20,
          height: 20,
          decoration: BoxDecoration(
            color: Colors.white.withOpacity(0.8),
            borderRadius: BorderRadius.circular(4),
          ),
          child: const Icon(Icons.drag_handle, size: 16, color: Colors.black),
        ),
      ),
    );
  }

  @override
  void dispose() {
    _panGesture.dispose();
    super.dispose();
  }
}

2. 修改原页面代码

集成拖拽手柄,添加单元格尺寸计算与数据更新逻辑:

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  List<Map<String, dynamic>> value = [
    {'height': 100.0, 'width': 50.0, 'color': Colors.red, 'column': 2, 'row': 1},
    {'height': 200.0, 'width': 100.0, 'color': Colors.green, 'column': 2, 'row': 2},
    {'height': 100.0, 'width': 200.0, 'color': Colors.blue, 'column': 1, 'row': 1},
    {'height': 50.0, 'width': 50.0, 'color': Colors.orange, 'column': 1, 'row': 1},
    {'height': 150.0, 'width': 50.0, 'color': Colors.teal, 'column': 2, 'row': 3},
    {'height': 200.0, 'width': 200.0, 'color': Colors.yellow, 'column': 1, 'row': 1},
    {'height': 100.0, 'width': 50.0, 'color': Colors.black, 'column': 1, 'row': 1},
    {'height': 100.0, 'width': 50.0, 'color': Colors.red, 'column': 2, 'row': 2},
  ];

  late double _cellWidth;
  late double _cellHeight;

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    // 计算单个单元格的宽高(减去间距)
    final screenWidth = MediaQuery.of(context).size.width;
    const crossAxisCount = 4;
    const spacing = 4.0;
    _cellWidth = (screenWidth - (crossAxisCount + 1) * spacing) / crossAxisCount;
    _cellHeight = _cellWidth; // 保持宽高比1:1,可按需调整
  }

  void _updateItemSize(int index, int newColumn, int newRow) {
    setState(() {
      value[index]['column'] = newColumn;
      value[index]['row'] = newRow;
      // 同步更新容器宽高
      value[index]['width'] = newColumn * _cellWidth;
      value[index]['height'] = newRow * _cellHeight;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Demo"),
      ),
      body: Container(
        padding: const EdgeInsets.all(4),
        child: StaggeredGrid.count(
          crossAxisCount: 4,
          mainAxisSpacing: 4,
          crossAxisSpacing: 4,
          children: List.generate(value.length, (index) {
            return StaggeredGridTile.count(
              crossAxisCellCount: value[index]['column'],
              mainAxisCellCount: value[index]['row'],
              child: Container(
                margin: const EdgeInsets.all(5),
                child: Stack(
                  fit: StackFit.expand,
                  children: [
                    Container(color: value[index]['color']),
                    // 保留原有更多按钮
                    Positioned(
                      right: 4,
                      top: 5,
                      child: InkWell(
                        onTap: () async {
                          final result = await Navigator.push(context, MaterialPageRoute(builder: (ctx) => NewScreen(value: value[index])));
                          value[index] = result;
                          setState(() {});
                        },
                        child: const Icon(Icons.more_vert, size: 30, color: Colors.white),
                      ),
                    ),
                    // 添加拖拽手柄
                    ResizeHandle(
                      index: index,
                      onResize: (newColumn, newRow) => _updateItemSize(index, newColumn, newRow),
                      cellWidth: _cellWidth,
                      cellHeight: _cellHeight,
                    ),
                  ],
                ),
              ),
            );
          }),
        ),
      ),
    );
  }
}

关键说明

  • 拖拽逻辑:通过PanGestureRecognizer监听拖拽位移,转换为单元格数量变化,确保拖拽方向仅扩展容器
  • 尺寸联动:单元格尺寸基于屏幕宽度动态计算,保证拖拽后容器宽高与单元格数量匹配
  • 边界控制:限制单元格数量最小为1,横向不超过总列数4,避免布局错乱
  • 固定对角:默认固定左上角,拖拽右下角实现单向扩展,符合需求

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 07:05:17