title: How to animate a circle by a path ? Flutter
body: <p>first,thks a lot for watch my question;
  i want to make a Bezier path by startClickPosition&amp;endPosition , and draw a circle go with it.
  among the code,i used _path.computeMetrics(),and get a PathMetrics,then i use pms.elementAt(0) to get the PathMetric,but i found a error in there is pms.length is 0.  there is my Code:</p>
  
  <pre><code>  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();
  
    }
  </code></pre>
  
  <p>thks a lot. :)</p>
  
  <p>the whole code :</p>
  
  <pre><code>class ParabolaAnimation extends StatefulWidget{
  
    Size screenSize;
    Offset startOffset;
    Offset endOffset;
  
    ParabolaAnimation(this.startOffset,this.endOffset,this.screenSize);
  
    @override
    State&lt;StatefulWidget&gt; createState() {
      // TODO: implement createState
      return ParabolaAnimationState();
    }
  
  }
  
  class ParabolaAnimationState extends State&lt;ParabolaAnimation&gt; 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&lt;Offset&gt; _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;
    }
  
  }
  </code></pre>