How to slow down AnimationController in Flutter - flutter

I am just trying out flutter animations, and I know that the Animation controller needs vsync ( TickerProvider ) , so I used the SingleTickerProviertateMixin. Now I want to slow down this following animation.
class AnimatedGradientText extends StatefulWidget {
final String data;
final double size;
AnimatedGradientText(this.data, {this.size});
#override
_AnimatedGradientTextState createState() => _AnimatedGradientTextState();
}
class _AnimatedGradientTextState extends State<AnimatedGradientText> with SingleTickerProviderStateMixin {
List listOfColors = <Color>[
Colors.red[200],
Colors.blue[200],
Colors.green[200],
Colors.yellow[200],
// Colors.orange[200],
// Colors.teal[200],
// Colors.cyan[200],
// Colors.purple[200],
// Colors.brown[200],
// Colors.amber[200],
// Colors.pink[200],
];
AnimationController _controller;
void shift(){
Color color = listOfColors.removeAt(Random().nextInt(listOfColors.length));
listOfColors.add(color);
}
#override
void initState() {
_controller = AnimationController(vsync: this , duration: Duration(seconds: 5),upperBound: 10,lowerBound: 0);
_controller.forward(from: 0);
_controller.addListener((){
setState(() {
shift();
});
});
super.initState();
}
#override
void deactivate() {
_controller.dispose();
super.deactivate();
}
#override
Widget build(BuildContext context) {
return Text(
widget.data,
style: TextStyle(
fontSize: widget.size*10,
fontFamily: 'AudioWide',
foreground: Paint()..shader = LinearGradient(
colors: listOfColors
).createShader(Rect.fromLTWH(0.0, 0.0, 200.0, 70.0)),
),
);
}
}
Basically what I am trying to achieve with this is some text that has gradient and changes the gradient over time. But the animation is too fast that it doesnt interpolate smoothly.
Any suggestions regarding an easier way to achieve this will also be helpful. Thanks for your time !

As addListener call shift method every time when data is change, so we have to control is ourselves.
In Following code i add delay of one second to change color, you can play with await and change whatever you find best for you.
Following code will help you to understand more.
class AnimatedGradientText extends StatefulWidget {
final String data;
final double size;
AnimatedGradientText(this.data, {this.size});
#override
_AnimatedGradientTextState createState() => _AnimatedGradientTextState();
}
class _AnimatedGradientTextState extends State<AnimatedGradientText>
with SingleTickerProviderStateMixin {
List listOfColors = <Color>[
Colors.red[200],
Colors.blue[200],
Colors.green[200],
Colors.yellow[200],
// Colors.orange[200],
// Colors.teal[200],
// Colors.cyan[200],
// Colors.purple[200],
// Colors.brown[200],
// Colors.amber[200],
// Colors.pink[200],
];
AnimationController _controller;
bool canCall = true;
void shift() async {
await Future.delayed(Duration(seconds: 1));
Color color = listOfColors.removeAt(Random().nextInt(listOfColors.length));
listOfColors.add(color);
canCall = !canCall;
}
callme() {
if (canCall) {
canCall = !canCall;
shift();
}
}
#override
void initState() {
_controller = AnimationController(
vsync: this,
duration: Duration(seconds: 5),
upperBound: 10,
lowerBound: 0,
);
_controller.forward(from: 0);
_controller.addListener(() {
setState(() {
// shift();
callme();
});
});
super.initState();
}
#override
void deactivate() {
_controller.dispose();
super.deactivate();
}
#override
Widget build(BuildContext context) {
return Text(
widget.data,
style: TextStyle(
fontSize: widget.size * 10,
fontFamily: 'AudioWide',
foreground: Paint()
..shader = LinearGradient(colors: listOfColors)
.createShader(Rect.fromLTWH(0.0, 0.0, 200.0, 70.0)),
),
);
}
}

You can use a Tween to control how fast your slow you want your animation.
The code below shows you how to use a Tween that works perfectly well.
Check the code below, it works well.
Tween<Int>(
begin:0,
end: 255,
);
I hope this helps.

Expose timeDilation property and set it to higher value to slowdown
import 'package:flutter/scheduler.dart' show timeDilation;
timeDilation = 2.0;
I came across this on StackOverFlow some time back

Related

Postpone the start of the AnimationController animation in Flutter

