Ant D
Ant D

Reputation: 2683

dart flutter: How to rotate around circle?

How to get the square to respond to the Gesture Detector for rotating the square around a circle. Turning it clockwise should give a positive increase from 0 to 360 and anticlockwise should decrease from 360 to 0. But here it increases beyond 360 and also goes below 0 into minus values. And the response to the drag is unpredictable.

The square should rotate only if IT detects the gesture and not the circle behind it.

I don't know even if this is the way to go about it. Any help will be very much appreciated. I've been at this for some days now.

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

void main() {
  SystemChrome.setPreferredOrientations(
  [DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
 runApp(new MyRotateApp());
}

class MyRotateApp extends StatefulWidget {
  @override
  MyRotateAppState createState() {
    return new MyRotateAppState();
  }
 }

class MyRotateAppState extends State<MyRotateApp> {
  double angleDelta;
  String strDialAngle;

  @override
  void initState() {
    angleDelta = -90.0; //start from top position
    strDialAngle = 'Angle Text';
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
        home: new Scaffold(
      appBar: new AppBar(title: new Text('ROTATE Square')),
      body: new Center(
        child: new Stack(alignment: Alignment.center,
          children: <Widget>[
        new Container(
          height: 350.0,
          decoration: new BoxDecoration(
              color: Colors.blueGrey, shape: BoxShape.circle),
        ),
        /// position the square
        new MyRadialPosition(
            radius: 130.0,
            angle: angleDelta * PI / 180.0,
            /// Gesture Detector
            child: new GestureDetector(
              onVerticalDragUpdate: _onVerticalDragUpdate,
              onHorizontalDragUpdate: _onHorizontalDragUpdate,
              child: new Container(
                width: 45.0,
                height: 45.0,
                color: Colors.green,
              ),
            )),
        /// update text based on angle value of rotation of square
        new Padding(
          padding: const EdgeInsets.only(bottom: 80.0),
          child: new Text('$strDialAngle',
            style: new TextStyle(fontSize: 25.0,color: Colors.white),),
            ),
          ],
        ),
      ),
    ));
  }

  void _onVerticalDragUpdate(DragUpdateDetails details) {
    angleDelta = angleDelta + (details.delta.dy).roundToDouble();
    strDialAngle = 'VERTICAL Drag : ${angleDelta + 90.0}';
    setState((){});
  }

  void _onHorizontalDragUpdate(DragUpdateDetails details) {
    angleDelta = angleDelta + (details.delta.dx).roundToDouble();
    strDialAngle = 'Horizontal Drag : ${angleDelta + 90.0}';
    setState((){});
  }
}

class MyRadialPosition extends StatelessWidget {
  final double radius;
  final double angle;
  final Widget child;
  MyRadialPosition({this.radius, this.angle, this.child});

  @override
  Widget build(BuildContext context) {
    final x = radius * cos(angle);
    final y = radius * sin(angle);
    return new Transform(
      transform: new Matrix4.translationValues(x, y, 0.0),
      child: child,);
  }
}

// for adding to the above Stack RadialDragGestureDetector
new MyRadialPosition(radius: 170.0,
         angle: angleDelta * PI / 180.0,
         child: new RadialDragGestureDetector(
           onRadialDragUpdate: _onRadialDragUpdate,
           child: new Container(
           width: 45.0,
           height: 45.0,
           color: Colors.amber,
       ),
         ),
       ),

_onRadialDragUpdate(PolarCoord updateCoord) {
setState((){
  angleDelta = (updateCoord.angle).roundToDouble();
  print('updateCoord.angle : ${(updateCoord.angle).roundToDouble()}');
});
}

Upvotes: 4

Views: 5359

Answers (3)

Godwin Vinny Carole
Godwin Vinny Carole

Reputation: 186

You can do it with simple calculation on panUpdate and panEnd:

 _panUpdateHandler(DragUpdateDetails d) {
/// Pan location on the wheel
bool onTop = d.localPosition.dy <= radius;
bool onLeftSide = d.localPosition.dx <= radius;
bool onRightSide = !onLeftSide;
bool onBottom = !onTop;

/// Pan movements
bool panUp = d.delta.dy <= 0.0;
bool panLeft = d.delta.dx <= 0.0;
bool panRight = !panLeft;
bool panDown = !panUp;

/// Absolute change on axis
double yChange = d.delta.dy.abs();
double xChange = d.delta.dx.abs();

/// Directional change on wheel
double verticalRotation = (onRightSide && panDown) || (onLeftSide && panUp)
    ? yChange
    : yChange * -1;

double horizontalRotation =
    (onTop && panRight) || (onBottom && panLeft) ? xChange : xChange * -1;

// Total computed change
double rotationalChange = verticalRotation + horizontalRotation;

double _value = degree + (rotationalChange / 5);

setState(() {
  degree = _value > 0 ? _value : 0;
  ctrl.value = degree;
});
}

On pan end:

_panEndHandler(DragEndDetails d) {
ctrl
    .animateTo(roundToBase(degree.roundToDouble(), 10),
        duration: Duration(milliseconds: 551), curve: Curves.easeOutBack)
    .whenComplete(() {
  setState(() {
    degree = roundToBase(degree.roundToDouble(), 10);
  });
});
}

And then attach them to a gesture detector:

GestureDetector draggableWheel = GestureDetector(
  onPanUpdate: _panUpdateHandler,
  onPanEnd: _panEndHandler,
  child: Stack(
    children: [
      AnimatedBuilder(
        animation: ctrl,
        builder: (ctx, w) {
          return Transform.rotate(
            angle: degreeToRadians(ctrl.value),
            child: wheelContainer,
          );
        },
      ),
      Container(
        width: wheelSize,
        height: wheelSize / 2,
        margin: const EdgeInsets.only(top: 30),
        child: Center(
          child: Padding(
            padding: EdgeInsets.only(top: this.longNeedleHeight + 10),
            child: Image(
              image: NetworkImage(
                  'https://d20nqim3b55fln.cloudfront.net/images/ic_needle_light.png'),
            ),
          ),
        ),
      ),
      Container(
        width: wheelSize,
        height: wheelSize,
        child: CustomPaint(
          painter: WheelDecoration(context),
        ),
      ),
    ],
  ),
);

Here is the full example for your reference: codepen link: https://codepen.io/godwinvc/pen/oNxrOeX

iframe {
  display:block;
    vertical-align:top;
  width: 600px;
  height: 800px;
  
}
<iframe style="overflow-y: auto; -webkit-overflow-scrolling: touch; display:block; vertical-align:top;" src="https://codepen.io/godwinvc/full/oNxrOeX"></iframe>

Upvotes: 5

Chun Keat
Chun Keat

Reputation: 98

You can use onPanUpdate.

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

class MyRotateApp extends StatefulWidget {
  @override
  MyRotateAppState createState() {
    return new MyRotateAppState();
  }
}

class MyRotateAppState extends State<MyRotateApp> {
  double angleDelta;
  String strDialAngle;

  @override
  void initState() {
    angleDelta = -90.0; //start from top position
    strDialAngle = 'Angle Text';
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
        home: new Scaffold(
          appBar: new AppBar(title: new Text('ROTATE Square')),
          body: new Center(
            child: new Stack(alignment: Alignment.center,
              children: <Widget>[
                new Container(
                  height: 260.0,
                  width: 260.0,
                  decoration: new BoxDecoration(
                      color: Colors.blueGrey, shape: BoxShape.circle),
                ),
                /// position the square
                new MyRadialPosition(
                    radius: 130.0,
                    angle: angleDelta * 3.142 / 180.0,
                    /// Gesture Detector
                    child: new GestureDetector(
                      onPanUpdate: _onPanUpdate,
//                      onVerticalDragUpdate: _onVerticalDragUpdate,
//                      onHorizontalDragUpdate: _onHorizontalDragUpdate,
                      child: new Container(
                        width: 45.0,
                        height: 45.0,
                        color: Colors.green,
                      ),
                    )),
                /// update text based on angle value of rotation of square
                new Padding(
                  padding: const EdgeInsets.only(bottom: 80.0),
                  child: new Text('$strDialAngle',
                    style: new TextStyle(fontSize: 25.0,color: Colors.white),),
                ),
              ],
            ),
          ),
        ));
  }

  void _onVerticalDragUpdate(DragUpdateDetails details) {
    angleDelta = angleDelta + (details.delta.dy).roundToDouble();
    strDialAngle = 'VERTICAL Drag : ${angleDelta + 90.0}';
    setState((){});
  }

  void _onHorizontalDragUpdate(DragUpdateDetails details) {
    angleDelta = angleDelta + (details.delta.dx).roundToDouble();
    strDialAngle = 'Horizontal Drag : ${angleDelta + 90.0}';
    setState((){});
  }

  //use onPanUpdate here
  void _onPanUpdate(DragUpdateDetails details) {
    Offset center = Offset(130.0, 130.0);
    var a = (details.delta.dx) - center.dx;
    var b = center.dy - (details.delta.dy);

    angleDelta = angleDelta + atan2(b, a);
    setState(() {
    });
  }
}

class MyRadialPosition extends StatelessWidget {
  final double radius;
  final double angle;
  final Widget child;
  MyRadialPosition({this.radius, this.angle, this.child});

  @override
  Widget build(BuildContext context) {
    final x = radius * cos(angle);
    final y = radius * sin(angle);
    return new Transform(
      transform: new Matrix4.translationValues(x, y, 0.0),
      child: child,);
  }
}

Upvotes: 1

Tree
Tree

Reputation: 31431

You can use fluttery package. It has radial drag gesture detector.

      child: new RadialDragGestureDetector(
        onRadialDragStart: _onRadialDragStart,
        onRadialDragUpdate: _onRadialDragUpdate,
        onRadialDragEnd: _onRadialDragEnd,
        child: new ...

and listeners get PolarCoord on start and update so you can see radians.

You didnt mention if you do one or two finger gesture, so let me know if this helps

Upvotes: 1

Related Questions