简体   繁体   中英

How can I use animation/animation not running during a Hero transition in Flutter?

I have a grid of cards depicting products. When a card is tapped, I want it to flip over (around the Y axis) to reveal the "other side" that shows the details, while simultaneously growing to fill the screen.

  • Duration 0.0 - Card shows front side and is in grid view
  • Duration 0.5 - Card is 50% of the way to full-screen and perpendicular to the screen (front side facing to the right, "back" side facing to the left)
  • Duration 1.0 - Card is fully expanded and the "back" card is showing.

I have managed to get a flip animation working, but am having trouble figuring out how to also get it to run during the Hero transition. From this article it seems like I may need to make use of flightShuttleBuilder to be able to animate the overlay but my animation does not run during the transition:

return Hero(
  tag: 'test',
  flightShuttleBuilder: (
    BuildContext flightContext,
    Animation<double> animation,
    HeroFlightDirection flightDirection,
    BuildContext fromHeroContext,
    BuildContext toHeroContext,
  ) {
    final Hero toHero = toHeroContext.widget;
    return Transform(
      transform: Matrix4.identity()..rotateY(-pi * animation.value),
      alignment: FractionalOffset.center,
      child: toHero,
    );
  },
  child: Card(...),
);

As it turns out, flightShuttleBuilder only emits values at the start and end of the transition instead of throughout the animation. Capturing from this issue on GitHub , it is apparently expected behavior.

The workaround is to create your own transition which extends from AnimatedWidget ; that will emit values normally and can be used in the flightShuttleBuilder :

class FlipcardTransition extends AnimatedWidget {
  final Animation<double> flipAnim;
  final Widget child;

  FlipcardTransition({@required this.flipAnim, @required this.child})
      : assert(flipAnim != null),
        assert(child != null),
        super(listenable: flipAnim);

  @override
  Widget build(BuildContext context) {
    return Transform(
      transform: Matrix4.identity()
        ..rotateY(-pi * flipAnim.value),
      alignment: FractionalOffset.center,
      child: child,
    );
  }
}

...

flightShuttleBuilder: (BuildContext flightContext,
  Animation<double> animation,
  HeroFlightDirection flightDirection,
  BuildContext fromHeroContext,
  BuildContext toHeroContext,) {
    final Hero toHero = toHeroContext.widget;
    return FlipcardTransition(
      flipAnim: animation,
      child: toHero,
    );
},

As Matt said, the default behavior is only emitting start and end value, so we should listen the animation to get the full animatable widget. Here is an example:

Hero(
  tag: "YourHeroTag",
  flightShuttleBuilder: (BuildContext flightContext,
    Animation<double> animation,
    HeroFlightDirection flightDirection,
    BuildContext fromHeroContext,
    BuildContext toHeroContext,) {
      return AnimatedBuilder(
        animation: animation,
        builder: (context, value) {
          return Container(
            color: Color.lerp(Colors.white, Colors.black87, animation.value),
          );
        },
      );
  },
  child: Material( // Wrap in Material to prevent missing theme, mediaquery, ... features....
    // ...
  )
)

It's recommended to wrap in Material widget to prevent unexpected missing style.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM