Flutter可缩放小部件

26
我想要构建的小部件是一种可以使其子小部件变焦,类似于可变焦的行为。我想涵盖的手势包括:
  1. 捏合缩放
  2. 双击缩放
  3. 点击获取小部件的本地位置
这是我的小部件计划:
ZoomableWidget(
   child: // My custom Widget which should be zoomable.
)

这是我的当前进展情况:

import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:vector_math/vector_math_64.dart';

class ZoomableWidget extends StatefulWidget {
  final Widget child;

  const ZoomableWidget({Key key, this.child}) : super(key: key);
  @override
  _ZoomableWidgetState createState() => _ZoomableWidgetState();
}

class _ZoomableWidgetState extends State<ZoomableWidget> {
  double _scale = 1.0;
  double _previousScale;
  @override
  Widget build(BuildContext context) {
    return ClipRect(
      child: GestureDetector(
        onScaleStart: (ScaleStartDetails details) {
          _previousScale = _scale;
        },
        onScaleUpdate: (ScaleUpdateDetails details) {
          setState(() {
            _scale = _previousScale * details.scale;
          });
        },
        onScaleEnd: (ScaleEndDetails details) {
          _previousScale = null;
        },
        child: Transform(
          transform: Matrix4.diagonal3(Vector3(_scale.clamp(1.0, 5.0),
              _scale.clamp(1.0, 5.0), _scale.clamp(1.0, 5.0))),
          alignment: FractionalOffset.center,
          child: widget.child,
        ),
      ),
    );
  }
}
我遇到的问题是,我无法更改捏合手势的中心位置,因此即使我在角落处缩放,图像仍只在 (0,0) 处缩放。此外,我无法使用水平拖动和垂直拖动来滚动小部件。
提前致谢。

3
请参考 https://dev59.com/V7Lma4cB1Zd3GeqPdZux#55324411 - pskink
谢谢你的评论,它完美地解决了我的问题。我整天都很忙,抱歉回复晚了。再次感谢你! - Aawaz Gyawali
6个回答

52

Flutter 1.20开始,InteractiveViewer小部件支持开箱即用的平移和缩放。
要使任何小部件可缩放,只需使用InteractiveViewer包装子项即可。

@override
Widget build(BuildContext context) {
  return Center(
    child: InteractiveViewer(
      panEnabled: false, // Set it to false to prevent panning. 
      boundaryMargin: EdgeInsets.all(80),
      minScale: 0.5,
      maxScale: 4, 
      child: FlutterLogo(size: 200),
    ),
  );
}

2
似乎无法在“WebView”中缩小HTML页面,只有放大才有效。 - kakyo
交互结束后,如何使其恢复为默认设置。以与图像交互为例。 - Mrak Vladar
1
@MrakVladar,您可以通过将TransformationController分配给InteractiveViewer并将控制器的值设置为Matrix4.identity()来恢复默认设置。 - Till Friebe
如何使其在ListView内平稳工作?有时需要用力按下才能缩放。 - Noor
这是一个有用的功能,但我在使用InteractiveViewer和GestureDetector时改变了多个小部件的位置,这时它并不能完美地工作,因为InteractiveViewer只能根据子小部件的大小来工作。所以我无法在Stack小部件中给每个子小部件完整的大小。我们该怎么解决这个问题呢?请帮忙,谢谢。 - Alpit Panchal

17

现在已经完美地解决了,感谢参考 @pskink。

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

class ZoomableWidget extends StatefulWidget {
  final Widget child;

  const ZoomableWidget({Key key, this.child}) : super(key: key);
  @override
  _ZoomableWidgetState createState() => _ZoomableWidgetState();
}

class _ZoomableWidgetState extends State<ZoomableWidget> {
  Matrix4 matrix = Matrix4.identity();

  @override
  Widget build(BuildContext context) {
    return MatrixGestureDetector(
      onMatrixUpdate: (Matrix4 m, Matrix4 tm, Matrix4 sm, Matrix4 rm) {
        setState(() {
          matrix = m;
        });
      },
      child: Transform(
        transform: matrix,
        child: widget.child,
      ),
    );
  }
}