I'm trying to delay the animation for 5 seconds from the beginning, here's my code snippet, but it doesn't work properly, as it would be better to delay for 5 seconds from the start of the startup screen. I tried to make a separate function that would be responsible for the delay but it also caused me to display errors on the emulator. I tried to use a ternary operator and delay it, but it also didn't work properly. I also want my animation to come in 7 seconds, I think it can also be done in a ternary operator. I don't yet know exactly how to stop the animation, so I used a long delay in the animation to make it invisible.
class BubbleBackground extends StatefulWidget {
final Widget child;
final double size;
const BubbleBackground({
Key key,
#required this.child,
#required this.size,
}) : super(key: key);
#override
State<BubbleBackground> createState() => _BubbleBackgroundState();
}
class _BubbleBackgroundState extends State<BubbleBackground>
with SingleTickerProviderStateMixin {
bool timer = false;
final longAnimationDuration = 100000;
final shortAnimationDuration = 700;
AnimationController _controller;
#override
void initState() {
setState(() {
// Future.delayed(Duration(seconds: 5), () {
// timer = true;
// });
timer = true;
});
_controller = AnimationController(
lowerBound: 0.9,
duration: Duration(
milliseconds: timer
? Future.delayed(
Duration(seconds: 5),
() {
shortAnimationDuration;
},
)
: longAnimationDuration,
),
vsync: this,
)..repeat(reverse: true);
super.initState();
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _controller,
builder: (_, __) {
return Transform.scale(
scale: _controller.value,
child: Container(
width: widget.size,
height: widget.size,
alignment: Alignment.center,
decoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
),
child: widget.child,
),
);
},
);
}
}
i figured out that I could just write initState normally, and I'll get desirable result for time delay
#override
void initState() {
super.initState();
_controller = AnimationController(
lowerBound: 0.9,
duration: Duration(milliseconds: shortAnimationDuration),
vsync: this,
);
Future.delayed(Duration(seconds: 5), () {
_controller.repeat(reverse: true);
});
}

Flutter: Creating an add_close AnimatedIcons

I'd like to create a beginning icon as Icon.add and end icon which is Icon.close in the AnimatedIcon widget. For e.g. their is a prebuilt animation of add_event that corresponds to begin animation = add and end animation = event. I'd like to change the end animation to be Icon.close. It's unclear how to do this as there's no documentation readily available for creating custom animations. The most relevant code I could find is: https://github.com/flutter/flutter/blob/e10df3c1a65f9d7db3fc5340cffef966f7bd40a6/packages/flutter/lib/src/material/animated_icons/data/add_event.g.dart. I believe I should use vitool. How can I go about creating new animations?
Yes, friend, you need to create your own animation
I have written code for the situation that you talked about
I used to Transform widget , and AnimationController
import 'dart:math';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
AnimationController animatedController;
double _angle = 0;
#override
void initState() {
animatedController =
AnimationController(vsync: this, duration: Duration(milliseconds: 300));
animatedController.addListener(() {
setState(() {
_angle = animatedController.value * 45 / 360 * pi * 2;
});
});
super.initState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: InkResponse(
onTap: () {
if (animatedController.status == AnimationStatus.completed)
animatedController.reverse();
else if (animatedController.status == AnimationStatus.dismissed)
animatedController.forward();
},
child: Transform.rotate(
angle: _angle,
child: Icon(
Icons.add,
size: 50,
),
),
),
)));
}
}
If you need to customize more than this, Take a look at the AnimatedContainer
So I had some free time and this is something many people might want to use at some point as we do not have much options for AnimatedIcons that are already given to us.
So I went ahead and built this small package and uploaded it on pub dart that solves what you are looking for.
With this package you can animate any two icons.
Check on pub dart animate_icons
Simply add the package into pubspec.yaml like this:
animate_icons:
Then use this Widget like this:
AnimateIcons(
startIcon: Icons.add,
endIcon: Icons.close,
size: 60.0,
onStartIconPress: () {
print("Clicked on Add Icon");
},
onEndIconPress: () {
print("Clicked on Close Icon");
},
duration: Duration(milliseconds: 500),
color: Theme.of(context).primaryColor,
clockwise: false,
),
if the simple transition between the two icons is enough, then simple_animated_icon package might be useful.
class AnimatedIconButton extends StatefulWidget {
#override
_AnimatedIconButtonState createState() => _AnimatedIconButtonState();
}
class _AnimatedIconButtonState extends State<AnimatedIconButton>
with SingleTickerProviderStateMixin {
bool _isOpened = false;
AnimationController _animationController;
Animation<double> _progress;
#override
void initState() {
super.initState();
_animationController =
AnimationController(vsync: this, duration: Duration(milliseconds: 300))
..addListener(() {
setState(() {});
});
_progress =
Tween<double>(begin: 0.0, end: 1.0).animate(_animationController);
}
#override
void dispose() {
_animationController.dispose();
super.dispose();
}
void animate() {
if (_isOpened) {
_animationController.reverse();
} else {
_animationController.forward();
}
setState(() {
_isOpened = !_isOpened;
});
}
#override
Widget build(BuildContext context) {
return IconButton(
onPressed: animate,
icon: SimpleAnimatedIcon(
startIcon: Icons.add,
endIcon: Icons.close,
progress: _progress,
));
}
}

