how to give a curve effect to a specific range from animation value - flutter - flutter

I need to divide the range of animation value to a smaller ranges like this simple example:
class TextAnimation extends StatefulWidget {
const TextAnimation({Key? key}) : super(key: key);
#override
State<TextAnimation> createState() => _TextAnimationState();
}
class _TextAnimationState extends State<TextAnimation>
with SingleTickerProviderStateMixin {
late AnimationController anController;
#override
void initState() {
// TODO: implement initState
super.initState();
anController = AnimationController(
vsync: this,
duration: Duration(seconds: 5),
reverseDuration: Duration(seconds: 5));
}
#override
void dispose() {
// TODO: implement dispose
super.dispose();
anController.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: AnimatedBuilder(
animation: anController,
builder: (context, child) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
width: double.infinity,
),
Container(
width: 300 *
Tween(begin: 0.0, end: 1.0)
.animate(CurvedAnimation(
parent: anController,
curve:
Curves.fastOutSlowIn))
.value,
height: 100,
color: Colors.red,
),
Container(
width: 300 *
(Tween(begin: 0.0, end: 1.0) .animate(CurvedAnimation(
parent: anController, curve: Curves.fastOutSlowIn))
.value.clamp(0.5, 1)),
height: 100,
color: Colors.blue),
InkWell(
onTap: () {
anController.forward();
},
child: Text(
'Start',
style: TextStyle(
color: Colors.black,
fontSize: 20,
),
))
],
);
}),
);
}
}
the second container need to start the animation from the half of the range to the end...
the problem is, the curve effect is lost in this range ,I need to give this range (0.5-1.0) its own curve ,
start fast in 0.5 and end with slow motion.
is there a way to do it without using another animation controller to drive this range?
note : without AnimatedContainer the code above is just a simple example for the problem.

Related

Flutter FadeTransition and backdrop filter

FadeTransition animation is not working for backdropfilter.
The opacity goes from 0 to 1 suddenly, without the smooth transition that the animation should give.
As you can see from the code below, I have a background image and then a backdropfilter on top to blur the background at app startup.
(I then show other widgets, but the animation is working fine for them).
import 'dart:ui';
import 'package:flutter/material.dart';
class SplashScreen extends StatefulWidget {
#override
_SplashScreenState createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen>
with SingleTickerProviderStateMixin {
AnimationController _controller;
Animation<double> _backgroundOpacity;
bool _visible = true;
#override
void initState() {
_controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
)..forward();
// background opacity animation
_backgroundOpacity = Tween<double>(
begin: 0,
end: 1,
).animate(
CurvedAnimation(
parent: _controller,
curve: Interval(0, 1, curve: Curves.linear),
),
);
super.initState();
}
#override
void dispose() {
super.dispose();
_controller.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.green[800],
),
body: Stack(
children: [
Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Image(
image: AssetImage('assets/field.jpg'),
fit: BoxFit.cover,
),
),
FadeTransition(
opacity: _backgroundOpacity,
child: Container(
child: BackdropFilter(
filter: new ImageFilter.blur(sigmaX: 6.0, sigmaY: 6.0),
child: new Container(
decoration: new BoxDecoration(
color: Colors.grey.shade200.withOpacity(0.5),
),
),
),
),
),
],
),
);
}
}
basically what I get from this code is the background image clear and nice, then after 2 seconds suddenly blurred, without any transition.
What should I do to make fadeTransition to work with Backdropfilter?
The preferred way to fade in a blur is to use a TweenAnimationBuilder:
TweenAnimationBuilder<double>(
duration: Duration(milliseconds: 500),
tween: Tween<double>(begin: 0, end: 6),
builder: (context, value, _) {
return BackdropFilter(
key: GlobalObjectKey('background'),
filter: ImageFilter.blur(sigmaY: value, sigmaX: value),
child: Container(
decoration: new BoxDecoration(
color: Colors.grey.shade200.withOpacity(0.5),
),
),
);
},
),

