Home > Software design >  How to zoom an image without the size
How to zoom an image without the size

Time:03-16

I want to zoom an image but I don't want to care about the size of the image. This widget is for wrapping any widget. The widget I transform is somewhere I don't know. That's why I add 220 to be visible. Could someone enhance my code to be adaptive for any size of widget.

enter image description here

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)))),
            ),
          )
        ],
      ),
    );
  }
}

CodePudding user response:

To get the size of the child, you can use a GlobalKey and assign it to the child (image or whatever widget), then in your Listener (you can probably use MouseRegion instead of Listener, similar purpose, slightly easier to use) you can get the size of its child widget using GlobalKey.

Note, however, a widget's size can only be determined after it has finished the layout process. If you don't mind lagging one frame behind, you can just do the magnifier effect with a Stack (what you are doing currently) in the next frame. Otherwise, you can consider using an OverlayEntry to do the magnifier instead, because overlays are built in a separate flow after normal widgets.

Note on the previous note, I personally wouldn't mind lagging one frame in this particular case, because the magnifier only shows up when user hovers it, so skipping 1 frame when the page is first loaded won't be noticeable.


Edit:

This question is pretty interesting to me, so I made a widget to do this.

demo

Usage:

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

Source:

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),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}
  • Related