Flutter Pageview Indicator指示器实现

3,661 阅读2分钟

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

github.com/Zhengyi66/f…