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

Flutter中如何实现图片悬停时的居中缩放动画效果

解决Flutter图片鼠标悬停居中缩放动画问题

问题原因解析

  • Transform.scale默认以**左上角(0,0)**为缩放原点,导致缩放时图片向右下方偏移,无法实现居中效果。
  • 仅修改BoxFit只能调整图片填充逻辑,无法触发平滑的缩放过渡动画。

可行实现方案

结合MouseRegion监听悬停状态,通过AnimatedContainer的transform属性实现居中缩放动画,同时用ClipRRect裁剪溢出部分,保证界面整洁。

完整代码示例

import 'dart:typed_data';
import 'package:flutter/material.dart';

class HoverZoomImage extends StatefulWidget {
  final Uint8List imageBytes;
  final double size;
  final double zoomScale;
  final Duration animationDuration;

  const HoverZoomImage({
    super.key,
    required this.imageBytes,
    this.size = 200,
    this.zoomScale = 1.2,
    this.animationDuration = const Duration(milliseconds: 200),
  });

  @override
  State<HoverZoomImage> createState() => _HoverZoomImageState();
}

class _HoverZoomImageState extends State<HoverZoomImage> {
  bool _isHovering = false;

  @override
  Widget build(BuildContext context) {
    return MouseRegion(
      onEnter: (_) => setState(() => _isHovering = true),
      onExit: (_) => setState(() => _isHovering = false),
      child: ClipRRect(
        borderRadius: BorderRadius.circular(8.0),
        child: AnimatedContainer(
          duration: widget.animationDuration,
          curve: Curves.easeInOut,
          width: widget.size,
          height: widget.size,
          transform: Matrix4.identity()
            ..scale(_isHovering ? widget.zoomScale : 1.0),
          transformAlignment: Alignment.center, // 核心:设置缩放对齐中心
          child: Image.memory(
            widget.imageBytes,
            fit: BoxFit.cover,
            width: widget.size,
            height: widget.size,
          ),
        ),
      ),
    );
  }
}

// 使用示例
// HoverZoomImage(
//   imageBytes: Uint8List.fromList(images!.bytes),
//   size: 200,
// )

关键细节说明

  • transformAlignment: Alignment.center:强制以容器中心为缩放原点,实现居中缩放效果。
  • Matrix4.identity()..scale(...):通过矩阵变换实现缩放,配合AnimatedContainer自动完成平滑过渡动画。
  • ClipRRect:裁剪缩放后超出容器边界的图片部分,保持圆角或矩形的外观完整性。
  • MouseRegion:监听鼠标进入/离开事件,切换悬停状态触发动画。

替代方案(Transform.scale + AnimatedBuilder)

若偏好使用Transform.scale,可通过设置origin为组件中心,配合AnimatedBuilder实现动画:

ClipRRect(
  borderRadius: BorderRadius.circular(8.0),
  child: MouseRegion(
    onEnter: (_) => setState(() => _isHovering = true),
    onExit: (_) => setState(() => _isHovering = false),
    child: AnimatedBuilder(
      animation: AlwaysStoppedAnimation(_isHovering),
      builder: (context, child) {
        return Transform.scale(
          scale: _isHovering ? 1.2 : 1.0,
          origin: Offset(widget.size / 2, widget.size / 2), // 手动设置缩放原点为中心
          child: child,
        );
      },
      child: Image.memory(
        widget.imageBytes,
        width: widget.size,
        height: widget.size,
        fit: BoxFit.cover,
      ),
    ),
  ),
)

内容的提问来源于stack exchange,提问作者muhammed jasir

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 04:46:15