【问题标题】:how to make Percent Indicator change color programmatically in flutter?如何使百分比指示器在颤动中以编程方式更改颜色?
【发布时间】:2020-03-16 06:00:58
【问题描述】:

我正在使用一个名为百分比指示器https://pub.dev/packages/percent_indicator的包

我目前正在使用它的 CircularPercentIndicator()

我只是想知道当达到一定百分比时如何更改进度颜色?

例如:我在 0% 时的起始进度颜色为绿色,当达到 60% 进度时颜色应变为橙色,达到 80% 时颜色应为红色。

这是我目前得到的:

import 'package:flutter/material.dart';
import 'package:percent_indicator/circular_percent_indicator.dart';

class RoutinePage extends StatefulWidget {
  @override
  _RoutinePageState createState() => _RoutinePageState();
}

class _RoutinePageState extends State<RoutinePage> {
  double progress = 0;

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: <Widget>[
        Container(
          color: Colors.white,
          alignment: Alignment(0, 0),
          child: CircularPercentIndicator(
            animationDuration: 100,
            animateFromLastPercent: true,
            arcType: ArcType.FULL,
            arcBackgroundColor: Colors.black12,
            backgroundColor: Colors.white,
            progressColor: Colors.green,
            percent: progress,
            animation: true,
            radius: 250.0,
            lineWidth: 12.0,
            circularStrokeCap: CircularStrokeCap.round,
          ),
        ),
        Container(
          alignment: Alignment(0, 0),
          child: Text("${this.progress * 100}%",
            style: TextStyle(
              fontSize: 30,
              fontWeight: FontWeight.bold,
            ),
          ),
        ),
        Container(
          alignment: Alignment(0.3, 0.5),
          child: RaisedButton(
              color: Colors.green,
               onPressed: () {
                final updated = ((this.progress + 0.1).clamp(0.0, 1.0) * 100);
                setState(() {
                  this.progress = updated.round() / 100;
                });
                print(progress);
              },
              child: Text('+10%',
              style: TextStyle(
                fontWeight: FontWeight.bold,
                color: Colors.white,
              ),)),
        ),
        Container(
          alignment: Alignment(-0.3, 0.5),
          child: RaisedButton(
              color: Colors.red,
              onPressed: () {
                final updated = ((this.progress - 0.1).clamp(0.0, 1.0) * 100);
                setState(() {
                  this.progress = updated.round() / 100;
                });
                print(progress);
              },
              child: Text('-10%',
              style: TextStyle(
                fontWeight: FontWeight.bold,
                color: Colors.white,
              ),)),
        ),
      ],
    );
  }
}

我不知道这是否有帮助,但这是 CircularPercentIndicator() 的代码

//import 'dart:math';

import 'package:flutter/material.dart';
import 'package:vector_math/vector_math_64.dart' as math;

enum CircularStrokeCap { butt, round, square }

enum ArcType {
  HALF,
  FULL,
}

class CircularPercentIndicator extends StatefulWidget {
  ///Percent value between 0.0 and 1.0
  final double percent;
  final double radius;

  ///Width of the line of the Circle
  final double lineWidth;

  ///Color of the background of the circle , default = transparent
  final Color fillColor;

  ///First color applied to the complete circle
  final Color backgroundColor;

  Color get progressColor => _progressColor;

  Color _progressColor;

  ///true if you want the circle to have animation
  final bool animation;

  ///duration of the animation in milliseconds, It only applies if animation attribute is true
  final int animationDuration;

  ///widget at the top of the circle
  final Widget header;

  ///widget at the bottom of the circle
  final Widget footer;

  ///widget inside the circle
  final Widget center;

  final LinearGradient linearGradient;

  ///The kind of finish to place on the end of lines drawn, values supported: butt, round, square
  final CircularStrokeCap circularStrokeCap;

  ///the angle which the circle will start the progress (in degrees, eg: 0.0, 45.0, 90.0)
  final double startAngle;

  /// set true if you want to animate the linear from the last percent value you set
  final bool animateFromLastPercent;

  /// set false if you don't want to preserve the state of the widget
  final bool addAutomaticKeepAlive;

  /// set the arc type
  final ArcType arcType;

  /// set a circular background color when use the arcType property
  final Color arcBackgroundColor;

  /// set true when you want to display the progress in reverse mode
  final bool reverse;

