Flutter Contain-Cover 大图-预览图 切换 Hero动画

367 阅读1分钟

引言

我最近在做仿微信朋友圈的页面,其中有个功能就是预览图点击后查看大图。在实现细节的过程中,我发现Flutter的Hero动画在只传入tag&child的情况下,大图的 contain 在切换预览图的 cover 过程中会“闪一下",很不丝滑,cover 切换 contain 过渡也不完美。如下图:

12月3日.gif

于是我上网搜索了关于Fit改变情况下的Hero动画解决方案,并没有搜索到相关内容,于是就有了这篇文章。

实现方法

flightShuttleBuilder

为了控制Hero动画飞行时的动画,我们需要用到flightShuttleBuilder
官方文档传送门
flightShuttleBuilder是Hero的一个参数,用于控制Hero动画飞行时的组件。可以拿到以下内容 image.png

核心代码

缩略图Hero内

flightShuttleBuilder: (
  BuildContext flightContext,
  Animation<double> animation,
  HeroFlightDirection flightDirection,
  BuildContext fromHeroContext,
  BuildContext toHeroContext,
) {
  return LayoutBuilder(builder: (context, heroCovariant) {
    // 先让限制图片的Sizedbox尺寸铺满Hero
    double height = heroCovariant.maxHeight; 
    double width = heroCovariant.maxWidth;
    // 提前获取好的图片的长宽比
    double imageRatio = imageWidth / imageHeight;
    // 手搓类似contain的逻辑,计算出大图的contain下的实际宽高
    if (width / height > imageRatio) {
      // 使用Hero的height 并让width跟随宽高比(fit height)
      width = height * imageRatio;
    } else {
      // 反之使用Hero的width 并让height跟随宽高比(fit width)
      height = width * (1 / imageRatio);
    }
    return Center( // 防止 SizedBox 铺满Hero
      child: SizedBox(
          // 使用 flightShuttleBuilder 提供的 animation.value 获取Hero动画进度
          // 在缩略图宽高的基础上,根据动画进度慢慢增加Hero的宽高,直到大图的contain实际宽高(上方已经计算出)
          width: thumbnailCovariant.maxWidth +
              (width - thumbnailCovariant.maxWidth) * animation.value,
          height: thumbnailCovariant.maxHeight +
              (height - thumbnailCovariant.maxHeight) * animation.value,
          child: FittedBox(
              fit: BoxFit.cover,
              clipBehavior: Clip.antiAlias,
              child: toHeroContext.widget)),
    );
  });
},

实现后效果

201c83a40bf69b53f8f1dfe5407ab6b8.gif

效果也是非常的丝滑~