Flipping an animation controller view property value in Flutter - dart

I'm trying to flip the progress value of an AnimatedIcon. For example:
icon: AnimatedIcon(
icon: AnimatedIcons.close_menu,
progress: _controller.view,
),
Right now the icon animation is backwards for what I need. So when I'm expecting _controller.view to be 0.0 it's actually showing 1.0.
I tried:
progress: _controller.view == 1.0 ? 0.0 : 1.0
but _controller.view is an Animation<double> and not just a <double>.
How can I set progress to a hard coded value?

You can use a Tween to create an animation that transforms the controller's range of values [0.0, 1.0] to an inverted range [1.0, 0.0] using Tween.animate. For example,
// create your tween.
final tween = Tween<double>(begin: 1.0, end: 0.0);
// apply it to a controller to create an animation.
final animation<double> = tween.animate(controller);
AnimatedIcon(
icon: AnimatedIcons.close_menu,
progress: animation,
);

I find using ReverseAnimation is more intuitive for me
icon: AnimatedIcon(
icon: AnimatedIcons.close_menu,
progress: ReverseAnimation(_controller),
),

Related

how do I remove oringal background color from animation

How do I make my animation only use colors in the “colors” array and not use oringal white background as it’s first key frame? Please help
Any way to skip/delete/ the first key frame which is white.
var color1 = UIColor.purple
var color2 = UIColor.blue
var color3 = UIColor.green
var color4 = UIColor.yellow
var color5 = UIColor.orange
extension UIViewController {
func viewDidLoad(){
let colors = [color1, color2, color3, color4, color5]
let totalDuration = 5.0
let relDuration: CGFloat = 1.0 / CGFloat(colors.count)
var relStartTime: TimeInterval = 0.0
UIView.animateKeyframes(withDuration: totalDuration, delay: 0.0, options: [.repeat, .allowUserInteraction, .autoreverse, .calculationModeCubicPaced], animations: {
colors.forEach { c in
UIView.addKeyframe(withRelativeStartTime: relStartTime, relativeDuration: relDuration, animations: {
self.view.backgroundColor = c
})
relStartTime += relDuration
}}
)
}
}
It's unclear what you want / expect. Consider: the view background is white, so the first keyframe of your animation animates from that white color to color1, which is purple. Moreover, you have .autoreverse, so the last keyframe is the opposite of that, which means we animate from purple to white. Then we start over again, animating from white to purple.
Therefore, during that last frame of the first cycle followed by the first frame of the second cycle, the view background is going to spend, roughly, the last third of the last frame and first third of the first frame of the next cycle, being white.
In other words, there is no "pause". Your animation is "continuous". There is a noticeable white period at the end of each cycle and the start of the next cycle, because that is what you asked for as part of the animation you configured. The animation goes
white
purple
blue
green
yellow
orange [and now reverse]
yellow
green
blue
purple
white
white [and now begin again]
purple
...
If you didn't want to see white for that long, you should not have involved white at the start and end of the animation. It's easy to arrange that, but you need to be more specific in your own mind, and in the question, about what you want the animation to be. When you have decided what the animation should be, you can write that animation, or if you can't, you can ask about it. But at the moment you are simply getting the animation you asked for and you don't seem to have a clear vision of what animation you actually want.

How to set the opacity of an image drawn to a canvas with drawImageRect?

I'm drawing to a Canvas in Flutter using drawImageRect:
canvas.drawImageRect(image, sourceRect, destRect, _paint);
I have an Opacity value (a double between 0 and 1). How do I paint the image using this value (so that 0 is not visible and 0.5 is half-opacity)?
Just set the color opacity
final _paint = Paint();
final opacity = 0.5;
_paint.color = Color.fromRGBO(0, 0, 0, opacity);
canvas.drawImageRect(image, sourceRect, destRect, _paint);
You can process the image before rendering using this library. Also I would suggest you to do all operations of this library using the isolates, using the compute() function of flutter would be easy as it blocks the whole UI.

Gradient.radial() from the documentation isn't available?

