Reputation: 2683
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
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
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
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