Comments (25)
Another approach, I tried is
Animate(
adapter: TriggerAdapter(triggerCondition),
effects: triggeredEffects,
child: const Icon(Icons.favorite, size: 50, color: Colors.red),
),
This allowed me to decouple and differ the programming of effects to a button such as,
ElevatedButton(
onPressed: () {
setState(() {
triggeredEffects
..clear()
//The effect to apply to the Animated Icon defined earlier
..add(ShakeEffect(hz: 5, duration: 1.seconds));
triggerCondition = true;
});
},
child: const Text("Flutter"),
),
And to achieve this I defined a TriggerAdapter like so,
@immutable
class TriggerAdapter extends Adapter {
TriggerAdapter(this.triggerCondition);
final bool triggerCondition;
@override
void init(AnimationController controller) {
if (triggerCondition) controller.forward(from: 0);
}
}
Do you see any drawbacks to this approach?
from flutter_animate.
@thesensibledev I am about to embark on this same mission. This could be handled through the NotificationListener. It means we'll have to create a base widget for taps that emits a notification when tapped. The library can have a widget that is "EventDriven" and expects a specific notification type. When that's received the animation is triggered.
I'm going to implement this in my project now, if I like it I will report back. Otherwise I'll wait around here to see what @gskinner recommends 👀
from flutter_animate.
We have been just dealing with that as a state change, either at the widget level, or via a builder using, for example a ChangeNotifier.
Related, there's also some work going on to add adapters that can animate between states, which could potentially enable this (just pass a new value into the adapter). See: #9
To turn this around, how would you like this to work? For example, maybe write a little code sketch that shows what you'd like to see within (to a reasonable extent) the norms of Flutter.
from flutter_animate.
I'm hesitant to bind the interaction to the animation so directly, because there will be cases where you want to animate something other than what was clicked.
With the changes in PR #9 , I think this should be pretty straightforward. Quick sketch:
var adapter = ValueAdapter(0.0, animate: true);
// ...
Text('animate me').animate(adapter: adapter).fadeIn();
// ...
Button(onPress: () => adapter.value = 1.0); // this would trigger the animation
You could of course also create more robust adapters that would do things like "bounce" the animation once when activated.
You could also create simple widgets (like your AnimateOnTap
) that would reduce boilerplate for common cases.
Does that make sense? Cover the use case?
from flutter_animate.
Rather than use an adapter, its likely more straightforward for event driven animations, to cache the controller itself:
Text('animate me').animate(
onPlay: (c) {
_fadeController = c;
_c.stop();
}
).fadeIn();
Then:
onPress: () => _fadeController.forward(from: 0)
Existence of autoplay would make this more declarative:
Text('animate me').animate(
autoPlay: false,
onPlay: (c) =>_fadeController = c;
).fadeIn();
But the new onPlay
naming feels wrong in this case, onInit
would be more accurate :/
from flutter_animate.
True. The adapter solution is potentially reasonable for "transient" animations where losing state isn't really an issue (ex. hover), but it will lose state if the widget is rebuilt.
from flutter_animate.
I just added a new parameter to Animate
that handles many of these cases, and I would love feedback before I roll it into a v2.1
release.
You can now set target
, and it will automatically animate to that position (0
=beginning, 1
=end) of the animation.
MyButton().animate(target: _over ? 1 : 0)
.fade(end: 0.8).scaleXY(end: 1.1)
We debated the naming a lot (ex. target, position, value), but reluctantly settled on target since that's what animateTo
uses, and it's the closest analog. Open to feedback on this.
I'd also love feedback on:
- Whether this addresses your needs
- Does the documentation for it (and the
Reactive Animation
section near the end of the README) make sense - How do you see this working with
AnimateList
(if at all) - It currently modifies the timing based on the delta — ex. animating from 0.9-1 takes 10% as long as 0-1. Does that make sense? It's a bit different than say AnimatedOpacity, which always uses the full duration, but I think the end result is nicer for things like roll-over effects where it might change quickly in response to user input.
Hoping to publish in the next week.
from flutter_animate.
so you want it to start at scale 1, then when the user presses, it jumps to scale=0, and animates back up to scale=1?
from flutter_animate.
maybe something like:
IconButton(
icon: Icon(Icons.thumb_up),
onPressed: _thumbController.reverse(from: 1),
).animate(
autoPlay: false,
onInit:(c) => _thumbController = c,
).scale(end: 0);
Basically set it up so the animation starts paused on the scale you want to show by default, then play it in reverse when the action happens.
from flutter_animate.
@gskinner a state change means there's going to be values manually tracking if the button has been pressed or not. i.e. Storing a value for every touchable widget on the screen. If I understand it correctly.
There's 2 type of tap animations I have in mind.
- Repeat the same animation on tap: Similar to Flutter Bounce
- Forward and reverse animations on tap: As shown in #9
I'm referring to no. 1 when asking the question so my example would be something like.
AnimateOnTap(
effects: [...],
child: Text('I pulse when tapped'),
)
For no. 2 the state change track will work given that if your button is currently not animated but changes a state you're already tracking the state change so the interpolation between those states on a curve will be a great addition to the package.
from flutter_animate.
What about adding animations only at runtime if a certain event occurs, like this:
class TestView extends StatelessWidget {
TestView({Key? key}) : super(key: key);
late Text text;
@override
Widget build(BuildContext context) {
return Center(
child: DefaultTextStyle(
style: const TextStyle(fontSize: 36, fontWeight: FontWeight.bold),
child: GestureDetector(
onTap: () => _onTap(),
child: text = const Text("Hello World"),
),
),
);
}
_onTap() {
text.animate()
.slide(curve: Curves.easeOutCubic)
.fadeOut();
}
}
from flutter_animate.
@gskinner Yeah that should work as well. It's verbose and will require state tracking per UI element that needs to update but it should be good enough for now. Thanks for the prompt responses. I appreciate it.
I'll keep an eye out for when #9 is live and I'll see how it feels using it.
from flutter_animate.
@gskinner I will try and close the issue with an example when I can validate the solution. Two issues are
- The PR doesn't include the
animate
parameter in the ValueAdapter - The solution is a one-shot trigger. It can't be retriggered unless the value is set back to zero. But setting it back to 0 would cause the widget to first animate in reverse
Till then the work around is to use an animation controller. such as
_controller = AnimationController(
vsync: this, // the SingleTickerProviderStateMixin
value: 1.0); // Display the widget in the end state
Pass the controller in animate
Icon(Icons.favorite, size: 50, color: Colors.red)
.animate(controller: _controller)
.shake(hz: 5, duration: 1.seconds),
And finally on the triggering widget
onPressed: () {
_controller.forward(from: 0.0);
}
from flutter_animate.
@esDotDev that's definitely an option too, if you don't mind storing the controller in the local state. It can be even a tiny bit easier (or at least more compact):
foo.animate(onPlay: (c) => _fadeController = c..stop())
from flutter_animate.
Wouldn't the adapter in this case need to be hoisted out into local state as well? Otherwise it could lose state if the parent is ever rebuilt...?
eg:
var adapter = ValueAdapter(0.0, animate: true);
return Button(onPress: (){
adapter.value = 1.0
setState((){});
});
Pretty sure that would blow out the old adapter and break playback if it's not cached in a stateful class field? So then it really just becomes extra code that is serving as a 2nd controller, when you could just use the primary controller.
from flutter_animate.
Both of these options seem like more work and complication than just doing onPlay: (c) => controller = c..stop()
and then onPressed: () => controller.forward()
.
I guess the latter saves you storing off each controller, so if you had many controllers and didn't want to create a field for each one, it might be nice?
from flutter_animate.
The target
param is now available in v2.1. Let me know if it does what you need.
https://github.com/gskinner/flutter_animate#reacting-to-state-changes
from flutter_animate.
The
target
param is now available in v2.1. Let me know if it does what you need. https://github.com/gskinner/flutter_animate#reacting-to-state-changes
the target param does help to animate to that point. But I have a specific requirement which might be common. I want to be able to have an animation effect not render at all until triggered. ie, Say i have an IconButton and I want to do a scale animation on it. I want to do this only when I press it (onPress) and not when its normally rendered.
target=1.0 doesnt work as it animates when rendering as well (not desired). I tried c.stop() but that also isnt working and it still animates the iconbutton on render (without any onpress). Is this a bug or am i doing something wrong here?
IconButton(
icon: Icon(Icons.thumb_up),
onPressed: () {
if(thumbsupcontroller!=null) {
thumbsupcontroller!.forward(from: 0);
}
},
).animate(target: 1,onPlay: (controller) {thumbsupcontroller=controller; controller.stop();}).scale());
from flutter_animate.
If you just set target to 0 initially, it should not animate.
from flutter_animate.
If you just set target to 0 initially, it should not animate.
@gskinner Thanks for the quick response :)
I did that as the first thing, it doesnt animate yes, but it also doesnt show the iconbutton at all, as 0 means scale value is 0, so it comes as blank there
from flutter_animate.
so you want it to start at scale 1, then when the user presses, it jumps to scale=0, and animates back up to scale=1?
@gskinner yes correct, thats what it does, what i want is, it should remain in scale 1 in the beginning (so basically not animate) and then onpress go for it.
from flutter_animate.
@gskinner i also tried to set the controller value, controller.value=1.0, that also doesnt work and it just first shows up the whole thing and then again animates it. I also tried controller.duration = Duration(microsecs: 0), that also flickers and does the animation quickly but it still does it, not stopped
from flutter_animate.
@gskinner nice :) That worked, So basically we ask it to end at offset(0,0) and make autoplay off.
As I will have many places where then I will further trigger it via other logic for other things/activites, is this a clean solution?, or should we use TriggerAdapter like @thesensibledev showed. I havent checked that to be working yet for my usecase, but i was planning to check that and also looked like in boundaries of your framework.
I also have two followup questions:
-
why is target=1 and controller.stop() approach not working, is this inherent design?
-
why animate(onPlay(c) { c.value=1.0,}).scale() is not acknowledged by scale? Is this inherent by design, ie, internal controller's manipulation wont propagate further to the chain of effects?
Thanks for the awesome library, love your work!
from flutter_animate.
I haven't looked at TriggerAdapter in depth, but definitely feel free to test it out.
- Two reasons: First, setting
target
starts an animation (viaanimateTo
), so if you immediatelystop
it, it won't "jump" to 1, it'll just stop on the current value. Second, settingtarget
bypassesonPlay
— I'll think about adding a warning for that, actually. - That should work. Update: Confirmed, I just tested and it works for me — you must have something else going on. All effects that comprise an animation are driven by the single controller.
Text("Playground 🛝")
.animate(onPlay:(controller) => controller.value = 1,)
.slideY(duration: 900.ms, curve: Curves.easeOutCubic)
.fadeIn(),
If you wanted to avoid the one frame flicker, use onInit
instead (with autoPlay=false
):
Text("Playground 🛝")
.animate(autoPlay: false, onInit: (controller) => controller.value = 1,)
.slideY(duration: 900.ms, curve: Curves.easeOutCubic)
.fadeIn(),
from flutter_animate.
@gskinner I also checked it works. I got the issue, why it didnt work for me.
I initially was making
IconButton(icon: Icon(Icons.thumb_up),onPressed: _thumbController.reverse(from: 1),
).animate(autoPlay: false, onPlay:(c) {_thumbcontroller=c; c.value = 1.0;}).scale();
Here, the onPlay was never called, I didnt know that onPlay is skipped with autoplay=false.
Then I went on with target=1.0 as another way, but there also I get now that it does a animateTo(..) and bypasses onPlay as well, so that also didnt work.
If i did, autoplay=false with onInit call, it would have worked. Then I got trying other combinations :)
Thanks for the help Grant. I think its a good idea to document that (Also the below behavior makes sense, just
possible a warning like you said):
-If using target, it inherently always does a animateTo and skips onPlay
-If autoPlay=false, it skips onPlay
from flutter_animate.
Related Issues (20)
- Combining 2 scaleXY is buggy HOT 1
- Can't get animation to run when calling set state HOT 2
- animate has no property controller on widget list HOT 1
- How do I restart an animation when a widget gets rebuilt?
- Animate custom property with minimal boilerplate (Similar to Implict Widgets) HOT 2
- feature request: clone "xyz extension" documentation to chaining methods HOT 3
- Animate widgets Bug on app release mode
- Allow onInit to prevent play
- max for repeat play only once HOT 1
- Having two Widgets with the same AnimationController can cause errors HOT 3
- Defer animation on list items until scrolled into view HOT 2
- Setting autoPlay to false but defining an onPlay callback tints the console yellow HOT 2
- Kindly Add VISUAL (gif) images on the pub.dev page, for most common animations woth example HOT 3
- [Question] Why is this behaviour happening? HOT 1
- The `flip` effect HOT 1
- How to use different animations delay on appear than on going away HOT 1
- Cannot Nest multiple animated instances in order to separately control two animations in a single widget HOT 1
- Target ignores the reset of a bloc state and triggers the animation HOT 1
- hi i have a issue with using arabic text HOT 1
- kindly update and support and improve for web as well
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from flutter_animate.