otp_page.dart 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. import 'dart:async';
  2. import 'dart:math';
  3. import 'package:flutter/material.dart';
  4. import 'package:fluttertoast/fluttertoast.dart';
  5. import 'package:localstore/localstore.dart';
  6. import '../providers/otp_provider.dart';
  7. class OTPPage extends StatefulWidget {
  8. const OTPPage({Key? key}) : super(key: key);
  9. @override
  10. State<OTPPage> createState() => _OTPPageState();
  11. }
  12. class _OTPPageState extends State<OTPPage> {
  13. final TextEditingController _codigo = TextEditingController();
  14. final _otpProvider = OTPProvider();
  15. final db = Localstore.instance;
  16. late Timer _timer;
  17. late String _oneTimePwd;
  18. int _errorCounter = 0;
  19. int _minutes = 5;
  20. int _seconds = 0;
  21. bool _verifyActive = false;
  22. bool _isWaiting = true;
  23. dynamic _logInfo;
  24. @override
  25. void initState() {
  26. super.initState();
  27. _oneTimePwd = _generateKey();
  28. _init();
  29. }
  30. void _startTimer(){
  31. const oneSec = Duration(seconds: 1);
  32. _timer = Timer.periodic(oneSec, (timer) => setState(() {
  33. if(_minutes < 1 && _seconds < 1){
  34. timer.cancel();
  35. }else if(_seconds == 0){
  36. _minutes -= 1;
  37. _seconds = 59;
  38. }else{
  39. _seconds -= 1;
  40. }
  41. }));
  42. }
  43. String _generateKey(){
  44. final rand = Random();
  45. String num = rand.nextInt(999999).toString();
  46. num = num.length == 5 ? "0$num" : num.length == 4 ? "00$num" : num.length == 3 ? "000$num" : num.length == 2 ? "0000$num" : num.length == 1 ? "00000$num" : num;
  47. return num;
  48. }
  49. void _init() async{
  50. final items = await db.collection('quiosco').get();
  51. final key = items!.keys.last;
  52. final info = items[key];
  53. _logInfo = info;
  54. _otpProvider.fetchOTP(info['accessToken'], info['dinum'], _oneTimePwd,
  55. _formattedDate(DateTime.now().toIso8601String()),
  56. _formattedDate(DateTime.now().add(const Duration(minutes: 5)).toIso8601String()),
  57. DateTime.now().year.toString());
  58. _startTimer();
  59. setState(() => _isWaiting = false);
  60. }
  61. @override
  62. void dispose() {
  63. _timer.cancel();
  64. super.dispose();
  65. }
  66. @override
  67. Widget build(BuildContext context) {
  68. final _screenSize = MediaQuery.of(context).size;
  69. return Scaffold(
  70. body: SafeArea(
  71. child: SingleChildScrollView(
  72. child: Column(
  73. children: [
  74. const SizedBox(height: 32.0),
  75. Row(
  76. children: const [
  77. SizedBox(
  78. width: 32.0,
  79. ),
  80. Image(
  81. image: AssetImage('assets/logo.png'),
  82. width: 64.0,
  83. ),
  84. SizedBox(width: 16.0),
  85. Expanded(
  86. child: Image(
  87. image: AssetImage('assets/logo_completo.png'),
  88. ),
  89. ),
  90. SizedBox(width: 32.0)
  91. ],
  92. ),
  93. const SizedBox(height: 48.0),
  94. const Image(
  95. image: AssetImage('assets/quiosco.png'),
  96. width: 88.0,
  97. fit: BoxFit.cover,
  98. ),
  99. const SizedBox(height: 16.0),
  100. const Text(
  101. 'QUIOSCO',
  102. textAlign: TextAlign.center,
  103. style: TextStyle(
  104. fontFamily: 'Helvetica Neue Black Cond',
  105. fontSize: 28.0
  106. ),
  107. ),
  108. const Text(
  109. 'Colaborador',
  110. textAlign: TextAlign.center,
  111. style: TextStyle(
  112. fontFamily: 'Helvetica Neue Light',
  113. fontSize: 22.0,
  114. fontWeight: FontWeight.w600,
  115. color: Color.fromRGBO(104, 104, 104, 1)
  116. ),
  117. ),
  118. const SizedBox(height: 16.0),
  119. if(_seconds > 0 || _minutes > 0)
  120. Container(
  121. margin: const EdgeInsets.symmetric(horizontal: 32.0),
  122. child: Text(
  123. 'Hola ${_isWaiting ? '' : _logInfo['user']}, hemos enviado un código a tu correo. '
  124. 'Por favor introdúcelo debajo para saber que eres tú.',
  125. textAlign: TextAlign.center,
  126. style: const TextStyle(
  127. fontFamily: 'Helvetica Neue Light',
  128. fontSize: 16.0,
  129. fontWeight: FontWeight.w300,
  130. color: Colors.grey
  131. ),
  132. ),
  133. ),
  134. if(_seconds < 1 && _minutes < 1)
  135. Container(
  136. margin: const EdgeInsets.symmetric(horizontal: 32.0),
  137. child: Text(
  138. 'Hola ${_logInfo['user']}, el código anterior ha expirado. '
  139. 'Por favor genera uno nuevo.',
  140. textAlign: TextAlign.center,
  141. style: const TextStyle(
  142. fontFamily: 'Helvetica Neue Light',
  143. fontSize: 16.0,
  144. fontWeight: FontWeight.w300,
  145. color: Colors.grey
  146. ),
  147. ),
  148. ),
  149. const SizedBox(height: 8.0),
  150. Text(
  151. _time(_minutes, _seconds),
  152. textAlign: TextAlign.center,
  153. style: const TextStyle(
  154. fontFamily: 'Helvetica Neue Light',
  155. fontSize: 22.0,
  156. fontWeight: FontWeight.w600,
  157. color: Color.fromRGBO(104, 104, 104, 1)
  158. ),
  159. ),
  160. const SizedBox(height: 16.0),
  161. Container(
  162. margin: const EdgeInsets.symmetric(horizontal: 32.0),
  163. child: Column(
  164. children: <Widget>[
  165. if(_seconds > 0 || _minutes > 0)
  166. TextField(
  167. keyboardType: TextInputType.number,
  168. controller: _codigo,
  169. decoration: const InputDecoration(
  170. hintText: 'Código de verificación:',
  171. contentPadding: EdgeInsets.zero,
  172. ),
  173. maxLines: 1,
  174. cursorColor: const Color.fromRGBO(180, 4, 4, 1),
  175. style: const TextStyle(
  176. fontSize: 20.0,
  177. ),
  178. onChanged: (value){
  179. setState(() {
  180. if(value.length == 6){
  181. _verifyActive = true;
  182. }else{
  183. _verifyActive = false;
  184. }
  185. });
  186. },
  187. ),
  188. if(_seconds < 1 && _minutes < 1)
  189. MaterialButton(
  190. elevation: 4.0,
  191. padding: const EdgeInsets.symmetric(vertical: 8.0),
  192. minWidth: _screenSize.width-64.0,
  193. color: const Color.fromRGBO(255, 0, 0, 1),
  194. child: const Text(
  195. 'Generar nuevo código',
  196. style: TextStyle(
  197. fontFamily: 'Helvetica Neue Black Cond',
  198. fontSize: 28.0,
  199. color: Colors.white,
  200. ),
  201. ),
  202. onPressed: () async{
  203. setState(() {
  204. _minutes = 5;
  205. _isWaiting = true;
  206. });
  207. _oneTimePwd = _generateKey();
  208. await _otpProvider.fetchOTP(
  209. _logInfo['accessToken'],
  210. _logInfo['dinum'],
  211. _oneTimePwd,
  212. _formattedDate(DateTime.now().toIso8601String()),
  213. _formattedDate(DateTime.now().add(const Duration(minutes: 5)).toIso8601String()),
  214. DateTime.now().year.toString()
  215. );
  216. _startTimer();
  217. setState(() {
  218. _isWaiting = false;
  219. });
  220. },
  221. ),
  222. const SizedBox(height: 16.0),
  223. if(_isWaiting)
  224. const CircularProgressIndicator(),
  225. if(!_isWaiting)
  226. MaterialButton(
  227. elevation: 4.0,
  228. padding: const EdgeInsets.symmetric(vertical: 8.0),
  229. minWidth: _screenSize.width-64.0,
  230. color: const Color.fromRGBO(255, 0, 0, 1),
  231. disabledColor: Colors.grey,
  232. child: const Text(
  233. 'Verificar',
  234. style: TextStyle(
  235. fontFamily: 'Helvetica Neue Black Cond',
  236. fontSize: 28.0,
  237. color: Colors.white,
  238. ),
  239. ),
  240. onPressed: !_verifyActive ? null : () async{
  241. FocusScope.of(context).requestFocus(FocusNode());
  242. if(_codigo.text == _oneTimePwd){ //Si entra en el intervalo de tiempo da true
  243. Fluttertoast.showToast(
  244. msg: 'Bienvenido ${_logInfo['user']}',
  245. toastLength: Toast.LENGTH_SHORT,
  246. gravity: ToastGravity.CENTER,
  247. timeInSecForIosWeb: 1,
  248. );
  249. final action = await Navigator.pushNamed(context, 'home');
  250. if(action == 'logout'){
  251. Navigator.pop(context);
  252. }else{
  253. print(action);
  254. }
  255. }else{
  256. setState(() {
  257. Fluttertoast.showToast(
  258. msg: 'Código inválido, intento ${_errorCounter + 1} de 3',
  259. toastLength: Toast.LENGTH_SHORT,
  260. gravity: ToastGravity.CENTER,
  261. timeInSecForIosWeb: 1,
  262. );
  263. _codigo.text = '';
  264. _verifyActive = false;
  265. _errorCounter += 1;
  266. if(_errorCounter > 2){
  267. Fluttertoast.showToast(
  268. msg: 'Ha exedido el número de intentos permitidos.',
  269. toastLength: Toast.LENGTH_LONG,
  270. gravity: ToastGravity.CENTER,
  271. timeInSecForIosWeb: 3,
  272. );
  273. _minutes = 0;
  274. _seconds = 0;
  275. }
  276. });
  277. }
  278. },
  279. ),
  280. ],
  281. ),
  282. ),
  283. const SizedBox(height: 32.0),
  284. ],
  285. ),
  286. ),
  287. ),
  288. );
  289. }
  290. String _formattedDate(String date){
  291. final year = date.substring(2, 4);
  292. final month = date.substring(5, 7);
  293. final day = date.substring(8, 10);
  294. final hour = date.substring(11, 13);
  295. final minuts = date.substring(14, 16);
  296. final seconds = date.substring(17, 19);
  297. return '$day/$month/$year $hour:$minuts:$seconds';
  298. }
  299. String _time(int min, int sec){
  300. if(sec < 10){
  301. return '0$min:0$sec';
  302. }else{
  303. return '0$min:$sec';
  304. }
  305. }
  306. }