1

My main goal is, to create a flexible widget with features.

  1. manage widget position.
  2. rotate widget.
  3. Resize the widget from all sides.

1 and 2, I've managed to make. but the 3rd point, when the widget finishes rotating and resizing the widget, the position will change and be erratic.

i really need you guys help. thanks

enter image description here

this is my code.

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      home: const TestingDesign2(),
    );
  }
}

class Item{
  Size size;
  Offset offset;
  double rotation;
  var parentKey;
  Item({required this.parentKey, required this.size, required this.offset, required this.rotation });
}

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

  @override
  State<TestingDesign2> createState() => _TestingDesign2State();
}

class _TestingDesign2State extends State<TestingDesign2> {

    
  Item item = new Item(
  parentKey: new GlobalKey(),
  offset: Offset(10,10),
    size: Size(150,150),
    rotation: 0
  );
  

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(title: Text("Testing design 2")),
        body: Container(
          color: Colors.blueAccent.withOpacity(0.5),
         
          child: Stack(
            alignment: Alignment.center,
           
            children: [ItemTesting(key: item.parentKey, item:item)],
          ),
        ));
  }
}

class ItemTesting extends StatefulWidget {
  Item item;
  ItemTesting({super.key, required this.item});

  @override
  State<ItemTesting> createState() => _ItemTestingState();
}

class _ItemTestingState extends State<ItemTesting> {
  
  double offsetAngle = 0;
  bool isRotate = false;

  @override
  Widget build(BuildContext context) {
    return _Item();
  }

  Widget _Item() {
    return Positioned(
      left: widget.item.offset.dx,
      top: widget.item.offset.dy,
      child: Transform.rotate(
        angle: widget.item.rotation,
        child: SizedBox(
          height: widget.item.size.height,
          width: widget.item.size.width,
          child: Stack(
            children: [
              GestureDetector(
                onPanStart: onPanStart,
                onPanUpdate: onPanUpdate,
                behavior: HitTestBehavior.translucent,
                onPanEnd: (details) {
                  isRotate = false;
                },
                onPanCancel: () {
                  isRotate = false;
                },
                child: Container(
                  color: Colors.red,
                  height: widget.item.size.height,
                  width: widget.item.size.width,
                ),
              ),
              IgnorePointer(
                child: Align(
                  alignment: Alignment.topRight,
                  child: ClipOval(
                      child: Container(
                          height: 25,
                          width: 25,
                          color: Colors.blue,
                          child: Icon(Icons.rotate_right_outlined))),
                ),
              ),
              Align(
                  alignment: Alignment.centerRight,
                  child: GestureDetector(
                      onPanUpdate: resizeRight,
                      child: Container(
                          height: 25,
                          width: 25,
                          color: Colors.yellow,
                          child: Icon(Icons.arrow_forward)))),
              Align(
                  alignment: Alignment.bottomCenter,
                  child: GestureDetector(
                      onPanUpdate: resizeBottom,
                      child: Container(
                          height: 25,
                          width: 25,
                          color: Colors.yellow,
                          child: Icon(Icons.arrow_downward))))
            ],
          ),
        ),
      ),
    );
  }

  var touchPosition = Offset.zero;
  onPanStart(DragStartDetails details) {
    Offset centerOfGestureDetector = Offset(widget.item.size.width / 2, widget.item.size.height / 2);
    final touchPositionFromCenter =
        details.localPosition - centerOfGestureDetector;
    offsetAngle = touchPositionFromCenter.direction - widget.item.rotation;

    final RenderBox referenceBox =
        widget.item.parentKey.currentContext.findRenderObject();
    var x = referenceBox.globalToLocal(details.globalPosition);

    touchPosition = Offset(x.dx, x.dy + 55);
    // top right
    if (details.localPosition.dx > (widget.item.size.width - 25) &&
        details.localPosition.dy <= 25) {
      isRotate = true;
    }
  }

