Axel
Axel

Reputation: 5111

Rotating image based on drag handle in flutter

My end goal is to achieve somethinng like this:

enter image description here

As you can see there's the drag handle is required to rotate this image.

I have a following code:

import 'package:flutter/material.dart';

double ballRadius = 7.5;

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

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  double _angle = 0.0;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: SafeArea(
          child: Stack(
            children: [
              Positioned(
                top: 100,
                left: 100,
                child: Transform.rotate(
                  angle: _angle,
                  child: Column(
                    children: [
                      Container(
                        width: 30,
                        height: 30,
                        decoration: BoxDecoration(
                          color: Colors.black,
                          borderRadius: BorderRadius.circular(30),
                        ),
                        child: LayoutBuilder(
                          builder: (context, constraints) {
                            return GestureDetector(
                              behavior: HitTestBehavior.translucent,
                              onPanUpdate: (DragUpdateDetails details) {
                                Offset centerOfGestureDetector = Offset(
                                  constraints.maxWidth / 2,
                                  constraints.maxHeight / 2,
                                );
                                final touchPositionFromCenter =
                                    details.localPosition -
                                        centerOfGestureDetector;
                                print(touchPositionFromCenter.direction);
                                setState(() {
                                  _angle = touchPositionFromCenter.direction;
                                });
                              },
                            );
                          },
                        ),
                      ),
                      Container(
                        height: 30,
                        width: 5,
                        color: Colors.black,
                      ),
                      Container(
                        height: 200,
                        width: 200,
                        color: Colors.red,
                      ),
                    ],
                  ),
                ),
              )
            ],
          ),
        ),
      ),
    );
  }
}

It is working. But sometimes it's too fast or too slow. Please help me fix this issue.

Upvotes: 7

Views: 2333

Answers (1)

ggordon
ggordon

Reputation: 10035

I made a few modifications to the code, notably

  1. Treating the "real" centerOfGestureDetector as the center of all the items you would like to rotate
  2. Determining and tracking the change in angle with the onPanStart,onPanEnd and onPanUpdate methods
import 'package:flutter/material.dart';

double ballRadius = 7.5;

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

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  double _angle = 0.0;
  double _oldAngle = 0.0;
  double _angleDelta = 0.0;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: SafeArea(
          child: Stack(
            children: [
              Positioned(
                top: 100,
                left: 100,
                child: Transform.rotate(
                  angle: _angle,
                  child: Column(
                    children: [
                      Container(
                        width: 30,
                        height: 30,
                        decoration: BoxDecoration(
                          color: Colors.black,
                          borderRadius: BorderRadius.circular(30),
                        ),
                        child: LayoutBuilder(
                          builder: (context, constraints) {
                            //   Offset centerOfGestureDetector = Offset(
                            // constraints.maxWidth / 2, constraints.maxHeight / 2);
                            /**
                           * using center of positioned element instead to better fit the
                           * mental map of the user rotating object.
                           * (height = container height (30) + container height (30) + container height (200)) / 2
                           */
                            Offset centerOfGestureDetector =
                                Offset(constraints.maxWidth / 2, 130);
                            return GestureDetector(
                              behavior: HitTestBehavior.translucent,
                              onPanStart: (details) {
                                final touchPositionFromCenter =
                                    details.localPosition -
                                        centerOfGestureDetector;
                                _angleDelta = _oldAngle -
                                    touchPositionFromCenter.direction;
                              },
                              onPanEnd: (details) {
                                setState(
                                  () {
                                    _oldAngle = _angle;
                                  },
                                );
                              },
                              onPanUpdate: (details) {
                                final touchPositionFromCenter =
                                    details.localPosition -
                                        centerOfGestureDetector;

                                setState(
                                  () {
                                    _angle = touchPositionFromCenter.direction +
                                        _angleDelta;
                                  },
                                );
                              },
                            );
                          },
                        ),
                      ),
                      Container(
                        height: 30,
                        width: 5,
                        color: Colors.black,
                      ),
                      Container(
                        height: 200,
                        width: 200,
                        color: Colors.red,
                      ),
                    ],
                  ),
                ),
              )
            ],
          ),
        ),
      ),
    );
  }
}

Upvotes: 10

Related Questions