You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Flutter中实现Image从BoxFit.cover到BoxFit.contain的流畅Hero动画

问题原因

Flutter官方Hero组件默认仅对位置、尺寸属性做动画插值,不会自动处理BoxFit枚举的过渡逻辑。当图库页图片使用BoxFit.cover适配、预览页使用BoxFit.contain适配时,动画起止帧会直接切换适配规则,就会出现生硬跳转的问题。

修复方案

通过自定义Hero的flightShuttleBuilder参数接管动画过程的组件渲染,即可实现适配规则的平滑过渡,修改后的完整可运行代码如下:

// main.dart
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart' show timeDilation;

const String url =
    'https://images.unsplash.com/photo-1635335333546-41f848cea96c';

void main() {
  runApp(
    const MaterialApp(
      home: GalleryWidget(),
    ),
  );
}

class GalleryWidget extends StatelessWidget {
  const GalleryWidget({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    timeDilation = 10.0;
    return Scaffold(
      appBar: AppBar(
        title: const Text('Gallery'),
      ),
      body: Material(
        child: InkWell(
          child: Hero(
            tag: url,
            // 新增动画穿梭器自定义逻辑
            flightShuttleBuilder: (flightContext, animation, flightDirection, fromHeroContext, toHeroContext) {
              final isPush = flightDirection == HeroFlightDirection.push;
              return AnimatedBuilder(
                animation: animation,
                builder: (context, child) {
                  final progress = isPush ? animation.value : 1 - animation.value;
                  return Image.network(
                    url,
                    fit: BoxFit.values.lerp(BoxFit.cover, BoxFit.contain, progress),
                  );
                },
              );
            },
            child: Image.network(
              url,
              width: 100.0,
              height: 100.0,
              fit: BoxFit.cover,
            ),
          ),
          onTap: () {
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context) => const PhotoViewerWidget(url: url),
                fullscreenDialog: true,
              ),
            );
          },
        ),
      ),
    );
  }
}

class PhotoViewerWidget extends StatelessWidget {
  final String url;

  const PhotoViewerWidget({Key? key, required this.url}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Material(
      color: Colors.black87,
      child: InkWell(
        child: Hero(
          tag: url,
          child: Image.network(
            url,
            fit: BoxFit.contain,
          ),
        ),
        onTap: () => Navigator.of(context).pop(),
      ),
    );
  }
}

效果说明

上述代码通过BoxFit.values.lerp方法对适配模式做动画插值,配合AnimatedBuilder监听动画进度实时更新图片适配规则,动画过程不会再出现突兀的适配切换效果。如果需要后续添加缩放查看细节的能力,可在预览页嵌套InteractiveViewer组件实现。

内容的提问来源于stack exchange,提问作者Ryan Hoo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 23:06:06