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

/// Animated progress bar. Behaves like implicitly animated widget.
/// Check basic implicit animated Flutter widgets like [AnimatedContainer]
/// It animates [value] changes.
/// Requires [duration] to set filling duration timer
/// [onEnd] callback to trigger additional actions (e.g. another animation)
/// at the end of the current animation
/// [color] or [gradient] to fill the progress bar. Only one parameter is allowed.
/// Optional [backgroundColor], defaults to transparent
/// Optional [width] defaults to 200.0
/// Optional [height] defaults to 10.0
/// Optional [curve] defaults to [Curves.linear]
class AnimatedProgressBar extends ImplicitlyAnimatedWidget {
  const AnimatedProgressBar({
    key,
    required duration,
    required this.value,
    this.width = 200.0,
    this.height = 10.0,
    this.color,
    this.gradient,
    this.backgroundColor = Colors.transparent,
    curve = Curves.linear,
    onEnd,
  }) : super(key: key, duration: duration, curve: curve, onEnd: onEnd);

  ///progress bar width
  final double width;

  ///progress bar height
  final double height;

  ///current progress value
  final double? value;

  ///progress bar gradient parameter
  final Gradient? gradient;

  ///progress bar color parameter
  final Color? color;

  ///progress bar color parameter
  final Color backgroundColor;

  @override
  AnimatedWidgetBaseState<AnimatedProgressBar> createState() => _AnimatedBarState();
}

class _AnimatedBarState extends AnimatedWidgetBaseState<AnimatedProgressBar> {
  Tween<double>? _progressValue;

  @override
  void forEachTween(TweenVisitor<dynamic> visitor) {
    _progressValue = visitor(_progressValue, widget.value, (value) => Tween<double>(begin: value)) as Tween<double>?;
  }

  @override
  Widget build(BuildContext context) {
    return ProgressBar(
      value: _progressValue?.evaluate(animation),
      width: widget.width,
      height: widget.height,
      gradient: widget.gradient,
      color: widget.color,
      backgroundColor: widget.backgroundColor,
    );
  }

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder description) {
    super.debugFillProperties(description);
    description.add(DiagnosticsProperty<Tween>('progressValue', _progressValue, showName: false, defaultValue: null));
  }
}

class ProgressBar extends StatelessWidget {
  const ProgressBar({
    Key? key,
    required this.value,
    this.width = 200.0,
    this.height = 10.0,
    this.color,
    this.backgroundColor = Colors.transparent,
    this.gradient,
  })  : assert(
          gradient == null || color == null,
          'Cannot provide both a color and a gradient',
        ),
        assert(
          gradient != null || color != null,
          'Need to provide color or gradient',
        ),
        super(key: key);

  ///progress bar width
  final double width;

  ///progress bar height
  final double height;

  ///current progress value
  final double? value;

  ///progress bar gradient parameter
  final Gradient? gradient;

  ///progress bar color parameter
  final Color? color;

  ///progress bar color parameter
  final Color backgroundColor;

  @override
  Widget build(BuildContext context) {
    return CustomPaint(
      size: Size(width, height),
      foregroundPainter: ProgressPainter(
        value: value!,
        color: color,
        gradient: gradient,
      ),
      painter: BackgroundPainter(
        backgroundColor: backgroundColor,
      ),
    );
  }
}

class BackgroundPainter extends CustomPainter {
  const BackgroundPainter({required this.backgroundColor});

  ///progress bar backgroundColor
  final Color backgroundColor;

  @override
  void paint(Canvas canvas, Size size) {
    Paint paint = Paint()..color = backgroundColor;
    canvas.drawRRect(RRect.fromRectAndRadius(Offset.zero & size, Radius.circular(6)), paint);
  }

  @override
  bool shouldRepaint(covariant BackgroundPainter oldDelegate) => false;
}

class ProgressPainter extends CustomPainter {
  const ProgressPainter({required this.value, this.gradient, this.color});

  ///current progress bar value
  final double value;

  ///progress bar gradient infill
  final Gradient? gradient;

  ///progress bar gradient color
  final Color? color;

  @override
  void paint(Canvas canvas, Size size) {
    Paint paint = Paint();
    if (gradient != null) {
      paint.shader = gradient?.createShader(Offset.zero & size);
    }
    if (color != null) {
      paint.color = color!;
    }
    canvas.clipRRect(RRect.fromRectAndRadius(Offset.zero & size, Radius.circular(6)));
    canvas.drawRRect(RRect.fromRectAndRadius(Rect.fromLTRB(0, 0, size.width * value, size.height), Radius.circular(6)), paint);
  }

  @override
  bool shouldRepaint(covariant ProgressPainter oldDelegate) {
    return value != oldDelegate.value;
  }
}
