flutter_animate icon indicating copy to clipboard operation
flutter_animate copied to clipboard

Effects triggered by unmounting a Widget

Open oravecz opened this issue 3 years ago • 5 comments
trafficstars

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?

oravecz avatar Oct 10 '22 17:10 oravecz

Good question. I'll have to look into this. If anyone wants to tackle this in the interim, it would be very welcome, even if it's just to throw some ideas at it.

gskinner avatar Jan 09 '23 16:01 gskinner

As a follow up question: How do you see this working, not in terms of specific implementation in the library but in terms of how you'd use it as a developer? Feel free to sketch some code examples.

gskinner avatar Feb 07 '23 21:02 gskinner

Bear in mind I haven't thought about your internals and how they might be impacted by such an API...

Under typical conditions, the "remove animation" should happen automatically when Flutter determines my widget will no longer be rendered.

Text("Hello").animate()
  .fadeIn(delay: 300.ms, duration: 500.ms)
  .then()
    .slide(duration: 400.ms)
  .then(delay: 200.ms)
  .blur()
  .remove()
    .fadeOut(delay: 300.ms)

For example, I have a TextField inside of a Card that I expose (height animation currently) based on a user's tapping on a card. If they tap on the card again, I want the TextField to animate shut. In my code, I don't want to be concerned about any of this, I just want to be able to declaratively control whether my TextField is rendered or not.

Column(
  ...
  children: [
    ...
    if (selected) TextField().animate(
      effects: MyGlobalEffects.transitionIn,
      removeEffects: MyGlobalEffects.transitionOut,
    ),
    ...
  ]
),

oravecz avatar Feb 07 '23 21:02 oravecz

I don't think there's any way to interrupt or defer a removal like that in Flutter. That widget is simply not created in that build, so it and the Animate instance are disposed.

The easiest way to get something similar is to use target, but that requires that the "out" animation is just a reverse of "in".

foo.animate(target: selected ? 1 : 0).fadeIn().slideX()

Otherwise, I think you're going to have to do some state management, and the hardest bit will likely be avoiding a starting transition out. For example, this would mostly work I think, except it would run the "out" transition on the first build:

Animate(
  effects: selected ? [...] : [...],
  child: foo,
)

Maybe this could be solved with a more specific wrapping widget that maps the state change to appropriate effects for you? Something like this:

ToggledAnimate( // naming TBD
  toggle: selected ? true : false,
  inEffects: [...],
  outEffects: [...],
  child: foo,
)

Where the above would hold on the zero position for "inEffects" if it starts with toggle=false, then play when toggle=true, then switch to playing outEffects when toggle returns to false. You could then use VisibilityEffect to effectively get rid of the widget when it's hidden. I hope that makes sense.

gskinner avatar Feb 07 '23 22:02 gskinner

I would like to see a syntax similar to what AnimatedSwitcher or AnimatedCrossFade provide but with the ease of flutter_animate e.g.:

Animate().toggle(
  index: 0, // 1, 2, 3....
  duration: 100.ms,
  inEffects: [FadeInEffect(), ScaleInEffect()],
  outEffects: [FadeOutEffect(), ScaleOutEffect()],
  builder: (context, index) {
    if (index == 0) {
      return Container(
        width: 40,
        height: 40,
        color: Colors.red,
      );
    } else if (index == 1){
      // and so on...
    } else {
      // 
    }
  },
)

This would make for a pretty generic variant of AnimatedCrossFade that typically allows only for switching 2 widgets. In case of removing a widget this would mean switching from actual widget to a SizedBox or something similar

orestesgaolin avatar May 10 '23 13:05 orestesgaolin