Flutter - Slide Transition between two different sized widgets - flutter

I am trying to animate between widgets as follows:
AnimatedSwitcher(
duration: const Duration(seconds: 1),
transitionBuilder: (Widget child, Animation<double> animation) {
return SlideTransition(
position: Tween(
begin: Offset(1.0, 0.0),
end: Offset(0.0, 0.0),)
.animate(animation),
child: child,
);
},
child: Provider.of<UserWidgets>(context, listen: false).renderWidget(context),
),
This works fine but for two different sized widgets its not smooth because of OffSet.

Try wrapping both your child widgets inside an Align widget like this,
child: Align(
alignment: Alignment.topCenter,
child: Provider.of<UserWidgets>(context, listen: false).renderWidget(context),
)
This should ensure that both your old and new children are always aligned to the topCenter while animating.
Here is the full working example.
class Switcher extends StatefulWidget {
State<StatefulWidget> createState() => SwitcherS();
}
class SwitcherS extends State<Switcher> {
bool state = false;
buildChild (index) => Align(
alignment: Alignment.topCenter,
child: Container(
width: index == 0 ? 100 : 150,
height: index == 0 ? 200 : 150,
color:index == 0 ? Colors.deepPurple : Colors.deepOrange,
),
key: ValueKey('$index'));
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => setState(() { state = !state; }),
child: Padding(
padding: EdgeInsets.all(24),
child: AnimatedSwitcher(
duration: const Duration(seconds: 1),
transitionBuilder: (Widget child, Animation<double> animation) {
return SlideTransition(
position: Tween(
begin: Offset(1.0, 1.0),
end: Offset(0.0, 0.0),
).animate(animation),
child: child,
);
},
child: buildChild(state ? 0 : 1),
),
);
}
}

Related

Revealing a widget from beneath another thourgh animation in Flutter

I'm trying to make an animation with Flutter Hooks where I have a symbol going up and back down from behind a container when I tap it, but I can't get the symbol to stay behind the container. It instead goes way down. What am I doing wrong?
#override
Widget build(BuildContext context) {
AnimationController controller = useAnimationController(
duration: const Duration(seconds: 3), initialValue: 0);
Animation<double> animation = Tween<double>(begin: 0, end: 1)
.animate(CurvedAnimation(parent: controller, curve: Curves.easeOut));
return Stack(alignment: Alignment.bottomCenter, children: [
AnimatedBuilder(
animation: animation,
builder: (context, child) {
return Transform.translate(
offset: Offset(0, cos(animation.value) * 500),
child: child,
);
},
child: const CircleAvatar(
backgroundColor: Colors.red,
radius: 30,
)),
GestureDetector(
child: Container(
color: Colors.brown,
width: 300,
height: 100,
),
onTap: () {
print("TAP : ${(controller.status != AnimationStatus.completed)}");
if (controller.status != AnimationStatus.completed) {
controller.forward().whenComplete(() => controller.reverse());
}
},
),
]);
}

Animating the front card to the back in a stack view of cards in flutter

I have got a stack of cards and scroll wheel. I am trying to animate the front card, move it to the right, then bring it back in a way it goes to the back of the cards.
I have used a future function to specify witch part of code should be done first. But what I get is; it changes the index of the card first the animation takes place. Here is my code:
class AnimationsPractice extends StatefulWidget {
static const String id = 'test_screen';
#override
_AnimationsPracticeState createState() => _AnimationsPracticeState();
}
class _AnimationsPracticeState extends State<AnimationsPractice>
with SingleTickerProviderStateMixin {
FixedExtentScrollController _scrollController =
FixedExtentScrollController(initialItem: 0);
AnimationController _controller;
Animation<Offset> _offsetAnimation;
int selected;
List<Widget> sampleCard;
Animation<Offset> _offsetAnimation2;
bool halfWayAnimation;
#override
void initState() {
super.initState();
_controller =
AnimationController(duration: const Duration(seconds: 1), vsync: this)
..repeat();
_offsetAnimation = Tween<Offset>(
begin: Offset.zero,
end: const Offset(1.5, 0.0),
).animate(
CurvedAnimation(
parent: _controller,
curve: Interval(
0.0,
0.5,
curve: Curves.elasticIn,
),
),
);
_offsetAnimation2 = Tween<Offset>(
begin: const Offset(1.5, 0.0),
end: Offset.zero,
).animate(
CurvedAnimation(
parent: _controller,
curve: Interval(
0.5,
1.0,
curve: Curves.elasticIn,
),
),
);
halfWayAnimation = false;
_controller.stop();
sampleCard = [
Container(
height: 60,
width: 40,
color: Colors.red,
),
Transform.rotate(
angle: 10 * (pi / 180),
child: Container(
height: 60,
width: 40,
color: Colors.blueGrey,
)),
SlideTransition(
position: halfWayAnimation ? _offsetAnimation2 : _offsetAnimation,
child: Container(
height: 60,
width: 40,
color: Colors.yellow,
),
),
];
}
#override
void dispose() {
super.dispose();
_controller.dispose();
}
Future<void> _playAnimation() async {
try {
await _controller.forward().orCancel;
await siftingIndex();
await _controller.reverse().orCancel;
} on TickerCanceled {
// the animation got canceled, probably because it was disposed of
}
}
Future<void> siftingIndex() {
return Future.delayed(const Duration(microseconds: 200), () {
sampleCard.insert(0, sampleCard[sampleCard.length - 1]);
sampleCard.removeLast();
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: PreferredSize(
preferredSize: const Size.fromHeight(180.0),
child: SafeArea(
child: TextButton(
child: Text('back to login'),
onPressed: () {
Navigator.pushNamed(context, LoginScreen.id);
},
),
),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Stack(children: sampleCard),
CustomScrollWheel(
onItemChange: (x) {
setState(() {
_playAnimation();
halfWayAnimation = true;
});
},
scrollController: _scrollController,
),
],
),
);
}
}
enter image description here

