Reputation: 1505
I have a flutter code for cursor wheel widget.
import 'package:flutter/material.dart';
import 'dart:math';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: RotateText(),
);
}
}
class RotateText extends StatefulWidget {
RotateText({
Key key
}): super(key: key); // changed
@override
_RotateTextState createState() => _RotateTextState();
}
class _RotateTextState extends State < RotateText > {
double finalAngle = 0.0;
double offsetAngle = 0.0;
@override
Widget build(BuildContext context) {
return _defaultApp(context);
}
_defaultApp(BuildContext context) {
var radius = 300.0;
var left = MediaQuery.of(context).size.width / 2 - radius;
var size = 1.6 * radius;
var place = 60;
var item = 100.0;
var distance = 180;
return Scaffold(
appBar: AppBar(
title: Text('Single finger Rotate text'), // changed
),
body: new Stack(
children: <Widget>[
Positioned(
left: left,
bottom: place - radius,
child: Container(
width: 2 * radius,
height: 2 * radius,
decoration: new BoxDecoration(
shape: BoxShape.circle,
color: Colors.grey,
),
child: LayoutBuilder(
builder: (context, constraints) {
return GestureDetector(
behavior: HitTestBehavior.translucent,
onPanStart: (details) {
Offset centerOfGestureDetector = Offset(constraints.maxWidth / 2, constraints.maxHeight / 2);
final touchPositionFromCenter = details.localPosition - centerOfGestureDetector;
offsetAngle = touchPositionFromCenter.direction - finalAngle;
},
onPanUpdate: (details) {
Offset centerOfGestureDetector = Offset(constraints.maxWidth / 2, constraints.maxHeight / 2);
final touchPositionFromCenter = details.localPosition - centerOfGestureDetector;
setState(() {
finalAngle = touchPositionFromCenter.direction - offsetAngle;
});
},
child: Transform.rotate(
angle: finalAngle,
child: Icon(
Icons.settings,
color: Colors.white,
size: size,
),
),
);
},
),
)
),
Positioned(
left: left + radius / 2 + item + distance * cos(finalAngle - pi / 2),
bottom: place - item / 2 - distance * sin(finalAngle - pi / 2),
child: Container(
height: item,
width: item,
child: Image.network(
'https://picsum.photos/250?image=9',
),
)
),
]
)
);
}
}
It works fine and spins by dragging but I doesn't stay and calibrate automatically on one menu item with animation.
How can I make it rotate (with finalAngle float number) to reposition at the center of the items when it's on the radial area range?
Thanks.
Upvotes: 2
Views: 755
Reputation: 2249
You should use AnimationController to animate position of the items and you must set the correct position of menu item in the onPanEnd method, i completely review your code and finally fixed it as below:
import 'dart:math';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: RotateText(),
);
}
}
class RotateText extends StatefulWidget {
RotateText({Key key}) : super(key: key); // changed
@override
_RotateTextState createState() => _RotateTextState();
}
class _RotateTextState extends State<RotateText>
with SingleTickerProviderStateMixin {
double finalAngle = 0.0;
double offsetAngle = 0.0;
Tween<double> _tween;
Animation<double> _animation;
AnimationController _animationController;
@override
void initState() {
super.initState();
_animationController =
AnimationController(duration: Duration(milliseconds: 700), vsync: this);
_tween = Tween(begin: 0.0, end: finalAngle);
_animation = _tween.animate(CurvedAnimation(
parent: _animationController, curve: Curves.easeOut))
..addListener(() {
setState(() {});
});
}
void updateFinalAngle(double angle, [bool withDuration = false]) {
_tween.begin = _tween.end;
_animationController.reset();
_tween.end = angle;
_animationController.forward();
}
@override
Widget build(BuildContext context) {
return _defaultApp(context);
}
_defaultApp(BuildContext context) {
var radius = 300.0;
var left = MediaQuery.of(context).size.width / 2 - radius;
var size = 1.6 * radius;
var place = 60;
var item = 100.0;
var distance = 180;
return Scaffold(
appBar: AppBar(
title: Text('Single finger Rotate text'), // changed
),
body: Stack(children: <Widget>[
Positioned(
left: left,
bottom: place - radius,
child: Container(
width: 2 * radius,
height: 2 * radius,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.grey,
),
child: LayoutBuilder(
builder: (context, constraints) {
return GestureDetector(
behavior: HitTestBehavior.translucent,
onPanStart: (details) {
Offset centerOfGestureDetector = Offset(
constraints.maxWidth / 2,
constraints.maxHeight / 2);
final touchPositionFromCenter =
details.localPosition - centerOfGestureDetector;
offsetAngle =
touchPositionFromCenter.direction - finalAngle;
},
onPanUpdate: (details) {
Offset centerOfGestureDetector = Offset(
constraints.maxWidth / 2,
constraints.maxHeight / 2);
final touchPositionFromCenter =
details.localPosition - centerOfGestureDetector;
setState(() {
finalAngle =
touchPositionFromCenter.direction - offsetAngle;
updateFinalAngle(finalAngle);
});
},
onPanEnd: (detail) {
double fixedAngle = _calcFinalAngle(finalAngle);
finalAngle = fixedAngle;
updateFinalAngle(finalAngle, true);
},
child: Transform.rotate(
angle: _animation.value,
child: Icon(
Icons.settings,
color: Colors.white,
size: size,
),
),
);
},
),
)),
Positioned(
left: left +
radius / 2 +
item +
distance * cos(_animation.value - pi / 2),
bottom:
place - item / 2 - distance * sin(_animation.value - pi / 2),
child: Container(
height: item,
width: item,
child: Image.network(
'https://picsum.photos/250?image=9',
),
)),
]));
}
double _calcFinalAngle(double radianAngle) {
int degreeAngle =
double.parse((radianAngle * (180 / pi)).toStringAsFixed(2)).round();
int criticalAngle = 60;
int criticalOffset = 30;
bool isNegative = false;
if (degreeAngle < 0) isNegative = true;
degreeAngle = degreeAngle.abs();
int rem60 = degreeAngle % criticalAngle;
if (rem60 >= criticalOffset) {
degreeAngle += (60 - rem60);
} else {
degreeAngle -= rem60;
}
double resultAngleInRad = degreeAngle * (pi / 180);
return isNegative ? -resultAngleInRad : resultAngleInRad;
}
}
Upvotes: 1