(Flutter)实现简洁好看的PageView指示器
2021-02-18 本文已影响0人
rhyme_lph
简洁好看的指示器
1.介绍
在使用PageView
时,我们通常需要添加指示器,以避免用户产生只有一张图片显示的错觉,所以,添加指示器是必不可少的!但是,有时候图片一多,指示器也同样的出现多的情况,导致显示的指示器不能显示太大,并且间距也需要适当的减少,下面来看一下我实现的效果,简洁大方!
2.开始实现
首先,我们要确定传进来的参数有哪些
-
PageController
controller 用于绑定指示器 -
int
itemCount 与PageView
对应,需要确认有多少页 -
Color
indicatorColor 指示器的颜色 -
double
maxSize 指示器到达中间时的大小 -
double
minSize 指示器两边圆点的大小 -
double
space 指示器两圆点之间的间距
确认上面的参数后,我们来新建一个dart
文件,命名为simple_page_indicator.dart
,然后添加下面的内容
class SimplePageIndicator extends StatelessWidget {
final PageController controller;
final int itemCount;
final Color indicatorColor;
final double maxSize;
final double minSize;
final double space;
const SimplePageIndicator(
{Key key,
this.controller,
this.itemCount,
this.indicatorColor,
this.maxSize,
this.space,
this.minSize})
: super(key: key);
@override
Widget build(BuildContext context) {
return Container();
}
}
接下来,我们需要使用AnimatedBuilder
,并传入animation
参数为controller
//...
@override
Widget build(BuildContext context) {
// return Container();
return AnimatedBuilder(animation: controller, builder: _buildAnimatedItem);
}
Widget _buildAnimatedItem(BuildContext context, Widget child) {
return Container();
}
//...
这样我们就能监听到PageController
的值发生改变时做出指示器对应的变化,下面我们来使用CustomPaint
对指示器进行绘制,新加一个SimplePageIndicatorPainter
类并将对应的值传递过去继续修改
//...
Widget _buildAnimatedItem(BuildContext context, Widget child) {
// return Container();
return CustomPaint(
painter: SimplePageIndicatorPainter(
itemCount: itemCount,
indicatorColor: indicatorColor,
maxSize: maxSize,
minSize: minSize,
space: space,
pageIndex: 0,
pageOffset: .0
),
);
}
/// ...
class SimplePageIndicatorPainter extends CustomPainter {
final int itemCount;
final Color indicatorColor;
final double maxSize;
final double minSize;
final int pageIndex;
final double pageOffset;
final double space;
const SimplePageIndicatorPainter(
{this.itemCount,
this.indicatorColor,
this.maxSize,
this.space,
this.minSize,
this.pageIndex,
this.pageOffset});
@override
void paint(Canvas canvas, Size size) {}
@override
bool shouldRepaint(covariant SimplePageIndicatorPainter oldDelegate) {
return true;
}
}
下面,我们来开始画对应的小圆点,我们默认的将指示器的位置设置为0起点和偏移量也为0,我们需要画三个点,因为当前是在0起始位,我们只需要画两个点,一个大的点在中间,另一个小点在最右边
// ...
const _kMaxCircleCount = 3; // 默认三个圆点
class SimplePageIndicatorPainter extends CustomPainter {
final int itemCount;
final Color indicatorColor;
final double maxSize;
final double minSize;
final int pageIndex;
final double pageOffset;
final double space;
final bool isStart;
final bool isEnd;
const SimplePageIndicatorPainter(
{this.itemCount,
this.indicatorColor,
this.maxSize,
this.space,
this.minSize,
this.pageIndex,
this.pageOffset,
this.isStart,
this.isEnd});
@override
void paint(Canvas canvas, Size size) {
//初始化画笔
Paint mPaint = Paint()
..color = indicatorColor
..isAntiAlias = true;
//获取中间的圆点为第几个
final _centerCircleIndex = _kMaxCircleCount ~/ 2;
//获取每个圆点占据的大小,把widget分为3份
double childWidth = size.width / _kMaxCircleCount;
//遍历画圆点
for (int i = 0; i < _kMaxCircleCount; i++) {
// 当页数为0的时候,也就是第一页,不画第一个圆点
if (pageIndex == 0 && i == 0) {
continue;
}
if (isEnd && i == _kMaxCircleCount - 1) {
continue;
}
//画中心圆
if (_centerCircleIndex == i) {
canvas.drawCircle(
Offset(
(i * childWidth) + (childWidth / 2) - childWidth * pageOffset,
childWidth / 2),
maxSize - (maxSize - minSize) * pageOffset,
mPaint..color = indicatorColor);
}
//画左边的圆
else if (i < _centerCircleIndex) {
canvas.drawCircle(
Offset(
(i * childWidth) + (childWidth / 2) - childWidth * pageOffset,
childWidth / 2),
minSize * (1 - pageOffset),
mPaint..color = indicatorColor.withOpacity(1 - pageOffset));
}
//话右边的圆
else {
canvas.drawCircle(
Offset(
(i * childWidth) + (childWidth / 2) - childWidth * pageOffset,
childWidth / 2),
minSize + (maxSize - minSize) * pageOffset,
mPaint..color = indicatorColor);
}
}
//向左移的时候画后面的圆
if (isStart && !isEnd) {
canvas.drawCircle(
Offset(
(_kMaxCircleCount * childWidth) +
(childWidth / 2) -
childWidth * (pageOffset),
childWidth / 2),
minSize * pageOffset,
mPaint..color = indicatorColor.withOpacity(pageOffset));
}
}
@override
bool shouldRepaint(covariant SimplePageIndicatorPainter oldDelegate) {
return itemCount != oldDelegate.itemCount ||
indicatorColor != oldDelegate.indicatorColor ||
maxSize != oldDelegate.maxSize ||
minSize != oldDelegate.minSize ||
space != oldDelegate.space ||
pageIndex != oldDelegate.pageIndex ||
isStart != oldDelegate.isStart ||
isEnd != oldDelegate.isEnd;
}
}
// ...
最后我们来处理一下 pageController
传过来的值即可
Widget _buildAnimatedItem(BuildContext context, Widget child) {
// return Container();
//当前页数
int index;
//偏移量
double offset;
//如果获取不了,则使用初始化的值,一般第一次渲染的时候无法获取到
if (!controller.hasClients || controller.page == null) {
index = controller.initialPage;
offset = controller.initialPage.toDouble();
} else {
index = controller.page ~/ 1;
offset = controller.page;
}
return CustomPaint(
size: Size(
maxSize * _kMaxCircleCount + space * (_kMaxCircleCount - 1), maxSize),
painter: SimplePageIndicatorPainter(
itemCount: itemCount,
indicatorColor: indicatorColor,
maxSize: maxSize,
minSize: minSize,
pageIndex: index,
space: space,
pageOffset: offset - index,
isStart:
(offset > index) && (index + _kMaxCircleCount - 1 < itemCount),
isEnd: index + _kMaxCircleCount - 2 >= itemCount),
);
}
详细代码请查看:这里
直接使用:
dependencies:
simple_page_indicator: ^1.0.0
- 本文首发布于公众号:
Dart客栈