最近正好用到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地址: