首先,请多多注意我的问题;
我想通过startClickPosition&endPosition来创建Bezier路径,并绘制一个圆圈。
在代码中,我使用_path.computeMetrics()并获取PathMetrics,然后使用pms.elementAt(0)来获取PathMetric,但是我发现pms.length为0时出错。我的代码是:

  Path getPath(){
    Path path = Path();
    path.moveTo(widget.startOffset.dx, widget.startOffset.dy);
    // i'm ensure this 4 var got value and is right value,below this line.
    double startX = widget.endOffset.dx / 2;
    double startY = widget.startOffset.dy;
    double endX = widget.endOffset.dx;
    double endY = widget.endOffset.dy;
    path.quadraticBezierTo(startX,startY,endX ,endY);
    return path;
  }

    startAnimation(){
    _path  = getPath();
    if(_path == null) print("path is null");
    PathMetrics pms = _path.computeMetrics(forceClosed: false);
    // here pms.length is always 0;
    PathMetric pm = pms.elementAt(0);
    double pathLen = pm.length;

    _animation = Tween(begin: 0.0,end: pathLen).animate(_controller)
          ..addListener((){
            setState(() {
              _fraction = _animation.value;
              print("fraction  _____ $_fraction");
            });
          })
          ..addStatusListener((status){
            if(status == AnimationStatus.completed){
              _controller.stop();
            }
          });
  _controller.forward();

  }

非常感谢。 :)

整个代码:
class ParabolaAnimation extends StatefulWidget{

  Size screenSize;
  Offset startOffset;
  Offset endOffset;

  ParabolaAnimation(this.startOffset,this.endOffset,this.screenSize);

  @override
  State<StatefulWidget> createState() {
    // TODO: implement createState
    return ParabolaAnimationState();
  }

}

class ParabolaAnimationState extends State<ParabolaAnimation> with SingleTickerProviderStateMixin {

  AnimationController _controller;
  Animation _animation;
  double _fraction = 0.0;
  int _seconds = 3;
  Path _path;

  GlobalKey _key = GlobalKey();

  @override
  void initState() {
    // TODO: implement initState
    _controller = AnimationController(vsync: this,duration: Duration(seconds: _seconds));
    super.initState();


  }

  @override
  void dispose() {
    // TODO: implement dispose
    _controller.dispose();
    super.dispose();

  }


  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    WidgetsBinding.instance.addPostFrameCallback((_){
      startAnimation();
    });
    return CustomPaint(
      painter: PathPainter(_path, _fraction),
//      child: Container(
//        width: widget.screenSize.width,
//        height: widget.screenSize.height,
//      ),
    );
  }

  startAnimation(){
    _path  = getPath();
    print("path   ${_path.toString()}  ___ ");
    if(_path == null) print("path is null");
    PathMetrics pms = _path.computeMetrics(forceClosed: false);
    if(pms.length == 0) return;
    int plen = pms.length;
    //only one path
    PathMetric pm = pms.elementAt(0);
    double pathLen = pm.length;

    print("path len : $pathLen");

    _animation = Tween(begin: 0.0,end: pathLen).animate(_controller)
          ..addListener((){
            setState(() {
              _fraction = _animation.value;
              print("fraction  _____ $_fraction");
            });
          })
          ..addStatusListener((status){
            if(status == AnimationStatus.completed){
              _controller.stop();
            }
          });
  _controller.forward();

  }

  Path getPath(){
    print("start offset ${widget.startOffset.toString()}");
    print("end offset ${widget.endOffset.toString()}");
    Path path = Path();
    path.moveTo(widget.startOffset.dx, widget.startOffset.dy);
    double startX = widget.endOffset.dx / 2;
    double startY = widget.startOffset.dy;
    double endX = widget.endOffset.dx;
    double endY = widget.endOffset.dy;
    path.quadraticBezierTo(startX,startY,endX ,endY);
    return path;
  }


}


class PathPainter extends CustomPainter{

  double fraction;
  Path _path;
  List<Offset> _points = List();

  PathPainter(this._path,this.fraction);

  Paint circleP = Paint()
      ..color = Colors.orange
      ..style = PaintingStyle.fill;

  @override
  void paint(Canvas canvas, Size size) {
    if(_path == null) return;
    print("fraction  paint _____ $fraction");
    PathMetrics pms = _path.computeMetrics();
    PathMetric pm = pms.elementAt(0);
    double pathLen = pm.length;
    double circleR = 10;

    Offset circleCenterOffset;
    Tangent t = pm.getTangentForOffset(fraction);// 圆心
    circleCenterOffset = t.position;
    print("circle center ${circleCenterOffset.dx} + ${circleCenterOffset.dy}");
    canvas.drawCircle(circleCenterOffset, circleR, circleP);

  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    // TODO: implement shouldRepaint
    return true;
  }

}

最佳答案

class Circle extends StatefulWidget {
  @override
  _CircleState createState() => _CircleState();
}

class _CircleState extends State<Circle> with SingleTickerProviderStateMixin {
  double _fraction = 0.0;
  Animation<double> _animation;
  AnimationController _controller;
  @override
  void initState() {
    super.initState();

    _controller =
        AnimationController(duration: Duration(milliseconds: 300), vsync: this);

    _animation = Tween(begin: 0.0, end: 1.0).animate(_controller)
      ..addListener(() {
        setState(() {
          _fraction = _animation.value;
        });
      });

    _controller.forward();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Center(
        child: AspectRatio(
          aspectRatio: 1.0,
          child: Padding(
            padding: const EdgeInsets.all(24.0),
            child: CustomPaint(
              painter: CirclePainter(fraction: _fraction),
            ),
          ),
        ),
      ),
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
}

class CirclePainter extends CustomPainter {
  final double fraction;
  var _circlePaint;

  CirclePainter({this.fraction}) {
    _circlePaint = Paint()
      ..color = circleColor
      ..style = PaintingStyle.stroke
      ..strokeWidth = 12.0
      ..strokeCap = StrokeCap.round;
  }

  @override
  void paint(Canvas canvas, Size size) {
    var rect = Offset(0.0, 0.0) & size;

    canvas.drawArc(rect, -pi / 2, pi * 2 * fraction, false, _circlePaint);
  }

  @override
  bool shouldRepaint(CirclePainter oldDelegate) {
    return oldDelegate.fraction != fraction;
  }
}

10-05 19:38