AnimatedSwitcher with different in vs. out animations

2,077 views
Skip to first unread message

Dale King

unread,
Jun 30, 2018, 1:56:55 PM6/30/18
to Flutter Dev
I have a case where I would like to use an AnimatedSwitcher, but the way Animated Switcher is designed it assumes you want symmetric animations for the new view coming in and the reverse animation for the item that is exiting. I would like either different animations for entering vs. leaving or only animate one of the 2, haven't decided which yet.

To make the idea clear, imagine the case of a stack of cards that shows and I am switching to a new card on top of the stack. I might want to animate the new card moving onto the stack and covering the previous top of the stack while the previous top of the stack stays where it is instead of moving off the screen.

I am trying to figure out what the best way to accomplish that without resorting to making my own version of AnimatedSwitcher to make minor changes to it.. Essentially what I need in either case is the ability to have an animation that only runs in a single direction.

A first thought I had was that AnimatedSwitcher accepts forward and reverse curves so could I just pass in a curve that essentially mapped the value to a constant. For example a curve implementation that just returns 1.0 so the animation only runs in the forward direction. Only problem is that CurvedAnimation will throw an exception if the endpoints are not approximately 0.0 at t = 0.0 and 1.0 and t = 1.0.

I could try to make an Animation transformer that wraps an Animation and returns a constant if the animation being wrapped is run in reverse (with extra logic to handle the transition from reverse to dismissed).

Just wondering if there is an easy way to do this.

Dale King

unread,
Jun 30, 2018, 3:27:40 PM6/30/18
to Flutter Dev
Ok, I think I have figured it out myself.

My confusion was around the way I thought the transitionBuilder was used. I thought it was only called when the item became the current item and you were then stuck with the animation that you wrapped it with then. But whenever the state changes transitionBuilder is called on the new item and on the previous item.

All you have to do is in the transitionBuilder is look to see if the child passed in represents the current item or not (most likely by looking at the key for the widget passed to the transition builder to see if it matches your current state) and then wrapping it with the animation you want.

For example taking the example code from the AnimatedSwitcher example code from the docs and changing the transitionBuilder parameter to this:

transitionBuilder: (Widget child, Animation<double> animation) {
if (child.key == ValueKey(_count)) {
return new ScaleTransition(child: child, scale: animation);
} else {
return new FadeTransition(child: child, opacity: animation);
}
},

would cause the previous value to fade away and the new value to scale up from 0 to full size.

To accomplish what I mentioned about only animating the new item, just have the else return child.

Dale King

unread,
Jun 30, 2018, 3:36:27 PM6/30/18
to Flutter Dev
On Saturday, June 30, 2018 at 3:27:40 PM UTC-4, Dale King wrote:

My confusion was around the way I thought the transitionBuilder was used. I thought it was only called when the item became the current item and you were then stuck with the animation that you wrapped it with then. But whenever the state changes transitionBuilder is called on the new item and on the previous item.


Rereading the documentation, I think the documentation for transitionBuilder could be improved as it lead to my misunderstanding. The documentation says:

A function that wraps a new child with an animation that transitions the child in when the animation runs in the forward direction and out when the animation runs in the reverse direction. This is only called when a new child is set (not for each build), or when a new transitionBuilder is set. 

I read that to say that it is only called on new children because that is basically what the docs say. This should be rewritten to make it clear it is also called on items being removed. 

Dale King

unread,
Jun 30, 2018, 3:54:51 PM6/30/18
to Flutter Dev
OK the fact that it is calling the transitionBuilder again for previous items is actually sort of an accident. The reason it is calling it on the previous items in the code I posted earlier is because I am creating an anonymous function for the transitionBuilder. Since the anonymous function is a new object each time it is called, the AnimatedSwitch sees that as though I have changed the transitionBuilder and this code in AnimatedSwitcher causes the transitionBuilder to be run on the previous items:

// If the transition builder changed, then update all of the previous transitions
if (widget.transitionBuilder != oldWidget.transitionBuilder) {
_previousChildren.forEach(updateTransition);
if (_currentChild != null) {
updateTransition(_currentChild);
}
_markChildWidgetCacheAsDirty();
}

If I extract that anonymous function out into a named function then that first if will be false and transtionBuilder is only called on the new item like the docs say.

This seems like something that should be more concretely defined rather than depending on the accident of anonymous vs. named functions.
Reply all
Reply to author
Forward
0 new messages