Flutter FadeIn/FadeOut animation together - flutter

in this simple sample code i want to have fadeIn and fadeOut animation together, but in this code fadeIn work only and reverse not work, how can i have both of them together?
import 'package:flutter/material.dart';
void main()=>runApp(MaterialApp(home: FadeTransitionSample(),));
class FadeTransitionSample extends StatefulWidget {
#override
State<StatefulWidget> createState() => _Fade();
}
class _Fade extends State<FadeTransitionSample> with TickerProviderStateMixin {
AnimationController animation;
Animation<double> _fadeInFadeOut;
#override
void initState() {
super.initState();
animation = AnimationController(vsync: this, duration: Duration(seconds: 3),);
_fadeInFadeOut = Tween<double>(begin: 0.0, end: 0.1).animate(animation);
animation.addListener((){
if(animation.isCompleted){
animation.reverse();
}else{
animation.forward();
}
});
animation.repeat();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Center(
child: FadeTransition(
opacity: animation,
child: Container(
color: Colors.green,
width: 100,
height: 100,
),
),
),
),
);
}
}

Alright, I am assuming, you are looking to get a FadeIn & FadeOut animation on your container.
There are a few things you need to change.
The FadeTransition class should not take the animation for opacity, rather it should be the _fadeInFadeOut variable
The animation starts, when you call the animation.forward() rather than animation.repeat() (Since in your case, you also need to reverse the animation, always start with the animation.forward() call).
Make sure to use the addStatusListener() method instead of addListener() since you get much better control over your states.
So, all of this put together, below is the working code to make your animation work.
import 'package:flutter/material.dart';
void main()=>runApp(MaterialApp(home: FadeTransitionSample(),));
class FadeTransitionSample extends StatefulWidget {
#override
State<StatefulWidget> createState() => _Fade();
}
class _Fade extends State<FadeTransitionSample> with TickerProviderStateMixin {
AnimationController animation;
Animation<double> _fadeInFadeOut;
#override
void initState() {
super.initState();
animation = AnimationController(vsync: this, duration: Duration(seconds: 3),);
_fadeInFadeOut = Tween<double>(begin: 0.0, end: 0.5).animate(animation);
animation.addStatusListener((status){
if(status == AnimationStatus.completed){
animation.reverse();
}
else if(status == AnimationStatus.dismissed){
animation.forward();
}
});
animation.forward();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Center(
child: FadeTransition(
opacity: _fadeInFadeOut,
child: Container(
color: Colors.green,
width: 100,
height: 100,
),
),
),
),
);
}
}

Option #1
Simple solution and straight forward with less code, the idea is to combine FadeOut inside FadeIn and give the FadeOut delay amount greater than the duration of the FadeIn, just copy and paste then just change the Image.asset widget to anything you want to fade in/out
1- Add this package.
2- Import it in your page import 'package:animate_do/animate_do.dart';
3- Add this Widget:
Widget _animateLogo() {
return Container(
child: FadeIn(
animate: true,
duration: Duration(seconds: 2),
child: FadeOut(
animate: true,
delay: Duration(seconds: 2),
duration: Duration(seconds: 1),
// Just change the Image.asset widget to anything you want to fade in/out:
child: Image.asset(
"assets/images/logo.png",
height: 150,
width: 150,
fit: BoxFit.contain,
), //Image.asset
) // FadeOut
),
);
}
Option #2:
Use native class AnimatedOpacity class with setState:
// 1- Declare
bool _shouldFade = false;
// 2- Animation fucntion
Widget _animateLogo() {
return AnimatedOpacity(
duration: Duration(milliseconds: 200),
opacity: _shouldFade ? 1 : 0,
child: Container(
// Whatever you want to fadein fadeout
),
);
}
// 3- Animate whenever needed
setState(() {
// Fade in
_shouldFade = true;
});
// Fadeout after 3 seconds
Future.delayed(
Duration(seconds: 3),
() => setState(() {
_shouldFade = false;
}),
);

Related

How to create animated stacked card with left and right slider in flutter?

I want it to look like the image, but I couldn't find a way, please help?
i am try to using carousel_slider, pub.dev/packages/flutter_tindercard,flutter_multi_carousel,FlutterCardSwipe plugin for this and try to modify this plugins but it not implement exactly look like video.
Consider using AnimatedPositioned inside a Stack. You can the play with the values of their position (top, bottom, left, right) and sizes (height, width).
Here is a video the flutter team made: https://www.youtube.com/watch?v=hC3s2YdtWt8
Consider this as just one way to do it, there are many ways.
A simple of doing this using AnimatedContainer and changing active container with Timer.
Run on dartPad
class AnimC3 extends StatefulWidget {
const AnimC3({Key? key}) : super(key: key);
#override
_AnimC3State createState() => _AnimC3State();
}
class _AnimC3State extends State<AnimC3> {
final duration = const Duration(seconds: 1);
int _currentIndex = 0;
late Timer _timer;
#override
void initState() {
_timer = Timer.periodic(duration, (timer) {
setState(() {
_currentIndex++;
if (_currentIndex > 2) _currentIndex = 0;
});
});
super.initState();
}
#override
void dispose() {
_timer.cancel();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: SizedBox(
width: 200,
child: Row(
children: [
AnimatedContainer(
duration: duration,
width: _currentIndex == 0 ? 100 : 50,
color: Colors.purpleAccent,
),
AnimatedContainer(
duration: duration,
color: Colors.cyanAccent,
width: _currentIndex == 1 ? 100 : 50,
),
AnimatedContainer(
duration: duration,
color: Colors.red,
width: _currentIndex == 2 ? 100 : 50,
),
],
),
),
),
);
}
}

