import 'package:flutter/material.dart';

class ScanImageView extends StatefulWidget {
  final Widget child;
  final Color scanColor;
  final Rect scanRect;

  const ScanImageView({Key key, this.child, this.scanRect, this.scanColor})
      : super(key: key);

  @override
  _ScanImageViewState createState() => _ScanImageViewState();
}

class _ScanImageViewState extends State<ScanImageView>
    with TickerProviderStateMixin {
  AnimationController controller;

  @override
  void initState() {
    super.initState();
    controller = AnimationController(
        vsync: this, duration: Duration(milliseconds: 1000));

    Future.delayed(Duration(milliseconds: 600), () {
      controller.repeat(reverse: true);
    });
  }

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

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
        animation: controller,
        builder: (BuildContext context, Widget child) => CustomPaint(
              foregroundPainter: _ScanPainter(
                  controller.value, widget.scanColor, widget.scanRect),
              child: widget.child,
              willChange: false,
            ));
  }
}

class _ScanPainter extends CustomPainter {
  final double value;
  final Color scanColor;
  final Rect scanRect;

  _ScanPainter(this.value, this.scanColor, this.scanRect);

  Paint _paint;

  @override
  void paint(Canvas canvas, Size size) {
    if (_paint == null) {
      initPaint();
    }
    double borderOffset = 3;
    double _borderLength = 20;
    final cutInnerRect = Rect.fromLTRB(
        scanRect.left + borderOffset / 2,
        scanRect.top + borderOffset / 2,
        scanRect.right - borderOffset / 2,
        scanRect.bottom - borderOffset / 2);

    final Path topLeft = Path()
      ..moveTo(cutInnerRect.left, cutInnerRect.top + _borderLength)
      ..lineTo(cutInnerRect.left, cutInnerRect.top)
      ..lineTo(cutInnerRect.left + _borderLength, cutInnerRect.top);

    final Path bottomLeft = Path()
      ..moveTo(cutInnerRect.left, cutInnerRect.bottom - _borderLength)
      ..lineTo(cutInnerRect.left, cutInnerRect.bottom)
      ..lineTo(cutInnerRect.left + _borderLength, cutInnerRect.bottom);

    final Path topRight = Path()
      ..moveTo(cutInnerRect.right, cutInnerRect.top + _borderLength)
      ..lineTo(cutInnerRect.right, cutInnerRect.top)
      ..lineTo(cutInnerRect.right - _borderLength, cutInnerRect.top);

    final Path bottomRight = Path()
      ..moveTo(cutInnerRect.right, cutInnerRect.bottom - _borderLength)
      ..lineTo(cutInnerRect.right, cutInnerRect.bottom)
      ..lineTo(cutInnerRect.right - _borderLength, cutInnerRect.bottom);

    final borderPaint = Paint()
      ..color = scanColor
      ..style = PaintingStyle.stroke
      ..strokeWidth = borderOffset;

    _paint.color = Colors.blueAccent;
    _paint.strokeWidth = 1;
    final scanLineRect = Rect.fromLTWH(
        scanRect.left + 10,
        scanRect.top + 10 + (value * (scanRect.height - 20)),
        scanRect.width - 20,
        1);

    _paint.shader = LinearGradient(colors: <Color>[
      Colors.lightBlue,
      Colors.blue,
      Colors.lightBlue,
    ], stops: [
      0.0,
      0.5,
      1,
    ]).createShader(scanLineRect);

    final backgroundPaint = Paint()
      ..color = const Color.fromRGBO(0, 0, 0, 50)
      ..style = PaintingStyle.fill;

    final boxPaint = Paint()
      ..color = scanColor
      ..style = PaintingStyle.fill
      ..blendMode = BlendMode.dstOut;

    canvas.saveLayer(
      Rect.fromLTWH(0, 0, size.width, size.height),
      backgroundPaint,
    );
    canvas
      ..drawRect(
        Rect.fromLTWH(0, 0, size.width, size.height),
        backgroundPaint,
      )
      ..drawRect(scanRect, boxPaint)
      ..drawPath(topLeft, borderPaint)
      ..drawPath(bottomLeft, borderPaint)
      ..drawPath(topRight, borderPaint)
      ..drawPath(bottomRight, borderPaint)
      ..drawRect(scanLineRect, _paint)
      ..restore();
  }

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

  void initPaint() {
    _paint = Paint()
      ..style = PaintingStyle.stroke
      ..strokeWidth = 1
      ..isAntiAlias = true
      ..strokeCap = StrokeCap.round
      ..strokeJoin = StrokeJoin.round;
  }
}
