首页 > 解决方案 > 在 Flutter 中重复动画和调整

问题描述

我正在尝试克隆我锻炼时使用的应用程序的计时器:强。到目前为止,我已经设法实现了所有主要功能和动画(并不多)。我知道我的代码很混乱,如果有任何建议也值得赞赏,可能会有更好的方法来实现我正在尝试做的事情。我的申请有两个问题。1.当计时器启动时,我收到以下错误:

flutter: ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY 

╞═══════════════════════════════════════════════════════════
flutter: The following NoSuchMethodError was thrown building Peinture(dirty, state: _PeintureState#82e34):
flutter: The getter 'inMilliseconds' was called on null.
flutter: Receiver: null
flutter: Tried calling: inMilliseconds
flutter:
flutter: When the exception was thrown, this was the stack:
flutter: #0      Object.noSuchMethod (dart:core/runtime/libobject_patch.dart:50:5)
flutter: #1      _PeintureState.build (package:workout_timer/main.dart:241:72)
flutter: #2      StatefulElement.build (package:flutter/src/widgets/framework.dart:3809:27)
flutter: #3      ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3721:15)
flutter: #4      Element.rebuild (package:flutter/src/widgets/framework.dart:3547:5)
flutter: #5      StatefulElement.update (package:flutter/src/widgets/framework.dart:3878:5)
flutter: #6      Element.updateChild (package:flutter/src/widgets/framework.dart:2742:15)
flutter: #7      SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4867:14)
flutter: #8      Element.updateChild (package:flutter/src/widgets/framework.dart:2742:15)
flutter: #9      ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3732:16)
flutter: #10     Element.rebuild (package:flutter/src/widgets/framework.dart:3547:5)
flutter: #11     ProxyElement.update (package:flutter/src/widgets/framework.dart:3990:5)
flutter: #12     Element.updateChild (package:flutter/src/widgets/framework.dart:2742:15)
flutter: #13     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3732:16)
flutter: #14     Element.rebuild (package:flutter/src/widgets/framework.dart:3547:5)
flutter: #15     StatelessElement.update (package:flutter/src/widgets/framework.dart:3781:5)
flutter: #16     Element.updateChild (package:flutter/src/widgets/framework.dart:2742:15)
flutter: #17     RenderObjectElement.updateChildren (package:flutter/src/widgets/framework.dart:4585:32)
flutter: #18     MultiChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4975:17)
flutter: #19     Element.updateChild (package:flutter/src/widgets/framework.dart:2742:15)
flutter: #20     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3732:16)
flutter: #21     Element.rebuild (package:flutter/src/widgets/framework.dart:3547:5)
flutter: #22     ProxyElement.update (package:flutter/src/widgets/framework.dart:3990:5)
flutter: #23     Element.updateChild (package:flutter/src/widgets/framework.dart:2742:15)
flutter: #24     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3732:16)
flutter: #25     Element.rebuild (package:flutter/src/widgets/framework.dart:3547:5)
flutter: #26     ProxyElement.update (package:flutter/src/widgets/framework.dart:3990:5)
flutter: #27     Element.updateChild (package:flutter/src/widgets/framework.dart:2742:15)
flutter: #28     RenderObjectElement.updateChildren (package:flutter/src/widgets/framework.dart:4585:32)
flutter: #29     MultiChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4975:17)
flutter: #30     Element.updateChild (package:flutter/src/widgets/framework.dart:2742:15)
flutter: #31     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3732:16)
flutter: #32     Element.rebuild (package:flutter/src/widgets/framework.dart:3547:5)
flutter: #33     StatefulElement.update (package:flutter/src/widgets/framework.dart:3878:5)
flutter: #34     Element.updateChild (package:flutter/src/widgets/framework.dart:2742:15)
flutter: #35     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3732:16)
flutter: #36     Element.rebuild (package:flutter/src/widgets/framework.dart:3547:5)
flutter: #37     ProxyElement.update (package:flutter/src/widgets/framework.dart:3990:5)
flutter: #38     Element.updateChild (package:flutter/src/widgets/framework.dart:2742:15)
flutter: #39     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3732:16)
flutter: #40     Element.rebuild (package:flutter/src/widgets/framework.dart:3547:5)
flutter: #41     StatefulElement.update (package:flutter/src/widgets/framework.dart:3878:5)
flutter: #42     Element.updateChild (package:flutter/src/widgets/framework.dart:2742:15)
flutter: #43     SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4867:14)
flutter: #44     Element.updateChild (package:flutter/src/widgets/framework.dart:2742:15)
flutter: #45     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3732:16)
flutter: #46     Element.rebuild (package:flutter/src/widgets/framework.dart:3547:5)
flutter: #47     StatelessElement.update (package:flutter/src/widgets/framework.dart:3781:5)
flutter: #48     Element.updateChild (package:flutter/src/widgets/framework.dart:2742:15)
flutter: #49     SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4867:14)
flutter: #50     Element.updateChild (package:flutter/src/widgets/framework.dart:2742:15)
flutter: #51     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3732:16)
flutter: #52     Element.rebuild (package:flutter/src/widgets/framework.dart:3547:5)
flutter: #53     StatefulElement.update (package:flutter/src/widgets/framework.dart:3878:5)
flutter: #54     Element.updateChild (package:flutter/src/widgets/framework.dart:2742:15)
flutter: #55     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3732:16)
flutter: #56     Element.rebuild (package:flutter/src/widgets/framework.dart:3547:5)
flutter: #57     StatefulElement.update (package:flutter/src/widgets/framework.dart:3878:5)
flutter: #58     Element.updateChild (package:flutter/src/widgets/framework.dart:2742:15)
flutter: #59     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3732:16)
flutter: #60     Element.rebuild (package:flutter/src/widgets/framework.dart:3547:5)
flutter: #61     ProxyElement.update (package:flutter/src/widgets/framework.dart:3990:5)
flutter: #62     Element.updateChild (package:flutter/src/widgets/framework.dart:2742:15)
flutter: #63     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3732:16)
flutter: #64     Element.rebuild (package:flutter/src/widgets/framework.dart:3547:5)
flutter: #65     ProxyElement.update (package:flutter/src/widgets/framework.dart:3990:5)
flutter: #66     Element.updateChild (package:flutter/src/widgets/framework.dart:2742:15)
flutter: #67     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3732:16)
flutter: #68     Element.rebuild (package:flutter/src/widgets/framework.dart:3547:5)
flutter: #69     StatefulElement.update (package:flutter/src/widgets/framework.dart:3878:5)
flutter: #70     Element.updateChild (package:flutter/src/widgets/framework.dart:2742:15)
flutter: #71     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3732:16)
flutter: #72     Element.rebuild (package:flutter/src/widgets/framework.dart:3547:5)
flutter: #73     BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2286:33)
flutter: #74     _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&SemanticsBinding&RendererBinding&WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:676:20)
flutter: #75     _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&SemanticsBinding&RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:219:5)
flutter: #76     _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:990:15)
flutter: #77     _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:930:9)
flutter: #78     _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:842:5)
flutter: #79     _invoke (dart:ui/hooks.dart:154:13)
flutter: #80     _drawFrame (dart:ui/hooks.dart:143:3)
flutter: ════════════════════════════════════════════════════════════════════════════════════════════════════

我确保检查我没有得到任何空值,但我仍然得到它。

第二个。当计时器完成时,我收到以下错误:

[VERBOSE-2:shell.cc(184)] Dart Error: Unhandled exception:
setState() called after dispose(): _TimeState#10749(lifecycle state: defunct, not mounted)
This error happens if you call setState() on a State object for a widget that no longer appears in the widget tree (e.g., whose parent widget no longer includes the widget in its build). This error can occur when code calls setState() from a timer or an animation callback. The preferred solution is to cancel the timer or stop listening to the animation in the dispose() callback. Another solution is to check the "mounted" property of this object before calling setState() to ensure the object is still in the tree.
This error might indicate a memory leak if setState() is being called because another object is retaining a reference to this State object after it has been removed from the tree. To avoid memory leaks, consider breaking the reference to this object during dispose().
#0      State.setState.<anonymous closure> (package:flutter/src/widgets/framewo<…&gt;

当我尝试再次启动计时器时,我得到了这个:

flutter: Another exception was thrown: NoSuchMethodError: The getter 'inMilliseconds' was called on null.

这是我的代码:

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

import 'package:flutter/material.dart';
import 'package:fluttery/layout.dart';

Duration time;
Duration previousTime;
Duration currentTime;
AnimationController timeController, circleController;
bool reverse = false;

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {

  Stopwatch stopwatch = new Stopwatch();

  @override
  void initState() {
    super.initState();
    timeController = new AnimationController(
      duration: const Duration(milliseconds: 250),
      vsync: this,
    );
    timeController.addListener(() {
      setState(() {});
    });
  }

  @override
  void dispose() {
    timeController.dispose();
    circleController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: <Widget>[
          Center(
            child: Opacity(
              opacity: timeController.value != null ? 1.0 - timeController.value : 1.0,
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  new FlatButton(
                    onPressed: () {
                      setState(() {
                        previousTime = Duration(minutes: 1);
                        time = previousTime;
                        timeController.forward();
                        stopwatch.start();
                        circleController = new AnimationController(
                          duration: Duration(milliseconds: time.inMilliseconds),
                          vsync: this,
                        );
                        circleController.addListener(() {
                          setState(() {});
                        });
                        circleController.forward();
                      });
                    },
                    child: Text("1:00",
                        style: TextStyle(
                            fontWeight: FontWeight.bold, fontSize: 16.0)),
                  ),
                  new FlatButton(
                    onPressed: () {
                      setState(() {
                        previousTime = Duration(minutes: 2);
                        time = previousTime;
                        timeController.forward();
                        stopwatch.start();
                        circleController = new AnimationController(
                          duration: Duration(milliseconds: time.inMilliseconds),
                          vsync: this,
                        );
                        circleController.addListener(() {
                          setState(() {});
                        });
                        circleController.forward();
                      });
                    },
                    child: Text("2:00",
                        style: TextStyle(
                            fontWeight: FontWeight.bold, fontSize: 16.0)),
                  ),
                  new FlatButton(
                    onPressed: () {
                      setState(() {
                        previousTime = Duration(minutes: 3);
                        time = previousTime;
                        timeController.forward();
                        stopwatch.start();
                        circleController = new AnimationController(
                          duration: Duration(milliseconds: time.inMilliseconds),
                          vsync: this,
                        );
                        circleController.addListener(() {
                          setState(() {});
                        });
                        circleController.forward();
                      });
                    },
                    child: Text("3:00",
                        style: TextStyle(
                            fontWeight: FontWeight.bold, fontSize: 16.0)),
                  ),
                  new FlatButton(
                    onPressed: () {
                      setState(() {
                        previousTime = Duration(minutes: 5);
                        time = previousTime;
                        timeController.forward();
                        stopwatch.start();
                        circleController = new AnimationController(
                          duration: Duration(milliseconds: time.inMilliseconds),
                          vsync: this,
                        );
                        circleController.addListener(() {
                          setState(() {});
                        });
                        circleController.forward();
                      });
                    },
                    child: Text("5:00",
                        style: TextStyle(
                            fontWeight: FontWeight.bold, fontSize: 16.0)),
                  ),
                ],
              ),
            ),
          ),
          Center(
            child: Opacity(
              opacity: timeController.value != null ? 0.0 + timeController.value : 0.0,
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: time != null ? buildTimes(stopwatch, time, previousTime) : []
              ),
            ),
          ),
          CenterAbout(
              position: Offset(MediaQuery.of(context).size.width / 2,
                  MediaQuery.of(context).size.height / 2),
              child: Peinture(color: Color.fromRGBO(224, 245, 255, 1.0), animate: false)
          ),
          CenterAbout(
            position: Offset(MediaQuery.of(context).size.width / 2,
                MediaQuery.of(context).size.height / 2),
            child: Peinture(color: Colors.blue, animate: true)
          )
        ],
      ),
    );
  }
}