Flutter staggered animation on same property

I want to animate the scale of a widget.
On tap down it should scale down and on tap up, scale up.
Problem is how can I use staggered animations on the same property?
There is an older question here on SO (Chaining seperate animations that work on the same properties) but it has no working answers.
Ive tried to adapt its solution but the problem is each animation listener is called even if the specified Interval has not been reached.
Ive added a condition which checks if _animationFuture is set but with this pattern you have to keep a value for any running animation you have in your widget which is a bit cumbersome.
Is there a "native" solution where you dont need a work around or are multiple animations on same value just not supported?
I do not want to reverse() the animation. Each tween must be able to have a custom curve, begin and end values.
Heres my widget:
class AnimatedIconButton extends StatefulWidget {
final Widget child;
final Function onPress;
AnimatedIconButton({#required this.child, this.onPress}): assert(child != null);
#override
_AnimatedIconButtonState createState() => _AnimatedIconButtonState();
}
class _AnimatedIconButtonState extends State<AnimatedIconButton> with SingleTickerProviderStateMixin {
AnimationController _animationController;
Animation<double> _animationIn;
Animation<double> _animationOut;
double _scale = 1;
TickerFuture _animationFuture;
Function get onPress => widget.onPress ?? () => null;
_tapDown(TapDownDetails details) {
print("_tapDown progress=${_animationController.value} scale=$_scale");
_animationFuture = _animationController.animateTo(0.5);
}
_tapUp(TapUpDetails details) {
assert(_animationFuture != null);
print("_tapUp progress=${_animationController.value} scale=$_scale");
_animationFuture.then((_) {
_animationFuture = null;
_animationController.animateTo(1);
});
}
#override
void initState() {
super.initState();
_animationController = AnimationController(value: 0, vsync: this, duration: const Duration(milliseconds: 1500), animationBehavior: AnimationBehavior.preserve);
_animationIn = Tween<double>(begin: 1.0, end: 0.9).animate(CurvedAnimation(
parent: _animationController,
curve: Interval(
0, 0.5, curve: Curves.easeOut,
),
))..addListener(() {
if (_animationFuture == null) return;
print("_animationIn.value ${_animationOut.value}");
_scale = _animationIn.value;
});
_animationOut = Tween<double>(begin: 0.9, end: 1).animate(CurvedAnimation(
parent: _animationController,
curve: Interval(
0.5, 1, curve: Curves.elasticOut,
),
))..addListener(() {
if (_animationFuture != null) return;
print("_animationOut.value ${_animationOut.value}");
_scale = _animationOut.value;
});
}
#override
void dispose() {
_animationController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return GestureDetector(
onTapUp: _tapUp,
onTapDown: _tapDown,
child: AnimatedBuilder(
animation: _animationController,
child: widget.child,
builder: (_, child) {
return Transform.scale(
scale: _scale,
transformHitTests: false,
child: child,
);
},
),
);
}
}
You can use a TweenSequence to stagger multiple animations behind each other. Borrowing the example from the documentation and changing it a bit, here's a copy-paste widget that animates a blue container from 100x100 to 200x200, pauses for a bit, and animates it back:
class _StaggeredState extends State<Staggered>
with SingleTickerProviderStateMixin {
/// Controller managing the animation
late final AnimationController controller;
/// The aninmation of our TweenSequence
late final Animation<double> animation;
/// An Animatable consisting of a series of tweens
final Animatable<double> tweenSequence = TweenSequence<double>(
<TweenSequenceItem<double>>[
// Animate from .5 to 1 in the first 40/80th of this animation
TweenSequenceItem<double>(
tween: Tween<double>(begin: 100, end: 200)
.chain(CurveTween(curve: Curves.ease)),
weight: 40.0,
),
// Maintain still at 1.0 for 20/80th
TweenSequenceItem<double>(
tween: ConstantTween<double>(200),
weight: 20.0,
),
// Animate back from 1 to 0.5 for the last 40/80th
TweenSequenceItem<double>(
tween: Tween<double>(begin: 200, end: 100)
.chain(CurveTween(curve: Curves.ease)),
weight: 40.0,
),
],
);
#override
void initState() {
super.initState();
controller = AnimationController(
vsync: this,
duration: const Duration(seconds: 1),
);
// Animate our TweenSequence using our controller
animation = tweenSequence.animate(controller);
// Add a listener that calls [setState] so our widget rebuilds
// when the animation value changes.
controller.addListener(() => setState(() {}));
// Set the controller to repeat indefinitely
controller.repeat();
}
#override
void dispose() {
super.dispose();
controller.dispose();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Container(
width: animation.value,
height: animation.value,
color: Colors.blue,
),
),
),
);
}
}

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,
),
);
}
}

