美文网首页Flutter中文社区博客资源Flutter自定义View
使用 Flutter 自定义一个 ProgressBar - I

使用 Flutter 自定义一个 ProgressBar - I

作者: stefanJi | 来源:发表于2019-06-09 14:13 被阅读1次

    一个支持间隔,多色,圆角,水平,竖直的进度条

    Interval Progressbar

    特点

    • 支持水平和竖直方向
    • 支持进度条间隔颜色
    • 支持多色设置
    • 支持起始圆角

    使用方式

    Center(
       child: Row(
           mainAxisAlignment: MainAxisAlignment.center,
           children: [10, 29, 18, 27, 16, 15, 24, 3, 20, 10].map<Widget>((i) {
             return Padding(
                 padding: EdgeInsets.only(right: 10),
                 child: IntervalProgressBar(
                     direction: IntervalProgressDirection.vertical,
                     max: 30,
                     progress: i,
                     intervalSize: 2,
                     size: Size(12, 200),
                     highlightColor: Colors.red,
                     defaultColor: Colors.grey,
                     intervalColor: Colors.transparent,
                     intervalHighlightColor: Colors.transparent,
                     reverse: true,
                     radius: 0));
           }).toList())
    )
    

    属性

    属性 类型 说明
    direction enum 进度条的方向,水平或竖直
    max int 进度块的个数
    progress int 高亮进度块的个数
    intervalSize int 进度块之间间隔的大小。当水平时,代表宽度;当竖直时,代表高度
    size Size 控件的尺寸
    highlightColor Color 高亮进度块的颜色
    defaultColor Color 进度块的默认颜色
    intervalColor Color 间隔块的默认颜色
    intervalHighlightColor Color 高亮进度块之间的间隔块的颜色
    reverse bool 是否逆序填充
    radius int 起始块的圆角

    ⌨ → https://github.com/stefanJi/IntervalProgressBar

    实现

    抽象基类

    • 负责计算一些水平和竖直进度条的公有属性
    • 调用子类的 paintBlock 执行具体的 draw 操作
    abstract class IntervalProgressPainter extends CustomPainter {
      final int max;
      final int progress;
      final int intervalSize;
      final Color highlightColor;
      final Color defaultColor;
      final Color intervalColor;
      final Color intervalHighlightColor;
      final double radius;
      final bool reverse;
    
      final Paint _paint = Paint()
        ..style = PaintingStyle.fill
        ..isAntiAlias = true;
    
      Rect bound;
    
      IntervalProgressPainter(
          this.max,
          this.progress,
          this.intervalSize,
          this.highlightColor,
          this.defaultColor,
          this.intervalColor,
          this.intervalHighlightColor,
          this.radius,
          this.reverse);
    
      @override
      @mustCallSuper
      void paint(Canvas canvas, Size size) {
        if (progress > max) {
          throw Exception("progress must <= max");
        }
        bound = Offset.zero & size;
        Size blockSize = calBlockSize();
        for (int i = 0; i < max; i++) {
          /// 调用子类执行绘制
          paintBlock(canvas, i, blockSize);
        }
      }
    
      @override
      bool shouldRepaint(CustomPainter oldDelegate) {
        final old = oldDelegate as IntervalProgressPainter;
        return old.max != max ||
            old.progress != progress ||
            old.intervalSize != intervalSize ||
            old.intervalColor != intervalColor ||
            old.defaultColor != defaultColor ||
            old.highlightColor != highlightColor ||
            old.intervalHighlightColor != intervalHighlightColor ||
            old.radius != radius;
      }
    
      bool highlightBlock(int index) =>
          reverse ? index >= (max - progress) : index < progress;
    
      bool highlightInterval(int index) =>
          reverse ? index >= (max - progress - 1) : index < progress - 1;
    
      void paintBlock(Canvas canvas, int blockIndex, Size blockSize);
    
      Size calBlockSize();
    
      bool shouldDrawStartRadius(int index) => index == 0 && radius > 0;
    
      bool shouldDrawEndRadius(int index) => index == max - 1 && radius > 0;
    
      bool shouldDrawInterval(int index) =>
          index != max - 1 &&
          (intervalColor != IntervalProgressBar.TRANSPARENT ||
              intervalHighlightColor != IntervalProgressBar.TRANSPARENT);
    }
    

    水平进度条

    • 计算每个进度块的尺寸
      @override
      Size calBlockSize() =>
          Size(((bound.width - intervalSize * (max - 1)) / max), bound.height);
    
    • 绘制每个块
      @override
      void paintBlock(Canvas canvas, int i, Size blockSize) {
        /// blockSize: calBlockSize 的返回值 
        /// i: 块的索引
    
        final blockWidth = blockSize.width;
        final highlight = highlightBlock(i);
        final dx = (blockWidth + intervalSize) * i;
    
        Rect rect = Rect.fromLTRB(0, 0, blockWidth, bound.height);
        _paint.color = highlight ? highlightColor : defaultColor;
        canvas.save();
        /// 画布移动 dx: 进度块宽度+间隔块宽度
        canvas.translate(dx, 0);
        /// 绘制起始圆角
        if (shouldDrawStartRadius(i)) {
          rect = _drawLeftRound(canvas, rect);
        }
        
        /// 绘制末尾圆角
        if (shouldDrawEndRadius(i)) {
          rect = _drawRightRound(canvas, rect);
        }
        /// 绘制块
        canvas.drawRect(rect, _paint);
    
        /// 绘制间隔块
        if (shouldDrawInterval(i)) {
          _paint.color =
              highlightInterval(i) ? intervalHighlightColor : intervalColor;
          canvas.drawRect(
              Rect.fromLTRB(
                blockWidth,
                0,
                blockWidth + intervalSize,
                bound.height,
              ),
              _paint);
        }
        canvas.restore();
      }
    
    • 绘制一边带圆角,另一半不带圆角的矩形
      void _drawRadius(Canvas canvas, Rect rect, Rect clipRect) {
        final roundRect = RRect.fromLTRBR(
            rect.left, rect.top, rect.right, rect.bottom, Radius.circular(radius));
        final path = Path()..addRRect(roundRect);
        canvas.save();
        canvas.clipRect(clipRect, clipOp: ClipOp.difference);
        canvas.drawPath(path, _paint);
        canvas.restore();
      }
    }
    

    竖直进度条类似,具体请看源码

    https://github.com/stefanJi/IntervalProgressBar

    相关文章

      网友评论

        本文标题:使用 Flutter 自定义一个 ProgressBar - I

        本文链接:https://www.haomeiwen.com/subject/hdmgxctx.html