import 'package:flutter/material.dart'; import 'package:flutter_lucide/flutter_lucide.dart'; import 'package:get/get.dart'; import 'package:marco/controller/auth/login_controller.dart'; import 'package:marco/helpers/theme/app_theme.dart'; import 'package:marco/helpers/utils/mixins/ui_mixin.dart'; import 'package:marco/helpers/widgets/my_button.dart'; import 'package:marco/helpers/widgets/my_spacing.dart'; import 'package:marco/helpers/widgets/my_text.dart'; import 'package:marco/helpers/widgets/my_text_style.dart'; import 'package:marco/view/layouts/auth_layout.dart'; import 'package:marco/images.dart'; import 'package:marco/view/auth/request_demo_bottom_sheet.dart'; import 'package:marco/helpers/services/api_endpoints.dart'; class LoginScreen extends StatefulWidget { const LoginScreen({super.key}); @override State createState() => _LoginScreenState(); } class _LoginScreenState extends State with UIMixin { late final LoginController controller; bool get isBetaEnvironment => ApiEndpoints.baseUrl.contains("stage"); @override void initState() { controller = Get.put(LoginController(), tag: 'login_controller'); super.initState(); } @override Widget build(BuildContext context) { return AuthLayout( child: GetBuilder( tag: 'login_controller', builder: (_) { return Obx(() { if (controller.isLoading.value) { return const Center(child: CircularProgressIndicator()); } return Form( key: controller.basicValidator.formKey, child: SingleChildScrollView( padding: MySpacing.xy(2, 40), child: Container( width: double.infinity, padding: MySpacing.all(24), decoration: BoxDecoration( color: theme.colorScheme.primary.withOpacity(0.02), borderRadius: BorderRadius.circular(5), border: Border.all( color: contentTheme.primary.withOpacity(0.5), ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ /// Logo Center( child: Image.asset( Images.logoDark, height: 120, fit: BoxFit.contain, ), ), MySpacing.height(20), /// Welcome + Beta Center( child: Column( children: [ if (isBetaEnvironment) Container( padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 4), margin: const EdgeInsets.only(bottom: 12), decoration: BoxDecoration( color: Colors.blueAccent, borderRadius: BorderRadius.circular(5), ), child: Text( 'BETA', style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 12, ), ), ), MyText.bodyLarge("Welcome Back!", fontWeight: 600), ], ), ), MySpacing.height(4), Center( child: MyText.bodySmall("Please sign in to continue."), ), MySpacing.height(20), /// Email MyText.bodySmall("Email Address", fontWeight: 600), MySpacing.height(8), _buildInputField( controller.basicValidator.getController('username')!, controller.basicValidator.getValidation('username'), hintText: "Enter your email", icon: LucideIcons.mail, keyboardType: TextInputType.emailAddress, ), MySpacing.height(16), /// Password MyText.bodySmall("Password", fontWeight: 600), MySpacing.height(8), Obx(() { return _buildInputField( controller.basicValidator.getController('password')!, controller.basicValidator.getValidation('password'), hintText: "Enter your password", icon: LucideIcons.lock, obscureText: !controller.showPassword.value, suffix: IconButton( icon: Icon( controller.showPassword.value ? LucideIcons.eye : LucideIcons.eye_off, size: 18, ), onPressed: controller.onChangeShowPassword, ), ); }), MySpacing.height(16), /// Remember me + Forgot password Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Obx(() { return InkWell( onTap: () => controller.onChangeCheckBox( !controller.isChecked.value), child: Row( children: [ Checkbox( value: controller.isChecked.value, onChanged: controller.onChangeCheckBox, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(5), ), fillColor: MaterialStateProperty .resolveWith( (Set states) { if (states .contains(WidgetState.selected)) { return Colors.blueAccent; } return Colors.white; }, ), checkColor: contentTheme.onPrimary, visualDensity: getCompactDensity, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ), MySpacing.width(8), MyText.bodySmall("Remember Me"), ], ), ); }), MyButton.text( onPressed: controller.goToForgotPassword, elevation: 0, padding: MySpacing.xy(8, 0), splashColor: contentTheme.secondary.withAlpha(36), child: MyText.bodySmall( 'Forgot password?', fontWeight: 600, color: contentTheme.secondary, ), ), ], ), MySpacing.height(28), /// Login button Center( child: MyButton.rounded( onPressed: controller.onLogin, elevation: 2, padding: MySpacing.xy(24, 16), borderRadiusAll: 5, backgroundColor:contentTheme.brandGreen, child: MyText.labelMedium( 'Login', fontWeight: 600, color: contentTheme.onPrimary, ), ), ), MySpacing.height(16), /// Request demo Center( child: MyButton.text( onPressed: () { OrganizationFormBottomSheet.show(context); }, elevation: 0, padding: MySpacing.xy(12, 8), splashColor: contentTheme.secondary.withAlpha(30), child: MyText.bodySmall( "Request a Demo", color: contentTheme.secondary, fontWeight: 600, ), ), ), ], ), ), ), ); }); }, ), ); } Widget _buildInputField( TextEditingController controller, FormFieldValidator? validator, { required String hintText, required IconData icon, TextInputType keyboardType = TextInputType.text, bool obscureText = false, Widget? suffix, }) { return Material( elevation: 2, shadowColor: contentTheme.secondary.withAlpha(30), borderRadius: BorderRadius.circular(5), child: TextFormField( controller: controller, validator: validator, obscureText: obscureText, keyboardType: keyboardType, style: MyTextStyle.labelMedium(), decoration: InputDecoration( hintText: hintText, hintStyle: MyTextStyle.bodySmall(xMuted: true), filled: true, fillColor: theme.cardColor, border: OutlineInputBorder( borderRadius: BorderRadius.circular(5), borderSide: BorderSide.none, ), prefixIcon: Icon(icon, size: 18), suffixIcon: suffix, contentPadding: MySpacing.xy(12, 16), ), ), ); } }