  /// Creates a mask filter that takes the progress shape being drawn and blurs it.
  final MaskFilter maskFilter;

  CircularPercentIndicator(
      {Key key,
      this.percent = 0.0,
      this.lineWidth = 5.0,
      this.startAngle = 0.0,
      @required this.radius,
      this.fillColor = Colors.transparent,
      this.backgroundColor = const Color(0xFFB8C7CB),
      Color progressColor,
      this.linearGradient,
      this.animation = false,
      this.animationDuration = 500,
      this.header,
      this.footer,
      this.center,
      this.addAutomaticKeepAlive = true,
      this.circularStrokeCap,
      this.arcBackgroundColor,
      this.arcType,
      this.animateFromLastPercent = false,
      this.reverse = false,
      this.maskFilter})
      : super(key: key) {
    if (linearGradient != null && progressColor != null) {
      throw ArgumentError(
          'Cannot provide both linearGradient and progressColor');
    }
    _progressColor = progressColor ?? Colors.red;

    assert(startAngle >= 0.0);
    if (percent < 0.0 || percent > 1.0) {
      throw Exception("Percent value must be a double between 0.0 and 1.0");
    }

    if (arcType == null && arcBackgroundColor != null) {
      throw ArgumentError('arcType is required when you arcBackgroundColor');
    }
  }

  @override
  _CircularPercentIndicatorState createState() =>
      _CircularPercentIndicatorState();
}

class _CircularPercentIndicatorState extends State<CircularPercentIndicator>
    with SingleTickerProviderStateMixin, AutomaticKeepAliveClientMixin {
  AnimationController _animationController;
  Animation _animation;
  double _percent = 0.0;

  @override
  void dispose() {
    if (_animationController != null) {
      _animationController.dispose();
    }
    super.dispose();
  }

  @override
  void initState() {
    if (widget.animation) {
      _animationController = AnimationController(
          vsync: this,
          duration: Duration(milliseconds: widget.animationDuration));
      _animation =
          Tween(begin: 0.0, end: widget.percent).animate(_animationController)
            ..addListener(() {
              setState(() {
                _percent = _animation.value;
              });
            });
      _animationController.forward();
    } else {
      _updateProgress();
    }
    super.initState();
  }

  @override
  void didUpdateWidget(CircularPercentIndicator oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (oldWidget.percent != widget.percent ||
        oldWidget.startAngle != widget.startAngle) {
      if (_animationController != null) {
        _animationController.duration =
            Duration(milliseconds: widget.animationDuration);
        _animation = Tween(
                begin: widget.animateFromLastPercent ? oldWidget.percent : 0.0,
                end: widget.percent)
            .animate(_animationController);
        _animationController.forward(from: 0.0);
      } else {
        _updateProgress();
      }
    }
  }

  _updateProgress() {
    setState(() {
      _percent = widget.percent;
    });
  }

  @override
  Widget build(BuildContext context) {
    super.build(context);
    var items = List<Widget>();
    if (widget.header != null) {
      items.add(widget.header);
    }
    items.add(Container(
        height: widget.radius + widget.lineWidth,
        width: widget.radius,
        child: CustomPaint(
          painter: CirclePainter(
              progress: _percent * 360,
              progressColor: widget.progressColor,
              backgroundColor: widget.backgroundColor,
              startAngle: widget.startAngle,
              circularStrokeCap: widget.circularStrokeCap,
              radius: (widget.radius / 2) - widget.lineWidth / 2,
              lineWidth: widget.lineWidth,
              arcBackgroundColor: widget.arcBackgroundColor,
              arcType: widget.arcType,
              reverse: widget.reverse,
              linearGradient: widget.linearGradient,
              maskFilter: widget.maskFilter),
          child: (widget.center != null)
              ? Center(child: widget.center)
              : Container(),
        )));

    if (widget.footer != null) {
      items.add(widget.footer);
    }

    return Material(
      color: widget.fillColor,
      child: Container(
          child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        mainAxisSize: MainAxisSize.min,
        children: items,
      )),
    );
  }

  @override
  bool get wantKeepAlive => widget.addAutomaticKeepAlive;
}

