原文链接:A Deep Dive Into PageView In Flutter (With Custom Transitions) - 原文作者 Deven Joshi
本文采用意译的方式
本文,我们首先看看 PageView
挂件的内容,然后为它自定义一些特效。
探索 PageViews
PageViews
是一个可以在屏幕上生成滚动页面的挂件。这可以是固定的页面列表或者构建重复页面的 builder
函数。PageView
的行为跟 ListView
的在构建元素的意义上类似。
PageViews
的类型有:
PageView
PageView.builder
PageView.custom
PageView(默认构造器)
我们以固定列表页面为例,使其可滚动。
dart
PageView(
children: <Widget>[
Container(
color: Colors.pink,
),
Container(
color: Colors.cyan,
),
Container(
color: Colors.deepPurple,
),
],
)
上面的代码产生下面的效果:
PageView.builder
该构造器传入 itemBuilder
函数和 itemCount
属性,这和 ListView.builder
类似。
dart
PageView.builder(
itemBuilder: (context, position) {
return _buildPage();
},
itemCount: listItemCount, // 可以是 null
)
就如 ListView.builder
那样,也是根据需求构建子挂件。
如果 itemCount
被设置为 null
(或者不设计),页面的列表会被无限生成。
比如,下面代码:
dart
PageView.builder(
itemBuilder: (context, position) {
return Container(
color: position % 2 == 0 ? Colors.pink : Colors.cyan
);
},
)
下面是粉红色和青色交替的页面无限列表:
注意:
PageView.custom
工作方式和ListView.custom
一样,这里不再介绍。
Orientation
所有 Page Views
类型都可以有 水平方向
或者 垂直方向
的滚动页面。
dart
PageView(
children: <Widget>[
// 这里添加子挂件
],
scrollDirection: Axis.vertical,
)
上面代码效果:
PageSnapping
页面贴合(Page Snapping
)允许我们将页面保留在干扰值上。我们可以通过关闭 pageSnapping
的属性来实现。在这种情况下,页面不会滚动到一个整数位置,而是像普通的 ListView
一样的行为。
dart
PageView(
children: <Widget> [
// 添加子挂件
],
pageSnapping: false,
)
ScrollPhysics
PageView
可以像 ListView
那样有自定义滚动行为。
dart
PageView(
children: <Widget>[
// 添加子挂件
],
physics: BouncingScrollPhysics(),
)
Controlling a PageView
PageView
可以通过添加 PageController
被程序控制。
dart
// 在 build 方法外
PageController controller = PageController();
// 在 build 方法内
PageView(
controller: controller,
children: <Widget>[
// 添加子挂件
]
)
滚动的位置,当前页面等通过使用控制器都可以被检测。
注意:
controller.currentPage
返回一个double
值。比如,当滑动页面时,该值逐渐从 1 变为 2,并且不会立即跳到 2。
添加自定义过渡到 PageViews
下面我们讨论使用 Transform + PageView
来添加一些自定义的页面过渡。这部分我们将广泛使用 Transform
挂件,我们推荐大家阅读关于此小挂件的多篇文章。
我们推荐 Deep Dive I wrote 和 WM Leler's Transform article。
Transform 1
设置
首先,我们使用一个基本的 PageView.builder
:
dart
PageView.builder(
controller: controller,
itemBuilder: (context, position) {
},
itemCount: 10,
)
现在我们有 10
项。
我们使用了一个 PageController
和一个变量来保存当前页面 currentPage
。
定义 PageController
和变量:
dart
PageController controller = PageController();
var currentPageValue = 0.0;
当 PageView
滚动时,更新变量。
dart
controller.addListener(() {
setState(() {
currentPageValue = controller.page;
});
});
最后,我们构建 PageView
。
现在,我们检查三个条件:
- 如果页面是正在被滑动的页面
- 如果页面是正在被滑动到的页面
- 如果页面是一个离屏的页面
dart
PageView.builder(
controller: controller,
itemBuilder: (context, position) {
if (position == currentPageValue.floor()) {
} else if (position == currentPageValue.floor() + 1) {
} else {
}
},
itemCount: 10,
)
现在,我们返回一样的页面,但是通过一个 Transform
小挂件来包裹,当我们滑动页面时实现页面效果。
dart
PageView.builder(
controller: controller,
itemBuilder: (context, position) {
if(position == currentPageValue.floor()) {
return Transform(
transform: Matrix4.identity()..rotateX(currentPageValue - position),
child: Container(
color: position % 2 == 0 ? Colors.blur : Colors.pink,
child: Center(
child: Text(
"Page",
style: TextStyle(color: Colors.white, fontSize: 22.0),
),
),
)
);
} else if (position == currentPageValue.floor() + 1) {
return Transform(
transfrom: Matrix4.identity..rotateX(currentPageValue - position),
child: Container(
color: position % 2 == 0 ? Colors.blue : Colors.pink,
child: Center(
child: Text(
"Page",
style: TextStyle(color: Colors.white, fontSize: 22.0),
),
),
),
);
} else {
return Container(
color: position % 2 ? Colors.blue : Colors.pink,
child: Center(
child: Text(
"Page",
style: TextStyle(color: Colors.white, fontSize: 22.0),
),
),
);
}
},
itemCount: 10,
)
现在,我们更改从滑动来的页面到滑动到的页面。
currentPageValue.floor()
获取到左侧的页面,而 currentPageValue.floor() + 1
获取到右侧的页面。
在这个例子中,我们在 X
方向旋转页面,因为它通过 currentPageValue
减去 index
的弧度值进行滑动。我们可以通过乘于这个值放大这种效果。
我们可以调整此变换和变换的对齐方式,来获取多种类型的页面转产场效果。
Transition 2
和上面相似的代码结构,只是不同的转场效果。
dart
PageView.builder(
controller: controller,
itemBuilder: (context, position) {
if (position == currentPageValue.floor()) {
return Transition(
transform: Matrix4.identity()..rotateY(currentPageValue - position)..rotateZ(currentPageValue - position),
child: Container(
color: position % 2 == 0 ? Colors.blue : Colors.pink,
child: Center(
child: Text(
"Page",
style: TextStyle(color: Colors.white, fontSize: 22.0),
),
),
),
);
} else if (position == currentPageValue.floor() + 1) {
return Transform(
transform: Matrix4.identity()..rotateY(currentPageValue - position)..rotateZ(currentPageValue - position),
child: Container(
color: position % 2 == 0 ? Colors.blue : Colors.pink,
child: Center(
child: Text(
"Page",
style: TextStyle(color: Colors.white, fontSize: 22.0),
),
),
),
);
} else {
return Container(
color: position % 2 == 0 ? Colors.blue : Colors.pink,
child: Center(
child: Text(
"Page",
style: TextStyle(color: Colors.white, fontSize: 22.0),
),
),
);
}
},
itemCount: 10,
)
这里,我们 Y
轴和 Z
轴都进行旋转。
Transition 3
这个和上一个过渡类似,但是我们添加了 3-D
特效。
dart
PageView.builder(
controller: controller,
itemBuilder: (context, position) {
if(position == currentPageValue.floor()) {
return Transform(
transform: Matrix4.identity()..setEntry(3, 2, 0.004)..rotateY(currentPageValue - position)..rotateZ(currentPageValue - position),
child: Container(
color: position % 2 == 0 ? Colors.blue : Colors.pink,
child: Center(
child: Text(
"Page",
style: TextStyle(color: Colors.white, fontSize: 22.0),
),
),
),
);
} else if (postion == currentPageValue.floor() + 1) {
return Transform(
transform: Matrix4.identity()..setEntry(3, 2, 0.004)..rotateY(currentPageValue - position)..rotateZ(currentPageValue - position),
child: Container(
color: position % 2 == 0 ? Colors.blue : Colors.pink,
child: Center(
child: Text(
"Page",
style: TextStyle(color: Colors.white, fontSize: 22.0),
),
),
),
);
} else {
return Container(
color: position % 2 == 0 ? Colors.blue : Colors.pink,
child: Center(
child: Text(
"Page",
style: TextStyle(color: Colors.white, fontSize: 22.0),
),
),
);
}
},
itemCount: 10,
)
这行代码:
dart
..setEntry(3, 2, 0.004)
给页面类似 3-D
的效果。
Transition 4
dart
PageView.builder(
controller: controller,
itemBuilder: (context, position) {
if(position == currentPageValue.floor()) {
return Transform(
alignment: Alignment.center,
transform: Matrix4.identity()..setEntry(3, 2, 0.001)
..rotateX(currentPageValue - position)
..rotateY(currentPageValue - position)
..rotateZ(currentPageValue - position),
child: Container(
color: position % 2 == 0 ? Colors.blue : Colors.pink,
child: Center(
child: Text(
"Page",
style: TextStyle(color: Colors.white, fontSize: 22.0),
),
),
),
);
} else if (postion == currentPageValue.floor() + 1) {
return Transform(
transform: Matrix4.identity()..setEntry(3, 2, 0.001)
..rotateX(currentPageValue - position)
..rotateY(currentPageValue - position)
..rotateZ(currentPageValue - position),
child: Container(
color: position % 2 == 0 ? Colors.blue : Colors.pink,
child: Center(
child: Text(
"Page",
style: TextStyle(color: Colors.white, fontSize: 22.0),
),
),
),
);
} else {
return Container(
color: position % 2 == 0 ? Colors.blue : Colors.pink,
child: Center(
child: Text(
"Page",
style: TextStyle(color: Colors.white, fontSize: 22.0),
),
),
);
}
},
itemCount: 10,
)
我们可以通过简单更改旋转的角度,坐标轴,对齐方式和平移来创建更多类型的过渡效果。
Demo App using PageView
在 Flutter
中,为了演示使用 PageView
来创建一个简单的应用,我创建了一个来学习 GRE
词汇的应用。这个应用使用了 SQLite
存储,为用户展示了单词并保存难懂的词汇。它也有单词发音的功能。
对应的仓库地址为 github.com/deven98/Flu...。
官方位置👉 PageView class