From ab46fdaafd2f46dc9dee9becb8eb9bb149bccacb Mon Sep 17 00:00:00 2001 From: Raghav Date: Wed, 14 Apr 2021 10:33:00 +0530 Subject: [PATCH] ptch: Timer to Resend Email Button --- lib/screens/verify/timer_button.dart | 204 ++++++++++++++++++++++++++ lib/screens/verify/verify_screen.dart | 74 ++++++++-- 2 files changed, 262 insertions(+), 16 deletions(-) create mode 100644 lib/screens/verify/timer_button.dart diff --git a/lib/screens/verify/timer_button.dart b/lib/screens/verify/timer_button.dart new file mode 100644 index 00000000..da18ea67 --- /dev/null +++ b/lib/screens/verify/timer_button.dart @@ -0,0 +1,204 @@ +import 'package:codephile/resources/colors.dart'; +import 'package:flutter/material.dart'; +import 'dart:async'; +import 'dart:ui' show lerpDouble; + + +enum ButtonState { Busy, Idle } + +class TimerButton extends StatefulWidget { + final double height; + final double width; + final double minWidth; + final Function(int time) loader; + final Duration animationDuration; + final Curve curve; + final Curve reverseCurve; + final Widget child; + final Function(Function startTimer, ButtonState btnState) onTap; + final Color color; + final EdgeInsetsGeometry padding; + final bool roundLoadingShape; + final double borderRadius; + final BorderSide borderSide; + final Color disabledColor; + final Color disabledTextColor; + final int initialTimer; + + TimerButton( + {@required this.height, + @required this.width, + this.minWidth: 0, + this.loader, + this.animationDuration: const Duration(milliseconds: 450), + this.curve: Curves.easeInOutCirc, + this.reverseCurve: Curves.easeInOutCirc, + @required this.child, + this.onTap, + this.color, + this.padding: const EdgeInsets.all(0), + this.borderRadius: 0.0, + this.roundLoadingShape: true, + this.borderSide: const BorderSide(color: Color.fromRGBO(51, 102, 255, 1), width: 1.5), + this.disabledColor, + this.disabledTextColor, + this.initialTimer: 0}); + + @override + _TimerButtonState createState() => _TimerButtonState(); +} + +class _TimerButtonState extends State + with TickerProviderStateMixin { + double loaderWidth; + + Animation _animation; + AnimationController _controller; + ButtonState btn; + int secondsLeft = 0; + Timer _timer; + Stream emptyStream = Stream.empty(); + double _minWidth = 0; + + @override + void initState() { + super.initState(); + + _controller = + AnimationController(vsync: this, duration: widget.animationDuration); + + _animation = Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation( + parent: _controller, + curve: widget.curve, + reverseCurve: widget.reverseCurve)); + + _animation.addStatusListener((status) { + if (status == AnimationStatus.dismissed) { + setState(() { + btn = ButtonState.Idle; + }); + } + }); + + if (widget.initialTimer == 0) { + btn = ButtonState.Idle; + } else { + startTimer(widget.initialTimer); + btn = ButtonState.Busy; + } + + minWidth = widget.height; + loaderWidth = widget.height; + } + + @override + void dispose() { + if (_timer != null) _timer.cancel(); + _controller.dispose(); + super.dispose(); + } + + void animateForward() { + setState(() { + btn = ButtonState.Busy; + }); + _controller.forward(); + } + + void animateReverse() { + _controller.reverse(); + } + + lerpWidth(a, b, t) { + if (a == 0.0 || b == 0.0) { + return null; + } else { + return a + (b - a) * t; + } + } + + get minWidth => _minWidth; + set minWidth(double w) { + if (widget.minWidth == 0) { + _minWidth = w; + } else { + _minWidth = widget.minWidth; + } + } + + void startTimer(int newTime) { + if (newTime == 0) { + throw ("Count Down Time can not be null"); + } + + animateForward(); + + setState(() { + secondsLeft = newTime; + }); + + if (_timer != null) { + _timer.cancel(); + } + + var oneSec = const Duration(seconds: 1); + _timer = new Timer.periodic( + oneSec, + (Timer timer) => setState( + () { + if (secondsLeft < 1) { + timer.cancel(); + } else { + secondsLeft = secondsLeft - 1; + } + }, + ), + ); + } + + @override + Widget build(BuildContext context) { + return AnimatedBuilder( + animation: _controller, + builder: (context, child) { + return buttonBody(); + }, + ); + } + + Widget buttonBody() { + return Container( + height: widget.height, + width: lerpWidth(widget.width, minWidth, _animation.value), + child: ButtonTheme( + height: widget.height, + shape: RoundedRectangleBorder( + side: widget.borderSide, + borderRadius: BorderRadius.circular(widget.roundLoadingShape + ? lerpDouble( + widget.borderRadius, widget.height / 2, _animation.value) + : widget.borderRadius), + ), + child: OutlineButton( + borderSide: BorderSide(color: codephileMain), + highlightedBorderColor: codephileMain, + color: widget.color, + padding: widget.padding, + disabledTextColor: widget.disabledTextColor, + onPressed: () { + widget.onTap((newCounter) => startTimer(newCounter), btn); + }, + child: btn == ButtonState.Idle + ? widget.child + : StreamBuilder( + stream: emptyStream, + builder: (context, snapshot) { + if (secondsLeft == 0) { + animateReverse(); + } + return widget.loader(secondsLeft); + })), + ), + ); + } +} \ No newline at end of file diff --git a/lib/screens/verify/verify_screen.dart b/lib/screens/verify/verify_screen.dart index 2f0d5d04..deb533b8 100644 --- a/lib/screens/verify/verify_screen.dart +++ b/lib/screens/verify/verify_screen.dart @@ -1,8 +1,10 @@ import 'package:codephile/models/token.dart'; import 'package:codephile/resources/colors.dart'; +import 'package:codephile/screens/verify/timer_button.dart'; import 'package:codephile/services/login.dart'; import 'package:codephile/services/send_verify_email.dart'; import 'package:codephile/services/upload_user_image.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -12,7 +14,9 @@ class VerifyScreen extends StatefulWidget { final String username; final String password; final String id; + VerifyScreen({this.password, this.username, this.id}); + @override _VerifyScreenState createState() => _VerifyScreenState(); } @@ -54,9 +58,33 @@ class _VerifyScreenState extends State { flex: 3, child: Padding( padding: const EdgeInsets.all(8.0), - child: FlatButton( - color: Colors.white, - onPressed: () async { + child: TimerButton( + borderRadius: 2, + height: 51, + width: MediaQuery.of(context).size.width * 0.45, + minWidth: MediaQuery.of(context).size.width * 0.30, + color: codephileMain, + child: Text( + "Resend Email", + style: TextStyle( + color: codephileMain, + fontSize: 16, + ), + textAlign: TextAlign.center, + ), + loader: (timeLeft) { + return Text( + "Wait | $timeLeft", + style: TextStyle( + color: codephileMain, + fontSize: 16, + ), + ); + }, + onTap: (startTimer, btnState) async { + if (btnState == ButtonState.Idle) { + startTimer(3); + } final response = await sendVerifyEmail(widget.id); if (response != 1) { Fluttertoast.showToast( @@ -67,20 +95,34 @@ class _VerifyScreenState extends State { ); } }, - child: Container( - width: double.infinity, - decoration: BoxDecoration( - border: Border.all(color: codephileMain, width: 1.5), - ), - padding: EdgeInsets.all(16.0), - child: Text( - "Resend Email", - textAlign: TextAlign.center, - style: - TextStyle(color: codephileMain, fontSize: 16.0), - ), - ), ), + // child: FlatButton( + // color: Colors.white, + // onPressed: () async { + // final response = await sendVerifyEmail(widget.id); + // if (response != 1) { + // Fluttertoast.showToast( + // msg: + // "Something went wrong, can not send new verification email.", + // toastLength: Toast.LENGTH_LONG, + // gravity: ToastGravity.CENTER, + // ); + // } + // }, + // child: Container( + // width: double.infinity, + // decoration: BoxDecoration( + // border: Border.all(color: codephileMain, width: 1.5), + // ), + // padding: EdgeInsets.all(16.0), + // child: Text( + // "Resend Email", + // textAlign: TextAlign.center, + // style: + // TextStyle(color: codephileMain, fontSize: 16.0), + // ), + // ), + // ), ), ), Expanded(