BoxConstraints has a negative minimum width

in this class as an widget which i use animation to show and hide, when i multiple show or hiding by click, sometimes i get this error:
BoxConstraints has a negative minimum width.The offending constraints were: BoxConstraints(w=-0.8, 0.0<=h<=Infinity; NOT NORMALIZED)
this part of code has problem
child: Row( //<--- problem cause on this line of code
children: <Widget>[
...
],
),
My class code:
class FollowingsStoryList extends StatefulWidget {
final List<Stories> _stories;
FollowingsStoryList({#required List<Stories> stories}) : _stories = stories;
#override
_FollowingsStoryListState createState() => _FollowingsStoryListState();
}
class _FollowingsStoryListState extends State<FollowingsStoryList> with TickerProviderStateMixin {
List<Stories> get stories => widget._stories;
bool _isExpanded = false;
AnimationController _barrierController;
#override
void initState() {
super.initState();
_barrierController = AnimationController(duration: const Duration(milliseconds: 400), vsync: this);
}
#override
Widget build(BuildContext context) {
return AnimatedContainer(
curve: _isExpanded ? Curves.elasticOut : Curves.elasticIn,
duration: Duration(milliseconds: 800),
child: Row(
children: <Widget>[
AnimatedContainer(
curve: _isExpanded ? Curves.elasticOut : Curves.elasticIn,
duration: Duration(milliseconds: 800),
width: _isExpanded ? 90 : 1,
color: Colors.white,
padding: EdgeInsets.only(bottom: 65.0),
child: Column(
children: <Widget>[
],
),
),
GestureDetector(
onTap: _toggleExpanded,
child: StoriesShapeBorder(
alignment: Alignment(1, 0.60),
icon: Icons.adjust,
color: Colors.white,
barWidth: 4,
),
),
],
),
);
}
_toggleExpanded() {
setState(() {
_isExpanded = !_isExpanded;
if (_isExpanded) {
_barrierController.forward();
} else {
_barrierController.reverse();
}
});
}
}
Because Curves.elasticOut and Curves.elasticIn are
oscillating curves that grows/shrinks in magnitude while overshooting
its bounds.
When _isExpanded is false, you are expecting the width to go from 90 to 1 however Curves.elasticOut overshoots and becomes much less than 1 (hence a negative width) for a short period of time.
This is what is causing the error imho. Plus for what reason do you have nested AnimatedContainers? Top AnimatedContainer seems unnecessary.
Try getting rid of the top AnimatedContainer and changing all 4 of the Curves to Curves.linear and I think your error will be gone. Actually you can use any Curves which don't overshoot.
If you really need Elastic Curve, then maybe this would work too:
#override
Widget build(BuildContext context) {
return Row(
children: <Widget>[
AnimatedContainer(
curve: _isExpanded ? Curves.elasticOut : Curves.easeInSine,
duration: Duration(milliseconds: 800),
width: _isExpanded ? 90 : 1,
color: Colors.white,
padding: EdgeInsets.only(bottom: 65.0),
child: Column(
children: <Widget>[
],
),
),
GestureDetector(
onTap: _toggleExpanded,
child: StoriesShapeBorder(
alignment: Alignment(1, 0.60),
icon: Icons.adjust,
color: Colors.white,
barWidth: 4,
),
),
],
);
}
Try it and let me know.
For example, you can use AnimatedController, to (oddly enough) control your animation value and set minimum value "0".
import 'dart:math' as math;
double getValue(...) {
// some logic / curves
// ....
return math.max(0, value);
}

AnimatedContainer Rotation gives unwanted rotation

I have been struggling with Flutter with something that in my head seems to simple. I want a container that spins when pressing a button and this should be animated. I have a container in the center of my screen. I have 2 buttons, 1 with "+" and with a "-". When I press the "+" I want the container to rotate 180 degrees in a clockwise rotation, If I press the "+" again I want it to perform another clockwise rotation of 180 degrees. When pressing "-" I want it to spin counter clockwise for 180 degrees.
Currently I have it build so that it will rotate container however the axis is on the top left point of the container instead of the center. I have tried to tackle this but nothing seems to change this behaviour and I found this issue but it's since been closed
https://github.com/flutter/flutter/issues/27419
It's mind boggling to me that I cannot perform such a simple operation and was wondering if someone knows where I'm going wrong.
Some code:
import 'package:flutter/material.dart';
import 'dart:math';
void main() => runApp(Spinner());
class Spinner extends StatefulWidget {
#override
_SpinnerState createState() => _SpinnerState();
}
class _SpinnerState extends State<Spinner> {
double _angle = 0;
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: AnimatedContainer(
alignment: FractionalOffset.center,
height: 200,
width: 200,
transform: Matrix4.rotationZ(_angle),
decoration: BoxDecoration(
color: Colors.blue
),
duration: Duration(seconds: 2),
),
),
floatingActionButton: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
FloatingActionButton(onPressed: () {
setState(() {
_angle += 180 * pi / 180;
});
},
child: const Icon(Icons.add),
),
Container(
height: 20,
),
FloatingActionButton(onPressed: () {
setState(() {
_angle -= 180 * pi / 180;
});
},
child: const Icon(Icons.remove),
)
],
)
)
);
}
}
Edit:
I did find this post however when using it the container instantly snaps to the new position and I want this to be animted.
How can I rotate a Container widget in 2D around a specified anchor point?
I found a solution to tackle this problem, instead of using the animatedContained I decided to use an Animation Controller.
my SpinnerState class now looks like this:
class _SpinnerState extends State<RadialMenu> with SingleTickerProviderStateMixin {
AnimationController controller;
#override
void initState() {
super.initState();
controller = AnimationController(duration: Duration(milliseconds: 900), vsync: this);
}
#override
Widget build(BuildContext context) {
return RadialAnimation(controller: controller);
}
}
I then created a stateless widget which contains the animations etc.
class SpinnerAnimation extends StatelessWidget {
SpinnerAnimation({Key key, this.controller})
: rotation = Tween<double>(
begin: 0.0,
end: 180.0,
).animate(
CurvedAnimation(
parent: controller,
curve: Interval(
0.0,
0.75,
curve: Curves.linear,
),
),
),
super(key: key);
final AnimationController controller;
final Animation<double> rotation;
build(context) {
return AnimatedBuilder(
animation: controller,
builder: (context, builder) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
FloatingActionButton(
child: Icon(Icons.add),
onPressed: _open,
backgroundColor: Colors.green),
FloatingActionButton(
child: Icon(Icons.remove),
onPressed: _close,
backgroundColor: Colors.red),
Transform.rotate(
angle: radians(rotation.value),
child: Column(
children: [
Container(
width: 200,
height: 200,
decoration: new BoxDecoration(
image: DecorationImage(
image: AssetImage('images/CRLogo.png'))),
)
],
))
],
);
});
}
_open() {
controller.forward(from: 0);
}
_close() {
controller.reverse(from: 1);
}
}
This has at least solved this problem for me and I hope it helps someone else in the future.

