import 'package:flutter/material.dart';

class Toast {
  static ToastView preToast;
  static void show(BuildContext context, String msg, int seconds, String toastType) {
    var overlayState = Overlay.of(context);
    var controllerShowAnim = AnimationController(
      vsync: overlayState,
      duration: Duration(milliseconds: 250),
    );
    var controllerShowOffset = AnimationController(
      vsync: overlayState,
      duration: Duration(milliseconds: 350),
    );
    var controllerHide = AnimationController(
      vsync: overlayState,
      duration: Duration(milliseconds: 250),
    );
    var opacityAnim1 = Tween(begin: 0.0, end: 1.0).animate(controllerShowAnim);
    var controllerCurvedShowOffset = CurvedAnimation(parent: controllerShowOffset, curve: _BounceOutCurve._());
    var offsetAnim = Tween(begin: 30.0, end: 0.0).animate(controllerCurvedShowOffset);
    var opacityAnim2 = Tween(begin: 1.0, end: 0.0).animate(controllerHide);

    OverlayEntry overlayEntry;
    overlayEntry = OverlayEntry(builder: (context) {
      return ToastWidget(
        opacityAnim1: opacityAnim1,
        opacityAnim2: opacityAnim2,
        offsetAnim: offsetAnim,
        child: buildToastLayout(msg, toastType),
      );
    });
    var toastView = ToastView();
    if (seconds != null) {
      toastView.seconds = seconds;
    }

    toastView.overlayEntry = overlayEntry;
    toastView.controllerShowAnim = controllerShowAnim;
    toastView.controllerShowOffset = controllerShowOffset;
    toastView.controllerHide = controllerHide;
    toastView.overlayState = overlayState;
    preToast = toastView;

    toastView._show();
  }

  static void  hide() {
    if (preToast != null) {
      preToast.dismiss();
      preToast = null;
    }
  }

  static LayoutBuilder buildToastLayout(String msg, String toastType) {
    return LayoutBuilder(builder: (context, constraints) {
      return Container(
        color: Colors.white.withOpacity(0),
        child: IgnorePointer(
          ignoring: true,
          child: Container(
            child: Material(
                color: Colors.white.withOpacity(0),
                child: Container(
                  margin: EdgeInsets.only(top: toastType == 'num' ? 0 : 0),
                  padding: EdgeInsets.symmetric(horizontal: toastType == 'info' ? 15 : (toastType == 'num' ? 0 : 36)),
                  height: toastType == 'info' ? 40 : (toastType == 'num' ? 60 : 100),
                  width: toastType == 'num' ? 60 : null,
                  decoration: ShapeDecoration(
                    color: Colors.black.withOpacity(toastType == 'num' ? 0.8 : 0.4),
                    shape: RoundedRectangleBorder(
                      borderRadius: BorderRadius.all(
                        Radius.circular(toastType == 'num' ? 60 : 8.0),
                      ),
                    ),
                  ),
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    crossAxisAlignment: CrossAxisAlignment.center,
                    children: _contentWidget(toastType, msg),
                  ),
                )),
            alignment: Alignment.center,
          ),
        ),
      );
    });
  }
}

List<Widget> _contentWidget(toastType, msg) {
  List<Widget> widgetList = [];
  if (toastType == 'loading') {
    widgetList = [
      CircularProgressIndicator(),
      Padding(
        padding: const EdgeInsets.only(
          top: 20.0,
        ),
        child: Text(
          msg,
          style: TextStyle(fontSize: 14.0, color: Colors.white),
        ),
      ),
    ];
  } else if (toastType == 'success') {
    widgetList = [
      Icon(Icons.done, color: Colors.green, size: 50),
      Padding(
        padding: const EdgeInsets.only(
          top: 10.0,
        ),
        child: Text(
          msg,
          style: TextStyle(fontSize: 14.0, color: Colors.white),
        ),
      ),
    ];
  } else if (toastType == 'num') {
    widgetList = [
      Text(
        msg,
        style: TextStyle(fontSize: 24.0, color: Colors.white),
      ),
    ];
  } else {
    widgetList = [
      Text(
        msg,
        style: TextStyle(fontSize: 14.0, color: Colors.white),
      ),
    ];
  }

  return widgetList;
}

class ToastView {
  OverlayEntry overlayEntry;
  AnimationController controllerShowAnim;
  AnimationController controllerShowOffset;
  AnimationController controllerHide;
  OverlayState overlayState;
  bool dismissed = false;
  int seconds = 3500;

  Future<void> _show() async {
    overlayState.insert(overlayEntry);
    controllerShowAnim.forward();
    controllerShowOffset.forward();
    if (seconds != -1) {
      await Future.delayed(Duration(milliseconds: seconds));
      this.dismiss();
    }
  }

  Future<void> dismiss() async {
    if (dismissed) {
      return;
    }
    this.dismissed = true;
    controllerHide.forward();
    await Future.delayed(Duration(milliseconds: 250));
    overlayEntry?.remove();
  }
}

class ToastWidget extends StatelessWidget {
  final Widget child;
  final Animation<double> opacityAnim1;
  final Animation<double> opacityAnim2;
  final Animation<double> offsetAnim;

  ToastWidget({this.child, this.offsetAnim, this.opacityAnim1, this.opacityAnim2});

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: opacityAnim1,
      child: child,
      builder: (context, child_to_build) {
        return Opacity(
          opacity: opacityAnim1.value,
          child: AnimatedBuilder(
            animation: offsetAnim,
            builder: (context, _) {
              return Transform.translate(
                offset: Offset(0, offsetAnim.value),
                child: AnimatedBuilder(
                  animation: opacityAnim2,
                  builder: (context, _) {
                    return Opacity(
                      opacity: opacityAnim2.value,
                      child: child_to_build,
                    );
                  },
                ),
              );
            },
          ),
        );
      },
    );
  }
}

class _BounceOutCurve extends Curve {
  const _BounceOutCurve._();

  @override
  double transform(double t) {
    t -= 1.0;
    return t * t * ((2 + 1) * t + 2) + 1.0;
  }
}
