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

Flutter点击按钮时如何每次生成一个新的可拖动Container?

Flutter 多可拖动Container实现方案

以下是完整可直接运行的实现代码,已经覆盖你需要的「点击按钮新增、多实例独立拖动、自由摆放」需求:

import 'package:flutter/material.dart';
import 'dart:math';

// 可拖动盒子的数据模型
class DraggableBox {
  final String id;
  Offset offset; // 盒子位置
  final Size size; // 盒子尺寸
  final Color color; // 盒子颜色,方便区分不同实例

  DraggableBox({
    required this.id,
    required this.offset,
    required this.size,
    required this.color,
  });
}

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

  @override
  State<DemoPage> createState() => _DemoPageState();
}

class _DemoPageState extends State<DemoPage> {
  final List<DraggableBox> _boxList = [];
  final Random _random = Random();
  // 固定盒子尺寸,你也可以改成可调整的
  final Size _defaultBoxSize = const Size(100, 100);

  // 新增可拖动盒子
  void _addNewBox() {
    // 初始位置每次偏移20,避免新增的盒子完全重叠
    final initialOffset = Offset(50 + _boxList.length * 20, 80 + _boxList.length * 20);
    // 随机生成背景色
    final randomColor = Color.fromRGBO(
      _random.nextInt(256),
      _random.nextInt(256),
      _random.nextInt(256),
      1,
    );
    setState(() {
      _boxList.add(DraggableBox(
        id: DateTime.now().millisecondsSinceEpoch.toString(),
        offset: initialOffset,
        size: _defaultBoxSize,
        color: randomColor,
      ));
    });
  }

  // 更新盒子位置
  void _updateBoxPosition(int index, DragUpdateDetails details) {
    setState(() {
      // 计算新位置,同时做边界限制,避免拖出屏幕
      var newDx = _boxList[index].offset.dx + details.delta.dx;
      var newDy = _boxList[index].offset.dy + details.delta.dy;
      // 水平边界限制
      if (newDx < 0) newDx = 0;
      if (newDx > MediaQuery.of(context).size.width - _defaultBoxSize.width) {
        newDx = MediaQuery.of(context).size.width - _defaultBoxSize.width;
      }
      // 垂直边界限制
      if (newDy < 0) newDy = 0;
      if (newDy > MediaQuery.of(context).size.height - _defaultBoxSize.height - kToolbarHeight) {
        newDy = MediaQuery.of(context).size.height - _defaultBoxSize.height - kToolbarHeight;
      }
      // 更新当前盒子的位置
      _boxList[index] = DraggableBox(
        id: _boxList[index].id,
        offset: Offset(newDx, newDy),
        size: _boxList[index].size,
        color: _boxList[index].color,
      );
      // 可选优化:把当前拖动的盒子放到列表最后,提升层级避免被其他盒子挡住
      final currentBox = _boxList.removeAt(index);
      _boxList.add(currentBox);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("可拖动容器演示"), actions: [
        // 新增按钮放在导航栏
        ElevatedButton(
          onPressed: _addNewBox,
          child: const Text("新增容器"),
        ),
        const SizedBox(width: 16),
      ]),
      // 用Stack承载所有可拖动容器,实现绝对定位
      body: Stack(
        children: _boxList.asMap().entries.map((entry) {
          final index = entry.key;
          final box = entry.value;
          return Positioned(
            left: box.offset.dx,
            top: box.offset.dy,
            child: GestureDetector(
              onPanUpdate: (details) => _updateBoxPosition(index, details),
              child: Container(
                width: box.size.width,
                height: box.size.height,
                decoration: BoxDecoration(
                  color: box.color,
                  borderRadius: BorderRadius.circular(8),
                  boxShadow: const [BoxShadow(color: Colors.black12, blurRadius: 4, offset: Offset(2,2))],
                ),
                alignment: Alignment.center,
                child: Text("容器${index+1}", style: const TextStyle(color: Colors.white, fontSize: 16)),
              ),
            ),
          );
        }).toList(),
      ),
    );
  }
}

// 主函数直接运行测试
void main() {
  runApp(const MaterialApp(
    home: DemoPage(),
    debugShowCheckedModeBanner: false,
  ));
}

核心实现逻辑说明

  • 数据层:用DraggableBox模型统一管理每个容器的位置、尺寸、标识等属性,避免零散维护变量
  • 布局层:用Stack+Positioned实现容器的绝对定位,符合自由摆放的需求
  • 交互层:通过GestureDetector的onPanUpdate回调获取手势偏移量,实时更新对应容器的位置后触发UI刷新
  • 优化点:
    • 新增容器时初始位置自动偏移,不会完全重叠
    • 增加屏幕边界限制,避免容器被拖到可视区域外
    • 拖动时自动提升当前容器的层级,不会被其他容器遮挡

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.21 00:36:05