feat: Update authentication flow and UI enhancements for sign-in and forgot password screens
This commit is contained in:
parent
0da8d5621c
commit
1967fdb6b8
|
@ -116,7 +116,7 @@ class AuthenticationRepository extends GetxController {
|
|||
}
|
||||
} catch (e) {
|
||||
Logger().e('Error in screenRedirect: $e');
|
||||
_navigateToRoute(AppRoutes.checkLocation);
|
||||
_navigateToRoute(AppRoutes.signIn);
|
||||
} finally {
|
||||
_isRedirecting = false;
|
||||
Logger().d('Screen redirect completed');
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:sigap/src/features/auth/data/repositories/authentication_repository.dart';
|
||||
import 'package:sigap/src/shared/widgets/state_screeen/state_screen.dart';
|
||||
import 'package:sigap/src/utils/constants/image_strings.dart';
|
||||
import 'package:sigap/src/utils/popups/loaders.dart';
|
||||
import 'package:sigap/src/utils/validators/validation.dart';
|
||||
|
||||
|
@ -45,9 +47,6 @@ class ForgotPasswordController extends GetxController {
|
|||
try {
|
||||
isLoading.value = true;
|
||||
|
||||
// Simulate API call
|
||||
await Future.delayed(const Duration(seconds: 2));
|
||||
|
||||
await AuthenticationRepository.instance.sendResetPasswordForEmail(
|
||||
emailController.text,
|
||||
);
|
||||
|
@ -58,6 +57,19 @@ class ForgotPasswordController extends GetxController {
|
|||
title: 'Success',
|
||||
message: 'Reset password email sent successfully.',
|
||||
);
|
||||
|
||||
Get.off(
|
||||
() => StateScreen(
|
||||
title: 'Check Your Email',
|
||||
subtitle: 'Please check your email for the reset link.',
|
||||
image: TImages.customerSupport,
|
||||
isSvg: true,
|
||||
primaryButtonTitle: 'Go Back',
|
||||
showButton: true,
|
||||
|
||||
onPressed: () => Get.back(),
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
Get.snackbar(
|
||||
'Error',
|
||||
|
|
|
@ -7,44 +7,44 @@ import 'package:sigap/src/utils/popups/loaders.dart';
|
|||
|
||||
class SignInController extends GetxController {
|
||||
static SignInController get instance => Get.find();
|
||||
|
||||
|
||||
final _logger = Logger();
|
||||
final _authRepo = Get.find<AuthenticationRepository>();
|
||||
|
||||
|
||||
// Form controllers
|
||||
final email = TextEditingController();
|
||||
final password = TextEditingController();
|
||||
|
||||
|
||||
// Form error messages
|
||||
final RxString emailError = RxString('');
|
||||
final RxString passwordError = RxString('');
|
||||
|
||||
|
||||
// States
|
||||
final RxBool isLoading = RxBool(false);
|
||||
final RxBool isPasswordVisible = RxBool(false);
|
||||
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
email.dispose();
|
||||
password.dispose();
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
|
||||
// Toggle password visibility
|
||||
void togglePasswordVisibility() {
|
||||
isPasswordVisible.value = !isPasswordVisible.value;
|
||||
}
|
||||
|
||||
|
||||
// Navigate to forgot password screen
|
||||
void goToForgotPassword() {
|
||||
Get.toNamed(AppRoutes.forgotPassword);
|
||||
}
|
||||
|
||||
|
||||
// Navigate to sign up screen
|
||||
void goToSignUp() {
|
||||
Get.toNamed(AppRoutes.roleSelection);
|
||||
}
|
||||
|
||||
|
||||
// Clear error messages
|
||||
void clearErrors() {
|
||||
emailError.value = '';
|
||||
|
@ -55,7 +55,7 @@ class SignInController extends GetxController {
|
|||
Future<void> signIn(GlobalKey<FormState> formKey) async {
|
||||
// Clear previous errors
|
||||
clearErrors();
|
||||
|
||||
|
||||
// Validate form
|
||||
final isValid = formKey.currentState?.validate() ?? false;
|
||||
if (!isValid) return;
|
||||
|
@ -64,20 +64,19 @@ class SignInController extends GetxController {
|
|||
isLoading.value = true;
|
||||
|
||||
// Attempt to sign in
|
||||
final signInResult = await _authRepo.loginWithEmailPassword(
|
||||
await _authRepo.loginWithEmailPassword(
|
||||
email: email.text.trim(),
|
||||
password: password.text.trim(),
|
||||
);
|
||||
|
||||
|
||||
// Handle result
|
||||
_logger.i('Sign in successful: $signInResult');
|
||||
|
||||
// _logger.i('Sign in successful: $signInResult');
|
||||
|
||||
// Redirect based on user's profile status
|
||||
_authRepo.screenRedirect();
|
||||
|
||||
} catch (e) {
|
||||
isLoading.value = false;
|
||||
|
||||
|
||||
// Handle specific errors
|
||||
if (e.toString().contains('user-not-found')) {
|
||||
emailError.value = 'No user found with this email';
|
||||
|
@ -89,36 +88,110 @@ class SignInController extends GetxController {
|
|||
// Show general error
|
||||
TLoaders.errorSnackBar(title: 'Sign In Failed', message: e.toString());
|
||||
}
|
||||
|
||||
|
||||
_logger.e('Sign in error: $e');
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Sign in with Google
|
||||
Future<void> googleSignIn() async {
|
||||
try {
|
||||
isLoading.value = true;
|
||||
|
||||
|
||||
// Attempt to sign in with Google
|
||||
await _authRepo.signInWithGoogle();
|
||||
|
||||
|
||||
// Redirect based on user's profile status
|
||||
_authRepo.screenRedirect();
|
||||
|
||||
} catch (e) {
|
||||
isLoading.value = false;
|
||||
|
||||
|
||||
// Show error
|
||||
TLoaders.errorSnackBar(
|
||||
title: 'Google Sign In Failed',
|
||||
message: e.toString(),
|
||||
);
|
||||
|
||||
|
||||
_logger.e('Google sign in error: $e');
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Sign in with github
|
||||
Future<void> githubSignIn() async {
|
||||
try {
|
||||
isLoading.value = true;
|
||||
|
||||
// Attempt to sign in with GitHub
|
||||
await _authRepo.signInWithGithub();
|
||||
|
||||
// Redirect based on user's profile status
|
||||
_authRepo.screenRedirect();
|
||||
} catch (e) {
|
||||
isLoading.value = false;
|
||||
|
||||
// Show error
|
||||
TLoaders.errorSnackBar(
|
||||
title: 'GitHub Sign In Failed',
|
||||
message: e.toString(),
|
||||
);
|
||||
|
||||
_logger.e('GitHub sign in error: $e');
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Sign in with Facebook
|
||||
Future<void> facebookSignIn() async {
|
||||
try {
|
||||
isLoading.value = true;
|
||||
|
||||
// Attempt to sign in with Facebook
|
||||
await _authRepo.signInWithFacebook();
|
||||
|
||||
// Redirect based on user's profile status
|
||||
_authRepo.screenRedirect();
|
||||
} catch (e) {
|
||||
isLoading.value = false;
|
||||
|
||||
// Show error
|
||||
TLoaders.errorSnackBar(
|
||||
title: 'Facebook Sign In Failed',
|
||||
message: e.toString(),
|
||||
);
|
||||
|
||||
_logger.e('Facebook sign in error: $e');
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Sign in with Apple
|
||||
Future<void> appleSignIn() async {
|
||||
try {
|
||||
isLoading.value = true;
|
||||
|
||||
// Attempt to sign in with Apple
|
||||
await _authRepo.signInWithApple();
|
||||
|
||||
// Redirect based on user's profile status
|
||||
_authRepo.screenRedirect();
|
||||
} catch (e) {
|
||||
isLoading.value = false;
|
||||
|
||||
// Show error
|
||||
TLoaders.errorSnackBar(
|
||||
title: 'Apple Sign In Failed',
|
||||
message: e.toString(),
|
||||
);
|
||||
|
||||
_logger.e('Apple sign in error: $e');
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:sigap/src/features/auth/presentasion/controllers/forgot-password/forgot_password_controller.dart';
|
||||
import 'package:sigap/src/features/auth/presentasion/widgets/auth_button.dart';
|
||||
import 'package:sigap/src/features/auth/presentasion/widgets/auth_header.dart';
|
||||
import 'package:sigap/src/shared/widgets/text/custom_text_field.dart';
|
||||
import 'package:sigap/src/utils/constants/colors.dart';
|
||||
import 'package:sigap/src/utils/constants/image_strings.dart';
|
||||
import 'package:sigap/src/utils/constants/sizes.dart';
|
||||
import 'package:sigap/src/utils/helpers/helper_functions.dart';
|
||||
|
||||
class ForgotPasswordScreen extends StatelessWidget {
|
||||
const ForgotPasswordScreen({super.key});
|
||||
|
@ -15,6 +19,8 @@ class ForgotPasswordScreen extends StatelessWidget {
|
|||
// Get the controller
|
||||
final controller = Get.find<ForgotPasswordController>();
|
||||
|
||||
final isDarkMode = THelperFunctions.isDarkMode(context);
|
||||
|
||||
// Set system overlay style
|
||||
SystemChrome.setSystemUIOverlayStyle(
|
||||
const SystemUiOverlayStyle(
|
||||
|
@ -24,26 +30,29 @@ class ForgotPasswordScreen extends StatelessWidget {
|
|||
);
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: TColors.light,
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.transparent,
|
||||
elevation: 0,
|
||||
leading: IconButton(
|
||||
icon: Icon(Icons.arrow_back, color: TColors.textPrimary),
|
||||
icon: Icon(
|
||||
Icons.arrow_back,
|
||||
color: isDarkMode ? TColors.accent : TColors.primary,
|
||||
),
|
||||
onPressed: controller.goBack,
|
||||
),
|
||||
),
|
||||
body: SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(24.0),
|
||||
child: Obx(
|
||||
() => Form(
|
||||
key: controller.formKey,
|
||||
child:
|
||||
controller.isEmailSent.value
|
||||
? _buildSuccessView(controller)
|
||||
: _buildFormView(controller),
|
||||
child: Center(
|
||||
child: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(TSizes.defaultSpace),
|
||||
child: Obx(
|
||||
() => Form(
|
||||
key: controller.formKey,
|
||||
child:
|
||||
controller.isEmailSent.value
|
||||
? _buildSuccessView(controller)
|
||||
: _buildFormView(controller, context),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -52,16 +61,28 @@ class ForgotPasswordScreen extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
|
||||
Widget _buildFormView(ForgotPasswordController controller) {
|
||||
Widget _buildFormView(
|
||||
ForgotPasswordController controller,
|
||||
BuildContext? context,
|
||||
) {
|
||||
final isDark = THelperFunctions.isDarkMode(context!);
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
// Header
|
||||
// Logo centered
|
||||
SvgPicture.asset(TImages.lightAppBgLogo, height: 100, width: 100),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Header centered
|
||||
const AuthHeader(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
title: 'Forgot Password',
|
||||
subtitle: 'Enter your email to reset your password',
|
||||
),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Email field
|
||||
Obx(
|
||||
() => CustomTextField(
|
||||
|
@ -70,6 +91,12 @@ class ForgotPasswordScreen extends StatelessWidget {
|
|||
validator: controller.validateEmail,
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
errorText: controller.emailError.value,
|
||||
hintText: 'enter your email',
|
||||
prefixIcon: Icon(
|
||||
Icons.email_outlined,
|
||||
size: 20,
|
||||
color: isDark ? TColors.accent : TColors.primary,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
|
@ -89,8 +116,11 @@ class ForgotPasswordScreen extends StatelessWidget {
|
|||
|
||||
Widget _buildSuccessView(ForgotPasswordController controller) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
// Logo centered
|
||||
SvgPicture.asset(TImages.lightAppBgLogo, height: 100, width: 100),
|
||||
const SizedBox(height: 32),
|
||||
|
||||
// Success icon
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:flutter_tabler_icons/flutter_tabler_icons.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:sigap/src/features/auth/presentasion/controllers/signin/signin_controller.dart';
|
||||
|
@ -8,6 +9,7 @@ import 'package:sigap/src/features/auth/presentasion/widgets/auth_header.dart';
|
|||
import 'package:sigap/src/features/auth/presentasion/widgets/password_field.dart';
|
||||
import 'package:sigap/src/features/auth/presentasion/widgets/social_button.dart';
|
||||
import 'package:sigap/src/shared/widgets/text/custom_text_field.dart';
|
||||
import 'package:sigap/src/utils/constants/image_strings.dart'; // Added for logo image
|
||||
import 'package:sigap/src/utils/helpers/helper_functions.dart';
|
||||
import 'package:sigap/src/utils/validators/validation.dart';
|
||||
|
||||
|
@ -26,116 +28,173 @@ class SignInScreen extends StatelessWidget {
|
|||
final isDarkMode = THelperFunctions.isDarkMode(context);
|
||||
|
||||
return Scaffold(
|
||||
// Use dynamic background color from theme
|
||||
body: SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(24.0),
|
||||
child: Form(
|
||||
key: formKey,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Header - pass isDarkMode to AuthHeader if needed
|
||||
const AuthHeader(
|
||||
title: 'Welcome Back',
|
||||
subtitle: 'Sign in to your account to continue',
|
||||
),
|
||||
|
||||
// Email field
|
||||
Obx(
|
||||
() => CustomTextField(
|
||||
label: 'Email',
|
||||
controller: controller.email,
|
||||
validator: TValidators.validateEmail,
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
errorText: controller.emailError.value,
|
||||
textInputAction: TextInputAction.next,
|
||||
child: Center(
|
||||
child: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(24.0),
|
||||
child: Form(
|
||||
key: formKey,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
// Logo centered at the top
|
||||
SvgPicture.asset(
|
||||
TImages.lightAppBgLogo,
|
||||
height: 100,
|
||||
width: 100,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Password field
|
||||
Obx(
|
||||
() => PasswordField(
|
||||
label: 'Password',
|
||||
controller: controller.password,
|
||||
validator: TValidators.validatePassword,
|
||||
isVisible: controller.isPasswordVisible,
|
||||
errorText: controller.passwordError.value,
|
||||
onToggleVisibility: controller.togglePasswordVisibility,
|
||||
// Header centered
|
||||
const AuthHeader(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
title: 'Welcome Back',
|
||||
subtitle: 'Sign in to your account to continue',
|
||||
),
|
||||
),
|
||||
|
||||
// Forgot password
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: TextButton(
|
||||
onPressed: controller.goToForgotPassword,
|
||||
child: Text(
|
||||
'Forgot Password?',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).primaryColor,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Email field with icon
|
||||
Obx(
|
||||
() => CustomTextField(
|
||||
label: 'Email',
|
||||
controller: controller.email,
|
||||
validator: TValidators.validateEmail,
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
errorText: controller.emailError.value,
|
||||
textInputAction: TextInputAction.next,
|
||||
prefixIcon: const Icon(TablerIcons.mail, size: 20),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Sign in button
|
||||
Obx(
|
||||
() => AuthButton(
|
||||
text: 'Sign In',
|
||||
onPressed: () => controller.signIn(formKey),
|
||||
isLoading: controller.isLoading.value,
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Or divider
|
||||
const AuthDivider(text: 'OR'),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Social sign in buttons
|
||||
SocialButton(
|
||||
text: 'Continue with Google',
|
||||
icon: Icon(
|
||||
TablerIcons.brand_google,
|
||||
color: Colors.white,
|
||||
size: 20,
|
||||
),
|
||||
onPressed: () => controller.googleSignIn(),
|
||||
),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Don't have an account
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'Don\'t have an account?',
|
||||
// Use theme color for text
|
||||
style: TextStyle(color: Theme.of(context).hintColor),
|
||||
// Password field with icon
|
||||
Obx(
|
||||
() => PasswordField(
|
||||
label: 'Password',
|
||||
controller: controller.password,
|
||||
validator: TValidators.validatePassword,
|
||||
isVisible: controller.isPasswordVisible,
|
||||
errorText: controller.passwordError.value,
|
||||
onToggleVisibility: controller.togglePasswordVisibility,
|
||||
prefixIcon: const Icon(TablerIcons.lock, size: 20),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: controller.goToSignUp,
|
||||
),
|
||||
|
||||
// Forgot password
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: TextButton(
|
||||
onPressed: controller.goToForgotPassword,
|
||||
child: Text(
|
||||
'Sign Up',
|
||||
'Forgot Password?',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).primaryColor,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Sign in button
|
||||
Obx(
|
||||
() => AuthButton(
|
||||
text: 'Sign In',
|
||||
onPressed: () => controller.signIn(formKey),
|
||||
isLoading: controller.isLoading.value,
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Or divider
|
||||
const AuthDivider(text: 'OR'),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Social sign in buttons - Row of social icons
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
// Google Sign In
|
||||
Expanded(
|
||||
child: SocialButton(
|
||||
shapeType: SocialButtonShapeType.circular,
|
||||
icon: const Icon(
|
||||
TablerIcons.brand_google,
|
||||
color: Colors.white,
|
||||
size: 20,
|
||||
),
|
||||
backgroundColor: const Color(0xFFDB4437),
|
||||
foregroundColor: Colors.white,
|
||||
onPressed: () => controller.googleSignIn(),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(width: 0),
|
||||
|
||||
// GitHub Sign In
|
||||
Expanded(
|
||||
child: SocialButton(
|
||||
shapeType: SocialButtonShapeType.circular,
|
||||
icon: const Icon(
|
||||
TablerIcons.brand_apple,
|
||||
color: Colors.white,
|
||||
size: 20,
|
||||
),
|
||||
backgroundColor: const Color(0xFF333333),
|
||||
foregroundColor: Colors.white,
|
||||
onPressed: () => controller.appleSignIn(),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(width: 0),
|
||||
|
||||
// Facebook Sign In
|
||||
Expanded(
|
||||
child: SocialButton(
|
||||
shapeType: SocialButtonShapeType.circular,
|
||||
icon: const Icon(
|
||||
TablerIcons.brand_facebook,
|
||||
color: Colors.white,
|
||||
size: 20,
|
||||
),
|
||||
backgroundColor: const Color(0xFF1877F2),
|
||||
foregroundColor: Colors.white,
|
||||
onPressed: () => controller.facebookSignIn(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Don't have an account
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'Don\'t have an account?',
|
||||
// Use theme color for text
|
||||
style: TextStyle(color: Theme.of(context).hintColor),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: controller.goToSignUp,
|
||||
child: Text(
|
||||
'Sign Up',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).primaryColor,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:sigap/src/utils/constants/colors.dart';
|
||||
import 'package:sigap/src/utils/constants/sizes.dart';
|
||||
import 'package:sigap/src/utils/helpers/helper_functions.dart';
|
||||
|
||||
class AuthButton extends StatelessWidget {
|
||||
final String text;
|
||||
|
@ -19,6 +21,7 @@ class AuthButton extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isDark = THelperFunctions.isDarkMode(context);
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
height: 55,
|
||||
|
@ -26,7 +29,8 @@ class AuthButton extends StatelessWidget {
|
|||
onPressed: isLoading ? null : onPressed,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: backgroundColor ?? Theme.of(context).primaryColor,
|
||||
foregroundColor: textColor ?? Theme.of(context).colorScheme.onPrimary,
|
||||
foregroundColor:
|
||||
textColor ?? (isDark ? TColors.primary : TColors.accent),
|
||||
elevation: 1,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(TSizes.buttonRadius),
|
||||
|
|
|
@ -5,15 +5,21 @@ import 'package:sigap/src/utils/helpers/helper_functions.dart';
|
|||
class AuthHeader extends StatelessWidget {
|
||||
final String title;
|
||||
final String subtitle;
|
||||
final CrossAxisAlignment crossAxisAlignment;
|
||||
|
||||
const AuthHeader({super.key, required this.title, required this.subtitle});
|
||||
const AuthHeader({
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.subtitle,
|
||||
this.crossAxisAlignment = CrossAxisAlignment.start,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final dark = THelperFunctions.isDarkMode(context);
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
crossAxisAlignment: crossAxisAlignment,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:sigap/src/utils/constants/sizes.dart';
|
||||
|
||||
enum SocialButtonShapeType { defaultShape, rounded, circular }
|
||||
|
||||
class SocialButton extends StatelessWidget {
|
||||
final String text;
|
||||
final String? text;
|
||||
final Icon? icon;
|
||||
final String? iconImage;
|
||||
final double? iconImageWidth;
|
||||
|
@ -12,10 +14,11 @@ class SocialButton extends StatelessWidget {
|
|||
final Color? foregroundColor;
|
||||
final Color? borderColor;
|
||||
final bool isVisible;
|
||||
final SocialButtonShapeType shapeType;
|
||||
|
||||
const SocialButton({
|
||||
super.key,
|
||||
required this.text,
|
||||
this.text,
|
||||
this.icon,
|
||||
this.iconImage,
|
||||
this.iconImageWidth,
|
||||
|
@ -25,53 +28,81 @@ class SocialButton extends StatelessWidget {
|
|||
this.foregroundColor,
|
||||
this.borderColor,
|
||||
this.isVisible = true,
|
||||
this.shapeType = SocialButtonShapeType.defaultShape,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (!isVisible) return const SizedBox.shrink();
|
||||
|
||||
OutlinedBorder shape;
|
||||
EdgeInsetsGeometry padding;
|
||||
|
||||
switch (shapeType) {
|
||||
case SocialButtonShapeType.rounded:
|
||||
shape = RoundedRectangleBorder(borderRadius: BorderRadius.circular(30));
|
||||
padding = const EdgeInsets.symmetric(
|
||||
vertical: TSizes.md,
|
||||
horizontal: TSizes.md,
|
||||
);
|
||||
break;
|
||||
case SocialButtonShapeType.circular:
|
||||
shape = const CircleBorder();
|
||||
padding = const EdgeInsets.all(0);
|
||||
break;
|
||||
default:
|
||||
shape = RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(TSizes.buttonRadius),
|
||||
);
|
||||
padding = const EdgeInsets.symmetric(
|
||||
vertical: TSizes.md,
|
||||
horizontal: TSizes.md,
|
||||
);
|
||||
}
|
||||
|
||||
Widget iconWidget =
|
||||
iconImage == null
|
||||
? icon ?? const SizedBox.shrink()
|
||||
: Image.asset(
|
||||
iconImage!,
|
||||
width: iconImageWidth,
|
||||
height: iconImageHeight,
|
||||
);
|
||||
|
||||
Widget child;
|
||||
if (text == null || text!.isEmpty) {
|
||||
child = iconWidget;
|
||||
} else {
|
||||
child = Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
SizedBox(height: 24, width: 24, child: iconWidget),
|
||||
const SizedBox(width: TSizes.md),
|
||||
Text(
|
||||
text!,
|
||||
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
|
||||
color: foregroundColor ?? Colors.black,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
width: shapeType == SocialButtonShapeType.circular ? 48 : double.infinity,
|
||||
height: shapeType == SocialButtonShapeType.circular ? 48 : null,
|
||||
child: OutlinedButton(
|
||||
onPressed: onPressed,
|
||||
style: OutlinedButton.styleFrom(
|
||||
backgroundColor: backgroundColor ?? Colors.white,
|
||||
foregroundColor: foregroundColor ?? Colors.black,
|
||||
side: BorderSide(color: borderColor ?? Colors.grey.shade300),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(TSizes.buttonRadius),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: TSizes.md,
|
||||
horizontal: TSizes.md,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 24,
|
||||
width: 24,
|
||||
child:
|
||||
iconImage == null
|
||||
? icon
|
||||
: Image.asset(
|
||||
iconImage!,
|
||||
width: iconImageWidth,
|
||||
height: iconImageHeight,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: TSizes.md),
|
||||
Text(
|
||||
text,
|
||||
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
|
||||
color: foregroundColor ?? Colors.black,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
],
|
||||
shape: shape,
|
||||
padding: padding,
|
||||
),
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'dart:async';
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:get_storage/get_storage.dart';
|
||||
import 'package:sigap/src/cores/services/location_service.dart';
|
||||
import 'package:sigap/src/features/onboarding/presentasion/pages/role-selection/role_signup_pageview.dart';
|
||||
import 'package:sigap/src/utils/constants/image_strings.dart';
|
||||
|
@ -11,6 +12,8 @@ class CheckLocationController extends GetxController
|
|||
final VoidCallback? onSuccess;
|
||||
final LocationService _locationService = LocationService.instance;
|
||||
|
||||
final storage = GetStorage();
|
||||
|
||||
// Reactive variables
|
||||
final RxInt currentPage = 0.obs;
|
||||
final RxBool isLoading = false.obs;
|
||||
|
@ -146,6 +149,8 @@ class CheckLocationController extends GetxController
|
|||
// Navigate to communication slide before zooming
|
||||
_navigateToCommunicationSlide();
|
||||
|
||||
storage.write('isFirstTime', false);
|
||||
|
||||
// Give time to see the success message before animating
|
||||
await Future.delayed(Duration(milliseconds: 800));
|
||||
animController
|
||||
|
|
|
@ -81,16 +81,16 @@ class OnboardingController extends GetxController
|
|||
}
|
||||
}
|
||||
|
||||
// Method to skip to welcome screen
|
||||
void skipToWelcomeScreen() {
|
||||
// Method to skip to check location screen
|
||||
void skipToCheckLocation() {
|
||||
skipOnboarding();
|
||||
}
|
||||
|
||||
// Method to navigate to welcome screen
|
||||
// Method to navigate to check location screen
|
||||
void skipOnboarding() {
|
||||
// Mark onboarding as completed in storage
|
||||
storage.write('isFirstTime', true);
|
||||
Get.offAllNamed(AppRoutes.welcome);
|
||||
storage.write('isFirstTime', false);
|
||||
Get.offAllNamed(AppRoutes.checkLocation);
|
||||
}
|
||||
|
||||
// Method to check location validity and proceed with auth flow
|
||||
|
@ -136,7 +136,7 @@ class OnboardingController extends GetxController
|
|||
}
|
||||
|
||||
void goToSignIn() {
|
||||
storage.write('isFirstTime', true);
|
||||
storage.write('isFirstTime', false);
|
||||
|
||||
Get.offAllNamed(AppRoutes.signIn);
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ class TLoaders {
|
|||
isDismissible: true,
|
||||
shouldIconPulse: true,
|
||||
colorText: Colors.white,
|
||||
backgroundColor: TColors.primary,
|
||||
backgroundColor: TColors.success,
|
||||
snackPosition: SnackPosition.TOP,
|
||||
duration: Duration(seconds: duration),
|
||||
margin: const EdgeInsets.all(10),
|
||||
|
|
Loading…
Reference in New Issue