Search code examples
flutterdartflutter-positioned

Flutter: after rotate widget inside stack, when changing the position it is move wrong direction


i try to rotate the widget and then try to move around, it moves into different direction.

I'm realize, I need another calculation to normalize the position when its move, with condition rotate more than zero , but I don't know how, if anyone can help, thank you 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 TestingDesign2 extends StatefulWidget {
  const TestingDesign2({super.key});

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

class _TestingDesign2State extends State<TestingDesign2> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(title: Text("Testing")),
        body: Container(
          color: Colors.blueAccent.withOpacity(0.5),
          child: Stack(
            alignment: Alignment.center,
            // clipBehavior: Clip.none,
            children: [ItemTesting()],
          ),
        ));
  }
}

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

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

class _ItemTestingState extends State<ItemTesting> {
  double rotation = 0;
  Size size = Size(300, 300);
  Offset position = Offset(10, 10);
  double offsetAngle = 0;
  bool isRotate = false;

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

  Widget _Item() {
    return Positioned(
      left: position.dx,
      top: position.dy,
      child: Transform.rotate(
        angle: rotation,
        child: SizedBox(
          height: size.height,
          width: size.width,
          child: Stack(
            children: [
              GestureDetector(
                onPanStart: onPanStart,
                onPanUpdate: onPanUpdate,
                onPanEnd: (details) {
                  isRotate = false;
                },
                onPanCancel: () {
                  isRotate = false;
                },
                child: Container(
                  color: Colors.red,
                  height: size.height,
                  width: size.width,
                ),
              ),
              IgnorePointer(
                child: Align(
                  alignment: Alignment.topRight,
                  child: ClipOval(
                      child: Container(
                    height: 25,
                    width: 25,
                    color: Colors.blue,
                  )),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  onPanStart(details) {
    Offset centerOfGestureDetector = Offset(size.width / 2, size.height / 2);
    final touchPositionFromCenter =
        details.localPosition - centerOfGestureDetector;
    offsetAngle = touchPositionFromCenter.direction - rotation;
    // top right
    if (details.localPosition.dx > (size.width - 25) &&
        details.localPosition.dy <= 25) {
      isRotate = true;
    }
  }

  onPanUpdate(details) {
    if (isRotate) {
      Offset centerOfGestureDetector = Offset(size.width / 2, size.height / 2);
      final touchPositionFromCenter =
          details.localPosition - centerOfGestureDetector;
      // print(touchPositionFromCenter.direction * 180 / math.pi);

      rotation = touchPositionFromCenter.direction - offsetAngle;
    } else {
      position = new Offset(
          position.dx + details.delta.dx, position.dy + details.delta.dy);
    }
    setState(() {});
  }
}

enter image description here

https://dartpad.dev/?id=1403cd7c7a121a7b26d6b6c3ab422cf0


Solution

  • Finally, I managed to make a widget that can be rotated and moved without moving in the wrong direction after rotated.

    this is the 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 TestingDesign2 extends StatefulWidget {
      const TestingDesign2({super.key});
    
      @override
      State<TestingDesign2> createState() => _TestingDesign2State();
    }
    
    class _TestingDesign2State extends State<TestingDesign2> {
      Size size = Size(200, 200);
    
      var keyItem = GlobalKey();
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
            appBar: AppBar(title: Text("Testing design 2")),
            body: Container(
              color: Colors.blueAccent.withOpacity(0.5),
              // key: keyContainer,
              child: Stack(
                alignment: Alignment.center,
                // clipBehavior: Clip.none,
                children: [ItemTesting(key: keyItem, keyItem: keyItem)],
              ),
            ));
      }
    }
    
    class ItemTesting extends StatefulWidget {
      var keyItem;
      ItemTesting({super.key, required this.keyItem});
    
      @override
      State<ItemTesting> createState() => _ItemTestingState();
    }
    
    class _ItemTestingState extends State<ItemTesting> {
      double rotation = 0;
      Size size = Size(300, 300);
      Offset position = Offset(10, 10);
      double offsetAngle = 0;
      bool isRotate = false;
    
      @override
      Widget build(BuildContext context) {
        return _Item();
      }
    
      Widget _Item() {
        return Positioned(
          left: position.dx,
          top: position.dy,
          child: Transform.rotate(
            angle: rotation,
            child: SizedBox(
              height: size.height,
              width: 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: size.height,
                      width: size.width,
                    ),
                  ),
                  IgnorePointer(
                    child: Align(
                      alignment: Alignment.topRight,
                      child: ClipOval(
                          child: Container(
                        height: 25,
                        width: 25,
                        color: Colors.blue,
                      )),
                    ),
                  ),
             
                ],
              ),
            ),
          ),
        );
      }
    
      
    
      var touchPosition = Offset.zero;
      onPanStart(DragStartDetails details) {
        Offset centerOfGestureDetector = Offset(size.width / 2, size.height / 2);
        final touchPositionFromCenter =
            details.localPosition - centerOfGestureDetector;
        offsetAngle = touchPositionFromCenter.direction - rotation;
    
        final RenderBox referenceBox =
            widget.keyItem.currentContext.findRenderObject();
        var x = referenceBox.globalToLocal(details.globalPosition);
    
        touchPosition = Offset(x.dx, x.dy + 55);
        // top right
        if (details.localPosition.dx > (size.width - 25) &&
            details.localPosition.dy <= 25) {
          isRotate = true;
        }
      }
    
      onPanUpdate(DragUpdateDetails details) {
        if (isRotate) {
          Offset centerOfGestureDetector = Offset(size.width / 2, size.height / 2);
          final touchPositionFromCenter =
              details.localPosition - centerOfGestureDetector;
    
          rotation = touchPositionFromCenter.direction - offsetAngle;
        } else {
          var positionG = position + details.globalPosition;
          var positiong2 = positionG - touchPosition;
          position = (positiong2 - position);
        }
        setState(() {});
      }
    }