Flutter image animate bigger and smaller

I want to make a simple recurring animation to an image. It should keep animating until the user closes the screen. What I want it that the image slowly gets bigger and then smaller.
I've checked the animatedContainer, but that doesn't seem to do this dynamically. Here is the code I used with AnimatedContainer:
#override
void didChangeDependencies() {
increaseSize(widget.seconds);
super.didChangeDependencies();
}
void increaseSize(int toSize) {
for (i = 0; i > toSize; i++) {
setState(() {
_width += i;
_height += i;
});
}
}
#override
Widget build(BuildContext context) {
return AnimatedContainer(
// Use the properties stored in the State class.
width: _width,
height: _height,
decoration: BoxDecoration(
color: _color,
borderRadius: _borderRadius,
),
// Define how long the animation should take.
duration: Duration(seconds: 30),
// Provide an optional curve to make the animation feel smoother.
curve: Curves.easeInOutBack,
);
;
}
Has anyone come across this before, any help would be appreciated.
Many thanks
Try something like this:
It's an example of a container which grows and shrinks from 50 to 100 and back to 50.
class SizeWidget extends StatefulWidget {
final int seconds;
const SizeWidget({this.seconds = 3});
#override
_SizeWidgetState createState() => _SizeWidgetState();
}
class _SizeWidgetState extends State<SizeWidget> with TickerProviderStateMixin {
AnimationController _animationController;
Animation _sizeAnimation;
bool reverse = false;
#override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this, duration: Duration(seconds: widget.seconds))
..addStatusListener((status) {
if (status == AnimationStatus.completed) {
_animationController.repeat(reverse: !reverse);
reverse = !reverse;
}
});
_sizeAnimation =
Tween<double>(begin: 50.0, end: 100.0).animate(_animationController);
_animationController.forward();
}
#override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _sizeAnimation,
builder: (context, child) => Container(
width: _sizeAnimation.value,
height: _sizeAnimation.value,
color: Colors.red,
),
);
}
}
Another option would be to use a TweenSequence and tell the animation controller to repeat itself:
class SizeWidget extends StatefulWidget {
final int seconds;
const SizeWidget({this.seconds = 3});
#override
_SizeWidgetState createState() => _SizeWidgetState();
}
class _SizeWidgetState extends State<SizeWidget> with TickerProviderStateMixin {
AnimationController _animationController;
Animation _sizeAnimation;
#override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this, duration: Duration(seconds: widget.seconds));
_sizeAnimation = TweenSequence<double>(
[
TweenSequenceItem<double>(
tween: Tween<double>(
begin: 50,
end: 100,
),
weight: 50,
),
TweenSequenceItem<double>(
tween: Tween<double>(
begin: 100,
end: 50,
),
weight: 50,
),
],
).animate(_animationController);
_animationController.repeat(reverse: true);
}
#override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _sizeAnimation,
builder: (context, child) => Container(
width: _sizeAnimation.value,
height: _sizeAnimation.value,
color: Colors.red,
),
);
}
}

Animate a color periodically in flutter