Rotating container indefinitely

I would like to rotate an Image indefinitely.
This container is one of the widget within the stack and would like this to be rotating continuously non stop.
final AnimationController animation = AnimationController(
duration: const Duration(milliseconds: 1800),
vsync: const NonStopVSync(),
)..repeat();
final Tween tween = Tween(begin: 0.0, end: math.pi);
var square = Container(
width: 100,
height: 100,
transform: Matrix4.identity(),
color: Colors.amber,
);
...
class Foo extends State<Bar> {
...
animation.addListener((){
square.transform = Matrix4.rotationZ(tween.evaluate(animation));
});
Widget build(BuildContext context) {
return Stack(
children: [
...
Center(
child: square
)
]
)
}
}
and I get this error: 'transform' can't be used as a setter because it's final. (assignment_to_final at [digital_clock] lib/digital_clock.dart:139)
How would I do what I'm trying to do?
Try something like this:
class InfiniteAnimation extends StatefulWidget {
final Widget child;
final int durationInSeconds;
InfiniteAnimation({#required this.child, this.durationInSeconds = 2,});
#override
_InfiniteAnimationState createState() => _InfiniteAnimationState();
}
class _InfiniteAnimationState extends State<InfiniteAnimation>
with SingleTickerProviderStateMixin {
AnimationController animationController;
Animation<double> animation;
​
#override
void initState() {
super.initState();
animationController = AnimationController(
vsync: this,
duration: Duration(seconds: widget.durationInSeconds),
);
animation = Tween<double>(
begin: 0,
end: 12.5664, // 2Radians (360 degrees)
).animate(animationController);
​
animationController.forward();
​
animation.addStatusListener((status) {
if (status == AnimationStatus.completed) {
animationController.repeat();
}
});
}
​
#override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: animationController,
builder: (context, child) => Transform.rotate(
angle: animation.value,
child: widget.child,
),
);
}
#override
void dispose() {
animationController?.dispose();
super.dispose();
}
}
You basically need to create a StatefulWidget that mixes in (with keyword) the SingleTickerProviderStateMixin, provide an AnimationController, start the animation, then when the animation completes, repeat.
The AnimationBuilder is a better way of telling the widget to update on every frame without having to listen to the animationController and call setState explicitly.
You can use it like this:
InfiniteAnimation(
durationInSeconds: 2, // this is the default value
child: Icon(
Icons.expand_more,
size: 50.0,
color: Colors.white,
),
)

Play & pause a Flutter animation

I was trying to add a button to this page that will (play or pause) the waves animation in the background.
code link: https://github.com/apgapg/flutter_profile/blob/master/lib/page/intro_page.dart
I've tried many things but since I still bad with flutter animations I still without result.
Thanks in advance.
Short answer:
AnimationController _controller = ...;
// To stop animation
_controller.stop();
// To start from beginning
_controller.forward();
// To start from a point other than the very beginning.
_controller.forward(from: 0.8);
Long answer:
I wasn't aware of that code, here is how I did. All you need is Controller.reset() to stop the animation and Controller.repeat() to start it.
However if you need to start the animation just once, use Controller.forward() and Controller.reverse().
void main() => runApp(MaterialApp(home: Scaffold(body: HomePage())));
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {
AnimationController _controller;
bool _isPlaying = true;
#override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
lowerBound: 0.3,
duration: Duration(seconds: 3),
)..repeat();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Animation")),
body: Stack(
alignment: Alignment.center,
children: <Widget>[
_buildCircularContainer(200),
_buildCircularContainer(250),
_buildCircularContainer(300),
Align(child: CircleAvatar(backgroundImage: AssetImage("assets/images/profile.png"), radius: 72)),
Align(
alignment: Alignment(0, 0.5),
child: RaisedButton(
child: Text(_isPlaying ? "STOP" : "START"),
onPressed: () {
if (_isPlaying) _controller.reset();
else _controller.repeat();
setState(() => _isPlaying = !_isPlaying);
},
),
),
],
),
);
}
Widget _buildCircularContainer(double radius) {
return AnimatedBuilder(
animation: CurvedAnimation(parent: _controller, curve: Curves.fastLinearToSlowEaseIn),
builder: (context, child) {
return Container(
width: _controller.value * radius,
height: _controller.value * radius,
decoration: BoxDecoration(color: Colors.black54.withOpacity(1 - _controller.value), shape: BoxShape.circle),
);
},
);
}
}
When you directly have access to the AnimationController this snippet will start/stop the animation, wherever it left off.
animationController.isAnimating
? animationController.stop()
: animationController.forward();
Here the .isAnimating property is of type bool and is true when the animationController is animating at the moment. Depending on the result .stop()/.forward() will stop/start the animation respectively.