I have two animation controllers using TickerProviderStateMixin the first animation is working smoothly while the second does not animate when I trigger it's forward method : Here are their declarations :
class HomeAnimator extends StatefulWidget {
#override
_HomeAnimatorState createState() => _HomeAnimatorState();
}
class _HomeAnimatorState extends State<HomeAnimator>
with TickerProviderStateMixin {
AnimationController _controller;
AnimationController _signupctrl;
#override
void initState() {
super.initState();
_controller =
AnimationController(duration: Duration(milliseconds: 900), vsync: this);
_signupctrl =
AnimationController(duration: Duration(milliseconds: 900), vsync: this);
// _controller.forward();
}
#override
void dispose() {
super.dispose();
_controller.dispose();
_signupctrl.dispose();
}
#override
Widget build(BuildContext context) {
return Dahome(controllers: [_controller,_signupctrl]);
}
}
I have two files for the different set of animations :
-- Mahome_EnterAnimation: when clicked on sign-in button
here's a relevant piece :
class EntAnime {
EntAnime(this.controller)
: opanime = Tween<double>(begin: 1, end: 0).animate(CurvedAnimation(
parent: controller,
curve: Interval(0, 0.5, curve: Curves.fastOutSlowIn),
)),
r_opanime = Tween<double>(begin: 0, end: 1).animate(CurvedAnimation(
parent: controller,
curve: Interval(0, 0.5, curve: Curves.fastOutSlowIn),
)),
hfact = Tween<double>(begin: 0, end: 1).animate(CurvedAnimation(
parent: controller,
curve: Interval(0, 0.5, curve: Curves.fastOutSlowIn),
)),
-- Mahome_SignupAnime: for when I press sign-up button
class SignupAnime {
SignupAnime(this.controller):
qopanime = Tween<double>(begin: 0, end: 1).animate(CurvedAnimation(
parent: controller,
curve: Interval(0.0, 0.5, curve: Curves.fastOutSlowIn),
)),
qhfact = Tween<double>(begin: 0, end: 1).animate(CurvedAnimation(
parent: controller,
curve: Interval(0.5, 0.9, curve: Curves.fastOutSlowIn),
));
final AnimationController controller;
final Animation<double> qopanime;
final Animation<double> qhfact;
}
This is how I declared it :
class Dahome extends StatefulWidget {
Dahome({Key key, this.title, #required List<AnimationController> controllers})
: daanime = EntAnime(controllers[0]),
signanime = SignupAnime(controllers[1]),
super(key: key);
final EntAnime daanime;
final SignupAnime signanime;
final String title;
Dastate createState() => Dastate(daanime, signanime);
// #override
}
class Dastate extends State<Dahome> {
Dastate(this.maanime, this.loganime);
final EntAnime maanime;
final SignupAnime loganime;
...
I called the forward methods in two different buttons:
The first one here and the second the same way:
RaisedButton(
onPressed: () => {
maanime.controller.forward()
},
Silly me, the problem was the animatedbuilder widget I had to change the animation parameter with the second controller
Related
I've made the quote fade out and in if the previous quote is from another category. However, I'm unable to replicate that if the quote is from the same category.
How do I make the quote fade out and in if the quote is from the same category?
I feel like tween sequence is getting there but still unable to get the results.
Any guidance is appreciated thanks!
AnimationController animationController;
Animation<double> opacityAnimation;
double opacity1 = 0.0;
///ditto for opacity2 and opacity3
String quotes1 = Quotes1[Random().nextInt(Quotes1.length)];
void generateQuotes1() {
setState(() {
quotes1 = Quotes1[Random().nextInt(Quotes1.length)];
});
}
///ditto for Quotes2 and Quotes 3
#override
void initState() {
super.initState();
changeOpacity();
animationController =
AnimationController(vsync: this, duration: Duration(seconds: 3));
animationController.forward();
opacityAnimation = TweenSequence(
<TweenSequenceItem<double>>[
TweenSequenceItem<double>(
tween: Tween<double>(begin: 1.0, end: 0.0)
.chain(CurveTween(curve: Curves.easeInOutBack)),
weight: 27),
TweenSequenceItem<double>(
tween: Tween<double>(begin: 0.0, end: 1.0)
.chain(CurveTween(curve: Curves.easeInOutBack)),
weight: 27),
],
).animate(
animationController,
);
}
#override
void dispose() {
animationController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold
...
AnimatedOpacity(
opacity: opacity1 == 1.0 ? opacityAnimation.value : 0.0,
duration: Duration(seconds: 1),
child: Text(quotes1),
///this shows the quote
ElevatedButton(
...
onPressed: () {
generateQuotes1();
///This returns randomized quote from the list.
opacity1 = 1.0;
opacity2 = 0.0;
opacity3 = 0.0;
},
),
///ditto for quotes2 and quotes3
...
}
}
I'm trying GetX for my new project and tried to use AnimationController which is inspired this comment. Everything works fine with default value for Tween -> blur & spread & AnimationController -> duration.
What I'm doing:
1: Created an widget with corresponding controller (controller is binded through initialBinding of GetMaterialApp).
GetMaterialApp(
...
initialBinding: AnimationBinding()
...
);
class AnimationBinding extends Bindings {
#override
void dependencies() {
Get.lazyPut<AnimationController>(
() => AnimationController(),
);
}
}
class CustomAnimatedWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return GetBuilder<LogoAnimationController>(
builder: (_) {
return Container(
width: 150,
height: 150,
child: Text('for demo only, originally its not text widget'),
...
remaining code that uses `_.animation.value`
...
),
);
},
);
}
}
class AnimationController extends GetxController with SingleGetTickerProviderMixin {
Animation animation;
AnimationController _animationController;
#override
void onInit() {
super.onInit();
_animationController = AnimationController(
vsync: this, duration: Duration(seconds: 2));
_animationController.repeat(reverse: true);
animation = Tween(begin: 2.0, end: 15.0)
.animate(_animationController)
..addListener(() => update());
}
#override
void onReady() {
super.onReady();
}
#override
void onClose() {
super.onClose();
_animationController.dispose();
}
}
2: Used this widget inside view.
child: CustomAnimatedWidget();
Till now, everything is working fine. But I want to update blur, spread & duration, with:
updating CustomAnimatedWidget:
final double blur;
final double spread;
final int duration;
CustomAnimatedWidget({this.blur, this.spread, this.duration});
...
builder: (_) => ...
...
initState: (s) {
_.blur.value = blur;
_.spread.value = spread;
_.duration.value = duration;
},
...
updating AnimationController:
Rx<double> blur = 2.0.obs;
Rx<double> spread = 15.0.obs;
Rx<int> duration = 2.obs;
and using/calling the CustomAnimationWidget using:
child: CustomAnimatedWidget(duration: 4, blur: 2, spread: 10);
but don't know how to use it with _animationController & _animation because they are called in onInit.
Please share your solutions, thanks.
I Extended GetxController with GetTickerProviderStateMixin and then make a AnimationController and CurvedAnimation by code below:
late final AnimationController _controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
)..repeat(reverse: false);
late final Animation<double> animation = CurvedAnimation(
parent: _controller,
curve: Curves.ease,
);
After that i just created object of my CustomGetxController like this final _customController = Get.put(CustomGetxController()); then i called it like this:
FadeTransition(
opacity: driverController.animation,
child:const Text('My Flashing Text')
),
I'm new to Flutter and would like to make a customized loader, but can't find a solution. When a user clicks on a button and some operation begins, I'd like to show a customized widget (basically a loader, notifying a user that operation has begun) that pops up to the center of the screen and grows in size (from 0x0 to 300x300), while it is rotating at the same time. When it reaches maximum size (300x300), I want it to shrink back to the size of 0x0 and hide/disappear, while rotating as well.
This animation should take 2 seconds. If after 2 seconds the operation is not completed, I'd like to start over with the animation.
You can create your own animations very easily. Using Animation and AnimationController you can do basically anything that you can think of.
Check out this video if you want to dig deeper into Animations with Flutter to make Complex UIs
To achieve what you are asking, you can build your Loading Indicator using a stateless widget.
Here is an interactive example
class MyLoadingIndicator extends StatefulWidget {
final Widget child;
const MyLoadingIndicator({
#required this.child,
Key key,
}) : super(key: key);
#override
_MyLoadingIndicatorState createState() => _MyLoadingIndicatorState();
}
class _MyLoadingIndicatorState extends State<MyLoadingIndicator> with TickerProviderStateMixin {
AnimationController rotateController;
Animation<double> rotateAnimation;
AnimationController scaleController;
Animation<double> scaleAnimation;
#override
void initState() {
super.initState();
rotateController = AnimationController(
duration: const Duration(seconds: 1),
reverseDuration: const Duration(seconds: 1),
vsync: this,
);
rotateAnimation = CurvedAnimation(parent: rotateController, curve: Curves.linear);
rotateController.repeat(
min: 0,
max: 1,
period: Duration(seconds: 2),
);
scaleController = AnimationController(
duration: const Duration(seconds: 1),
reverseDuration: const Duration(seconds: 1),
vsync: this,
);
scaleAnimation = CurvedAnimation(parent: scaleController, curve: Curves.linear);
scaleController.repeat(
min: 0,
max: 1,
period: Duration(seconds: 2),
reverse: true,
);
}
#override
void dispose() {
rotateController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return RotationTransition(
turns: rotateAnimation,
child: ScaleTransition(
scale: scaleAnimation,
child: Container(
height: 300,
width: 300,
color: Colors.red,
child: widget.child,
),
),
);
}
}
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,
),
),
),
);
}
}
I want to set the animation duration from my widget parameter but it doesn't work because duration wants to be initialized with a constant
class CircularTimer extends StatefulWidget {
CircularTimer({#required this.seconds});
_CircularTimer createState() => _CircularTimer();
final seconds;
}
class _CircularTimer extends State<CircularTimer>
with SingleTickerProviderStateMixin {
Animation<double> animation;
AnimationController controller;
#override
void initState() {
super.initState();
controller = AnimationController(
duration: const Duration(/*not working*/seconds: widget.seconds), vsync: this);
animation = Tween<double>(begin: 0, end: 300).animate(controller);
controller.forward();
}
#override
Widget build(BuildContext context) =>
CircularTimerWidget(animation: animation);
}
You can't pass data like this to const, so the solution would be either to remove const from Duration or simply use some const value.
Solution:1
controller = AnimationController(
duration: Duration(seconds: widget.seconds), // remove const
vsync: this,
);
Solution:2
controller = AnimationController(
duration: const Duration(seconds: 1), // some const value
vsync: this,
);