I have a container widget that I try to animate from Colors.blue to Colors.blueGrey and back periodically every 2 seconds.
How can I most easily tackle this in Flutter?
You can use an infinite while loop, don't think this is the best way of doing this, but it gets the job done.
I have a Color Changing class
class ColorChanger extends StatefulWidget {
#override
_ColorChangerState createState() => _ColorChangerState();
}
class _ColorChangerState extends State<ColorChanger>
with SingleTickerProviderStateMixin {
AnimationController _animationController;
Animation _colorTween;
#override
void initState() {
_animationController = AnimationController(
vsync: this, duration: Duration(milliseconds: 1999));
_colorTween = ColorTween(begin: Colors.blue, end: Colors.blueGrey)
.animate(_animationController);
changeColors();
super.initState();
}
Future changeColors() async {
while (true) {
await new Future.delayed(const Duration(seconds: 2), () {
if (_animationController.status == AnimationStatus.completed) {
_animationController.reverse();
} else {
_animationController.forward();
}
});
}
}
#override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _colorTween,
builder: (context, child) => Container(
color: _colorTween.value,
),
);
}
}
This is a rough example, but should lead you in the right direction.
Please see ColorTween Class
I would suggest using the AnimatedContainer. This widget allows you to build it with a particular atribute like color and when you rebuild it with a different value it performs linear interpolation between those values.
#override
Widget build(BuildContext context) {
return AnimatedContainer(
width: 100,
height: 100,
duration: _animationDuration,
color: _color,
);
}
Then you just have to rebuild the widget with a different color:
void _changeColor() {
final newColor = _color == Colors.blue ? Colors.blueGrey : Colors.blue;
setState(() {
_color = newColor;
});
}
The make it periodically I would use a timer class:
_timer = Timer.periodic(_animationDuration, (timer) => _changeColor());
The whole code:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
class AnimationTest extends StatefulWidget {
#override
_AnimationTestState createState() => _AnimationTestState();
}
class _AnimationTestState extends State<AnimationTest> {
final _animationDuration = Duration(seconds: 2);
Timer _timer;
Color _color;
#override
void initState() {
super.initState();
_timer = Timer.periodic(_animationDuration, (timer) => _changeColor());
_color = Colors.blue;
}
void _changeColor() {
final newColor = _color == Colors.blue ? Colors.blueGrey : Colors.blue;
setState(() {
_color = newColor;
});
}
#override
Widget build(BuildContext context) {
return AnimatedContainer(
width: 100,
height: 100,
duration: _animationDuration,
color: _color,
);
}
#override
void dispose() {
super.dispose();
_timer.cancel();
}
}

How do i access an inner widget's animation controller (or state) from outer widget?