  onPanUpdate(DragUpdateDetails details) {
    if (isRotate) {
      Offset centerOfGestureDetector = Offset(widget.item.size.width / 2, widget.item.size.height / 2);
      final touchPositionFromCenter =
          details.localPosition - centerOfGestureDetector;

      widget.item.rotation = touchPositionFromCenter.direction - offsetAngle;
    } else {
      var positionG = widget.item.offset + details.globalPosition;
      var positiong2 = positionG - touchPosition;
      widget.item.offset = (positiong2 - widget.item.offset);
    }
    setState(() {});
  }

//------------------------------------------------ resize widget
  void resizeRight(DragUpdateDetails details) {
    setState(() {
      widget.item.size = Size(widget.item.size.width + details.delta.dx, widget.item.size.height);
    });
  }

  void resizeBottom(DragUpdateDetails details) {
    setState(() {
      widget.item.size = Size(widget.item.size.width, widget.item.size.height + details.delta.dy);
    });
  }
}

https://dartpad.dev/?id=b1cb4617d4dd1a25e815a01135f95222

mamena tech
  • 496
  • 5
  • 16

1 Answers1

4

Rotational Offsets

You can compensate for the rotation by offsetting the position proportional to the cos and sin of the angle.

First, to use cos and sin functions, import the 'dart:math' library at the beginning:

import 'dart:math';

Then in your resizeRight function:

  void resizeRight(DragUpdateDetails details) {
    setState(() {
      widget.item.size = Size(widget.item.size.width + details.delta.dx, widget.item.size.height);
      var angle = widget.item.rotation;
      var rotationalOffset = Offset(cos(angle) - 1, sin(angle)) * details.delta.dx / 2;
      widget.item.offset += rotationalOffset;
    });
  }

Likewise, in your resizeBottom function:

  void resizeBottom(DragUpdateDetails details) {
    setState(() {
      widget.item.size = Size(widget.item.size.width, widget.item.size.height + details.delta.dy);
      var angle = widget.item.rotation;
      var rotationalOffset = Offset(-sin(angle), cos(angle) - 1) * details.delta.dy / 2;
      widget.item.offset += rotationalOffset;
    });
  }
Steve
  • 10,435
  • 15
  • 21
  • 1
    thank you so much i really appreciate it. You make my day – mamena tech Oct 03 '22 at 07:15
  • Hello @Steve, what if the button is located at the corners, e.g. BottomRight? What would be the correct formula to get the correct position for that? – snap May 24 '23 at 08:39
  • 1
    @snap You can combine the two resize functions, either by combining the x, y components for `widget.item.size` and `rotationalOffset` or alternatively just calling both functions, i.e. `void resizeBottomRight(DragUpdateDetails details) { resizeRight(details); resizeBottom(details); }` – Steve May 25 '23 at 21:05
  • Managed to do it by calling the two functions. But what if I want it to use the former, what will be the `rotationalOffset`'s value? Sorry I'm not knowledgeable with trigonometry at the moment :\ – snap Jun 13 '23 at 12:36
  • @Steve Can you please help me to resize from `topCenter`, `leftCenter`, `topLeft`, `topRight`, `bottomLeft`, `bottomRight`. – Vishal Parmar Jun 25 '23 at 13:17
  • I tried to do resizeLetf from the below code but no luck for me so can you please help me out here? `void resizeLeft(DragUpdateDetails details) { setState(() { widget.item.size = Size(widget.item.size.width + details.delta.dx, widget.item.size.height); var angle = widget.item.rotation; var rotationalOffset = Offset(cos(angle) + 1, sin(angle)) * details.delta.dx / 2; widget.item.offset -= rotationalOffset; }); }` – Vishal Parmar Jun 25 '23 at 13:55
  • @VishalParmar try using subtraction instead of addition when getting the size. Use the same rotational offset value. – snap Aug 29 '23 at 06:06