Alexandr
Alexandr

Reputation: 97

Animated Curve Line - Flutter

Is it possible to somehow do in flutter what is shown in the GIF?

enter image description here

And so that the starting point always displays a number (in what position this point is at the moment), in the range from 0-100.

Upvotes: 0

Views: 416

Answers (1)

TripleNine
TripleNine

Reputation: 1932

EDIT: The animation is now similar to the one in GIF

import 'dart:ui';
import 'package:flutter/material.dart';

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

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {

  late final AnimationController _controller = AnimationController(
    vsync: this,
    duration: const Duration(seconds: 3)
  );

  late final Size size = const Size(400,400);

  late Path path = Path()
  ..moveTo(3, size.height-4.5)
  ..quadraticBezierTo(
    size.width, size.height, 
    size.width-4.5, 3, 
  );
  late PathAnimation pathAnimation = PathAnimation(path: path);

  @override
  void initState() {
    super.initState();
    _controller.forward();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Material App',
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Material App Bar'),
        ),
        body: Center(
          child: ClipRect(
            child: AnimatedBuilder(
              animation: _controller,
              child: Container(
                alignment: Alignment.center,
                height: size.height,
                width: size.width,
                decoration: BoxDecoration(
                  color: Colors.white,
                  border: Border.all(
                    color: Colors.black,
                    width: 3,
                  )
                ),
              ),
              builder: (context, child) {
                return CustomPaint(
                  foregroundPainter: PathPainter(
                    animation: _controller.value,
                    pathAnimation: pathAnimation,
                  ),
                  child: child
                );
              }
            ),
          ),
        ),
      ),
    );
  }
}

class PathAnimation {

  final Path path;
  Path currentPath = Path();
  late final List<PathMetric> pathSegments;
  late final double totalLength;
  double currentLength = 0;
  int currentPathIndex = 0;

  PathAnimation({
    required this.path
  }) {
    pathSegments = path.computeMetrics().toList();
    totalLength = pathSegments.fold(0, (value, element) => value + element.length);
  }

  Path getCurrentPath(double animation) {
    while (animation > (currentLength + pathSegments[currentPathIndex].length) / totalLength) {
      double segmentLength = pathSegments[currentPathIndex].length;
      currentPath.addPath(
        pathSegments[currentPathIndex].extractPath(0, segmentLength),
        Offset.zero
      );
      currentPathIndex++;
      currentLength += segmentLength;
    }
    if (currentPathIndex >= pathSegments.length) return currentPath;
    double missingPartLength = animation - currentLength / totalLength;
    double newSegmentLength = pathSegments[currentPathIndex].length;
    return  
      currentPath..addPath(
      pathSegments[currentPathIndex].extractPath(0, missingPartLength * newSegmentLength),
      Offset.zero
    );
  }
}

class PathPainter extends CustomPainter{
  
  double animation;
  PathAnimation pathAnimation;

  PathPainter({
    required this.animation,
    required this.pathAnimation,
  });

  @override
  void paint(Canvas canvas, Size size) {
    canvas.drawPath(
      pathAnimation.getCurrentPath(animation),
      Paint()
      ..style = PaintingStyle.stroke
      ..strokeWidth = 3
      ..color = Colors.red
    );
  }

  @override
  bool shouldRepaint(PathPainter oldDelegate) {
    return animation != oldDelegate.animation;
  }
}

Upvotes: 1

Related Questions