Giter VIP home page Giter VIP logo

Comments (25)

thesensibledev avatar thesensibledev commented on September 28, 2024 4

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);
  }
}

Output
animation

Do you see any drawbacks to this approach?

from flutter_animate.

FilledStacks avatar FilledStacks commented on September 28, 2024 2

@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.

gskinner avatar gskinner commented on September 28, 2024 1

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.

gskinner avatar gskinner commented on September 28, 2024 1

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.

esDotDev avatar esDotDev commented on September 28, 2024 1

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.

gskinner avatar gskinner commented on September 28, 2024 1

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.

gskinner avatar gskinner commented on September 28, 2024 1

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:

  1. Whether this addresses your needs
  2. Does the documentation for it (and the Reactive Animation section near the end of the README) make sense
  3. How do you see this working with AnimateList (if at all)
  4. 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.

gskinner avatar gskinner commented on September 28, 2024 1

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.

gskinner avatar gskinner commented on September 28, 2024 1

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.

FilledStacks avatar FilledStacks commented on September 28, 2024

@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.

  1. Repeat the same animation on tap: Similar to Flutter Bounce
  2. 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.

rivella50 avatar rivella50 commented on September 28, 2024

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.

FilledStacks avatar FilledStacks commented on September 28, 2024

@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.

thesensibledev avatar thesensibledev commented on September 28, 2024

@gskinner I will try and close the issue with an example when I can validate the solution. Two issues are

  1. The PR doesn't include the animate parameter in the ValueAdapter
  2. 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.

gskinner avatar gskinner commented on September 28, 2024

@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.

esDotDev avatar esDotDev commented on September 28, 2024

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.

esDotDev avatar esDotDev commented on September 28, 2024

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.

gskinner avatar gskinner commented on September 28, 2024

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.

khuntia avatar khuntia commented on September 28, 2024

@gskinner

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.

gskinner avatar gskinner commented on September 28, 2024

If you just set target to 0 initially, it should not animate.

from flutter_animate.

khuntia avatar khuntia commented on September 28, 2024

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.

khuntia avatar khuntia commented on September 28, 2024

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.

khuntia avatar khuntia commented on September 28, 2024

@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.

khuntia avatar khuntia commented on September 28, 2024

@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:

  1. why is target=1 and controller.stop() approach not working, is this inherent design?

  2. 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.

gskinner avatar gskinner commented on September 28, 2024

I haven't looked at TriggerAdapter in depth, but definitely feel free to test it out.

  1. Two reasons: First, setting target starts an animation (via animateTo), so if you immediately stop it, it won't "jump" to 1, it'll just stop on the current value. Second, setting target bypasses onPlay — I'll think about adding a warning for that, actually.
  2. 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.

khuntia avatar khuntia commented on September 28, 2024

@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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.