2
votes

I'm fighting with zoom/pan gestures. I've found a partially suitable example in this book:

https://books.google.bg/books?id=ex-tDwAAQBAJ&pg=PA284&lpg=PA284&dq=flutter+_startLastOffset&source=bl&ots=YUQna09jIf&sig=ACfU3U0QrHwl2RdrVUv5EtpHaHFKx_cXhA&hl=en&sa=X&ved=2ahUKEwid9uPJ8abnAhVnlosKHTKQBn4Q6AEwAHoECAMQAQ#v=onepage&q=flutter%20_startLastOffset&f=false

which, I guess was based on this: https://chromium.googlesource.com/external/github.com/flutter/flutter/+/refs/heads/dev/examples/layers/widgets/gestures.dart

And my code based on it:

Offset _startLastOffset = Offset.zero;
Offset _lastOffset = Offset.zero;
Offset _currentOffset = Offset.zero;
double _lastScale = 1.0;
double _currentScale = 1.0;

void _onScaleStart(ScaleStartDetails details) {
  _startLastOffset = details.focalPoint;
  _lastOffset = _currentOffset;
  _lastScale = _currentScale;
}

void _onScaleUpdate(ScaleUpdateDetails details) {
  if (details.scale != 1.0) {
    // Scaling
    double currentScale = _lastScale * details.scale;
    if (currentScale < 0.5) {
      currentScale = 0.5;
    }
    _currentScale = currentScale;
    _bloc.setScale(_currentScale);
  } else if (details.scale == 1.0) {
    // We are not scaling but dragging around screen
    // Calculate offset depending on current Image scaling.
    Offset offsetAdjustedForScale = (_startLastOffset - _lastOffset) / _lastScale;
    Offset currentOffset = details.focalPoint - (offsetAdjustedForScale * _currentScale);
    _currentOffset = currentOffset;
    _bloc.setOffset(_currentOffset);
  }
}
---------------
child: StreamBuilder<Object>(
  stream: _bloc.scale,
  builder: (context, snapshot1) {
    double _cscale = snapshot1.hasData? snapshot1.data : _currentScale;
    return StreamBuilder<Object>(
      stream: _bloc.offset,
      builder: (context, snapshot2) {
        Offset _coffset = snapshot2.hasData? snapshot2.data : _currentOffset;
        return Transform(
          transform: Matrix4.identity()
            ..scale(_cscale, _cscale)
            ..translate(_coffset.dx, _coffset.dy),
          alignment: FractionalOffset.center,
          child: Stack(
            key: _imgStack,
            fit: StackFit.expand,
            children: <Widget>[
              Image(image: AssetImage('assets/images/image.png')),
              SvgPicture.asset(svgFile)
            ],
          ),
        );
      }
    );
  }
),

The Pan of which, however, behaves unnatural when the widget is zoomed - the widget moves much further than the finger, in the moving direction.

I managed to fix this by changing:

_currentOffset = currentOffset;
_bloc.setOffset(_currentOffset);

to:

_bloc.setOffset(currentOffset/_currentScale);
_currentOffset = currentOffset;

Which works exactly as expected, but just until the Second zoom. After the second time I zoom, the widget get shifted on first touch. On zoom-in it shifts to the right, on zoom-out it shifts to the left.

Any ideas?

1

1 Answers

1
votes

Use InteractiveViewer widget. Here is an example.

@override
Widget build(BuildContext context) {
  return Center(
    child: InteractiveViewer(
      boundaryMargin: EdgeInsets.all(100),
      minScale: 0.1,
      maxScale: 1.5,
      child: Container(width: 200, height: 200, color: Colors.blue),
    ),
  );
}