List<Widget> buildTimes(stopwatch, time, previousTime) {
  return [
    Time(stopwatch: stopwatch, time: time),
    Text(
      previousTime != null
          ? previousTime.toString().substring(3, 7)
          : "",
      style: TextStyle(
          fontWeight: FontWeight.bold,
          fontSize: 30.0,
          color: Colors.black54),
    ),
  ];
}

class Peinture extends StatefulWidget {

  final Color color;
  final bool animate;

  Peinture({this.color, this.animate});

  @override
  _PeintureState createState() => _PeintureState(color: color, animate: animate);
}

class _PeintureState extends State<Peinture> {
  Timer timer;
  Color color;
  bool animate;

  _PeintureState({this.color, this.animate}) {
    timer = Timer.periodic(Duration(milliseconds: 1), callback);
  }

  void callback(Timer timer) {
    setState(() {
      if (currentTime != null) {
        print(currentTime.inMilliseconds);
        if (currentTime.inMilliseconds <= 0) {
          reverse = true;
          timeController.reverse();
          previousTime = null;
          time = previousTime;
          currentTime = time;
          reverse = false;
        }
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Opacity(
      opacity: reverse == false ? 1.0 : timeController.value,
      child: CustomPaint(
        painter: CirclePainter(
          radius: 150.0,
          thickness: 8.0,
          color: color,
          startAngle: -pi / 2,
          endAngle: time != null && animate ? -pi / 2 + (((currentTime.inMilliseconds - circleController.value)) /  60000) * (2*pi) : -pi / 2 + (2 * pi) - (0 / (2*pi)),
        ),
      ),
    );
  }
}


class CirclePainter extends CustomPainter {
  final double radius;
  final double thickness;
  final Color color;
  final double startAngle;
  final double endAngle;
  final Paint circlePaint;

  CirclePainter({this.radius, this.thickness, this.color, this.startAngle, this.endAngle})
      : circlePaint = new Paint()
          ..color = color
          ..strokeWidth = thickness
          ..style = PaintingStyle.stroke
          ..strokeCap = StrokeCap.round;

  @override
  void paint(Canvas canvas, Size size) {
    canvas.drawArc(Rect.fromLTWH(-radius, -radius, radius * 2, radius * 2),
        startAngle, endAngle - startAngle, false, circlePaint);
  }

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

class Time extends StatefulWidget {
  final Duration time;
  final Stopwatch stopwatch;

  Time({this.stopwatch, this.time});

  @override
  _TimeState createState() => _TimeState(stopwatch: stopwatch, time: time);
}

class _TimeState extends State<Time> {
  Timer timer;
  Duration time;
  Stopwatch stopwatch;

  _TimeState({this.time, this.stopwatch}) {
    timer = new Timer.periodic(new Duration(milliseconds: 1), callback);
  }

  void callback(Timer timer) {
    if (stopwatch.isRunning) {
      setState(() {
        currentTime = time - stopwatch.elapsed;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Text(
      currentTime != null ? currentTime.toString().substring(3, 7) : "",
      style: TextStyle(
          fontWeight: FontWeight.bold, fontSize: 60.0, color: Colors.black87),
    );
  }
}

感谢您的帮助,并再次感谢您对如何进行操作的任何建议。

标签: animationdartfluttermobile-developmentflutter-animation

解决方案


尝试这个

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

import 'package:flutter/material.dart';
import 'package:fluttery/layout.dart';

Duration time;
Duration previousTime;
Duration currentTime;
AnimationController timeController, circleController;
bool reverse = false;

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
  Stopwatch stopwatch = new Stopwatch();

  @override
  void initState() {
    super.initState();
    timeController = new AnimationController(
      duration: const Duration(milliseconds: 250),
      vsync: this,
    );
    timeController.addListener(() {
      setState(() {});
    });
  }

  @override
  void dispose() {
    timeController.dispose();
    circleController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: <Widget>[
          Center(
            child: Opacity(
              opacity: timeController.value != null
                  ? 1.0 - timeController.value
                  : 1.0,
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  new FlatButton(
                    onPressed: () {
                      setState(() {
                        previousTime = Duration(minutes: 1);
                        time = previousTime;
                        timeController.forward();
                        stopwatch.start();
                        circleController = new AnimationController(
                          duration: Duration(milliseconds: time.inMilliseconds),
                          vsync: this,
                        );
                        circleController.addListener(() {
                          setState(() {});
                        });
                        circleController.forward();
                      });
                    },
                    child: Text("1:00",
                        style: TextStyle(
                            fontWeight: FontWeight.bold, fontSize: 16.0)),
                  ),
                  new FlatButton(
                    onPressed: () {
                      setState(() {
                        previousTime = Duration(minutes: 2);
                        time = previousTime;
                        timeController.forward();
                        stopwatch.start();
                        circleController = new AnimationController(
                          duration: Duration(milliseconds: time.inMilliseconds),
                          vsync: this,
                        );
                        circleController.addListener(() {
                          setState(() {});
                        });
                        circleController.forward();
                      });
                    },
                    child: Text("2:00",
                        style: TextStyle(
                            fontWeight: FontWeight.bold, fontSize: 16.0)),
                  ),
                  new FlatButton(
                    onPressed: () {
                      setState(() {
                        previousTime = Duration(minutes: 3);
                        time = previousTime;
                        timeController.forward();
                        stopwatch.start();
                        circleController = new AnimationController(
                          duration: Duration(milliseconds: time.inMilliseconds),
                          vsync: this,
                        );
                        circleController.addListener(() {
                          setState(() {});
                        });
                        circleController.forward();
                      });
                    },
                    child: Text("3:00",
                        style: TextStyle(
                            fontWeight: FontWeight.bold, fontSize: 16.0)),
                  ),
                  new FlatButton(
                    onPressed: () {
                      setState(() {
                        previousTime = Duration(minutes: 5);
                        time = previousTime;
                        timeController.forward();
                        stopwatch.start();
                        circleController = new AnimationController(
                          duration: Duration(milliseconds: time.inMilliseconds),
                          vsync: this,
                        );
                        circleController.addListener(() {
                          setState(() {});
                        });
                        circleController.forward();
                      });
                    },
                    child: Text("5:00",
                        style: TextStyle(
                            fontWeight: FontWeight.bold, fontSize: 16.0)),
                  ),
                ],
              ),
            ),
          ),
          Center(
            child: Opacity(
              opacity: timeController.value != null
                  ? 0.0 + timeController.value
                  : 0.0,
              child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: time != null
                      ? buildTimes(stopwatch, time, previousTime)
                      : []),
            ),
          ),
          CenterAbout(
            position: Offset(MediaQuery.of(context).size.width / 2,
                MediaQuery.of(context).size.height / 2),
            child: Peinture(
                color: Color.fromRGBO(224, 245, 255, 1.0), animate: false),
          ),
          CenterAbout(
            position: Offset(MediaQuery.of(context).size.width / 2,
                MediaQuery.of(context).size.height / 2),
            child: Peinture(color: Colors.blue, animate: true),
          )
        ],
      ),
    );
  }
}

List<Widget> buildTimes(stopwatch, time, previousTime) {
  return [
    Time(stopwatch: stopwatch, time: time),
    Text(
      previousTime != null ? previousTime.toString().substring(3, 7) : "",
      style: TextStyle(
          fontWeight: FontWeight.bold, fontSize: 30.0, color: Colors.black54),
    ),
  ];
}

class Peinture extends StatefulWidget {
  final Color color;
  final bool animate;