Flutter animated BackDropFilter

I wanted to know if it was possible to add blur on a screen with fade in and fade out.
Do you have any idea ? I'm currently using BackDropFilter to blur my screen but it doesn't fade when appear...
You can animate the sigma values for blur,
TweenAnimationBuilder<double>(
tween: Tween<double>(begin: 0.0, end: 15.0),
duration: const Duration(milliseconds: 500),
builder: (_, value, child) {
return BackdropFilter(
filter: ImageFilter.blur(sigmaX: value, sigmaY: value),
child: child,
);
},
child: DecoratedBox(
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.5),
),
),
https://api.flutter.dev/flutter/widgets/TweenAnimationBuilder-class.html
I found that I was able to animate the backDropFiter with a widget called AnimatedOpacity. You can use it as the AnimatedContainer!
Enjoy
Pretty much the same answer as #Damon's but including working example
class BackgroundBlurExample extends StatefulWidget {
#override
_BackgroundBlurExampleState createState() => _BackgroundBlurExampleState();
}
class _BackgroundBlurExampleState extends State<BackgroundBlurExample> {
double _begin = 10.0;
double _end = 0.0;
void _animateBlur() {
setState(() {
_begin == 10.0 ? _begin = 0.0 : _begin = 10.0;
_end == 0.0 ? _end = 10.0 : _end = 0.0;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
Align(
alignment: Alignment.center,
child: FlutterLogo(
size: 100,
),
),
// ... Things you want to blur above the IgnorePointer
IgnorePointer( // This is in case you want to tap things under the BackdropFilter
child: TweenAnimationBuilder<double>(
tween: Tween<double>(begin: _begin, end: _end),
duration: Duration(milliseconds: 500),
curve: Curves.easeIn,
builder: (_, value, __) {
return BackdropFilter(
filter: ImageFilter.blur(
sigmaX: value,
sigmaY: value,
),
child: Container(
width: double.maxFinite,
height: double.maxFinite,
color: Colors.transparent,
),
);
},
),
),
Align(
alignment: Alignment.bottomCenter,
child: ElevatedButton(
onPressed: _animateBlur,
child: Text('Animate'),
),
)
],
),
);
}
}

How to chain multiple Animation objects?

