content: ///
  /// * author: hunghd
  /// * email: hunghd.yb@gmail.com
  ///
  /// A package provides an easy way to add shimmer effect to Flutter application
  ///
  
  import 'package:flutter/material.dart';
  import 'package:flutter/rendering.dart';
  
  ///
  /// An enum defines all supported directions of shimmer effect
  ///
  /// * [ShimmerDirection.ltr] left to right direction
  /// * [ShimmerDirection.rtl right to left direction
  /// * [ShimmerDirection.ttb] top to bottom direction
  /// * [ShimmerDirection.btt] bottom to top direction
  ///
  enum ShimmerDirection { ltr, rtl, ttb, btt }
  
  ///
  /// A widget renders shimmer effect over [child] widget tree.
  ///
  /// [child] defines an area that shimmer effect blends on. You can build [child]
  /// from whatever [Widget] you like but there're some notices in order to get
  /// exact expected effect and get better rendering performance:
  ///
  /// * Use static [Widget] (which is an instance of [StatelessWidget]).
  /// * [Widget] should be a solid color element. Every colors you set on these
  /// [Widget]s will be overridden by colors of [gradient].
  /// * Shimmer effect only affects to opaque areas of [child], transparent areas
  /// still stays transparent.
  ///
  /// [period] controls the speed of shimmer effect. The default value is 1500
  /// milliseconds.
  ///
  /// [direction] controls the direction of shimmer effect. The default value
  /// is [ShimmerDirection.ltr].
  ///
  /// [gradient] controls colors of shimmer effect.
  ///
  class Shimmer extends StatefulWidget {
    final Widget child;
    final Duration period;
    final ShimmerDirection direction;
    final Gradient gradient;
  
    Shimmer({
      Key key,
      @required this.child,
      @required this.gradient,
      this.direction = ShimmerDirection.ltr,
      this.period = const Duration(milliseconds: 1500),
    }) : super(key: key);
  
    ///
    /// A convenient constructor provides an easy and convenient way to create a
    /// [Shimmer] which [gradient] is [LinearGradient] made up of `baseColor` and
    /// `highlightColor`.
    ///
    Shimmer.fromColors(
        {Key key,
          @required this.child,
          @required Color baseColor,
          @required Color highlightColor,
          this.period = const Duration(milliseconds: 1500),
          this.direction = ShimmerDirection.ltr})
        : gradient = LinearGradient(
        begin: Alignment.topLeft,
        end: Alignment.centerRight,
        colors: [
          baseColor,
          baseColor,
          highlightColor,
          baseColor,
          baseColor
        ],
        stops: [
          0.0,
          0.35,
          0.5,
          0.65,
          1.0
        ]),
          super(key: key);
  
    @override
    _ShimmerState createState() => _ShimmerState();
  
    @override
    void debugFillProperties(DiagnosticPropertiesBuilder properties) {
      super.debugFillProperties(properties);
      properties.add(DiagnosticsProperty<Gradient>('gradient', gradient, defaultValue: null));
      properties.add(EnumProperty<ShimmerDirection>('direction', direction));
      properties.add(DiagnosticsProperty<Duration>('period', period, defaultValue: null));
    }
  
  }
  
  class _ShimmerState extends State<Shimmer> with SingleTickerProviderStateMixin {
    AnimationController controller;
  
    @override
    void initState() {
      super.initState();
      controller = AnimationController(vsync: this, duration: widget.period)
        ..addListener(() {
          setState(() {});
        });
  /*      ..addStatusListener((status) {
          if (status == AnimationStatus.completed) {
            controller.repeat();
          }
        });*/
      controller.forward();
    }
  
    @override
    void didUpdateWidget(Shimmer oldWidget) {
  
      super.didUpdateWidget(oldWidget);
      controller.forward(from: 0.0);
    }
  
    @override
    Widget build(BuildContext context) {
      return _Shimmer(
        child: widget.child,
        direction: widget.direction,
        gradient: widget.gradient,
        percent: controller.value,
      );
    }
  
    @override
    void dispose() {
      controller.dispose();
      super.dispose();
    }
  }
  
  class _Shimmer extends SingleChildRenderObjectWidget {
    final double percent;
    final ShimmerDirection direction;
    final Gradient gradient;
  
    _Shimmer({Widget child, this.percent, this.direction, this.gradient})
        : super(child: child);
  
    @override
    _ShimmerFilter createRenderObject(BuildContext context) {
      return _ShimmerFilter(percent, direction, gradient);
    }
  
    @override
    void updateRenderObject(BuildContext context, _ShimmerFilter shimmer) {
      shimmer.percent = percent;
    }
  }
  
  class _ShimmerFilter extends RenderProxyBox {
    final _clearPaint = Paint();
    final Paint _gradientPaint;
    final Gradient _gradient;
    final ShimmerDirection _direction;
    double _percent;
    Rect _rect;
  
    _ShimmerFilter(this._percent, this._direction, this._gradient)
        : _gradientPaint = Paint()..blendMode = BlendMode.srcIn;
  
    @override
    bool get alwaysNeedsCompositing => child != null;
  
    set percent(double newValue) {
      if (newValue != _percent) {
        _percent = newValue;
        markNeedsPaint();
      }
    }
  
    @override
    void paint(PaintingContext context, Offset offset) {
      if (child != null) {
        assert(needsCompositing);
  
        final width = child.size.width;
        final height = child.size.height;
        Rect rect;
        double dx, dy;
        if (_direction == ShimmerDirection.rtl) {
          dx = _offset(width, -width, _percent);
          dy = 0.0;
          rect = Rect.fromLTWH(offset.dx - width, offset.dy, 3 * width, height);
        } else if (_direction == ShimmerDirection.ttb) {
          dx = 0.0;
          dy = _offset(-height, height, _percent);
          rect = Rect.fromLTWH(offset.dx, offset.dy - height, width, 3 * height);
        } else if (_direction == ShimmerDirection.btt) {
          dx = 0.0;
          dy = _offset(height, -height, _percent);
          rect = Rect.fromLTWH(offset.dx, offset.dy - height, width, 3 * height);
        } else {
          dx = _offset(-width, width, _percent);
          dy = 0.0;
          rect = Rect.fromLTWH(offset.dx - width, offset.dy, 3 * width, height);
        }
        if (_rect != rect) {
          _gradientPaint.shader = _gradient.createShader(rect);
          _rect = rect;
        }
  
        context.canvas.saveLayer(offset & child.size, _clearPaint);
        context.paintChild(child, offset);
        context.canvas.translate(dx, dy);
        context.canvas.drawRect(rect, _gradientPaint);
        context.canvas.restore();
      }
    }
  
    double _offset(double start, double end, double percent) {
      return start + (end - start) * percent;
    }
  }