zoukankan      html  css  js  c++  java
  • Flutter:教你用CustomPaint画一个自定义的CircleProgressBar

    https://www.jianshu.com/p/2ea01ae02ffe

    Flutter:教你用CustomPaint画一个自定义的CircleProgressBar

    paint_page.dart

    import 'package:flutter/material.dart';
    import 'package:tetris/paint/paint.dart';
    
    
    class ProgressBarPage extends StatefulWidget {
      @override
      State<StatefulWidget> createState() => ProgressBarState();
    }
    
    class ProgressBarState extends State<ProgressBarPage> {
      double progress = 0.0;
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('CircleProgressBar'),
          ),
          body: Stack(
            alignment: Alignment.center,
            children: <Widget>[
              Text('Degree:${(progress * 360.0).round()}°',style: TextStyle(fontSize: 20.0),),
              CircleProgressBar(
                radius: 120.0,
                dotColor: Colors.pink,
                dotRadius: 18.0,
                shadowWidth: 2.0,
                progress: 0.0,
                progressChanged: (value) {
                  setState(() {
                    progress = value;
                  });
                },
              ),
            ],
          ),
        );
      }
    }
    

      

    paint.dart

    import 'dart:math';
    
    import 'package:flutter/material.dart';
    
    typedef ProgressChanged<double> = void Function(double value);
    
    num degToRad(num deg) => deg * (pi / 180.0);
    
    num radToDeg(num rad) => rad * (180.0 / pi);
    
    class CircleProgressBar extends StatefulWidget {
      final double radius;
      final double progress;
      final double dotRadius;
      final double shadowWidth;
      final Color shadowColor;
      final Color dotColor;
      final Color dotEdgeColor;
      final Color ringColor;
    
      final ProgressChanged progressChanged;
    
      const CircleProgressBar({
        Key key,
        @required this.radius,
        @required this.dotRadius,
        @required this.dotColor,
        this.shadowWidth = 2.0,
        this.shadowColor = Colors.black12,
        this.ringColor = const Color(0XFFF7F7FC),
        this.dotEdgeColor = const Color(0XFFF5F5FA),
        this.progress,
        this.progressChanged,
      }) : super(key: key);
    
      @override
      State<StatefulWidget> createState() => _CircleProgressState();
    }
    
    class _CircleProgressState extends State<CircleProgressBar>
        with SingleTickerProviderStateMixin {
      AnimationController progressController;
      bool isValidTouch = false;
      final GlobalKey paintKey = GlobalKey();
    
      @override
      void initState() {
        super.initState();
        progressController =
            AnimationController(duration: Duration(milliseconds: 300), vsync: this);
        if (widget.progress != null) progressController.value = widget.progress;
        progressController.addListener(() {
          if (widget.progressChanged != null)
            widget.progressChanged(progressController.value);
          setState(() {});
        });
      }
    
      @override
      void dispose() {
        progressController.dispose();
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        final double width = widget.radius * 2.0;
        final size = new Size(width, width);
        return GestureDetector(
          onPanStart: _onPanStart,
          onPanUpdate: _onPanUpdate,
          onPanEnd: _onPanEnd,
          child: Container(
            alignment: FractionalOffset.center,
            child: CustomPaint(
              key: paintKey,
              size: size,
              painter: ProgressPainter(
                  dotRadius: widget.dotRadius,
                  shadowWidth: widget.shadowWidth,
                  shadowColor: widget.shadowColor,
                  ringColor: widget.ringColor,
                  dotColor: widget.dotColor,
                  dotEdgeColor: widget.dotEdgeColor,
                  progress: progressController.value),
            ),
          ),
        );
      }
    
      void _onPanStart(DragStartDetails details) {
        RenderBox getBox = paintKey.currentContext.findRenderObject();
        Offset local = getBox.globalToLocal(details.globalPosition);
        isValidTouch = _checkValidTouch(local);
        if (!isValidTouch) {
          return;
        }
      }
    
      void _onPanUpdate(DragUpdateDetails details) {
        if (!isValidTouch) {
          return;
        }
        RenderBox getBox = paintKey.currentContext.findRenderObject();
        Offset local = getBox.globalToLocal(details.globalPosition);
        final double x = local.dx;
        final double y = local.dy;
        final double center = widget.radius;
        double radians = atan((x - center) / (center - y));
        if (y > center) {
          radians = radians + degToRad(180.0);
        } else if (x < center) {
          radians = radians + degToRad(360.0);
        }
        progressController.value = radians / degToRad(360.0);
      }
    
      void _onPanEnd(DragEndDetails details) {
        if (!isValidTouch) {
          return;
        }
      }
    
      bool _checkValidTouch(Offset pointer) {
        final double validInnerRadius = widget.radius - widget.dotRadius * 3;
        final double dx = pointer.dx;
        final double dy = pointer.dy;
        final double distanceToCenter =
        sqrt(pow(dx - widget.radius, 2) + pow(dy - widget.radius, 2));
        if (distanceToCenter < validInnerRadius ||
            distanceToCenter > widget.radius) {
          return false;
        }
        return true;
      }
    }
    
    class ProgressPainter extends CustomPainter {
      final double dotRadius;
      final double shadowWidth;
      final Color shadowColor;
      final Color dotColor;
      final Color dotEdgeColor;
      final Color ringColor;
      final double progress;
    
      ProgressPainter({
        this.dotRadius,
        this.shadowWidth = 2.0,
        this.shadowColor = Colors.black12,
        this.ringColor = const Color(0XFFF7F7FC),
        this.dotColor,
        this.dotEdgeColor = const Color(0XFFF5F5FA),
        this.progress,
      });
    
      @override
      void paint(Canvas canvas, Size size) {
        final double center = size.width * 0.5;
        final Offset offsetCenter = Offset(center, center);
        final double drawRadius = size.width * 0.5 - dotRadius;
        final angle = 360.0 * progress;
        final double radians = degToRad(angle);
    
        final double radiusOffset = dotRadius * 0.4;
        final double outerRadius = center - radiusOffset;
        final double innerRadius = center - dotRadius * 2 + radiusOffset;
    
        // draw shadow.
        final shadowPaint = Paint()
          ..style = PaintingStyle.stroke
          ..color = shadowColor
          ..strokeWidth = shadowWidth
          ..maskFilter = MaskFilter.blur(BlurStyle.normal, shadowWidth);
    //    canvas.drawCircle(offsetCenter, outerRadius, shadowPaint);
    //    canvas.drawCircle(offsetCenter, innerRadius, shadowPaint);
    
        Path path = Path.combine(PathOperation.difference, Path()..addOval(Rect.fromCircle(center: offsetCenter, radius: outerRadius)), Path()..addOval(Rect.fromCircle(center: offsetCenter, radius: innerRadius)));
        canvas.drawShadow(path, shadowColor, 4.0, true);
    
        // draw ring.
        final ringPaint = Paint()
          ..style = PaintingStyle.stroke
          ..color = ringColor
          ..strokeWidth = (outerRadius - innerRadius);
        canvas.drawCircle(offsetCenter, drawRadius, ringPaint);
    
        final Color currentDotColor = Color.alphaBlend(
            dotColor.withOpacity(0.7 + (0.3 * progress)), Colors.white);
    
        // draw progress.
        if (progress > 0.0) {
          final progressWidth = outerRadius - innerRadius + radiusOffset;
          final double offset = asin(progressWidth * 0.5 / drawRadius);
          if (radians > offset) {
            canvas.save();
            canvas.translate(0.0, size.width);
            canvas.rotate(degToRad(-90.0));
            final Gradient gradient = new SweepGradient(
              endAngle: radians,
              colors: [
                Colors.white,
                currentDotColor,
              ],
            );
            final Rect arcRect =
            Rect.fromCircle(center: offsetCenter, radius: drawRadius);
            final progressPaint = Paint()
              ..style = PaintingStyle.stroke
              ..strokeCap = StrokeCap.round
              ..strokeWidth = progressWidth
              ..shader = gradient.createShader(arcRect);
            canvas.drawArc(
                arcRect, offset, radians - offset, false, progressPaint);
            canvas.restore();
          }
        }
    
        // draw dot.
        final double dx = center + drawRadius * sin(radians);
        final double dy = center - drawRadius * cos(radians);
        final dotPaint = Paint()..color = currentDotColor;
        canvas.drawCircle(new Offset(dx, dy), dotRadius, dotPaint);
        dotPaint
          ..color = dotEdgeColor
          ..style = PaintingStyle.stroke
          ..strokeWidth = dotRadius * 0.3;
        canvas.drawCircle(new Offset(dx, dy), dotRadius, dotPaint);
    
    //    canvas.drawLine(
    //        Offset(center, 0.0),
    //        Offset(center, size.height),
    //        Paint()
    //          ..strokeWidth = 1.0
    //          ..color = Colors.black);  // 测试基准线
      }
    
      @override
      bool shouldRepaint(CustomPainter oldDelegate) {
        return true;
      }
    }
    

      

  • 相关阅读:
    牛客网 二叉树的镜像 JAVA
    牛客网 反转链表 JAVA
    牛客网 调整数组顺序使奇数位于偶数前面 JAVA
    Integer to Roman LeetCode Java
    Valid Number leetcode java
    Longest Common Prefix
    Wildcard Matching leetcode java
    Regular Expression Matching
    Longest Palindromic Substring
    Add Binary LeetCode Java
  • 原文地址:https://www.cnblogs.com/pythonClub/p/10859169.html
Copyright © 2011-2022 走看看