PageView in ListView

如何为ListView中的每一项实现 PageView 滑动的效果?

因为 PageView 会独占整个显示区域的剩余部分,必须包上如 SizedBox 这样的限定尺寸的 Widget 才可以把它放到 ListView 中。但是 SizedBox 需要提前设置 heightwidth,但并没有很好的办法提前获取子元素的尺寸,而固定一个尺寸很多时候并不能满足需求。

经过研究,解决方法其实非常简单,即使用 SingleChildScrollView + PageScrollPhysics + PageController,就可以完美的实现 PageView 的效果,包括:
1. 滑动的动画,
2. 吸附到边界(snapping),
3. 指定初始页面索引。
4. 如果需要获取页面切换通知,使用 NotificationListener 包裹SingleChildScrollView即可。

NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification notification) {
if (notification.depth == 0 && notification is ScrollUpdateNotification) {
final PageMetrics metrics = notification.metrics as PageMetrics;
final int currentPage = metrics.page!.round();
if (currentPage != _lastReportedPage) {
_lastReportedPage = currentPage;

if (widget.onPageChanged != null) {
widget.onPageChanged!(currentPage);
}
}
}
return false;
},

SingleChildScrollView 的子元素:

... ...
screenSize ??= MediaQuery.of(context).size;
... ...
SingleChildScrollView(
scrollDirection: Axis.horizontal,
physics: const PageScrollPhysics(),
controller: this.controller,
child: SizedBox(
width: screenSize!.width * 2,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Flexible(child: xxx),
Flexible(child: xxx),
],
),
),
),

上面的 screenSize 的获取,使用 LayoutBuilder 更好。