gskinner / flutter_animate Goto Github PK
View Code? Open in Web Editor NEWAdd beautiful animated effects & builders in Flutter, via an easy, highly customizable unified API.
License: BSD 3-Clause "New" or "Revised" License
Add beautiful animated effects & builders in Flutter, via an easy, highly customizable unified API.
License: BSD 3-Clause "New" or "Revised" License
Hi,
The blur effect is not working on the flutter web giving the following error.
══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
The following TypeErrorImpl was thrown building AnimatedBuilder(animation:
AnimationController#697d7(⏮ 0.000; paused)➩Interval(0.6⋯1)➩Tween<Offset>(Offset(0.0, 0.0) →
Offset(4.0, 4.0))➩Offset(0.0, 0.0), dirty, state: _AnimatedState#47719):
Expected a value of type 'JavaScriptObject', but got one of type 'Null'
The relevant error-causing widget was:
AnimatedBuilder
AnimatedBuilder:file:///C:/src/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_animate-2.0.1/lib/effects/effect.dart:84:12
Can you look on this @gskinner
Thanks and Regards.
In the current SlideEffect
implementation, it's possible to set the begin
and end
offsets only with respect to the widget width and height:
SlideEffect(
duration: animInfo.duration.ms,
begin: Offset(-1, 0),
end: Offset(0, 0),
curve: animInfo.curve,
)
So, in the above example, if the effect is applied on a widget having height: 100
and width: 100
then beginning Offset(-1, 0)
signifies that the slide animation will start from 100 pixels to the left of the widget.
I believe there should be a way to allow setting the begin
and end
offsets in terms of raw pixel values, so that we can use something like begin Offset(-85, 0)
to signify the slide starts from 85 pixels left of the widget.
The current approach to effect variations / presets has some drawbacks. Use shake
as an example.
shakeX
and shakeY
variations exist, but only in the form of method extensionsAnimate(effects: [ ShakeXEffect() ])
. This is confusing / annoying.EffectName
and effect.variations()
, for example, you might have .fadeIn()
and FadeUpAndIn
both existing. With the class FadeIn
not existing... quite weird.Instead of using extension-method-first approach, a more component-based approach could be adopted. This would standardize the API, and not favor one syntax over the other.
psuedo code, eg:
class FadeEffect {} // core effect, adds .fade() extension
// presets/defaults are defined in the class
class FadeInEffect {
build() => FadeEffect(begin: 0, end: 1)
}
// extension method just uses the class
extension FadeInEffect on Animate {
fadeIn() => this.addEffect(FadeInEffect());
}
With this approach the presets are defined at the class level, the extension methods can still exist, but now the declarative form is also unlocked. There will also be no mish-mash, as everything will exist both as a FullEffect
and .extensionEffect
This approach would be non-breaking as the existing extension methods would continue to exist, they would just be augmented an additional syntax for their use.
eg fadeIn()
still exists, but now there is also FadeInEffect
.
More complicated presets need to support composition. Not sure exactly how implementation would look, but conceptually we want one effect to be composed of two or more core effects:
class FadeInUpEffect {
build() => FadeInEffect(
...
child: SlideInUpEffect( ... ));
// adds .fadeInUp() extension
}
The fundamental design challenge here is: How can 1 effect use 2 or more other effects? Is this even possible with the current architecture?
Add onControllerReady callback to expose its controller after init controller
I love your package so much it hides almost the boilerplate part. So declarative animation.
I make this effect but I got an issue about restarting the animation.
An animation I'm trying to replicate this animation.
Currently, my implementation is pretty close but I don't know how to restart animation so It's a bit weird.
My reproduce: https://gist.github.com/definev/d1aacf50122f62e79a956eb7245e495c
The UI is separated into two-part:
My border is regenerating every user increasing depth so it auto animates.
I want to trigger the center to re-animate when changing depth.
Adapter currently uses AnimationController.animateTo
to smooth value changes when animate=true
. It works for infrequent changes, but Flutter's implementation gets "change locked" when the value changes constantly.
It'll need access to a tick in a non-Widget class, should respect the animation duration, and should be optimized to disable the tick when it isn't needed.
It would be useful to have an effect similar to swap, but which returns the original child:
Currently to create a complex sequence you can do something like this (from the README):
Widget text = Text("Hello World!");
// then:
text.animate().fadeOut(300.ms) // fade out & then...
.swap(builder: (_) => text.animate().fadeIn()) // swap in original widget & fade back in
But the need to save off the original child is a pain point. We could do something like this:
Text("Hello World!").animate().fadeOut(300.ms) // fade out & then...
.clear(builder: (child) => child.animate().fadeIn()) // swap in original widget & fade back in
I'm pretty sure this is supposed to be 2023:
https://github.com/gskinner/flutter_animate/blob/71ace7b8ad6e4ca92c04f7d298ca3d1c70ccf2c1/CHANGELOG.md#300---2022-01-19
A couple of example tests that demonstrate how to use flutter_test would be a great starting point. It would then be easy for others in the community to extend this to a broader list that provides good coverage for the package as a whole.
This would allow interaction with gesture detectors (and similar) that are translated by MoveEffect.
I'm not sure if you have a road map, but I would love to help with some features and learn from you all as a contributor.
What appears to be important to me, based on a project I'm working on, are the items below.
JSON Serialization
of Effectsanimate()
My project is inspired from my Flash days. I'm using components like Stage, Scene, and MovieClip with assets and symbols, as the structure. I'm using that to create a sequential art framework for iOS and Android apps. The list above is on my list for the project.
Once issue #31 is finalized, it would be nice to add an initial layer of additional presets/variations using the architecture.
While there are unlimited tween behaviors one could think of, some common ones stand out at the top of the list:
(inspired by https://pub.dev/packages/animate_do and https://animate.style/)
FadeInUp
FadeInDown
FadeInLeft
FadeInRight
FadeOutUp
FadeOutDown
FadeOutLeft
FadeOutRight
SlideInUp
SlideInDown
SlideInLeft
SlideInRight
SlideOutUp
SlideOutDown
SlideOutLeft
SlideOutRight
FlipInX
FlipInY
FlipOutX
FlipOutY
ZoomIn
ZoomOut
BounceIn
BounceOut
Notably, some of these are simple variations of existing effects (SlideDown
or ZoomIn
), while others are composed of multiple effects (FadeInDown
). Ideally the new system supports composition, so FadeInDown
uses FadeIn
+ SlideInDown
rather than re-creating their logic.
This issue could be thought of as a first pass at the most obvious presets, and then other presets could be considered in the future. Mostly this can serve as dogfood for issue #31
Hi – I'm noticing that following a fade out with a fade in, results in the widget not appearing at all.
return Text('example').animate().fadeOut().then().fadeIn();
The other way around, fading in and then out, seems to work as expected.
When creating scroll driven animations, it is often a lot easier to set anchor points relative to an element in the scroll region (versus absolute pixel values). This is especially true with dynamic or responsive content.
Quick sketch follows for illustration, naming and implementation specifics TBD.
ScrollAdapter(
begin: ScrollAdapter.getPosition(myWidget, alignment: Alignment.center, offset: Offset(y: -100),
...
)
The above would set the begin property to 100px above the middle of myWidget
.
I have started a project using this package. I'm implementing a strategy where media assets (images, rive animations, custom painters) can be manipulated as a child within an Animate(). I'm dynamically creating effects from json (which could be another feature) and adding them to the effects: property.
So a list of json objects like below is pushed thru an Effect factory based on this context to get a List that becomes the value for the effects: property.
"onLoadEffects": [
{"name": "FadeEffect",
"duration": 2000,
"delay": 0,
"curve": "Curves.easeIn",
"begin": 1.0,
"end": 0.0
},
{"name": "MoveEffect",
"duration": 2000,
"delay": 0,
"curve": "Curves.easeOut",
"begin_dx": 800.0,
"begin_dy": 80.0,
"end_dx": 0.0,
"end_dy": 80.0
},
{"name": "ThenEffect",
"duration": 0,
"delay": 1000
},
{"name": "ScaleEffect",
"delay": 0,
"duration": 2000,
"curve": "Curves.easeOut",
"begin_dx": 0.0,
"begin_dy": 0.0,
"end_dx": 2.5,
"end_dy": 2.5,
"alignment_x": 0.0,
"alignment_y": 0.0
}
],
"onFrameEffects": [],
"onExitEffects": []
Can be applied like this:
Animate(
effects: effects,
controller: _animator,
onComplete: _onCompleteLoadEffects,
child: asset
);
The asset
, being the media asset, is manipulated by the Effects list.
The problem I am seeing during testing is the "thenEffect" doesn't get respected amongst the list. The animate should apply the move and fade effects for 2 sec, then apply the "wait" or ThenEffect for 1 sec, and finally apply the scale effect.
It seems to apply all of the effects at once. I can manually change the delay on each effect which works, but then there is no real purpose for the "thenEffect".
I could be missing something or expecting the wrong results, but this seems to be a bug based on the initial approach.
I added a callbackEffect to the end of the list and the callback was executed at the onset of the animation, not at the end.
Is there a way to prevent the initial state of any effect to get applied before it's triggered using the controller? In the following example, I don't want to show the container as faded to 0.5 initially:
Animate(
controller: controller,
adapter: adapter,
effects: [
FadeEffect(
delay: 10.ms,
duration: 1000.ms,
begin: 0.5,
end: 1.0,
)
],
child: Container(
height: 100,
width: 100,
decoration: BoxDecoration(
color: Colors.blueGrey,
borderRadius: BorderRadius.circular(16),
),
),
)
The above example might not be a good use case, but I need to do it with some other animation effects.
I wish this package allowed to do something like
UIView.animate(withDuration: 2.0, delay: (delay)/delayDivider, usingSpringWithDamping: 0.75, initialSpringVelocity: 0.5, options: .curveEaseInOut) {
in a painless way in Flutter (source: https://github.com/danielkarath/CircularToRectangularAnimation).
Rotation + Spring is my dream.
https://twitter.com/DanielKarath/status/1614621297903812609?s=20&t=l_MDri0o7M1fBtSsJOApmg
It may occasionally be useful to be able to specify a function to transform the notifier value before it is assigned to the controller.
It would be great if the existing Scale
effect could be used on only one axis as well, either by allowing (x, y) values be defined for the parameters begin
and end
or by adding ScaleX
and ScaleY
effects as done for the Shake
effect.
The Flame game engine allows this and therefore e.g. a game card flip effect (which is done solely on the x-axis) is possible with these lines of code:
animalSprite.add(SequenceEffect([
ScaleEffect.to(Vector2(0, 1), EffectController(duration: 0.2)),
ScaleEffect.to(Vector2(1, 1), EffectController(duration: 0.2))
]));
backSideSprite.add(ScaleEffect.to(Vector2(0, 1), EffectController(duration: 0.2)));
There are certain cases where it would be nice to just set the end
value for a set of tweens, and have the tweens run when that end value changes.
For example:
GridView(
children: imageWidgets.map((i){
bool selected = imageWidgets.indexOf(i) == selectedIndex;
return i.animate().scale(
duration: 200.ms,
curve: Curves.easeOut,
begin: 1,
end: selected? 1.2 : 1);
})
We could then easily create this effect where the selected image scales in, while the previously selected image scales out. The others do nothing.
Currently the easiest way to do this is still with the built in TweenAnimationBuilder
, which is a fine API, but it cant hook into any of Animate
s effects:
imageWidgets.map((i){
bool selected = imageWidgets.indexOf(i) == selectedIndex;
return TweenAnimationBuilder<double>(
tween: Tween(begin: 1, end: selected ? 1.15 : 1),
duration: 200.ms,
curve: Curves.easeOut,
builder: (_, value, child) => Transform.scale(scale: value, child: img),
)
}
If you matched the declarative behavior of TAB, then the old end
would become the new begin
if end
ever changes, which is quite nice for continuous effects (go from A > B > A). Changes to begin
are ignored until the widget state is reloaded.
Currently reads...
Animate().toggle(
duration: 1.ms,
builder: (_, value, __) => AnimatedContainer(
duration: 1.second,
color: value ? Colors.red : Colors.green,
),
)
Animate().toggle(
duration: 1.ms,
builder: (_, value, __) => AnimatedContainer(
duration: 1.seconds,
color: value ? Colors.red : Colors.green,
),
)
Currently, to create an animation that doesn't play automatically, you need to use onInit
:
foo.animate(onInit: (controller) => controller.stop()).fade()
This works, but less semantically clear than having an autoPlay
param.
However, implementation of such a feature has some ambiguities / concerns:
Animate.delay
be respected? It is a delay before the animation starts playing, so it's confusing either way.Animate.play
? Thus far all playback control is delegated to the AnimationController
, so this would be a possibly confusing departure.onInit
runs when the animation plays, you wouldn't have a mechanism to access to controller if there was a delay.autoPlay
you still need to save off the Animate
or AnimationController
in order to play it later, so I'm not certain this is providing a ton of value.AnimateList
uses Animate.delay
for interval
, which could cause confusionautoPlay
if didn't respect delay
Something to consider, but for now I'm leaning towards not adding this. It's an advanced convenience feature, that may add more confusion than value.
Fairly minor thing, but it would be nice if the lib followed the best practices on file structure:
Typically the outer file, provider.dart
in this case is the name of the lib, and exports all library classes.
Another provider.dart
often exists inside of the src
file, defining package level methods etc
So we'd be looking for:
flutter_animate.dart // barrel file, all exports
/src
flutter_animate.dart // global stuff like `AnimateManager` and `buildSubAnimation`
/effects
/adapters
etc
Could be interesting to support passing in a ValueNotifier, and only instantiate an AnimationController if it is not provided. This would facilitate two things:
foo.animate(controller: myController).etc()
Right now, we update the duration
of the controller as new effects are added, so we'd need to decide if this still happens if the controller that's passed in is an AnimationController
.
I'm seeing my animation being applied when the widget is added to my UI, but how would I indicate the animation which should play when the widget is removed from the render tree?
I am trying to use ScrollAdapter
inside one of the children of a ListView.builder
, but when the widget is disposed (out of the screen) and we keep scrolling, it will throw an error.
Here is the minimum reproducible example:
ListView.builder(
controller: _scrollController,
itemCount: 3,
itemBuilder: (context, index) {
final text = Text(
index.toString(),
);
return SizedBox(
height: MediaQuery.of(context).size.height,
child: index == 0
? text
.animate(adapter: ScrollAdapter(_scrollController))
.slideY(end: 2.5)
: text,
);
},
)
As you can see, I added an animation to the first child. When the first child goes out of view and is disposed, the error occurs.
Possibly something that mirrors repeat
but adds a count
parameter:
// this would "bounce" the animation (out and back):
controller.loop(reverse: true, count: 1)
Flutter Web
Running the code below, I am trying to swap a sequence of images and apply effects.
However, due to setState()
a new animate
object gets initialized. Resulting in numerous calls to update the image after time.
I was not able to cancel the old animation using onComplete: (controller) => controller.stop()
, the number of calls keeps piling up.
I thought about leaving out controller.repeat()
but then nextImage()
gets never called. It is a bit of strange behavior. For me onComplete
does not work consistently, I need to use callback()
to call nextImage()
.
Also tried to isolate everything in an Animator
object in a semi-singleton, but then the current image gets not updated.
Also tried swap()
but it did not work for me either.
Is there a correct approach to solve this?
Thank you!
class _FadeState extends State<Fade> {
final c = ImageService();
late Image img;
@override
initState() {
super.initState();
nextImage();
}
nextImage() {
setState(() => img = c.nextImage());
}
@override
Widget build(BuildContext context) {
return Container(
width: ..., height: ..., child: img)
.animate(
onPlay: ((controller) => controller.repeat()),
)
.tint(
duration: c.crossFadeDuration.ms,
color: c.color,
begin: 1.0,
end: 0.0)
.then()
.tint(
duration: c.crossFadeDuration.ms,
color: c.color2,
begin: 0.0,
end: 1.0)
.callback(
duration: c.crossFadeDuration.ms, callback: (_) => nextImage());
}
}
It's hard to produce minimal. You can run my project to get this error.
If I try to decrease the grid cell when the grid cell increase animation has not been completed, it generates this error.
═══════ Exception caught by widgets library ═══════════════════════════════════
The following assertion was thrown building Animate-[<'point : 0 | depth : 0 | false'>](state: _AnimateState#0613f(ticker active)):
'package:flutter/src/animation/curves.dart': Failed assertion: line 183 pos 12: 'end <= 1.0': is not true.
package:flutter/…/animation/curves.dart:183
2
Either the assertion indicates an error in the framework itself, or we should provide substantially more information in this error message to help you determine and fix the underlying cause.
In either case, please report this assertion by filing a bug on GitHub:
https://github.com/flutter/flutter/issues/new?template=2_bug.md
The relevant error-causing widget was
Animate-[<'point : 0 | depth : 0 | false'>]
lib/grid_zoom.dart:554
When the exception was thrown, this was the stack
#2 Interval.transformInternal
package:flutter/…/animation/curves.dart:183
#3 ParametricCurve.transform
package:flutter/…/animation/curves.dart:38
#4 Curve.transform
package:flutter/…/animation/curves.dart:92
#5 CurvedAnimation.value
package:flutter/…/animation/animations.dart:462
#6
Hey! I'm new to the party but really enjoy flutter_animate so far. The only effect, I've missed so far (and maybe I'm just blind) is a way to swap/fade through a range of widgets/builders similar to https://pub.dev/packages/cross_fade. Would that be something that's already easy to emulate with flutter_animate or something you've considered?
Cheers and keep up the great work!
A common use-cases is to trigger an animation stored in a widget when an event is triggered, such as
Since, the documentation doesn't cover this use-case, the one simple solution I could think of is to make the parent widget stateful to capture the state of a toggle flag. The flag is set to true on the press of a button and false once the playback is complete. While this works, the code is not concise and certainly doesn't make for a good reading.
Is there a better way to accomplish this that may be documented?
Currently, AnimateLists required setting the effects at the level of the list, so all children will be animated using the same effect. I couldn't find a straightforward way of overriding the effects for specific children (e.g. the third child in the list is animated using a different effect from the others) without magic numbers or manually calculating the delay.
Love the package. I can make widgets slide in and get the AnimationController:
Text.animate(onPlay: (controller) {
_animationController = controller;
}).slideX(),
Is there a what to make the widget then slide out with the controller? And then back in again?
I am using
.animate(
onPlay: (controller) =>
controller.loop(count: 2, reverse: true))
.shimmer(
delay: 400.ms,
duration: 1800.ms,
color: Colors.deepPurple.shade900)
.shake(hz: 5, curve: Curves.easeInOutCubic)
.scaleXY(end: 1.2, duration: 600.ms)
.then(delay: 600.ms)
.scaleXY(end: 1 / 1.1)
to animate an icon. When I navigate away from the page, the following crash is seen. How do I say cancel the pending animation?
2023-01-29 19:39:42.737004+0530 Runner[15014:4806242] [VERBOSE-2:dart_vm_initializer.cc(41)] Unhandled Exception: 'package:flutter/src/animation/animation_controller.dart': Failed assertion: line 533 pos 7: '_ticker != null': AnimationController.animateTo() called after AnimationController.dispose()
AnimationController methods should not be used after calling dispose.
#0 _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:51:61)
#1 _AssertionError._throwNew (dart:core-patch/errors_patch.dart:40:5)
#2 AnimationController.animateTo (package:flutter/src/animation/animation_controller.dart:533:7)
#3 AnimationControllerLoopExtensions.loop.<anonymous closure> (package:flutter_animate/src/extensions/animation_controller_loop_extensions.dart:51:9)
#4 _RootZone.run (dart:async/zone.dart:1654:54)
#5 Future.timeout.<anonymous closure> (dart:async/future_impl.dart:865:34)
#6 Timer._createTimer.<anonymous closure> (dart:async-patch/timer_patch.dart:18:15)
#7 _Timer._runTimers (dart:isolate-patch/timer_impl.dart:398:19)
#8 _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:429:5)
#9 _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:192:26)
2023-01-29 19:40:01.785378+0530 Runner[15014:4813608] [tcp] tcp_input [C12.1.1.10:3] flags=[R] seq=3463997066, ack=0, win=0 state=FIN_WAIT_1 rcv_nxt=3463997066, snd_una=4155211570
I know the adaptor is providing a 0 to 1 value, but how does that map in to the time-based effects like delay or duration or then? It's not obvious to me how to set up what would be a time-based animation and drive it by an adaptor. Or, if that isn't supported, could we get that supported somehow?
Animation is reset when Animate is used on a Tooltip's child and cursor is leaving->re-entering app's window.
import 'package:flutter/material.dart';
import 'package:flutter_animate/flutter_animate.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Material App',
home: Scaffold(
appBar: AppBar(
title: const Text('Material App Bar'),
),
body: Wrap(
spacing: 40.0,
children: [
Animate(
onPlay: (controller) => controller.repeat(reverse: true),
effects: const [
ShakeEffect(
hz: 2,
curve: Curves.easeInOutCubic,
duration: Duration(milliseconds: 1800),
delay: Duration(milliseconds: 450),
),
TintEffect(
curve: Curves.easeInOutCubic,
color: Colors.blue,
),
],
child: const Text('Normal'),
),
Tooltip(
message: '123',
child: Animate(
onPlay: (controller) => controller.repeat(reverse: true),
effects: const [
ShakeEffect(
hz: 2,
curve: Curves.easeInOutCubic,
duration: Duration(milliseconds: 1800),
delay: Duration(milliseconds: 450),
),
TintEffect(
curve: Curves.easeInOutCubic,
color: Colors.blue,
),
],
child: const Text('Issue'),
),
),
],
),
),
);
}
}
There are scenarios where you would only want the animation to run in one way when controlled by a ScrollAdapter.
It could use ScrollDirection.forward/reverse
. Though it's worth considering whether this should be added to all adapters.
Hello at all.
Is there any posibility to animate a ListView.builder in the same way,
as used for animating lists of widgets?
https://pub.dev/packages/flutter_animate#animating-lists
Thanks and best regards
Would be grate if we could do this:
CustomScrollView(
slivers: [
...
].animate(interval: 100.ms)
.move(
curve: Curves.easeOut,
duration: 300.ms,
begin: const Offset(100, 0))
.fade(duration: 100.ms),
)
This scenario should likely throw an error, at least if end isn't specified in absolute pixels.
Sometimes it can be desirable to blur in a specific direction.
Maybe modify BlurEffect to take a Offset
?
'dart:ui/painting.dart': Failed assertion: line 50 pos 10: '<optimized out>': Matrix4 entries must be finite., #0 _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:51:61)
#1 _AssertionError._throwNew (dart:core-patch/errors_patch.dart:40:5)
#2 _matrix4IsValid (dart:ui/painting.dart:50:10)
#3 new Gradient.linear (dart:ui/painting.dart:3726:34)
#4 LinearGradient.createShader (package:flutter/src/painting/gradient.dart:436:24)
#5 ShimmerEffect.build.<anonymous closure>.<anonymous closure> (package:flutter_animate/effects/shimmer_effect.dart:70:48)
#6 RenderShaderMask.paint (package:flutter/src/rendering/proxy_box.dart:1188:35)
#7 RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2796:7)
#8 PaintingContext.paintChild (package:flutter/src/rendering/object.dart:239:13)
#9 PaintingContext.pushLayer (package:flutter/src/rendering/object.dart:460:12)
#10 PaintingContext.pushClipPath (package:flutter/src/rendering/object.dart:600:7)
#11 RenderClipPath.paint (package:flutter/src/rendering/proxy_box.dart:1830:25)
#12 RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2796:7)
#13 PaintingContext.paintChild (package:flutter/src/rendering/object.dart:239:13)
#14 RenderProxyBoxMixin.paint (package:flutter/src/rendering/proxy_box.dart:144:15)
#15 RenderDecoratedBox.paint (package:flutter/src/rendering/proxy_box.dart:2371:11)
#16 RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2796:7)
#17 PaintingContext.paintChild (package:flutter/src/rendering/object.dart:239:13)
#18 RenderProxyBoxMixin.paint (package:flutter/src/rendering/proxy_box.dart:144:15)
#19 RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2796:7)
#20 PaintingContext.paintChild (package:flutter/src/rendering/object.dart:239:13)
#21 RenderBoxContainerDefaultsMixin.defaultPaint (package:flutter/src/rendering/box.dart:2900:15)
#22 RenderStack.paintStack (package:flutter/src/rendering/stack.dart:654:5)
#23 RenderStack.paint (package:flutter/src/rendering/stack.dart:670:7)
#24 RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2796:7)
#25 PaintingContext.paintChild (package:flutter/src/rendering/object.dart:239:13)
#26 RenderShiftedBox.paint (package:flutter/src/rendering/shifted_box.dart:84:15)
#27 RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2796:7)
#28 PaintingContext.paintChild (package:flutter/src/rendering/object.dart:239:13)
#29 PaintingContext.pushLayer (package:flutter/src/rendering/object.dart:460:12)
#30 PaintingContext.pushOpacity (package:flutter/src/rendering/object.dart:693:5)
#31 RenderOpacity.paint (package:flutter/src/rendering/proxy_box.dart:954:21)
#32 RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2796:7)
#33 PaintingContext.paintChild (package:flutter/src/rendering/object.dart:239:13)
#34 RenderProxyBoxMixin.paint (package:flutter/src/rendering/proxy_box.dart:144:15)
#35 RenderTransform.paint (package:flutter/src/rendering/proxy_box.dart:2617:17)
#36 RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2796:7)
#37 PaintingContext.paintChild (package:flutter/src/rendering/object.dart:239:13)
#38 RenderProxyBoxMixin.paint (package:flutter/src/rendering/proxy_box.dart:144:15)
#39 RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2796:7)
#40 PaintingContext._repaintCompositedChild (package:flutter/src/rendering/object.dart:155:11)
#41 PaintingContext.repaintCompositedChild (package:flutter/src/rendering/object.dart:98:5)
#42 PipelineOwner.flushPaint (package:flutter/src/rendering/object.dart:1116:31)
#43 RendererBinding.drawFrame (package:flutter/src/rendering/binding.dart:515:19)
#44 WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:884:13)
#45 RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:378:5)
#46 SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1175:15)
#47 SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1104:9)
#48 SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:1015:5)
#49 _invoke (dart:ui/hooks.dart:148:13)
#50 PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:318:5)#51 _drawFrame (dart:ui/hooks.dart:115:31)
Unfortunately i can not provide reproducible steps because it's very hard to recreate this issue in a new project. It happens on Windows when i minimize app with custom PostMessage method and use ShimmerEfect on a GridView child. Windows makes app smaller to show hiding animation and this issue appears.
It seems that width of gridview's child become 0 after minimizing the app.
Can something like this be implemented as a fix or do not play animation at all if one of child's constraints is 0?
To get the same error output you can just create a container with 0 width and apply this effect to it
Animate(
onPlay: (controller) => controller.repeat(reverse: true),
effects: [
ShimmerEffect(
color: Colors.white.withOpacity(0.40),
size: 2,
blendMode: BlendMode.srcATop,
delay: const Duration(milliseconds: 350),
duration: const Duration(milliseconds: 1000),
),
],
child: Container(
color: Colors.black,
width: 0,
height: 80,
child: Text('Hello World'),
),
)
const Icon(Icons.favorite, size: 28.0)
.animate()
.shake(duration: 1000.ms), // <--- work fine
const Icon(Icons.favorite, size: 28.0)
.animate()
.shake(duration: 999.ms), // <--- NOT work
I think it because entry.duration.inSeconds
return 0 if it is less than 1000 milliseconds.
As discussed in this talk about flutter_animate
, the performance of animations might get significantly improved by wrapping them in a RepaintBoundary.
I argue that this is not apparent to everyone. Especially newer Flutter developers might not be aware of this performance improvement opportunity.
Add an optional bool? useRepaintBoundary
to the Animate()
widget. If set true, the animation should be automatically wrapped with a RepaintBoundary
. The parameter should be false by default, as RepaintBoundaries also come with a cost and the usage should be transparent.
It would be vital to also add extensive explanation in the documentation of the parameter about when to use RepaintBoundary
and when not to, optimally with examples. This way, people not knowing about the widget would get educated and situations where RepaintBoundary
is applied suboptimally get minimized (although not eliminated!).
If the addition of such a parameter comes with too high of a risk of missusage, I propose to at least add a section to the ReadMe/Docs of this package explaining the potential benefits and drawbacks of using a RepaintBoundary
in combination with animations.
This matter was discussed briefly in this tweet with the author of the package.
Right now TintEffect
has a color
param, and uses a double
for its value to indicate the strength of the tint.
It might make sense to update it to remove the color
param completely, and change its value to be of type Color
. This would be a tiny bit more work for a monochromatic tint since the strength would be determined by the color's alpha, but would enable the ability to animate between different colors of tint.
It's worth noting though that you get a somewhat similar effect now by nesting tints (though this is not identical, and is more expensive).
// current (verbose):
foo.animate().tint(color: Colors.red, begin: 0, end: 1);
foo.animate().tint(color: Colors.blue, begin: 1, end: 0).tint(color: Colors.red, begin: 0, end: 1);
// proposed equivalent (verbose):
foo.animate().tint(begin: Colors.transparent, end: Colors.red);
foo.animate().tint(begin: Colors.blue, end: Colors.red);
I need to retest this, but I believe that Colors.transparent
is equivalent to Color(0)
, and that the default interpolation between a color and transparent affects the rgb channels and not just the alpha channel, leading to a darkening of the color. If that's the case, then devs would likely need to be more specific, like: Colors.red.withOpacity(0)
. Though the effect could automatically handle this case if begin or end is omitted or null.
Pros: can tint between different colors, and the value type is perhaps more appropriate.
Cons: likely a bit fussier to implement and use.
Considerations: How common is the use case for animating between different colored tints? I'm guessing fairly rare.
Consider the following animation:
typedef ValueType = Article;
class AnimatedZoomImage extends StatelessWidget {
const AnimatedZoomImage({
Key? key,
required this.scaleDuration,
required this.animationController,
}) : super(key: key);
final Duration scaleDuration;
final AnimationController animationController;
@override
Widget build(BuildContext context) {
return CachedNetworkImage(
fit: BoxFit.cover,
imageUrl: 'some-url',
alignment: Alignment.topCenter,
).animate(
controller: animationController,
onComplete: (c) => c.repeat(reverse: true),
).custom(
duration: scaleDuration,
builder: (context, value, child) {
final scale = 1.0 + (value * .5);
return Transform(
alignment: Alignment.center,
transform: Matrix4.identity().scaled(scale, scale, scale),
child: child,
);
});
}
}
Calling animationController.stop()
does not stop the animation from running aka the builder method is still being called, is that by design?
Currently ThenEffect
works by establishing a new inheritable delay
:
foo.animate()
.fadeIn(duration: 700.ms) // begins at 0ms, ends at 700ms
.then() // sets inheritable delay of 700ms (end of previous)
.slide() // inherits 700ms delay, begins at 700ms, ends at 1400ms
.flipX(delay: 0.ms) // overrides delay, begins at 0ms, ends at 700ms
It might be better if ThenEffect
instead establishes a new "baseline time", that subsequent effects can modify with relative delays (including negative delays):
foo.animate()
.fadeIn(duration: 700.ms) // begins at 0ms, ends at 700ms
.then() // baseline is now 700ms (end of previous)
.slide(delay: 0.ms) // begins at 700ms, ends at 1400ms
.flipX(delay: -200.ms) // begins at 500ms, ends at 1200ms
.then(delay: 300.ms) // baseline is now 1500ms (1200+300)
.shake() // begins at 1500ms
This would require adding a new property to Animate to track this, and could break existing animations in rare cases (ex. if someone has overridden delay after using then
).
Open to feedback on this.
flutter_animated version 2.0.1 has Blur effect with a bug.
In blur_effect.dart ImageFiltered is using a inexistent "enabled" attribute on BlurEffect class.
I downgraded Dart SDK from 2.17.1 to 2.15.1 and not resolved.
flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel stable, 3.0.1, on Microsoft Windows [versÆo 10.0.22621.819], locale pt-BR)
[√] Android toolchain - develop for Android devices (Android SDK version 33.0.0-rc1)
[√] Chrome - develop for the web
[√] Visual Studio - develop for Windows (Visual Studio Community 2022 17.2.3)
[√] Android Studio (version 2021.1)
[√] Connected device (4 available)
[√] HTTP Host Availability
• No issues found!
dart --version
Dart SDK version: 2.15.1 (stable) (Tue Dec 14 13:32:21 2021 +0100) on "windows_x64"
Hi, Thanks for the awesome library, but just wondering how to capture every frame of the animation as png, so that I can create a movie?
I do not have a smaller sample but below is some example where I encountered a weird error. If I don't have Positioned
marked as const
the hearts are not appearing at all (animation not starting). If I make it const
, it works just fine.
Video shows how it should behave (it has hardcoded svgImage
set to null so it can be const
)
if (rightButtonActive)
/// does not animate
Positioned(
bottom: 105,
right: 20,
child: _HeartSpray(svgImage: Assets.illustrations.games.swing.rightWindHeart),
),
if (leftButtonActive)
/// works as expected, as in the video
const Positioned(
bottom: 105,
left: 20,
child: _HeartSpray(svgImage: null),
),
class _HeartSpray extends StatelessWidget {
const _HeartSpray({required this.svgImage});
final SvgGenImage? svgImage;
@override
Widget build(BuildContext context) {
return Stack(
children: [
// spawn 10 hearts
for (var i = 0; i < 10; i++)
Padding(
padding: EdgeInsets.only(
left: i.isEven ? math.Random().nextDouble() * 50 : 0,
right: i.isEven ? 0 : math.Random().nextDouble() * 50,
),
child: (svgImage ?? Assets.illustrations.games.swing.rightWindHeart)
.svg()
.animate(
delay: (i * 0.1).seconds,
onPlay: (controller) {
// repeat animation when finished
controller.repeat();
},
)
.fade(duration: 0.3.seconds)
.scale(duration: 0.15.seconds)
.slideY(begin: 1.0, end: -15, duration: 0.9.seconds)
.shakeX(hz: 1)
.fadeOut(delay: 0.2.seconds, duration: (0.9 - 0.4).seconds),
),
],
);
}
}
Thanks for this package btw. Doing animations is much easier and more joyful and we can do magic stuff in just a few lines
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.