I want to create a gradient shader.
The Gradient.radial() constructor should return one.
However, VS Code claims that this method is not defined for Gradient. huh.
Now, from what I've seen online, you can create a RadialGradient object and then use .createShader(rect) to convert it into a shader (haven't got it work yet, but maybe I'm implementing it wrong), but that can't be the intended way, right?
Update:
As #pskink pointed out, I'm using a newer version of Flutter and have to import the Gradient shader class as part of the Dart ui library.
The resulting code is valid and looks like this:
paint.shader = ui.Gradient.radial(
Offset(-.5, -.5),
1, // The radius
List.from({
Colors.lightBlueAccent,
Colors.blue,
Colors.deepPurpleAccent},
growable: false
)
);
Unfortunately it doesn't render anything.
This is how the code with the .createShader() method on the other Gradient class looks.
RadialGradient _gradient = RadialGradient(
colors: List.from({
Colors.lightBlueAccent,
Colors.blue,
Colors.deepPurpleAccent},
growable: false),
);
// center is the center of the canvas
paint.shader = _gradient.createShader(
Rect.fromCircle(
center: center,
radius: size.shortestSide / 2
)
);
This is kinda ugly IMO (and less efficient?), but it does render the expected result.
2nd Update:
When having more than 2 color elements in the Gradient shader class you can't omit the stops. This info is just being presented as an errorlog, which is why I didn't see it. With that being fixed: Both versions return different results...
This code returns this gradient:
#override
void paint(Canvas canvas, Size size) {
final center = Offset(size.width / 2, size.height / 2);
final paint = Paint();
RadialGradient _gradient = RadialGradient(
radius: 1,
center: Alignment(-.5, -.5),
tileMode: TileMode.clamp,
colors: List.from({Colors.lightBlueAccent, Colors.blue, Colors.deepPurpleAccent}, growable: false),
stops: List.from({0.0, 0.5, 1.0}, growable: false),
);
paint.shader = _gradient.createShader(
Rect.fromCircle(
center: center,
radius: size.shortestSide / 2
)
);
canvas.drawCircle(center, size.shortestSide / 2, paint);
}
While this code renders this gradient:
#override
void paint(Canvas canvas, Size size) {
final center = Offset(size.width / 2, size.height / 2);
final paint = Paint();
paint.shader = ui.Gradient.radial(
Offset(-.5, -.5),
1, // this is the radius
List.from({Colors.lightBlueAccent, Colors.blue, Colors.deepPurpleAccent},
growable: false),
List.from({0.0, 0.5, 1.0}, growable: false),
TileMode.clamp,
);
canvas.drawCircle(center, size.shortestSide / 2, paint);
}

Run only part of a Staggered Animation

I'm working through the staggered animations tutorial and really like the way you can compose an animation out of multiple tweens by attaching them all to the
same animationcontroller:
AnimationController animationController;
Animation<double> opacity;
Animation<double> width;
Animation<double> height;
opacity = Tween<double>(
begin: 0.0,
end: 1.0,
).animate(animationController1), //attach to animationController
width = Tween<double>(
begin: 0.0,
end: 1.0,
).animate(animationController1), //attach to animationController
height = Tween<double>(
begin: 0.0,
end: 1.0,
).animate(animationController1), //attach to animationController
My question is:
Using this setup and assuming the animations overlap is it possible to run only part of the animation, i.e. only one of the tweens? Suppose I ONLY want to animate the opacity and not all three {opacity, width and height}. At the same time I want to have the flexibility to run all three. How would I do that?

Animate View with multiple arguments

I am trying to animate the opacity of a CALayer but the values are more than one value to begin with and one to end with.
For example: I want it to animate throw these values: 0.0, 0.7, 0.3, 1.0, 0.5, 0.0
I also want the animation to repeat with auto reverse.
This is what I have for now:
let redLineAnimation = CABasicAnimation(keyPath: "opacity")
redLineAnimation.duration = 0.4
redLineAnimation.autoreverses = true
redLineAnimation.fromValue = 0.0
redLineAnimation.toValue = 1.0
redLineAnimation.repeatCount = Float.infinity
movingRedLineLayer.addAnimation(redLineAnimation, forKey: nil)
I am new to iOS development. What can I do? Thanks!
Take a look at CAKeyframeAnimation. That will give you what you want.
And if what you are animating is a view's layer, you could use the easier-to-use UIView keyframe based animation methods. (Core Animation is pretty tricky, and the docs are spotty)

Resources