  Peinture({this.color, this.animate});

  @override
  _PeintureState createState() =>
      _PeintureState(color: color, animate: animate);
}

class _PeintureState extends State<Peinture> {
  Timer timer;
  Color color;
  bool animate;

  _PeintureState({this.color, this.animate}) {
    timer = Timer.periodic(Duration(milliseconds: 1), callback);
  }

  void callback(Timer timer) {
    setState(() {
      if (currentTime != null) {
        if (currentTime.inMilliseconds <= 0) {
          reverse = true;
          timeController.reverse();
          previousTime = null;
          time = previousTime;
          currentTime = time;
          reverse = false;
        }
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Opacity(
      opacity: reverse == false ? 1.0 : timeController.value,
      child: CustomPaint(
        painter: CirclePainter(
          radius: 150.0,
          thickness: 8.0,
          color: color,
          startAngle: -pi / 2,
          endAngle: time != null && animate && currentTime != null
              ? -pi / 2 +
                  (((currentTime.inMilliseconds - circleController.value)) /
                          60000) *
                      (2 * pi)
              : -pi / 2 + (2 * pi) - (0 / (2 * pi)),
        ),
      ),
    );
  }
}

class CirclePainter extends CustomPainter {
  final double radius;
  final double thickness;
  final Color color;
  final double startAngle;
  final double endAngle;
  final Paint circlePaint;

  CirclePainter(
      {this.radius, this.thickness, this.color, this.startAngle, this.endAngle})
      : circlePaint = new Paint()
          ..color = color
          ..strokeWidth = thickness
          ..style = PaintingStyle.stroke
          ..strokeCap = StrokeCap.round;

  @override
  void paint(Canvas canvas, Size size) {
    canvas.drawArc(Rect.fromLTWH(-radius, -radius, radius * 2, radius * 2),
        startAngle, endAngle - startAngle, false, circlePaint);
  }

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

class Time extends StatefulWidget {
  final Duration time;
  final Stopwatch stopwatch;

  Time({this.stopwatch, this.time});

  @override
  _TimeState createState() => _TimeState(stopwatch: stopwatch, time: time);
}

class _TimeState extends State<Time> {
  Timer timer;
  Duration time;
  Stopwatch stopwatch;

  _TimeState({this.time, this.stopwatch}) {
    timer = new Timer.periodic(new Duration(milliseconds: 1), callback);
  }

  void callback(Timer timer) {
    if (stopwatch.isRunning) {
      setState(() {
        currentTime = time - stopwatch.elapsed;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Text(
      currentTime != null ? currentTime.toString().substring(3, 7) : "",
      style: TextStyle(
          fontWeight: FontWeight.bold, fontSize: 60.0, color: Colors.black87),
    );
  }
}

推荐阅读