效果图:
要实现一个iMessage的多图效果,可以左右切换查看图片,遂查阅pub.dev,翻了好多只有一个是贴合主题的,下载跑下demo,结果体验很差,决定自己写一个。
查了资料,大致两种实现思路
- 用Stack做容器,顶层放一个 pageview,底层放置需要展示的内容,进而监听 pageview 滚动,动态 调整展示内容
- 进行监听 GestureDetector 手势,onPanStart、onPanUpdate等事件,自定义动画,手势抬起后,判断临界值,是 forward 还是 reverse,动态更改内容展示
这里选择第一种进行具体实现:
1.首先解决穿透点击
因为是用的Stack作用器,监听顶层的Pageview,那么在底层的widget想要实现被点击的事件,就要对Stack进行重写,来实现点击后面的children也能响应事件。
这里创建一个继承自Stack的Widget,重写他的createRenderObject,然后返回自定义的RenderStack,并重写其中的hitTestChildren方法,来实现穿透点击,判断自己的children是否被点击
final List<RenderBox> children = getChildrenAsList();
for (final RenderBox child in children) {
final StackParentData childParentData =
child.parentData as StackParentData;
final bool childHit = result.addWithPaintOffset(
offset: childParentData.offset,
position: position!,
hitTest: (BoxHitTestResult result, Offset transformed) {
assert(transformed == position - childParentData.offset);
return child.hitTest(result, position: transformed);
},
);
if (childHit) {
stackHit = true;
}
}
使用:
_ThroughStack(
children: <Widget>[
RepaintBoundary(child: _stackedCards(context)),
NotificationListener<ScrollNotification>(
onNotification: _scrollUpdated,
child: PageView.builder(
scrollDirection: Axis.horizontal,
controller: _pageController,
itemCount: _itemsLength,
itemBuilder: (BuildContext context, int index) {
return const SizedBox.shrink();
},
),
),
],
)
2. Z-Indexed
实现这个效果很重要的一点就是z-index的切换,比如最顶部的image滑到侧边临界值后,需要和下一张进行上下交换,但是flutter并不带有设置z-index的属性或方法,于是找了一个插件来实现这个效果:indexed,通过对index属性的更改来实现层级的改变。
层级能动态更改后,就可以按需求进行层级排序,这里有两个要点:
1.左右两边的图片排序方式的不同
因为默认pageview的分页索引在最左侧为 0,所以滑动的方向是从右向左滑动,按照默认索引滑到左边的叠层排序是正常的,而在右边的,则最后面的索引高于前面的索引,所以需要手动对z-index进行排序:
_decreasing(int current, bool left) {
final list = left
? _models.sublist(0, current).reversed.toList()
: _models.sublist(current, _models.length);
var z = _itemsLength - current;
for (int i = 0; i < list.length; i++) {
list[i].zIndex = z--;
}
}
2.顶层图片左右滑动时切换相邻左右图片索引
我们将顶层图片索引设为 0,当左右两边都有图片的时候,那么顶层图片向左滑动时露出右 1 图片,那么右 1 的索引就要高于左 1 的索引,反之亦然,这时候就也要手动设置左右两边相邻图片的索引,演示图中也有展示这个效果。这里需要判断顶层图片的偏移量和滑动方向进行计算即可。
<------ 0.9 ~ 0.1 | -0.1 ~ -0.9 ------>
之后的就是按效果需求对顶层图片和侧边图片的偏移,缩放和rotate分别进行计算了,然后赋值给Container的transform:
transform: Matrix4.identity()
..translate(-offset)
..scale(scale)
..rotateZ(-rotation),
抛砖引玉,内容不多,具体实现可以查看代码,写的很乱凑合看吧
附上地址
github: images_swiper, pub.dev: images_swiper
喜欢的话,可以star一下