diff --git a/lib/view/auth/forgot_password_screen.dart b/lib/view/auth/forgot_password_screen.dart index fbc15f3..9875b89 100644 --- a/lib/view/auth/forgot_password_screen.dart +++ b/lib/view/auth/forgot_password_screen.dart @@ -1,13 +1,12 @@ import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_lucide/flutter_lucide.dart'; import 'package:get/get.dart'; import 'package:marco/controller/auth/forgot_password_controller.dart'; -import 'package:marco/helpers/widgets/my_button.dart'; -import 'package:marco/helpers/widgets/my_text.dart'; import 'package:marco/helpers/services/api_endpoints.dart'; import 'package:marco/helpers/services/storage/local_storage.dart'; import 'package:marco/helpers/utils/mixins/ui_mixin.dart'; +import 'package:marco/helpers/widgets/my_button.dart'; +import 'package:marco/helpers/widgets/my_text.dart'; import 'package:marco/images.dart'; class ForgotPasswordScreen extends StatefulWidget { @@ -22,10 +21,10 @@ class _ForgotPasswordScreenState extends State final ForgotPasswordController controller = Get.put(ForgotPasswordController()); - late AnimationController _controller; - late Animation _logoAnimation; + late final AnimationController _controller; + late final Animation _logoAnimation; - bool get _isBetaEnvironment => ApiEndpoints.baseUrl.contains("stage"); + final bool _isBetaEnvironment = ApiEndpoints.baseUrl.contains("stage"); bool _isLoading = false; @override @@ -64,29 +63,9 @@ class _ForgotPasswordScreenState extends State SafeArea( child: Center( child: Column( - mainAxisSize: MainAxisSize.min, children: [ const SizedBox(height: 24), - ScaleTransition( - scale: _logoAnimation, - child: Container( - width: 100, - height: 100, - decoration: BoxDecoration( - color: Colors.white, - shape: BoxShape.circle, - boxShadow: const [ - BoxShadow( - color: Colors.black12, - blurRadius: 10, - offset: Offset(0, 4), - ), - ], - ), - padding: const EdgeInsets.all(20), - child: Image.asset(Images.logoDark), - ), - ), + _buildAnimatedLogo(), const SizedBox(height: 8), Expanded( child: SingleChildScrollView( @@ -96,36 +75,10 @@ class _ForgotPasswordScreenState extends State child: Column( children: [ const SizedBox(height: 12), - MyText( - "Welcome to Marco", - fontSize: 24, - fontWeight: 800, - color: Colors.black87, - textAlign: TextAlign.center, - ), - const SizedBox(height: 10), - MyText( - "Streamline Project Management\nBoost Productivity with Automation.", - fontSize: 14, - color: Colors.black54, - textAlign: TextAlign.center, - ), + _buildWelcomeText(), if (_isBetaEnvironment) ...[ const SizedBox(height: 12), - Container( - padding: const EdgeInsets.symmetric( - horizontal: 10, vertical: 4), - decoration: BoxDecoration( - color: Colors.orangeAccent, - borderRadius: BorderRadius.circular(6), - ), - child: MyText( - 'BETA', - color: Colors.white, - fontWeight: 600, - fontSize: 12, - ), - ), + _buildBetaBadge(), ], const SizedBox(height: 36), _buildForgotCard(), @@ -143,6 +96,66 @@ class _ForgotPasswordScreenState extends State ); } + Widget _buildAnimatedLogo() { + return ScaleTransition( + scale: _logoAnimation, + child: Container( + width: 100, + height: 100, + padding: const EdgeInsets.all(20), + decoration: const BoxDecoration( + color: Colors.white, + shape: BoxShape.circle, + boxShadow: [ + BoxShadow( + color: Colors.black12, + blurRadius: 10, + offset: Offset(0, 4), + ), + ], + ), + child: Image.asset(Images.logoDark), + ), + ); + } + + Widget _buildWelcomeText() { + return Column( + children: [ + MyText( + "Welcome to Marco", + fontSize: 24, + fontWeight: 600, + color: Colors.black87, + textAlign: TextAlign.center, + ), + const SizedBox(height: 10), + MyText( + "Streamline Project Management\nBoost Productivity with Automation.", + fontSize: 14, + color: Colors.black54, + textAlign: TextAlign.center, + ), + ], + ); + } + + Widget _buildBetaBadge() { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), + decoration: BoxDecoration( + color: Colors.orangeAccent, + borderRadius: BorderRadius.circular(6), + ), + child: MyText( + 'BETA', + color: Colors.white, + fontWeight: 600, + fontSize: 12, + ), + ); + } + Widget _buildForgotCard() { return Container( padding: const EdgeInsets.all(24), @@ -165,7 +178,7 @@ class _ForgotPasswordScreenState extends State MyText( 'Forgot Password', fontSize: 20, - fontWeight: 700, + fontWeight: 600, color: Colors.black87, textAlign: TextAlign.center, ), @@ -177,70 +190,80 @@ class _ForgotPasswordScreenState extends State textAlign: TextAlign.center, ), const SizedBox(height: 30), - TextFormField( - validator: controller.basicValidator.getValidation('email'), - controller: controller.basicValidator.getController('email'), - keyboardType: TextInputType.emailAddress, - style: const TextStyle(fontSize: 14), - decoration: InputDecoration( - labelText: "Email Address", - labelStyle: const TextStyle(color: Colors.black54), - filled: true, - fillColor: Colors.grey.shade100, - prefixIcon: const Icon(LucideIcons.mail, size: 20), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - borderSide: BorderSide.none, - ), - contentPadding: - const EdgeInsets.symmetric(horizontal: 16, vertical: 16), - ), - ), + _buildEmailInput(), const SizedBox(height: 32), - MyButton.rounded( - onPressed: _isLoading ? null : _handleForgotPassword, - elevation: 2, - padding: const EdgeInsets.symmetric(horizontal: 40, vertical: 16), - borderRadiusAll: 10, - backgroundColor: _isLoading - ? contentTheme.brandRed.withOpacity(0.6) - : contentTheme.brandRed, - child: _isLoading - ? const SizedBox( - height: 20, - width: 20, - child: CircularProgressIndicator( - color: Colors.white, - strokeWidth: 2, - ), - ) - : MyText.bodyMedium( - 'Send Reset Link', - color: Colors.white, - fontWeight: 700, - fontSize: 16, - ), - ), + _buildResetButton(), const SizedBox(height: 20), - TextButton.icon( - onPressed: () async => await LocalStorage.logout(), - icon: const Icon(Icons.arrow_back, - size: 18, color: Colors.redAccent), - label: MyText.bodyMedium( - 'Back to Login', - color: contentTheme.brandRed, - fontWeight: 600, - fontSize: 14, - ), - ), + _buildBackButton(), ], ), ), ); } + + Widget _buildEmailInput() { + return TextFormField( + validator: controller.basicValidator.getValidation('email'), + controller: controller.basicValidator.getController('email'), + keyboardType: TextInputType.emailAddress, + style: const TextStyle(fontSize: 14), + decoration: InputDecoration( + labelText: "Email Address", + labelStyle: const TextStyle(color: Colors.black54), + filled: true, + fillColor: Colors.grey.shade100, + prefixIcon: const Icon(LucideIcons.mail, size: 20), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide.none, + ), + contentPadding: + const EdgeInsets.symmetric(horizontal: 16, vertical: 16), + ), + ); + } + + Widget _buildResetButton() { + return MyButton.rounded( + onPressed: _isLoading ? null : _handleForgotPassword, + elevation: 2, + padding: const EdgeInsets.symmetric(horizontal: 40, vertical: 16), + borderRadiusAll: 10, + backgroundColor: _isLoading + ? contentTheme.brandRed.withOpacity(0.6) + : contentTheme.brandRed, + child: _isLoading + ? const SizedBox( + height: 20, + width: 20, + child: CircularProgressIndicator( + color: Colors.white, + strokeWidth: 2, + ), + ) + : MyText.bodyMedium( + 'Send Reset Link', + color: Colors.white, + fontWeight: 600, + fontSize: 16, + ), + ); + } + + Widget _buildBackButton() { + return TextButton.icon( + onPressed: () async => await LocalStorage.logout(), + icon: const Icon(Icons.arrow_back, size: 18, color: Colors.redAccent), + label: MyText.bodyMedium( + 'Back to Login', + color: contentTheme.brandRed, + fontWeight: 600, + fontSize: 14, + ), + ); + } } -// Red background using dynamic brandRed class _RedWaveBackground extends StatelessWidget { final Color brandRed; const _RedWaveBackground({required this.brandRed}); diff --git a/lib/view/auth/login_option_screen.dart b/lib/view/auth/login_option_screen.dart index 2432577..c3364ed 100644 --- a/lib/view/auth/login_option_screen.dart +++ b/lib/view/auth/login_option_screen.dart @@ -26,9 +26,8 @@ class WelcomeScreen extends StatefulWidget { class _WelcomeScreenState extends State with SingleTickerProviderStateMixin, UIMixin { - late AnimationController _controller; - late Animation _logoAnimation; - + late final AnimationController _controller; + late final Animation _logoAnimation; bool get _isBetaEnvironment => ApiEndpoints.baseUrl.contains("stage"); @override @@ -54,42 +53,39 @@ class _WelcomeScreenState extends State void _showLoginDialog(BuildContext context, LoginOption option) { showDialog( context: context, - barrierDismissible: false, // Prevent dismiss on outside tap + barrierDismissible: false, builder: (_) => Dialog( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), insetPadding: const EdgeInsets.all(24), child: SingleChildScrollView( - child: Padding( - padding: const EdgeInsets.all(24), - child: ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 420), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - // Row with title and close button - Row( - children: [ - Expanded( - child: MyText( - option == LoginOption.email - ? "Login with Email" - : "Login with OTP", - fontSize: 20, - fontWeight: 700, - ), + padding: const EdgeInsets.all(24), + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 420), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + children: [ + Expanded( + child: MyText( + option == LoginOption.email + ? "Login with Email" + : "Login with OTP", + fontSize: 20, + fontWeight: 700, ), - IconButton( - icon: const Icon(Icons.close), - onPressed: () => Navigator.of(context).pop(), - ), - ], - ), - const SizedBox(height: 20), - option == LoginOption.email - ? EmailLoginForm() - : const OTPLoginScreen(), - ], - ), + ), + IconButton( + icon: const Icon(Icons.close), + onPressed: () => Navigator.of(context).pop(), + ), + ], + ), + const SizedBox(height: 20), + option == LoginOption.email + ? EmailLoginForm() + : const OTPLoginScreen(), + ], ), ), ), @@ -100,6 +96,7 @@ class _WelcomeScreenState extends State @override Widget build(BuildContext context) { final screenWidth = MediaQuery.of(context).size.width; + final isNarrow = screenWidth < 500; return Scaffold( body: Stack( @@ -110,72 +107,18 @@ class _WelcomeScreenState extends State child: SingleChildScrollView( padding: const EdgeInsets.all(24), child: ConstrainedBox( - constraints: BoxConstraints( - maxWidth: screenWidth < 500 ? double.infinity : 420, - ), + constraints: BoxConstraints(maxWidth: isNarrow ? double.infinity : 420), child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ - // Logo with circular background - ScaleTransition( - scale: _logoAnimation, - child: Container( - width: 100, - height: 100, - decoration: BoxDecoration( - color: Colors.white, - shape: BoxShape.circle, - boxShadow: [ - BoxShadow( - color: Colors.black12, - blurRadius: 10, - offset: Offset(0, 4), - ), - ], - ), - padding: const EdgeInsets.all(20), - child: Image.asset(Images.logoDark), - ), - ), - + _buildLogo(), const SizedBox(height: 24), - - // Welcome Text - MyText( - "Welcome to Marco", - fontSize: 26, - fontWeight: 800, - color: Colors.black87, - textAlign: TextAlign.center, - ), - const SizedBox(height: 10), - MyText( - "Streamline Project Management\nBoost Productivity with Automation.", - fontSize: 14, - color: Colors.black54, - textAlign: TextAlign.center, - ), - + _buildWelcomeText(), if (_isBetaEnvironment) ...[ const SizedBox(height: 12), - Container( - padding: const EdgeInsets.symmetric( - horizontal: 10, vertical: 4), - decoration: BoxDecoration( - color: Colors.orangeAccent, - borderRadius: BorderRadius.circular(6), - ), - child: MyText( - 'BETA', - color: Colors.white, - fontWeight: 600, - fontSize: 12, - ), - ), + _buildBetaBadge(), ], - const SizedBox(height: 36), - _buildActionButton( context, label: "Login with Username", @@ -196,7 +139,6 @@ class _WelcomeScreenState extends State icon: LucideIcons.phone_call, option: null, ), - const SizedBox(height: 36), MyText( 'App version 1.0.0', @@ -214,6 +156,60 @@ class _WelcomeScreenState extends State ); } + Widget _buildLogo() { + return ScaleTransition( + scale: _logoAnimation, + child: Container( + width: 100, + height: 100, + padding: const EdgeInsets.all(20), + decoration: const BoxDecoration( + color: Colors.white, + shape: BoxShape.circle, + boxShadow: [BoxShadow(color: Colors.black12, blurRadius: 10, offset: Offset(0, 4))], + ), + child: Image.asset(Images.logoDark), + ), + ); + } + + Widget _buildWelcomeText() { + return Column( + children: [ + MyText( + "Welcome to Marco", + fontSize: 26, + fontWeight: 800, + color: Colors.black87, + textAlign: TextAlign.center, + ), + const SizedBox(height: 10), + MyText( + "Streamline Project Management\nBoost Productivity with Automation.", + fontSize: 14, + color: Colors.black54, + textAlign: TextAlign.center, + ), + ], + ); + } + + Widget _buildBetaBadge() { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), + decoration: BoxDecoration( + color: Colors.orangeAccent, + borderRadius: BorderRadius.circular(6), + ), + child: MyText( + 'BETA', + color: Colors.white, + fontWeight: 600, + fontSize: 12, + ), + ); + } + Widget _buildActionButton( BuildContext context, { required String label, @@ -236,9 +232,7 @@ class _WelcomeScreenState extends State style: ElevatedButton.styleFrom( backgroundColor: contentTheme.brandRed, foregroundColor: Colors.white, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(14), - ), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)), elevation: 4, shadowColor: Colors.black26, ), @@ -254,7 +248,7 @@ class _WelcomeScreenState extends State } } -/// Custom red wave background shifted lower to reduce red area at top +// Red wave background painter class _RedWaveBackground extends StatelessWidget { final Color brandRed; const _RedWaveBackground({required this.brandRed}); @@ -270,7 +264,6 @@ class _RedWaveBackground extends StatelessWidget { class _WavePainter extends CustomPainter { final Color brandRed; - _WavePainter(this.brandRed); @override @@ -284,18 +277,8 @@ class _WavePainter extends CustomPainter { final path1 = Path() ..moveTo(0, size.height * 0.2) - ..quadraticBezierTo( - size.width * 0.25, - size.height * 0.05, - size.width * 0.5, - size.height * 0.15, - ) - ..quadraticBezierTo( - size.width * 0.75, - size.height * 0.25, - size.width, - size.height * 0.1, - ) + ..quadraticBezierTo(size.width * 0.25, size.height * 0.05, size.width * 0.5, size.height * 0.15) + ..quadraticBezierTo(size.width * 0.75, size.height * 0.25, size.width, size.height * 0.1) ..lineTo(size.width, 0) ..lineTo(0, 0) ..close(); @@ -303,15 +286,9 @@ class _WavePainter extends CustomPainter { canvas.drawPath(path1, paint1); final paint2 = Paint()..color = Colors.redAccent.withOpacity(0.15); - final path2 = Path() ..moveTo(0, size.height * 0.25) - ..quadraticBezierTo( - size.width * 0.4, - size.height * 0.1, - size.width, - size.height * 0.2, - ) + ..quadraticBezierTo(size.width * 0.4, size.height * 0.1, size.width, size.height * 0.2) ..lineTo(size.width, 0) ..lineTo(0, 0) ..close();