如何在没有尺寸的情况下缩放图像

How to zoom an image without the size

我想缩放图像,但我不想关心图像的大小。此小部件用于包装任何小部件。我转换的小部件在我不知道的地方。这就是为什么我添加 220 以使其可见。有人可以增强我的代码以适应任何大小的小部件。

class ZoomDetailPhoto extends StatefulWidget {
  final Widget child;

  const ZoomDetailPhoto({Key? key, required this.child}) : super(key: key);

  @override
  _ZoomDetailPhotoState createState() => _ZoomDetailPhotoState();
}

class _ZoomDetailPhotoState extends State<ZoomDetailPhoto> {
  late Offset offset;

  @override
  void initState() {
    super.initState();
    offset = Offset.zero;
  }

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    return Listener(
      onPointerHover: (onPointerHover) {
        setState(() {
          offset = onPointerHover.localPosition;
        });
      },
      child: Stack(
        alignment: Alignment.center,
        children: [
          widget.child,
          Positioned(
            left: offset.dx - 90,
            top: offset.dy - 90,
            child: Container(
              decoration: BoxDecoration(
                  border: Border.all(
                      width: 9, color: theme.colorScheme.onBackground)),
              child: Container(
                  width: 180,
                  height: 180,
                  clipBehavior: Clip.hardEdge,
                  decoration: const BoxDecoration(),
                  child: FittedBox(
                      fit: BoxFit.cover,
                      child: Transform.scale(
                          scale: 4,
                          child: Transform.translate(
                              offset:
                                  Offset(-offset.dx + 220, -offset.dy + 220),
                              child: widget.child)))),
            ),
          )
        ],
      ),
    );
  }
}

要获得 child 的大小,您可以使用 GlobalKey 并将其分配给 child(图像或任何小部件),然后在您的 Listener (你可能可以使用 MouseRegion 而不是 Listener,类似的目的,使用起来稍微容易一点)你可以使用 GlobalKey.[=24 获取其 child 小部件的大小=]

但是请注意,小部件的大小只有在完成布局过程后才能确定。如果你不介意落后一帧,你可以在下一帧用 Stack (你当前正在做的)做放大镜效果。否则,您可以考虑使用 OverlayEntry 代替放大镜,因为叠加是在普通小部件之后的单独流程中构建的。

注意前面的注释,我个人不介意在这种特殊情况下滞后一帧,因为放大镜仅在用户将鼠标悬停时才会显示,因此首次加载页面时不会跳过一帧引人注目。


编辑:

这个问题对我来说很有趣,我只是想到了另一种在同一帧中获得child大小的方法:在Stack中使用Positioned.fill,不需要 GlobalKeyOverlay。利用这个想法,我做了一个小部件来做到这一点。

用法:

 Magnifier(
    magnification: 2.0,
    child: Scaffold(
       ...

来源:

class Magnifier extends StatefulWidget {
  final Widget child;
  final double magnification;

  const Magnifier({
    Key? key,
    required this.child,
    this.magnification = 2.0,
  }) : super(key: key);

  @override
  _MagnifierState createState() => _MagnifierState();
}

class _MagnifierState extends State<Magnifier> {
  Offset? _offset;

  @override
  Widget build(BuildContext context) {
    return Stack(
      alignment: Alignment.center,
      children: [
        widget.child,
        Positioned.fill(
          child: LayoutBuilder(
            builder: (_, BoxConstraints constraints) {
              final childSize = constraints.biggest;
              return MouseRegion(
                onHover: (event) {
                  setState(() => _offset = event.localPosition);
                },
                onExit: (_) => setState(() => _offset = null),
                child: _offset != null
                    ? _buildBox(_offset!.dx, _offset!.dy, childSize)
                    : null,
              );
            },
          ),
        )
      ],
    );
  }

  Widget _buildBox(double dx, double dy, Size childSize) {
    final magnifierSize = childSize.shortestSide / 2;
    return Transform.translate(
      offset: Offset(dx - magnifierSize / 2, dy - magnifierSize / 2),
      child: Align(
        alignment: Alignment.topLeft,
        child: Stack(
          children: [
            SizedBox(
              width: magnifierSize,
              height: magnifierSize,
              child: ClipRect(
                child: Transform.scale(
                  scale: widget.magnification,
                  child: Transform.translate(
                    offset: Offset(
                      childSize.width / 2 - dx,
                      childSize.height / 2 - dy,
                    ),
                    child: OverflowBox(
                      minWidth: childSize.width,
                      maxWidth: childSize.width,
                      minHeight: childSize.height,
                      maxHeight: childSize.height,
                      child: widget.child,
                    ),
                  ),
                ),
              ),
            ),
            Positioned.fill(
              child: Container(
                decoration: BoxDecoration(
                  border: Border.all(color: Colors.black, width: 2),
                  color: Colors.green.withOpacity(0.2),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}