Flutter implementing repeat Elastic animation

for implementing this animation
i wrote this below code but, Elastic animation doesn't work on project and i'm not sure whats problem,
i want to have repeat of this animation
import 'package:flutter/material.dart';
void main()=>runApp(MaterialApp(home: Avatar(),));
class Avatar extends StatefulWidget {
#override
State<StatefulWidget> createState()=>_Avatar();
}
class _Avatar extends State<Avatar> with TickerProviderStateMixin{
AnimationController avatarController;
Animation<double> avatarSize;
#override
void initState() {
super.initState();
avatarController= AnimationController(
duration: const Duration(seconds: 1),
vsync: this,
);
avatarSize = new Tween(begin: 0.0, end: 1.0).animate(
new CurvedAnimation(
parent: avatarController,
curve: new Interval(
0.100,
0.400,
curve: Curves.elasticOut,
),
),
);
avatarController.repeate();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
fit:StackFit.expand,
children: <Widget>[
AnimatedBuilder(
animation: avatarController,
builder: (context, widget) => Align(
child: Container(
width: 50.0,
height: 50.0,
color:Colors.green
),
),
)
],
),
);
}
}
Output:
You can play with duration and Tween to fine grain it.
void main() => runApp(MaterialApp(home: Avatar()));
class Avatar extends StatefulWidget {
#override
State<StatefulWidget> createState() => _Avatar();
}
class _Avatar extends State<Avatar> with TickerProviderStateMixin {
AnimationController _controller;
Tween<double> _tween = Tween(begin: 0.75, end: 2);
#override
void initState() {
super.initState();
_controller = AnimationController(duration: const Duration(milliseconds: 700), vsync: this);
_controller.repeat(reverse: true);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
Align(
child: ScaleTransition(
scale: _tween.animate(CurvedAnimation(parent: _controller, curve: Curves.elasticOut)),
child: SizedBox(
height: 100,
width: 100,
child: CircleAvatar(backgroundImage: AssetImage(chocolateImage)),
),
),
),
],
),
);
}
}
The Tween's begin and end values should be the values you want to animate between. You then need to use the animated value somewhere in your layout.
For example, change your Tween to Tween(begin: 50.0, end: 100.0) and your Container to
Container(
width: avatarSize.value,
height: avatarSize.value,
color:Colors.green
)
Don't forget to also dispose of the animation controller within your widget's dispose():
#override
void dispose() {
avatarController.dispose();
super.dispose();
}
Add this dependency https://pub.dev/packages/animator
Try this code.
class BounceAnimation extends StatefulWidget {
#override
_BounceAnimationState createState() => _BounceAnimationState();
}
class _BounceAnimationState extends State<BounceAnimation>
with SingleTickerProviderStateMixin {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey,
appBar: AppBar(title: Text("Bouncing Animation example")),
body: Center(
child: Container(
child: Animator(
duration: Duration(seconds: 1),
curve: Curves.elasticOut,
builder: (anim) {
return Transform.scale(
origin: Offset(00, -59),
scale: anim.value,
child: Transform.translate(
offset: Offset(00, -65),
child: CircleAvatar(
radius: 86,
backgroundColor: Colors.white,
child: CircleAvatar(
radius: 84,
backgroundColor: Colors.grey,
child: CircleAvatar(
radius: 80,
backgroundColor: Colors.white,
foregroundColor: Colors.black,
backgroundImage: NetworkImage(
"https://i1.wp.com/devilsworkshop.org/wp-content/uploads/sites/8/2013/01/enlarged-facebook-profile-picture.jpg?w=448&ssl=1",
),
),
),
),
),
);
},
)),
),
);
}
}

