import 'package:colorful_print/colorful_print.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../global/api.dart'; import '../../global/styles.dart'; import '../../global/utils.dart'; import '../../global/widgets/ui_button.dart'; class Login extends ConsumerStatefulWidget { const Login({super.key, required this.formKey, required this.exitNav}); final GlobalKey formKey; final Widget exitNav; @override ConsumerState createState() => _LoginState(); } class _LoginState extends ConsumerState { bool usingUsername = true; final emailController = TextEditingController(); final usernameController = TextEditingController(); final passwordController = TextEditingController(); final focusNodes = [ FocusNode(), FocusNode(), FocusNode(), ]; @override Widget build(BuildContext context) { final screen = MediaQuery.of(context).size; return Form( key: widget.formKey, child: Stack( children: [ Column( children: [ const Spacer(), const Text( 'Login', style: TextStyle(fontSize: 24), ), const Spacer(flex: 2), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Column( children: [ const Padding( padding: EdgeInsets.all(2.0), child: Text( 'Username', style: TextStyle( fontSize: 16, ), ), ), Checkbox( activeColor: Styles.lavender, onChanged: (bool? value) { setState(() => usingUsername = true); }, value: usingUsername, ), ], ), const SizedBox( width: 35, ), Column( children: [ const Padding( padding: EdgeInsets.all(2.0), child: Text( 'Email', style: TextStyle( fontSize: 16, ), ), ), Checkbox( activeColor: Styles.lavender, onChanged: (bool? value) { setState(() => usingUsername = false); }, value: !usingUsername, ), ], ), ], ), AnimatedSwitcher( duration: const Duration(milliseconds: 200), child: !usingUsername ? generateTextField( controller: emailController, size: screen, text: 'Email: ', validatorFunc: (s) { if (s != null && s.isNotEmpty && !isEmailValid(s)) { return 'Email entered is invalid'; } return null; }, index: 0) : generateTextField( controller: usernameController, size: screen, text: 'Username: ', validatorFunc: (s) { if (s == null || s.isEmpty) { return 'Invalid Username'; } if (s.length < 3) { return 'Username Not 3 Letters Long'; } if (s.length > 20) { return 'Username Too Long'; } if (!isUsernameValid(s)) { return 'Letters, Numbers, and ., -, _ Allowed'; } return null; }, index: 0), ), generateTextField( controller: passwordController, size: screen, text: 'Password: ', validatorFunc: (s) { if (s != null && s.length < 6) { return 'Please do a better password, at least 6 characters (you have to)'; } return null; }, index: 1, isPassword: true), const Spacer(flex: 2), UiButton( width: screen.width * 0.85 > 400 ? 400 : screen.width * 0.85, showLoading: true, onPressed: () => login(), text: 'Submit', color: Styles.seaweedGreen, ), const Spacer(), ], ), widget.exitNav, ], ), ); } Widget generateTextField({ required String text, String? label, required int index, required TextEditingController controller, isPassword = false, required Size size, required String? Function(String?) validatorFunc, }) { return Padding( padding: const EdgeInsets.all(8.0), child: Row( children: [ Padding( padding: const EdgeInsets.all(8.0), child: Text( text, style: TextStyle(fontSize: size.width < 350 ? 16 : 20), ), ), const Spacer(), Container( decoration: Styles.boxLavenderBubble, width: size.width * 0.5 > 300 ? 300 : size.width * 0.5, child: TextFormField( validator: validatorFunc, controller: controller, decoration: Styles.inputLavenderBubble(labelText: label), obscureText: isPassword, focusNode: focusNodes[index], style: const TextStyle(fontSize: 16), onFieldSubmitted: (_) { if (index != focusNodes.length - 1) { focusNodes[index + 1].requestFocus(); } }, ), ), SizedBox( width: size.width * 0.05, ), ], ), ); } Future login() async { try { if (widget.formKey.currentState != null && !widget.formKey.currentState!.validate()) { return; } final Map? data = await ref.read(apiProvider.notifier).post(path: 'auth/login', data: { 'username': usernameController.text.isEmpty ? null : usernameController.text, 'email': emailController.text.isEmpty ? null : emailController.text, 'password': passwordController.text, }); bool success = data?['success'] ?? false; String message = data?['message'] ?? (success ? 'Login success!' : 'Login unsuccessful.'); printColor(data, textColor: TextColor.yellow); showSnack(ref: ref, text: message, type: success ? SnackType.success : SnackType.error); } catch (err, st) { printColor('Error in login: $err\n$st', textColor: TextColor.red); } } }