'How to drag multiple objects inside a Container

I'm trying to have multiple images inside a big container and be able to move them around and rotate them, as shown in the image below:

enter image description here

I've been playing around with CostumePainter and this is my result whilst following this guide and

enter image description here

Does anyone have an idea on how to do this with Images and multiple of them?

My code:

  dynamic _balls;

  double xPos = 100;
  double yPos = 100;

  bool isClick = false;

  @override
  Widget build(BuildContext context) {
    _balls = _paint(xPosition: xPos, yPosition: yPos, ballRad: 20);

    return Scaffold(
      appBar: AppBar(
        title: const Text("Drag and Drop"),
      ),
      body: Center(
        child: GestureDetector(
          onHorizontalDragDown: (details) {
            setState(() {
              if (_balls.isBallRegion(
                  details.localPosition.dx, details.localPosition.dy)) {
                isClick = true;
              }
            });
          },
          onHorizontalDragEnd: (details) {
            setState(() {
              isClick = false;
            });
          },
          onHorizontalDragUpdate: (details) {
            if (isClick) {
              setState(() {
                xPos = details.localPosition.dx;
                yPos = details.localPosition.dy;
              });
            }
          },
          child: Container(
            height: 300,
            width: 300,
            color: Colors.lightBlueAccent,
            child: CustomPaint(
              painter: _balls,
            ),
          ),
        ),
      ),
    );
  }


Solution 1:[1]

I followed this guide and it helped me, btw this is the blog.

Here is the code btw:

// @dart=2.9
import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

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

  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomePage(),
    );
  }
}

class ContainerList {
  double height;
  double width;
  double scale;
  double rotation;
  double xPosition;
  double yPosition;

  ContainerList({
    this.height,
    this.rotation,
    this.scale,
    this.width,
    this.xPosition,
    this.yPosition,
  });
}

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

  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  List<ContainerList> list = [];
  Offset _initPos;
  Offset _currentPos = Offset(0, 0);
  double _currentScale;
  double _currentRotation;
  Size screen;

  @override
  void initState() {
    screen = Size(400, 500);
    list.add(ContainerList(
      height: 200.0,
      width: 200.0,
      rotation: 0.0,
      scale: 1.0,
      xPosition: 0.1,
      yPosition: 0.1,
    ));
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Container(
        padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 10.0),
        height: 500.0,
        color: Colors.blue.withOpacity(0.8),
        width: double.infinity,
        child: Stack(
          children: list.map((value) {
            return GestureDetector(
              onScaleStart: (details) {
                if (value == null) return;
                _initPos = details.focalPoint;
                _currentPos = Offset(value.xPosition, value.yPosition);
                _currentScale = value.scale;
                _currentRotation = value.rotation;
              },
              onScaleUpdate: (details) {
                if (value == null) return;
                final delta = details.focalPoint - _initPos;
                final left = (delta.dx / screen.width) + _currentPos.dx;
                final top = (delta.dy / screen.height) + _currentPos.dy;

                setState(() {
                  value.xPosition = Offset(left, top).dx;
                  value.yPosition = Offset(left, top).dy;
                  value.rotation = details.rotation + _currentRotation;
                  value.scale = details.scale * _currentScale;
                });
              },
              child: Stack(
                children: [
                  Positioned(
                    left: value.xPosition * screen.width,
                    top: value.yPosition * screen.height,
                    child: Transform.scale(
                      scale: value.scale,
                      child: Transform.rotate(
                        angle: value.rotation,
                        child: Container(
                          height: value.height,
                          width: value.width,
                          child: FittedBox(
                            fit: BoxFit.fill,
                            child: Listener(
                              onPointerDown: (details) {
                                // if (_inAction) return;
                                // _inAction = true;
                                // _activeItem = val;
                                _initPos = details.position;
                                _currentPos =
                                    Offset(value.xPosition, value.yPosition);
                                _currentScale = value.scale;
                                _currentRotation = value.rotation;
                              },
                              onPointerUp: (details) {
                                // _inAction = false;
                              },
                              child: Container(
                                height: value.height,
                                width: value.width,
                                color: Colors.red,
                              ),
                              // child: Image.network(value.name),
                            ),
                          ),
                        ),
                      ),
                    ),
                  )
                ],
              ),
            );
          }).toList(),
        ),
      ),
    );
  }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1