What i want is to chain multiple Animation object like if we have an Animation<double> that goes from 0 to 40 (let's call it the big one) and i have another 4 Animation<double> object what i want is
when the big animation starts the first animation start with it , but it end's when the big one reach 10.
when the big one reach 10 the second animation start's and end's when the big one reach 20.
etc...
any one knows how to do it in flutter ??
It sounds like staggered animations.
Basically, you can create an animation controller and set the total duration time.
Then, you can create a separate tween for each animation you want to perform. For each tween, you can define a curve, for that curve you can define an interval in the percentage of the total duration of the animation. There is a pretty good example in a flutter.dev when you search for staggered animations. Note: they don't have to be one after another, despite the name, they can be fired at the same time, but end at different time as you want. I'm not sure if it is appropriate to give an answer with just sharing the link to the flutter docs, but here it is
https://flutter.dev/docs/development/ui/animations/staggered-animations.
I have done something similar, but with 2 controllers, which I fire at the same time, but they have different durations.
Ah, I was second :)
edit: here is some code
one with 2 controlers
import 'package:flutter/material.dart';
//import 'package:flutter/scheduler.dart';
import 'package:flutter_color_picker/components/color_ripple.dart';
class ColorKnob extends StatefulWidget {
const ColorKnob({this.color, this.ratio, this.saveColor});
final Color color;
final double ratio;
final Function saveColor;
#override
_ColorKnobState createState() => _ColorKnobState();
}
class _ColorKnobState extends State<ColorKnob> with TickerProviderStateMixin {
AnimationController scaleAnimationController;
AnimationController rippleAnimationController;
Animation<double> scaleAnimation;
#override
void initState() {
super.initState();
scaleAnimationController =
AnimationController(vsync: this, duration: Duration(milliseconds: 100));
rippleAnimationController =
AnimationController(vsync: this, duration: Duration(milliseconds: 400));
scaleAnimationController.addStatusListener((AnimationStatus status) {
if (status == AnimationStatus.completed) {
scaleAnimationController.reverse();
}
});
scaleAnimation = Tween<double>(begin: 1.0, end: 0.8).animate(
CurvedAnimation(
parent: scaleAnimationController, curve: Curves.easeOut));
rippleAnimationController.addStatusListener((AnimationStatus status) {
if (status == AnimationStatus.completed) {
widget.saveColor();
}
});
scaleAnimation.addListener(() {
setState(() {});
});
}
#override
void dispose() {
super.dispose();
scaleAnimationController.dispose();
rippleAnimationController.dispose();
}
#override
Widget build(BuildContext context) {
return Center(
child: Container(
decoration: const BoxDecoration(
shape: BoxShape.circle, color: Colors.transparent),
child: FractionallySizedBox(
widthFactor: widget.ratio,
heightFactor: widget.ratio,
child: ClipOval(
clipBehavior: Clip.antiAlias,
child: Center(
child: Stack(children: <Widget>[
ColorRipple(
controller: rippleAnimationController,
color: widget.color,
),
GestureDetector(
onTap: () {
// timeDilation = 1.0;
scaleAnimationController.forward(from: 0.0);
rippleAnimationController.forward(from: 0.0);
},
child: Transform.scale(
scale: scaleAnimation.value,
alignment: Alignment.center,
child: Container(
width: 60.0,
height: 60.0,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: widget.color,
border: Border.all(
color: const Color(0xFFFFFFFF),
style: BorderStyle.solid,
width: 4.0,
),
boxShadow: const <BoxShadow>[
BoxShadow(
offset: Offset(0.0, 1.0),
blurRadius: 6.0,
spreadRadius: 1.0,
color: Color(0x44000000),
)
]),
),
),
),
]),
),
),
)),
);
}
}
and one with multiple tweens
import 'package:flutter/material.dart';
//import 'package:flutter/scheduler.dart';
class ColorRipple extends StatelessWidget {
ColorRipple({this.controller, this.color, this.size})
: scaleUpAnimation = Tween<double>(begin: 0.8, end: 5.0).animate(
CurvedAnimation(
parent: controller,
curve: const Interval(
0.2,
1.0,
curve: Cubic(0.25, 0.46, 0.45, 0.94),
),
),
),
opacityAnimation = Tween<double>(begin: 0.6, end: 0.0).animate(
CurvedAnimation(
parent: controller,
curve: const Interval(
0.4,
1.0,
curve: Cubic(0.25, 0.46, 0.45, 0.94),
),
),
),
scaleDownAnimation = Tween<double>(begin: 1.0, end: 0.8).animate(
CurvedAnimation(
parent: controller,
curve: const Interval(0.0, 0.2, curve: Curves.easeOut),
),
);
final AnimationController controller;
final Animation<double> scaleUpAnimation;
final Animation<double> scaleDownAnimation;
final Animation<double> opacityAnimation;
final Color color;
final Size size;
#override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: controller,
builder: (BuildContext context, Widget child) {
return Container(
child: Transform(
alignment: Alignment.center,
transform: Matrix4.identity()
..scale(scaleDownAnimation.value)
..scale(scaleUpAnimation.value),
child: Opacity(
opacity: opacityAnimation.value,
child: Container(
width: 60.0,
height: 60.0,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: color,
style: BorderStyle.solid,
width: 4.0 - (2 * controller.value))),
)),
),
);
});
}
}

How to animate a Container/Button vertically up and down?