我喜欢这个解决方案,唯一不喜欢的是当你缩放并双击再次缩放时,缩放似乎会随机开始。如果您使用新生成的密钥进行双击,则对我来说效果更好。如果有更好的解决方案,请告诉我。 - autlunatic
你能解释一下你的解决方案吗?@autlunatic,我不确定哪个小部件应该获得密钥。 - Jose Georges
我记不太清了,也许我搞混了。我有一个版本,在这个版本中,双击会恢复到“默认缩放”。问题在于,双击后缩放始于一个“错误”的点,因为它保存了矩阵或其他东西。为了解决这个问题,我为GestureDetector上的doubletap生成了一个新的gestureKey,对我来说效果很好。 - autlunatic
嘿@autlunatic,我也遇到了同样的问题。你能否提供一段代码片段,展示如何生成GestureDetector的密钥来解决这个问题?如果你能做到这一点,那就太好了。提前感谢你。 - litt
@ RCN 看一下 Jilly Tabogas 的回答。我添加了一个新创建的键来进行双击。 GestureDetector( key:_gestureKey, onDoubleTap:(){ setState((){ _gestureKey = GlobalKey(); matrix = zerada; }); }, child:MatrixGestureDetector( shouldRotate:false, onMatrixUpdate:(m,tm,sm,rm){ setState((){ matrix = m; }); }, child:Transform( transform:matrix, child:widget.child, ), ), ); - autlunatic

11

我很喜欢这个分辨率,你应该把它打包放在发布里,甚至可以添加一些自定义选项,在我的代码中,我将双击重置缩放和锁定旋转。

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

class ZoomableWidget extends StatefulWidget {
  final Widget child;

  const ZoomableWidget({Key key, this.child}) : super(key: key);
  @override
  _ZoomableWidgetState createState() => _ZoomableWidgetState();
}

class _ZoomableWidgetState extends State<ZoomableWidget> {
  Matrix4 matrix = Matrix4.identity();
  Matrix4 zerada =  Matrix4.identity();

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onDoubleTap: (){
        setState(() {
          matrix = zerada;
        });
      },
      child: MatrixGestureDetector(
        shouldRotate: false,
        onMatrixUpdate: (Matrix4 m, Matrix4 tm, Matrix4 sm, Matrix4 rm) {
          setState(() {
            matrix = m;
          });
        },
        child: Transform(
          transform: matrix,
          child: widget.child,
        ),
      ),
    );
  }
}

为什么缩放这么困难?我有一个使用轮播显示的图像列表,我使用了上述方法,但缩放非常困难。当我尝试缩放时,应用程序会尝试向下滚动,如果我将手指放置几秒钟,然后才能正常工作。 - Evripides Kyriacou

3
您可以使用 Zoom Widget Zoom Widget,它只需要设置画布大小和子元素即可。
    Zoom(
    width: 1800,
    height: 1800,
    child: Center(
        child: Text("Happy zoom!!"),
    )
);

在 MacOS 上无法工作,只会出现错误 'package:flutter/src/rendering/mouse_tracker.dart': Failed assertion: line 201 pos 12: '!_debugDuringDeviceUpdate': is not true. - Oliver Dixon

3

作为MatrixGestureDetector的替代选择,您可以使用photo_view包:https://pub.dev/packages/photo_view

它有很好的屏幕限制,因此您无法将子项拖到屏幕外,当达到最小/最大大小时会有反弹效果,还有许多其他选项。

它可以像这样与自定义子项一起使用:

PhotoView.customChild(
    child: <your widget>
)

0

我使用缩放小部件

首先,在 pubsec.yaml 中添加:

dependencies:
zoom_widget: ^2.0.0

然后导入:
import 'package:zoom_widget/zoom_widget.dart';

使用最大宽度和最大高度使文本居中:

Zoom(
maxZoomWidth: 1800,
maxZoomHeight: 1800,
child: Center(
    child: Text("Happy zoom!!"),
)

);


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接