So I’ve been experimenting with Flutter & Dart for the past few days.
I’m stuck on this one for over a day now, so I’m here.
So I have the AlarmScreen, and we have 2 objects inside it. One is the DraggableMoonWidget, and the other is the RisingSunWidget.
Currently, the RisingSunWidget animates onto the screen from the bottom, while the DraggableMoonWidget is draggable by touch.
What I want to achieve, is that when the RisingSunWidget’s animation would stop and change when the DraggableMoonWidget is being dragged. So I have the MoonDragListener in place and working, but I still can’t figure it out. (currently calls the listener back to the AlarmScreen, but then what?)
I have tried a whole bunch of methods to do that, all deleted since then, as not a single one worked.
TLDR
How do I control the RisingSunWidget’s animation controller when the user touches the DraggableMoonWidget, for example, I want to stop the controller and animate it to a different point.
What is the best approach in dart/flutter?
AlarmScreen
import 'package:flutter/widgets.dart';
import 'package:moonworshiper_app/backgrounds.dart';
import 'package:moonworshiper_app/ui/alarm/moon_draggable.dart';
import 'package:moonworshiper_app/ui/alarm/rising_sun.dart';
class AlarmScreen extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return new _AlarmScreenState();
}
}
class _AlarmScreenState extends State<AlarmScreen> {
bool _moonWasTouched = false;
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return new Stack(
children: <Widget>[
new DraggableMoonWidget(new MoonDragListener(this)),
new LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return new RisingSunWidget(constraints.heightConstraints().maxHeight, _moonWasTouched);
})
],
);
}
void _refreshSun() {
setState(() {
_moonWasTouched = true;
});
}
}
class MoonDragListener {
_AlarmScreenState state;
MoonDragListener(this.state);
void onMoonDragStarted() {
state._refreshSun();
}
}
DraggableMoonWidget
import 'package:flutter/widgets.dart';
import 'package:moonworshiper_app/ui/alarm/alarm_screen.dart';
class DraggableMoonWidget extends StatefulWidget {
final MoonDragListener moonStartListener;
DraggableMoonWidget(this.moonStartListener);
State<StatefulWidget> createState() => new _DraggableMoonState();
}
class _DraggableMoonState extends State<DraggableMoonWidget>
with TickerProviderStateMixin {
final moonDragTween = new Tween<Offset>(
begin: new Offset(0.0, -0.5),
end: new Offset(0.0, 0.5),
);
var moonAnimListener;
AnimationController _animationController;
Animation<Offset> _dragAnimation;
AnimationController _dragAnimationController;
bool isFirstDraw = true;
#override
initState() {
super.initState();
_animationController = new AnimationController(
vsync: this,
duration: const Duration(milliseconds: 3000),
);
_dragAnimationController = new AnimationController(vsync: this);
moonAnimListener = (AnimationStatus status) {
if (status == AnimationStatus.dismissed) {
_animationController.forward();
} else if (status == AnimationStatus.completed) {
_animationController.reverse();
} else if (status == AnimationStatus.forward) {}
};
_dragAnimation = moonDragTween.animate(new CurvedAnimation(
parent: _dragAnimationController,
curve: Curves.easeInOut,
reverseCurve: Curves.easeInOut));
_dragAnimationController.animateTo(0.5, duration: new Duration());
_animationController.addStatusListener(moonAnimListener);
_animationController.forward();
}
#override
Widget build(BuildContext context) {
return new Container(
child: new Center(
child: new SlideTransition(
position: _dragAnimation,
child: new GestureDetector(
child: new Image.asset(
"assets/moon.png",
width: 280.0,
height: 280.0,
),
onVerticalDragStart: (DragStartDetails details) {
print("start:" + details.globalPosition.toString());
_animationController.removeStatusListener(moonAnimListener);
_animationController.stop();
_dragStartDetails = details;
_dragAnimationController.animateTo(0.5,
duration: new Duration(milliseconds: 50));
if (isFirstDraw) {
isFirstDraw = false;
widget.moonStartListener.onMoonDragStarted();
}
},
),
),
),
// margin: new EdgeInsets.only(top: 48.0),
);
}
#override
void dispose() {
_animationController.dispose();
super.dispose();
}
}
RisingSunWidget
import 'package:flutter/widgets.dart';
class RisingSunWidget extends StatefulWidget {
// needed to calculate the offset map
final double screenHeight;
// that's how we know if the use touched the moon
final bool moonWasTouched;
RisingSunWidget(this.screenHeight, this.moonWasTouched);
#override
State<StatefulWidget> createState() {
return new RisingSunState();
}
}
class RisingSunState extends State<RisingSunWidget> with TickerProviderStateMixin {
AnimationController _animationController;
Animation<Offset> _sunAnimation;
final double sunSize = 320.0;
#override
initState() {
super.initState();
_animationController = new AnimationController(
vsync: this,
duration: const Duration(milliseconds: 6000),
);
// how many suns fit in the height of our screen
assert(widget.screenHeight > sunSize);
double sunsInHeight = widget.screenHeight / sunSize;
print(sunsInHeight.toString() + " suns could fit on the user's screen");
var sunsPlusMargins = sunsInHeight + 1; // required margins
final moonTween = new Tween<Offset>(
begin: new Offset(0.0, -0.5 * sunsPlusMargins),
end: new Offset(0.0, 0.5 * sunsPlusMargins), //move by 8% of height max
);
_sunAnimation = moonTween.animate(new CurvedAnimation(
parent: _animationController,
curve: Curves.easeInOut,
reverseCurve: Curves.easeInOut,
));
if (widget.moonWasTouched) {
_animationController.stop();
_animationController.animateTo(0.68,
duration: new Duration(milliseconds: 2000));
} else {
_animationController.animateTo(0.88,
duration: new Duration(milliseconds: 0));
_animationController.animateTo(0.75,
duration: new Duration(milliseconds: 15000));
}
}
#override
Widget build(BuildContext context) {
return new Center(
child: new SlideTransition(
position: _sunAnimation,
child: new Image.asset(
"assets/sun.png",
width: sunSize,
height: sunSize,
),
),
);
}
#override
void dispose() {
_animationController.dispose();
super.dispose();
}
}
2 possibilities :
Use the BuildContext context that is provided in your build method. BuildContext has a few methods get the closest parent of a specific type.
Pass a key attribute to the desired widget.
GlobalKey to be exact.
GlobalKey allows you directly access to a Widget or it's state.