class CirclePainter extends CustomPainter {
  final Paint _paintBackground = Paint();
  final Paint _paintLine = Paint();
  final Paint _paintBackgroundStartAngle = Paint();
  final double lineWidth;
  final double progress;
  final double radius;
  final Color progressColor;
  final Color backgroundColor;
  final CircularStrokeCap circularStrokeCap;
  final double startAngle;
  final LinearGradient linearGradient;
  final Color arcBackgroundColor;
  final ArcType arcType;
  final bool reverse;
  final MaskFilter maskFilter;

  CirclePainter(
      {this.lineWidth,
      this.progress,
      @required this.radius,
      this.progressColor,
      this.backgroundColor,
      this.startAngle = 0.0,
      this.circularStrokeCap = CircularStrokeCap.round,
      this.linearGradient,
      this.reverse,
      this.arcBackgroundColor,
      this.arcType,
      this.maskFilter}) {
    _paintBackground.color = backgroundColor;
    _paintBackground.style = PaintingStyle.stroke;
    _paintBackground.strokeWidth = lineWidth;

    if (arcBackgroundColor != null) {
      _paintBackgroundStartAngle.color = arcBackgroundColor;
      _paintBackgroundStartAngle.style = PaintingStyle.stroke;
      _paintBackgroundStartAngle.strokeWidth = lineWidth;
    }

    _paintLine.color = progressColor;
    _paintLine.style = PaintingStyle.stroke;
    _paintLine.strokeWidth = lineWidth;
    if (circularStrokeCap == CircularStrokeCap.round) {
      _paintLine.strokeCap = StrokeCap.round;
    } else if (circularStrokeCap == CircularStrokeCap.butt) {
      _paintLine.strokeCap = StrokeCap.butt;
    } else {
      _paintLine.strokeCap = StrokeCap.square;
    }
  }

  @override
  void paint(Canvas canvas, Size size) {
    final center = Offset(size.width / 2, size.height / 2);
    canvas.drawCircle(center, radius, _paintBackground);

    if (maskFilter != null) {
      _paintLine.maskFilter = maskFilter;
    }
    if (linearGradient != null) {
      /*
      _paintLine.shader = SweepGradient(
              center: FractionalOffset.center,
              startAngle: math.radians(-90.0 + startAngle),
              endAngle: math.radians(progress),
              //tileMode: TileMode.mirror,
              colors: linearGradient.colors)
          .createShader(
        Rect.fromCircle(
          center: center,
          radius: radius,
        ),
      );*/
      _paintLine.shader = linearGradient.createShader(
        Rect.fromCircle(
          center: center,
          radius: radius,
        ),
      );
    }

    double fixedStartAngle = startAngle;

    double startAngleFixedMargin = 1.0;
    if (arcType != null) {
      if (arcType == ArcType.FULL) {
        fixedStartAngle = 220;
        startAngleFixedMargin = 172 / fixedStartAngle;
      } else {
        fixedStartAngle = 270;
        startAngleFixedMargin = 135 / fixedStartAngle;
      }
    }

    if (arcBackgroundColor != null) {
      canvas.drawArc(
        Rect.fromCircle(center: center, radius: radius),
        math.radians(-90.0 + fixedStartAngle),
        math.radians(360 * startAngleFixedMargin),
        false,
        _paintBackgroundStartAngle,
      );
    }

    if (reverse) {
      final start =
          math.radians(360 * startAngleFixedMargin - 90.0 + fixedStartAngle);
      final end = math.radians(-progress * startAngleFixedMargin);
      canvas.drawArc(
        Rect.fromCircle(
          center: center,
          radius: radius,
        ),
        start,
        end,
        false,
        _paintLine,
      );
    } else {
      final start = math.radians(-90.0 + fixedStartAngle);
      final end = math.radians(progress * startAngleFixedMargin);
      canvas.drawArc(
        Rect.fromCircle(
          center: center,
          radius: radius,
        ),
        start,
        end,
        false,
        _paintLine,
      );
    }
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return true;
  }
}

