Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fade out then fade in results in invisible widget #54

Open
scopendo opened this issue Feb 1, 2023 · 12 comments
Open

Fade out then fade in results in invisible widget #54

scopendo opened this issue Feb 1, 2023 · 12 comments
Labels
enhancement New feature or request

Comments

@scopendo
Copy link

scopendo commented Feb 1, 2023

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.

@gskinner gskinner added the Support Question about using the library label Feb 2, 2023
@gskinner
Copy link
Owner

gskinner commented Feb 2, 2023

From the README:

Note that effects are "active" for the duration of the full animation, so for example, two fade effects on the same target can have unexpected results (SwapEffect detailed below, can help address this).

This specific scenario (fade out then in) is the only one I've run into that causes real difficulties due to this. Still thinking about ways to make this easier.

@gskinner gskinner closed this as completed Feb 2, 2023
@gskinner gskinner removed the Support Question about using the library label Feb 2, 2023
@gskinner
Copy link
Owner

gskinner commented Feb 2, 2023

Actually, I'm going to leave this open as a discussion thread on ways to improve this.

@gskinner gskinner added the enhancement New feature or request label Feb 2, 2023
@gskinner gskinner reopened this Feb 2, 2023
@gskinner
Copy link
Owner

gskinner commented Feb 2, 2023

Thinking out loud here, but one way to deal with this is to make FadeEffect aware of previous effects. This isn't trivial, and would likely require:

  1. Define an AdvancedEffect (SequencedEffect?) interface, either as a subclass of Effect or as a mixin.
  2. The interface would offer helper methods like findNextEffectByType.
  3. More importantly, it would define a method for getting access to the EffectEntry list (ex init(entries)). This would likely be called immediately before the first build of Animate (ie. after all effects have been added).

With the above, a FadeEffect implementing AdvancedEffect could check for other effect entries pointing to FadeEffect and modify or disable its own application as appropriate. It still leaves a lot of fuzzy edge cases. For example, what if two fades overlap in time (ex. fadeOut(duration: 1000.ms).fadeIn(delay: 600.ms))? What if they overlap by a tiny amount (1ms)?

It also seems like a lot of architecture to solve this one scenario.

@gskinner
Copy link
Owner

gskinner commented Feb 2, 2023

Another option would be a purpose built effect MultiFadeEffect (name TBD).

foo.animate()
  .multifade(values: [0, 1], durations: [500.ms, 700.ms])

This is a lot simpler to implement and a lot more contained (doesn't require any broad architecture changes), but has a couple drawbacks:

  1. It doesn't work with then
  2. It may cause confusion with inherited durations (the example above would have an inheritable duration of 1200ms)

@gskinner
Copy link
Owner

gskinner commented Feb 2, 2023

One more, that I don't think I like at all, but am writing up for reference: Add a setting to FadeEffect that tells it to return to neutral when it isn't active. Name very much TBD:

foo.animate()
  .fadeOut(disableWhenInactive: true)
  .then()
  .fadeIn(disableWhenInactive: true)

This has the major flaw that it doesn't work if the fades aren't directly adjacent (ex. if there's a delay on then). The first fade would disable, which would pop opacity back to 1 until the delay ends and the second fade starts up again.

@scopendo
Copy link
Author

scopendo commented Feb 2, 2023

From the README:

Note that effects are "active" for the duration of the full animation, so for example, two fade effects on the same target can have unexpected results (SwapEffect detailed below, can help address this).

This specific scenario (fade out then in) is the only one I've run into that causes real difficulties due to this. Still thinking about ways to make this easier.

Thanks, @gskinner – I hadn't noticed that in the README. The suggested approach works fine for me – I'm actually only partially fading out and back in again, rather than fading all the way to 0/1.

@jonmountjoy
Copy link

@gskinner I think it's not just fade where that causes a problem. An analogous problem occurs when you do scale (say from 1 to 1.1, then from 1.1 to 1). I ended up with a weird scale (which wasn't 1).

To create a set of buttons with a "button effect" of scaling out/in when pushed, I had to do something arduous like:

 ...[1, 2, 3, 4, 5, 6, 7, 8, 9, 0].map((e) {
     ElevatedButton(
                 onPressed: () {
                    setState(() {
                      selectedButton = e;
                    });
                    // Do work
                  },
                  child: Text('$e')
    ).animate(
                    target: selectedButton == e ? 1 : 0,
                    effects: buttonPushAnimation))

Where I need the whole selectedButton thing to allow me to push a button more than once :-(

Because of the problem you link to in #54 (comment)
I had to construct my animation like this:

buttonPushAnimation = <Effect>[
      ScaleEffect(
          duration: 90.ms,
          begin: ScaleEffect.neutralValue,
          end: const Offset(1.1, 1.1)),
      SwapEffect(builder: (_, child) {
        Future.delayed(Duration.zero, () {
          if (selectedButton != -9) {
            setState(() {
              selectedButton = -9;
            });
          }
        });
        return child.animate().scaleXY(
            duration: 90.ms, begin: 1.1, end: ScaleEffect.neutralScale);
      })
    ];

The combination of the resetting of selectedButton in a Future, and the need for SwapEffect makes it all rather, well, ugly. But, it appears to work...

@jonmountjoy
Copy link

In fact, I wonder if we couldn't extend animate to have a when that behaves like a "toggle the start of an animation" that can pass through its touch to the child components so that you can layer it on top of a button. ie.

ElevatedButton(...).animate(when:[Effect.onTap]).shake()

(with the idea being that it can "reset" its internal state so that I can touch the button repeatedly and repeat the animation)

@eggcaker
Copy link

is still no way to do that situation:
click a button ,then start animate fadeout button -> change button lable -> fadeIn ?

@gskinner
Copy link
Owner

@eggcaker - just switch your effect when you switch the button label from a fadeOut to a fadeIn.

@eggcaker
Copy link

is any demo for this ? no idea how to do this

@xiprox
Copy link

xiprox commented Oct 3, 2023

I am encountering this when trying to build a custom clipper effect to be used with then effects to animate back-and-forth between different values as part of a staggered animation.

EDIT: I took a step back to rethink and turns out I was using the wrong tool for the job. Rive covered my scenario perfectly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

5 participants