在计时器上进行的按钮周围的圆形进度指示器

Posted

技术标签:

【中文标题】在计时器上进行的按钮周围的圆形进度指示器【英文标题】:Circular Progress Indicator Around a Button that Progresses On a Timer 【发布时间】:2021-02-18 14:23:34 【问题描述】:

我有一个播放按钮,可以播放用户录制的消息。一旦用户点击播放按钮,它就会变成一个停止按钮,显示一个圆形进度指示器,该指示器根据记录的消息总时间和当前曲目时间的百分比进行进度。

我做了一些工作,但还不够准确,具体取决于轨道的长度(4 秒对 6.5 秒),在循环进度指示器完成后轨道会运行更长时间,或者轨道会在之前结束进度指示器已完成。

我也希望进展顺利,而不是间歇性跳跃。

这里是停止按钮的代码,它采用double totalTime,这是曲目播放的总时间,然后启动计时器和 AnimationController。

也是完全透明的,自定义画家是我在网上找到的,但并不完全理解它是如何工作的,所以即使没有问题,如果有人可以将其分解,我将不胜感激:D

import 'dart:async';
import 'dart:math';
import 'dart:ui';

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_icons/flutter_icons.dart';

class StopButton extends StatefulWidget 
  @override
  _StopButtonState createState() => _StopButtonState();

  final double totalTime;
  final dynamic onClickFunction;
  StopButton(this.totalTime, this.onClickFunction);


class _StopButtonState extends State<StopButton> with TickerProviderStateMixin 
  double percentage = 0.0;
  double newPercentage = 0.0;
  AnimationController percentageAnimationController;
  Timer timer;
  @override
  void initState() 
    // TODO: implement initState
    super.initState();
    setState(() 
      percentage = 0.0;
    );
    percentageAnimationController =
        AnimationController(vsync: this, duration: Duration(milliseconds: 1000))
          ..addListener(() 
            setState(() 
              percentage = lerpDouble(percentage, newPercentage,
                  percentageAnimationController.value);
            );
          );
    startTime();
  

  @override
  void dispose() 
    // TODO: implement dispose
    timer.cancel();
    percentageAnimationController.dispose();
    super.dispose();
  

  void startTime() 
    setState(() 
      percentage = newPercentage;
      newPercentage += 0.0;
      if (newPercentage > widget.totalTime) 
        percentage = 0.0;
        newPercentage = 0.0;
        timer.cancel();
      
      percentageAnimationController.forward(from: 0.0);
    );
    timer = Timer.periodic(Duration(seconds: 1), (timer) 
      print(timer.tick);
      setState(() 
        percentage = newPercentage;
        newPercentage += 1.0;
        if (newPercentage > widget.totalTime) 
          percentage = 0.0;
          newPercentage = 0.0;
          timer.cancel();
        
        percentageAnimationController.forward(from: 0.0);
      );
    );
  

  @override
  Widget build(BuildContext context) 
    return CustomPaint(
      foregroundPainter: MyPainter(
        lineColor: Colors.transparent,
        completeColor: Color(0xFF133343),
        completePercent: percentage,
        width: 3.0,
        totalTime: widget.totalTime,
      ),
      child: Padding(
        padding: EdgeInsets.all(0.0),
        child: FloatingActionButton(
          onPressed: () async 
            await widget.onClickFunction();
          ,
          backgroundColor: Colors.white,
          child: Icon(
            MaterialCommunityIcons.stop,
            color: Color(0xFF133343),
          ),
        ),
      ),
    );
  


class MyPainter extends CustomPainter 
  Color lineColor;
  Color completeColor;
  double completePercent;
  double width;
  double totalTime;
  MyPainter(
      this.lineColor,
      this.completeColor,
      this.completePercent,
      this.width,
      this.totalTime);
  @override
  void paint(Canvas canvas, Size size) 
    Paint line = Paint()
      ..color = lineColor
      ..strokeCap = StrokeCap.round
      ..style = PaintingStyle.stroke
      ..strokeWidth = width;
    Paint complete = Paint()
      ..color = completeColor
      ..strokeCap = StrokeCap.round
      ..style = PaintingStyle.stroke
      ..strokeWidth = width;
    Offset center = Offset(size.width / 2, size.height / 2);
    double radius = min(size.width / 2, size.height / 2);
    canvas.drawCircle(center, radius, line);
    double arcAngle = 2 * pi * (completePercent / totalTime);
    canvas.drawArc(Rect.fromCircle(center: center, radius: radius), -pi / 2,
        arcAngle, false, complete);
  

  @override
  bool shouldRepaint(CustomPainter oldDelegate) 
    return true;
  

【问题讨论】:

【参考方案1】:

您已在下方添加自定义 CustomTimerPainter 以创建圆形指示器

 class CustomTimerPainter extends CustomPainter 
  CustomTimerPainter(
    this.animation,
    this.backgroundColor,
    this.color,
  ) : super(repaint: animation);

  final Animation<double> animation;
  final Color backgroundColor, color;

  @override
  void paint(Canvas canvas, Size size) 
    Paint paint = Paint()
      ..color = backgroundColor
      ..strokeWidth = 6.0
      ..strokeCap = StrokeCap.butt
      ..style = PaintingStyle.stroke;

    canvas.drawCircle(size.center(Offset.zero), size.width / 2.0, paint);
    paint.color = color;
    double progress = (1.0 - animation.value) * 2 * math.pi;
    canvas.drawArc(Offset.zero & size, math.pi * 1.5, progress, false, paint);
  

  @override
  bool shouldRepaint(CustomTimerPainter old) 
    return animation.value != old.animation.value ||
        color != old.color ||
        backgroundColor != old.backgroundColor;
  

添加指标后定义控制器

AnimationController controller;

@override
    void initState() 
      super.initState();
      controller = AnimationController(
      vsync: this,
      duration: Duration(seconds: 5),
     );
   

最后一步是添加我们的自定义画家

floatingActionButton: Container(
    height: 60,
    width: 60,
    decoration: BoxDecoration(
      shape: BoxShape.circle,
      color: Colors.white
    ),
    child: GestureDetector(
      child: CustomPaint(
          painter: CustomTimerPainter(
            animation: controller,
            backgroundColor: Colors.white,
            color: themeData.indicatorColor,
          )),
      onTap: ()
        controller.reverse(
            from: controller.value == 0.0
                ? 1.0
                : controller.value);
      ,
    ),
  ),

【讨论】:

以上是关于在计时器上进行的按钮周围的圆形进度指示器的主要内容,如果未能解决你的问题,请参考以下文章

圆形进度条:简单实现倒计时圆形进度条

h5 Canvas实现圆形时间倒计时进度条

h5 Canvas实现圆形时间倒计时进度条

自定义圆形进度条 自定义倒计时进度条

圆形进度条,如何实现这一目标

猎豹MFC--进度条CProgressCtrl 定时器SetTimer