I'm trying to animate the center button vertically up and down like a lift after a button click but can't figure out how to go about it. I am using sequenceAnimations elsewhere on this button so I tried wrapping it in a Positioned widget and change the bottom offset partnered with a Tween to cycle between values, unfortuntley this results in LayoutErrors and crashes...
Tried Position wrapping.
class LiftPage extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _LiftPageState();
}
}
class _LiftPageState extends State<LiftPage> with TickerProviderStateMixin {
AnimationController _pageLoadController;
AnimationController _liftImageController;
SequenceAnimation _liftPageLoadAnimation;
SequenceAnimation _liftImageAnimation;
double _screenWidth = 2000.0;
double _screenHeight = 5000.0;
#override
void initState() {
super.initState();
initPlayer();
_pageLoadController = AnimationController(vsync: this);
_liftImageController = AnimationController(vsync: this);
_liftImageAnimation = SequenceAnimationBuilder()
.addAnimatable(
animatable: Tween<double>(
begin: 0,
end: _screenHeight,
),
from: Duration(seconds: 0),
to: Duration(seconds: _timeForLiftUp),
tag: "liftGoingUp",
)
.animate(_liftImageController);
_liftPageLoadAnimation = SequenceAnimationBuilder()
.addAnimatable(
animatable: ColorTween(
begin: Color(0xfff665c6),
end: Color(0xffF599E9),
),
from: Duration(seconds: 0),
to: Duration(seconds: 4),
tag: "color",
)
.addAnimatable(
animatable: Tween<double>(
begin: 125,
end: 0,
),
from: Duration(seconds: 0),
to: Duration(seconds: 2),
tag: "border",
)
.addAnimatable(
animatable: Tween<double>(
begin: 100,
end: _screenHeight,
),
from: Duration(seconds: 0),
to: Duration(seconds: 4),
tag: "height",
)
.addAnimatable(
animatable: Tween<double>(
begin: 100,
end: _screenWidth,
),
from: Duration(seconds: 0),
to: Duration(seconds: 4),
tag: "width",
)
.addAnimatable(
animatable: ColorTween(
begin: Colors.white,
end: Color(0xffF599E9),
),
from: Duration(seconds: 0),
to: Duration(seconds: 1),
tag: "iconFade",
)
.addAnimatable(
animatable: ColorTween(
begin: Color(0x00000000),
end: Color(0xfff665c6),
),
from: Duration(milliseconds: 2000),
to: Duration(milliseconds: 2300),
tag: "circleFadeIn",
)
.addAnimatable(
animatable: ColorTween(
begin: Color(0x00ffffff),
end: Color(0xfff665c6),
),
from: Duration(milliseconds: 2500),
to: Duration(milliseconds: 3500),
tag: "audioControlsFadeIn",
)
.animate(_pageLoadController);
}
#override
Widget build(BuildContext context) {
List<Widget> childrenStack = <Widget>[
Scaffold(
body: AnimatedBuilder(
animation: _pageLoadController,
builder: (BuildContext context, Widget child) {
return Stack(
children: <Widget>[
Center(child: _placeButton()),
Center(
child: _placeCircle(),
),
Align(
alignment: Alignment.topCenter,
child: _playBackControls(),
),
],
);
}),
),
];
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Center(
child: Text('Lift Page'),
),
backgroundColor: Color(0xfff665c6),
),
body: Center(child: Stack(children: childrenStack)),
));
}
GestureDetector _placeCircle() {
return GestureDetector(
onTap: () {
final _status = _pageLoadController.status;
if (_status == AnimationStatus.completed) {
_pageLoadController.reverse();
_liftPage_liftButtonTapped();
} else {
_pageLoadController.forward();
_liftPage_liftButtonTapped();
}
},
child: Container(
height: 100,
width: 100,
decoration: BoxDecoration(
color: _liftPageLoadAnimation["circleFadeIn"].value,
borderRadius: BorderRadius.circular(125.0),
),
),
);
}
I'm trying to animate the pink circle/button up and down.
https://imgur.com/a/Iu7i5uw
You can use AnimatedContainer, here is an example:
class UpDown extends StatefulWidget {
#override
UpDownState createState() {
return UpDownState();
}
}
class UpDownState extends State<UpDown> {
bool up = false;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: AnimatedContainer(
padding: EdgeInsets.all(10.0),
duration: Duration(milliseconds: 250), // Animation speed
transform: Transform.translate(
offset: Offset(0, up ? -100 : 0), // Change -100 for the y offset
).transform,
child: Container(
height: 50.0,
child: FloatingActionButton(
backgroundColor: Colors.red,
child: Icon(Icons.ac_unit),
onPressed: () {
setState(() {
up = !up;
});
},
),
),
),
),
);
}
}