Flutter pageview indicator指示器實現

最近正好用到pageview,發現官方好像沒有提供指示器。去pub上搜了一下indicator,點了star最多的一個看了下,發現他的刷新是連pageview一起刷新的,和我需要的不匹配。最後還是決定自己實現一下吧。

效果圖

在這裏插入圖片描述

項目地址

flutter_page_indicator

源碼

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聲明瞭一些變量,除了lengthpageController是必須的,其它都有默認值。

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地址:

https://github.com/Zhengyi66/flutter_page_indicator

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章