Flutter pageview指示器(indicator)实现
程序员文章站
2024-02-05 20:44:16
...
最近正好用到pageview,发现官方好像没有提供指示器。去pub上搜了一下indicator,点了star最多的一个看了下,发现他的刷新是连pageview一起刷新的,和我需要的不匹配。最后还是决定自己实现一下吧。
效果图
项目地址
源码
pub上的项目indicator圆点好像都是用paint画的。
我的第一印象其实就是最外层用Stack
包裹,里面放普通圆点和当前位置圆点。
下层普通的圆点用一个ListView
,ListView上层放一个表示当前进度的小圆点用Container
表示。当位置发生改变,我们只需要修改上层Container的位置就可以了。
使用Decoration
来修改圆点属性
1、创建一个PageIndicator
类
class PageIndicator extends StatefulWidget {
///PageView 子view集合的长度
final int length;
///PageController
final PageController pageController;
///默认颜色
final Color defaultColor;
///默认宽度
final double defaultWidth;
///默认高度
final double defaultHeight;
///默认Decoration
final Decoration defaultDecoration;
///当前颜色
final Color currentColor;
///当前宽度
final double currentWidth;
///当前高度
final double currentHeight;
///当前Decoration
final Decoration currentDecoration;
///间距
final double padding;
PageIndicator({
Key key,
this.length,
this.pageController,
this.defaultColor = Colors.white,
this.defaultWidth = 8,
this.defaultHeight = 8,
this.defaultDecoration,
this.currentColor = Colors.grey,
this.currentWidth = 8,
this.currentHeight = 8,
this.currentDecoration,
this.padding = 8,
}): assert(length != null), assert(pageController != null), super(key:key);
@override
State<StatefulWidget> createState() {
return _PageState();
}
}
在PageIndicator
声明了一些变量,除了length
和pageController
是必须的,其它都有默认值。
2、创建_PageState类
在PageIndicator构造方法中我们传入了PageController
,这样只需要在initState
给PageController添加一个addListener
监听就可以获得当前pageview的page值。然后调用setState(() {});
刷新即可。
在pageview滑动的时候,我们真正需要改变的其实是上层表示进度的圆点,所以我们不需要使用setState(() {});
来刷新整个指示器,只需要单独刷新表示进度的圆点就行了。这里使用StreamBuilder
来完成局部刷新
_PageState源码如下:
class _PageState extends State<PageIndicator> {
StreamController _streamController;
@override
void initState() {
super.initState();
_streamController = StreamController<double>();
widget.pageController.addListener((){
_streamController.sink.add(widget.pageController.page);
});
}
@override
Widget build(BuildContext context) {
return Container(
width: widget.normalWidth * widget.length +
widget.padding * (widget.length + 1),
height: widget.currentHeight,
child: Stack(
fit: StackFit.expand,
children: <Widget>[
Positioned(
left: 0,
right: 0,
top: 0,
bottom: 0,
///普通圆点用ListView显示
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: widget.length,
physics: NeverScrollableScrollPhysics(),
itemBuilder: (_, position) {
return Container(
width: widget.normalWidth,
height: widget.normalHeight,
margin: EdgeInsets.only(
left: widget.padding),
decoration: widget.normalDecoration ??
BoxDecoration(
color: widget.normalColor, shape: BoxShape.circle),
);
}),
),
Positioned(
///StreamBuilder刷新
child: StreamBuilder<double>(
stream: _streamController.stream,
initialData: 0,
builder: (context, snapshot) {
///表示当前进度的圆点
return Container(
width: widget.currentWidth,
height: widget.currentHeight,
decoration: widget.currentDecoration ?? BoxDecoration(
color: widget.currentColor, shape: BoxShape.circle),
margin: EdgeInsets.only(
left: left(snapshot.data),
),
);
}),
left: 0,
)
],
),
);
}
double left(double page){
if(widget.currentWidth > widget.normalWidth){
return widget.normalWidth * page + widget.padding*page + widget.padding - (widget.currentWidth - widget.normalWidth)/2;
}else{
return (widget.normalWidth * page + (page+1) * widget.padding);
}
}
@override
void dispose() {
super.dispose();
_streamController?.close();
}
}
使用
在pubspec.yaml
引入:
dependencies:
pageview_indicator_plugins: ^0.0.2
1、默认使用的话只需要传递pageview children的长度和pageController就行了。
PageIndicator(
length: 6,
pageController: pageController,
)
2、也可以通过修改选中圆点的宽高和修改Decoration属性来修改圆点属性
PageIndicator(
length: 6,
pageController: secondController,
currentWidth: 16,
currentDecoration: BoxDecoration(
color: Colors.cyanAccent,
borderRadius: BorderRadius.circular(10)),
)
github地址: