Interactiveviewer是什么?
在进行Android开发时,如果我们想要实现一张图片的双指放大缩小,放大后拖动图片,就需要利用ScaleGesturedetector
以及GestureDetecter
来对图片进行手势监听,然后在onDraw
方法中对其进行放缩以及移动的处理。但是在Flutter中,官方给我们提供了这样的一个处理类,我们只需要把想要放缩的Widget设置给Interactiveviewer,就可以实现放缩移动的效果,其内部也是使用了GestureDetector
以及Listener
来实现的手势监听来实现的这种效果。
// 这里是Interactiveviewer内部的build方法
@override
Widget build(BuildContext context) {
Widget child;
// ... 删除其他代码
return Listener(
key: _parentKey,
onPointerSignal: _receivedPointerSignal,
child: GestureDetector(
behavior: HitTestBehavior.opaque, // Necessary when panning off screen.
onScaleEnd: _onScaleEnd,
onScaleStart: _onScaleStart,
onScaleUpdate: _onScaleUpdate,
child: child,
),
);
}
为什么要写这篇文章
在网上一搜Interactiveviewer,就会出来很多关于如何使用Interactiveviewer的文章,那么我为什么还要去重复的去写这么一篇文章呢?显然,如果只是简单的说下它的用法,那我这是纯属浪费大家的时间,网上的文章基本都是教大家基本的用法,但是我们在需求开发中,经常会遇到如果要求对一个列表进行放缩的时候改怎么办呢?依然简单的使用Interactiveviewer吗?显然是不行的,因为列表本身是一个可滚动的组件,如果是简单的设置给Interactiveviewer,会存在一些手势的冲突,而本篇文章,就是为了解决这些问题而写的。
正文
这里就不再去浪费时间介绍Interactiveviewer的用法以及一个普通的ImageView该怎么去放缩了,直接进入正题,如果对一个可滚动组件进行处理。
step1
简单的进行Interactiveviewer进行包裹,看看会出现什么问题?
InteractiveViewer(
key: _key,
scaleEnabled: true,
boundaryMargin: margin,
minScale: 0.5,
maxScale: 5,
transformationController: _controller,
child: SizedBox.expand(
child: CustomScrollView(
controller: listController,
slivers: [
for (int i = 0; i < 9; i++)
SliverToBoxAdapter(
child: Container(
height: 400,
width: 400,
child: Image.asset('assets/images/test0${i + 1}.png', width: 400, height: 400,),
),
)
],
),
),
)
这时会发现,咦,这不是能正常的放大缩小,放大后也能正常移动吗?确实可以,但是很快就发现当放大后,列表不能滚动到最底部或者最顶部,这是为什么呢?因为在上下滚动的时候,并不是Interactiveviewer在处理手势事件,而是列表在处理上下滚动,而滚动列表的滚动行为是ScrollController在管理,ScrollController中的listController.position.maxScrollExtent
标记了列表的最大可滚动范围,当放大后,那个最大可滚动范围是不变的,所以滚动同样的距离,你当然不能看到最底部了,最顶部也是同样道理。
那该如何解决这个问题呢?
step2
解决放大后不能滚动到最底部和最顶部,这时就要借助NotificationListener
去监听OverscrollNotification
事件了,当监听到列表滚动到边界时,获取越界滚动值,手动处理列表的滑动。具体代码如下:
if (notification is OverscrollNotification) {
if((notification.metrics.axisDirection == AxisDirection.up
|| notification.metrics.axisDirection == AxisDirection.down)) {
// 判断如果是越界滚动,并且是上下滚动时
// 注意这个controller是Interactiveviewer的TransformationController
// 获取Interactiveviewer上下滑动的距离
var row12 = _controller.value.row1[3];
var temp = notification.dragDetails?.delta.dy ?? 0.0; // 获取越界手势间距
var value = _controller.value.clone();
// 此处的_key 是设置给Interactiveviewer的GlobalKey,主要是用来获取Interactiveviewer Widget的原始大小
var height = _key.currentContext?.size?.height ?? 0;