Flutter - Flip animation - Flip a card over its right or left side based on the tap's location

I've started playing with Flutter and now thinking about the best way how I can implement a card's flipping animation.
I found this flip_card package and I'm trying to adjust its source code to my needs.
Here is the app which I have now:
import 'dart:math';
import 'package:flutter/material.dart';
void main() => runApp(FlipAnimationApp());
class FlipAnimationApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text("Flip animation"),
),
body: Center(
child: Container(
width: 200,
height: 200,
child: WidgetFlipper(
frontWidget: Container(
color: Colors.green[200],
child: Center(
child: Text(
"FRONT side.",
),
),
),
backWidget: Container(
color: Colors.yellow[200],
child: Center(
child: Text(
"BACK side.",
),
),
),
),
),
),
),
);
}
}
class WidgetFlipper extends StatefulWidget {
WidgetFlipper({
Key key,
this.frontWidget,
this.backWidget,
}) : super(key: key);
final Widget frontWidget;
final Widget backWidget;
#override
_WidgetFlipperState createState() => _WidgetFlipperState();
}
class _WidgetFlipperState extends State<WidgetFlipper> with SingleTickerProviderStateMixin {
AnimationController controller;
Animation<double> _frontRotation;
Animation<double> _backRotation;
bool isFrontVisible = true;
#override
void initState() {
super.initState();
controller = AnimationController(duration: Duration(milliseconds: 500), vsync: this);
_frontRotation = TweenSequence(
<TweenSequenceItem<double>>[
TweenSequenceItem<double>(
tween: Tween(begin: 0.0, end: pi / 2).chain(CurveTween(curve: Curves.linear)),
weight: 50.0,
),
TweenSequenceItem<double>(
tween: ConstantTween<double>(pi / 2),
weight: 50.0,
),
],
).animate(controller);
_backRotation = TweenSequence(
<TweenSequenceItem<double>>[
TweenSequenceItem<double>(
tween: ConstantTween<double>(pi / 2),
weight: 50.0,
),
TweenSequenceItem<double>(
tween: Tween(begin: -pi / 2, end: 0.0).chain(CurveTween(curve: Curves.linear)),
weight: 50.0,
),
],
).animate(controller);
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Stack(
fit: StackFit.expand,
children: [
AnimatedCard(
animation: _backRotation,
child: widget.backWidget,
),
AnimatedCard(
animation: _frontRotation,
child: widget.frontWidget,
),
_tapDetectionControls(),
],
);
}
Widget _tapDetectionControls() {
return Stack(
fit: StackFit.expand,
children: <Widget>[
GestureDetector(
onTap: _leftRotation,
child: FractionallySizedBox(
widthFactor: 0.5,
heightFactor: 1.0,
alignment: Alignment.topLeft,
child: Container(
color: Colors.transparent,
),
),
),
GestureDetector(
onTap: _rightRotation,
child: FractionallySizedBox(
widthFactor: 0.5,
heightFactor: 1.0,
alignment: Alignment.topRight,
child: Container(
color: Colors.transparent,
),
),
),
],
);
}
void _leftRotation() {
_toggleSide();
}
void _rightRotation() {
_toggleSide();
}
void _toggleSide() {
if (isFrontVisible) {
controller.forward();
isFrontVisible = false;
} else {
controller.reverse();
isFrontVisible = true;
}
}
}
class AnimatedCard extends StatelessWidget {
AnimatedCard({
this.child,
this.animation,
});
final Widget child;
final Animation<double> animation;
#override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: animation,
builder: (BuildContext context, Widget child) {
var transform = Matrix4.identity();
transform.setEntry(3, 2, 0.001);
transform.rotateY(animation.value);
return Transform(
transform: transform,
alignment: Alignment.center,
child: child,
);
},
child: child,
);
}
}
Here is how it looks like:
What I'd like to achieve is to make the card flip over its right side if it was tapped on its right half and over its left side if it was tapped on its left half. If it is tapped several times in a row on the same half it should flip over the same side (not back and forth as it is doing now).
So the desired animation should behave as the following one from Quizlet app.
You should know when you tap on the right or left to change the animations dynamically, for that you could use a flag isRightTap. Then, you should invert the values of the Tweens if it has to rotate to one side or to the other.
And the side you should rotate would be:
Rotate to left if the front is visible and you tapped on the left, or, because the back animation is reversed, if the back is is visible and you tapped on the right
Otherwise, rotate to right
Here are the things I changed in _WidgetFlipperState from the code in the question:
_updateRotations(bool isRightTap) {
setState(() {
bool rotateToLeft = (isFrontVisible && !isRightTap) || !isFrontVisible && isRightTap;
_frontRotation = TweenSequence(
<TweenSequenceItem<double>>[
TweenSequenceItem<double>(
tween: Tween(begin: 0.0, end: rotateToLeft ? (pi / 2) : (-pi / 2))
.chain(CurveTween(curve: Curves.linear)),
weight: 50.0,
),
TweenSequenceItem<double>(
tween: ConstantTween<double>(rotateToLeft ? (-pi / 2) : (pi / 2)),
weight: 50.0,
),
],
).animate(controller);
_backRotation = TweenSequence(
<TweenSequenceItem<double>>[
TweenSequenceItem<double>(
tween: ConstantTween<double>(rotateToLeft ? (pi / 2) : (-pi / 2)),
weight: 50.0,
),
TweenSequenceItem<double>(
tween: Tween(begin: rotateToLeft ? (-pi / 2) : (pi / 2), end: 0.0)
.chain(CurveTween(curve: Curves.linear)),
weight: 50.0,
),
],
).animate(controller);
});
}
#override
void initState() {
super.initState();
controller =
AnimationController(duration: Duration(milliseconds: 500), vsync: this);
_updateRotations(true);
}
void _leftRotation() {
_toggleSide(false);
}
void _rightRotation() {
_toggleSide(true);
}
void _toggleSide(bool isRightTap) {
_updateRotations(isRightTap);
if (isFrontVisible) {
controller.forward();
isFrontVisible = false;
} else {
controller.reverse();
isFrontVisible = true;
}
}
import 'dart:math';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp();
#override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage();
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool _toggler = true;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(actions: [
TextButton(
onPressed: _onFlipCardPressed,
child: const Text('change', style: TextStyle(color: Colors.white)),
)
]),
body: Center(
child: SizedBox.square(
dimension: 140,
child: FlipCard(
toggler: _toggler,
frontCard: AppCard(title: 'Front'),
backCard: AppCard(title: 'Back'),
),
),
),
);
}
void _onFlipCardPressed() {
setState(() {
_toggler = !_toggler;
});
}
}
class AppCard extends StatelessWidget {
final String title;
const AppCard({
required this.title,
});
#override
Widget build(BuildContext context) {
return DecoratedBox(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20.0),
color: Colors.deepPurple[400],
),
child: Center(
child: Text(
title,
style: const TextStyle(
fontSize: 40.0,
color: Colors.white,
),
textAlign: TextAlign.center,
),
),
);
}
}
class FlipCard extends StatelessWidget {
final bool toggler;
final Widget frontCard;
final Widget backCard;
const FlipCard({
required this.toggler,
required this.backCard,
required this.frontCard,
});
#override
Widget build(BuildContext context) {
return GestureDetector(
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 800),
transitionBuilder: _transitionBuilder,
layoutBuilder: (widget, list) => Stack(children: [widget!, ...list]),
switchInCurve: Curves.ease,
switchOutCurve: Curves.ease.flipped,
child: toggler
? SizedBox(key: const ValueKey('front'), child: frontCard)
: SizedBox(key: const ValueKey('back'), child: backCard),
),
);
}
Widget _transitionBuilder(Widget widget, Animation<double> animation) {
final rotateAnimation = Tween(begin: pi, end: 0.0).animate(animation);
return AnimatedBuilder(
animation: rotateAnimation,
child: widget,
builder: (context, widget) {
final isFront = ValueKey(toggler) == widget!.key;
final rotationY = isFront ? rotateAnimation.value : min(rotateAnimation.value, pi * 0.5);
return Transform(
transform: Matrix4.rotationY(rotationY)..setEntry(3, 0, 0),
alignment: Alignment.center,
child: widget,
);
},
);
}
}
Try this code I've made some changes to your code, now the GestureDetector is divided equally in width on widget so when you tap on the left side of the box it will reverse the animation and if you tap on right side part it will forward the animation.
Widget _tapDetectionControls() {
return Flex(
direction: Axis.horizontal,
children: <Widget>[
Expanded(
flex: 1,
child: GestureDetector(
onTap: _leftRotation,
),
),
Expanded(
flex: 1,
child: GestureDetector(
onTap: _rightRotation,
),
),
],
);
}
void _leftRotation() {
controller.reverse();
}
void _rightRotation() {
controller.forward();
}