【问题讨论】:

    标签: flutter progress-bar percentage circular-dependency indicator


    【解决方案1】:

    一种可能的解决方案是AnimatedBuilder。 我将向您展示我们如何更改按钮的颜色,并且您可以轻松地将方法应用于进度指示器。 下面的示例仅显示当点击按钮开始更改动画时。当您需要启动进度棒时,您也一样,只需运行animationController 并检查结果。 如果您还有其他问题,请随时在 cmets 中提问

     @override
      void initState() {
        _animationController =
            AnimationController(vsync: this, duration: Duration(milliseconds: 300));
        _colorTween = ColorTween(begin: Colors.red, end: Colors.green)
            .animate(_animationController);
    
        super.initState();
      }
    
    @override
      Widget build(BuildContext context) {
        return AnimatedBuilder(
          animation: _colorTween,
          builder: (context, child) => RaisedButton(
                child: Text("Change my color"),
                color: _colorTween.value,
                onPressed: () {
                  if (_animationController.status == AnimationStatus.completed) {
                    _animationController.reverse();
                  } else {
                    _animationController.forward();
                  }
                },
              ),
        );
      }
    

    【讨论】:

      【解决方案2】:

      感谢您的回复。我还提出了其他解决方案,我认为我对我最终得到的结果很满意。顺便说一句,这是我想出的:

      import 'package:flutter/material.dart';
      import 'package:percent_indicator/circular_percent_indicator.dart';
      
      class RoutinePage extends StatefulWidget {
        @override
        _RoutinePageState createState() => _RoutinePageState();
      }
      
      class _RoutinePageState extends State<RoutinePage> {
        double progress = 0;
        currentProgressColor() {
          if (progress >= 0.6 && progress < 0.8) {
            return Colors.orange;
          }
          if(progress >= 0.8){
            return Colors.red;
          }
          else{
            return Colors.green;
          }
        }
      
        @override
        Widget build(BuildContext context) {
          return Stack(
            children: <Widget>[
              Container(
                color: Colors.white,
                alignment: Alignment(0, 0),
                child: CircularPercentIndicator(
                  animationDuration: 200,
                  animateFromLastPercent: true,
                  arcType: ArcType.FULL,
                  arcBackgroundColor: Colors.black12,
                  backgroundColor: Colors.white,
                  progressColor: currentProgressColor(),
                  percent: progress,
                  animation: true,
                  radius: 250.0,
                  lineWidth: 12.0,
                  circularStrokeCap: CircularStrokeCap.butt,
                ),
              ),
              Container(
                alignment: Alignment(0, 0),
                child: Text(
                  "${this.progress * 100}%",
                  style: TextStyle(
                    fontSize: 30,
                    fontWeight: FontWeight.bold,
                  ),
                ),
              ),
              Container(
                alignment: Alignment(0.3, 0.5),
                child: RaisedButton(
                    color: Colors.green,
                    onPressed: () {
                      final updated = ((this.progress + 0.1).clamp(0.0, 1.0) * 100);
                      setState(() {
                        this.progress = updated.round() / 100;
                      });
                      print(progress);
                    },
                    child: Text(
                      '+10%',
                      style: TextStyle(
                        fontWeight: FontWeight.bold,
                        color: Colors.white,
                      ),
                    )),
              ),
              Container(
                alignment: Alignment(-0.3, 0.5),
                child: RaisedButton(
                    color: Colors.red,
                    onPressed: () {
                      final updated = ((this.progress - 0.1).clamp(0.0, 1.0) * 100);
                      setState(() {
                        this.progress = updated.round() / 100;
                      });
                      print(progress);
                    },
                    child: Text(
                      '-10%',
                      style: TextStyle(
                        fontWeight: FontWeight.bold,
                        color: Colors.white,
                      ),
                    )),
              ),
            ],
          );
        }
      }
      

      【讨论】:

        【解决方案3】:
            class ChangeBackgroundColor StatelessWidget {
          @override
          Widget build(BuildContext context) {
            return StreamBuilder(
              stream: _changeColor(),
              initialData: 0xff0DE95B,
              builder: (context, snapshot) {
                return LinearPercentIndicator(
                      percent: 1,
                      animation: true,
                      animationDuration: 30000,
                      progressColor: Colors.grey[200],
                      backgroundColor: Color(
                        int.parse(snapshot.data.toString()),
                      )),
                );
              },
            );
          }
        }
        
        Stream<int> _changeColor() async* {
          yield* Stream.periodic(Duration(seconds: 1), (int a) {
            a++;
            if (a > 25) {
              return 0xffF33709;
            } else {
              return 0xff0DE95B;
            }
          });
        }
        

        【讨论】:

        • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-11-03
        • 2016-07-03
        • 1970-01-01
        • 2012-10-31
        相关资源
        最近更新 更多