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

Flutter拖拽组件排版异常,需实现类GridView布局的拖拽功能

Flutter拖拽布局排版问题解决:让DragTarget自动换行实现GridView样式

问题分析

你当前代码用Row包裹Draggable和Wrap,Row会强制子组件在一行布局,导致右侧的Wrap只能拿到Row分配的有限宽度,没法充分利用屏幕宽度实现自动换行。另外重复编写多个DragTarget代码冗余,不利于后续维护。

修复方案

1. 调整外层布局,让Wrap获得足够宽度

给右侧的Wrap套上Expanded,这样它能占据Row内的剩余空间,就能根据屏幕宽度自动换行了。如果需要适配小屏幕,也可以把外层的Row换成Wrap,让左侧Draggable和右侧DragTarget区域在空间不足时也能自动换行。

2. 封装重复的DragTarget组件

把重复的DragTarget逻辑抽成单独组件,减少代码冗余,提升可维护性。

修改后的完整代码

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('拖拽布局示例')),
        body: const DragGridDemo(),
      ),
    );
  }
}

class DragGridDemo extends StatefulWidget {
  const DragGridDemo({super.key});

  @override
  State<DragGridDemo> createState() => _DragGridDemoState();
}

class _DragGridDemoState extends State<DragGridDemo> {
  int acceptedData = 0;

  // 封装DragTarget组件
  Widget _buildDragTarget() {
    return DragTarget<int>(
      builder: (
        BuildContext context,
        List<dynamic> accepted,
        List<dynamic> rejected,
      ) {
        return Container(
          height: 50.0,
          width: 100.0,
          decoration: const BoxDecoration(
            borderRadius: BorderRadius.all(
              Radius.circular(20),
            ),
            color: Colors.deepOrange,
          ),
          child: Center(
            child: Text('$acceptedData'),
          ),
        );
      },
      onAccept: (int data) {
        setState(() {
          acceptedData += data;
        });
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return SizedBox.expand(
      child: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Row(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            // 左侧可拖拽元素
            Draggable<int>(
              data: 10,
              feedback: Container(
                decoration: const BoxDecoration(
                  borderRadius: BorderRadius.all(
                    Radius.circular(20),
                  ),
                  color: Colors.deepOrange,
                ),
                height: 50,
                width: 100,
                child: const Icon(Icons.directions_run),
              ),
              childWhenDragging: Container(
                height: 50.0,
                width: 100.0,
                decoration: const BoxDecoration(
                  borderRadius: BorderRadius.all(
                    Radius.circular(20),
                  ),
                  color: Colors.deepOrange,
                ),
                child: const Center(),
              ),
              child: Container(
                height: 50.0,
                width: 100.0,
                decoration: const BoxDecoration(
                  borderRadius: BorderRadius.all(
                    Radius.circular(20),
                  ),
                  color: Colors.deepOrange,
                ),
                child: const Center(
                  child: Text('Crawler 1'),
                ),
              ),
            ),
            const SizedBox(width: 20), // 左右间距
            // 右侧可换行的DragTarget区域,用Expanded让Wrap占满剩余宽度
            Expanded(
              child: Wrap(
                spacing: 20,
                runSpacing: 20,
                children: List.generate(6, (index) => _buildDragTarget()),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

额外优化:用GridView实现更规整的网格布局

如果想要更接近GridView的规整布局,直接用GridView.builder承载DragTarget也是不错的选择,代码示例:

// 替换上面的Expanded部分为GridView
Expanded(
  child: GridView.builder(
    padding: const EdgeInsets.all(8),
    gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
      crossAxisCount: 3, // 每行3个元素
      crossAxisSpacing: 20,
      mainAxisSpacing: 20,
      childAspectRatio: 2, // 保持和之前一致的宽高比
    ),
    itemCount: 6,
    itemBuilder: (context, index) => _buildDragTarget(),
  ),
)

不管用Wrap还是GridView,都能实现DragTarget自动换行的网格效果,同时代码更简洁易维护。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 09:10:35