feat: Update authentication flow and UI enhancements for sign-in and forgot password screens

This commit is contained in:
vergiLgood1 2025-05-27 04:34:04 +07:00
parent 0da8d5621c
commit 1967fdb6b8
11 changed files with 402 additions and 182 deletions

View File

@ -116,7 +116,7 @@ class AuthenticationRepository extends GetxController {
} }
} catch (e) { } catch (e) {
Logger().e('Error in screenRedirect: $e'); Logger().e('Error in screenRedirect: $e');
_navigateToRoute(AppRoutes.checkLocation); _navigateToRoute(AppRoutes.signIn);
} finally { } finally {
_isRedirecting = false; _isRedirecting = false;
Logger().d('Screen redirect completed'); Logger().d('Screen redirect completed');

View File

@ -1,6 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:sigap/src/features/auth/data/repositories/authentication_repository.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/popups/loaders.dart';
import 'package:sigap/src/utils/validators/validation.dart'; import 'package:sigap/src/utils/validators/validation.dart';
@ -45,9 +47,6 @@ class ForgotPasswordController extends GetxController {
try { try {
isLoading.value = true; isLoading.value = true;
// Simulate API call
await Future.delayed(const Duration(seconds: 2));
await AuthenticationRepository.instance.sendResetPasswordForEmail( await AuthenticationRepository.instance.sendResetPasswordForEmail(
emailController.text, emailController.text,
); );
@ -58,6 +57,19 @@ class ForgotPasswordController extends GetxController {
title: 'Success', title: 'Success',
message: 'Reset password email sent successfully.', 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) { } catch (e) {
Get.snackbar( Get.snackbar(
'Error', 'Error',

View File

@ -7,44 +7,44 @@ import 'package:sigap/src/utils/popups/loaders.dart';
class SignInController extends GetxController { class SignInController extends GetxController {
static SignInController get instance => Get.find(); static SignInController get instance => Get.find();
final _logger = Logger(); final _logger = Logger();
final _authRepo = Get.find<AuthenticationRepository>(); final _authRepo = Get.find<AuthenticationRepository>();
// Form controllers // Form controllers
final email = TextEditingController(); final email = TextEditingController();
final password = TextEditingController(); final password = TextEditingController();
// Form error messages // Form error messages
final RxString emailError = RxString(''); final RxString emailError = RxString('');
final RxString passwordError = RxString(''); final RxString passwordError = RxString('');
// States // States
final RxBool isLoading = RxBool(false); final RxBool isLoading = RxBool(false);
final RxBool isPasswordVisible = RxBool(false); final RxBool isPasswordVisible = RxBool(false);
@override @override
void onClose() { void onClose() {
email.dispose(); email.dispose();
password.dispose(); password.dispose();
super.onClose(); super.onClose();
} }
// Toggle password visibility // Toggle password visibility
void togglePasswordVisibility() { void togglePasswordVisibility() {
isPasswordVisible.value = !isPasswordVisible.value; isPasswordVisible.value = !isPasswordVisible.value;
} }
// Navigate to forgot password screen // Navigate to forgot password screen
void goToForgotPassword() { void goToForgotPassword() {
Get.toNamed(AppRoutes.forgotPassword); Get.toNamed(AppRoutes.forgotPassword);
} }
// Navigate to sign up screen // Navigate to sign up screen
void goToSignUp() { void goToSignUp() {
Get.toNamed(AppRoutes.roleSelection); Get.toNamed(AppRoutes.roleSelection);
} }
// Clear error messages // Clear error messages
void clearErrors() { void clearErrors() {
emailError.value = ''; emailError.value = '';
@ -55,7 +55,7 @@ class SignInController extends GetxController {
Future<void> signIn(GlobalKey<FormState> formKey) async { Future<void> signIn(GlobalKey<FormState> formKey) async {
// Clear previous errors // Clear previous errors
clearErrors(); clearErrors();
// Validate form // Validate form
final isValid = formKey.currentState?.validate() ?? false; final isValid = formKey.currentState?.validate() ?? false;
if (!isValid) return; if (!isValid) return;
@ -64,20 +64,19 @@ class SignInController extends GetxController {
isLoading.value = true; isLoading.value = true;
// Attempt to sign in // Attempt to sign in
final signInResult = await _authRepo.loginWithEmailPassword( await _authRepo.loginWithEmailPassword(
email: email.text.trim(), email: email.text.trim(),
password: password.text.trim(), password: password.text.trim(),
); );
// Handle result // Handle result
_logger.i('Sign in successful: $signInResult'); // _logger.i('Sign in successful: $signInResult');
// Redirect based on user's profile status // Redirect based on user's profile status
_authRepo.screenRedirect(); _authRepo.screenRedirect();
} catch (e) { } catch (e) {
isLoading.value = false; isLoading.value = false;
// Handle specific errors // Handle specific errors
if (e.toString().contains('user-not-found')) { if (e.toString().contains('user-not-found')) {
emailError.value = 'No user found with this email'; emailError.value = 'No user found with this email';
@ -89,36 +88,110 @@ class SignInController extends GetxController {
// Show general error // Show general error
TLoaders.errorSnackBar(title: 'Sign In Failed', message: e.toString()); TLoaders.errorSnackBar(title: 'Sign In Failed', message: e.toString());
} }
_logger.e('Sign in error: $e'); _logger.e('Sign in error: $e');
} finally { } finally {
isLoading.value = false; isLoading.value = false;
} }
} }
// Sign in with Google // Sign in with Google
Future<void> googleSignIn() async { Future<void> googleSignIn() async {
try { try {
isLoading.value = true; isLoading.value = true;
// Attempt to sign in with Google // Attempt to sign in with Google
await _authRepo.signInWithGoogle(); await _authRepo.signInWithGoogle();
// Redirect based on user's profile status // Redirect based on user's profile status
_authRepo.screenRedirect(); _authRepo.screenRedirect();
} catch (e) { } catch (e) {
isLoading.value = false; isLoading.value = false;
// Show error // Show error
TLoaders.errorSnackBar( TLoaders.errorSnackBar(
title: 'Google Sign In Failed', title: 'Google Sign In Failed',
message: e.toString(), message: e.toString(),
); );
_logger.e('Google sign in error: $e'); _logger.e('Google sign in error: $e');
} finally { } finally {
isLoading.value = false; 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;
}
}
} }

View File

@ -1,11 +1,15 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.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/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_button.dart';
import 'package:sigap/src/features/auth/presentasion/widgets/auth_header.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/shared/widgets/text/custom_text_field.dart';
import 'package:sigap/src/utils/constants/colors.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 { class ForgotPasswordScreen extends StatelessWidget {
const ForgotPasswordScreen({super.key}); const ForgotPasswordScreen({super.key});
@ -15,6 +19,8 @@ class ForgotPasswordScreen extends StatelessWidget {
// Get the controller // Get the controller
final controller = Get.find<ForgotPasswordController>(); final controller = Get.find<ForgotPasswordController>();
final isDarkMode = THelperFunctions.isDarkMode(context);
// Set system overlay style // Set system overlay style
SystemChrome.setSystemUIOverlayStyle( SystemChrome.setSystemUIOverlayStyle(
const SystemUiOverlayStyle( const SystemUiOverlayStyle(
@ -24,26 +30,29 @@ class ForgotPasswordScreen extends StatelessWidget {
); );
return Scaffold( return Scaffold(
backgroundColor: TColors.light,
appBar: AppBar( appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0, elevation: 0,
leading: IconButton( leading: IconButton(
icon: Icon(Icons.arrow_back, color: TColors.textPrimary), icon: Icon(
Icons.arrow_back,
color: isDarkMode ? TColors.accent : TColors.primary,
),
onPressed: controller.goBack, onPressed: controller.goBack,
), ),
), ),
body: SafeArea( body: SafeArea(
child: SingleChildScrollView( child: Center(
child: Padding( child: SingleChildScrollView(
padding: const EdgeInsets.all(24.0), child: Padding(
child: Obx( padding: const EdgeInsets.all(TSizes.defaultSpace),
() => Form( child: Obx(
key: controller.formKey, () => Form(
child: key: controller.formKey,
controller.isEmailSent.value child:
? _buildSuccessView(controller) controller.isEmailSent.value
: _buildFormView(controller), ? _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( return Column(
crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
// Header // Logo centered
SvgPicture.asset(TImages.lightAppBgLogo, height: 100, width: 100),
const SizedBox(height: 16),
// Header centered
const AuthHeader( const AuthHeader(
crossAxisAlignment: CrossAxisAlignment.center,
title: 'Forgot Password', title: 'Forgot Password',
subtitle: 'Enter your email to reset your password', subtitle: 'Enter your email to reset your password',
), ),
const SizedBox(height: 24),
// Email field // Email field
Obx( Obx(
() => CustomTextField( () => CustomTextField(
@ -70,6 +91,12 @@ class ForgotPasswordScreen extends StatelessWidget {
validator: controller.validateEmail, validator: controller.validateEmail,
keyboardType: TextInputType.emailAddress, keyboardType: TextInputType.emailAddress,
errorText: controller.emailError.value, 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) { Widget _buildSuccessView(ForgotPasswordController controller) {
return Column( return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
// Logo centered
SvgPicture.asset(TImages.lightAppBgLogo, height: 100, width: 100),
const SizedBox(height: 32), const SizedBox(height: 32),
// Success icon // Success icon

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:flutter_tabler_icons/flutter_tabler_icons.dart'; import 'package:flutter_tabler_icons/flutter_tabler_icons.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:sigap/src/features/auth/presentasion/controllers/signin/signin_controller.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/password_field.dart';
import 'package:sigap/src/features/auth/presentasion/widgets/social_button.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/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/helpers/helper_functions.dart';
import 'package:sigap/src/utils/validators/validation.dart'; import 'package:sigap/src/utils/validators/validation.dart';
@ -26,116 +28,173 @@ class SignInScreen extends StatelessWidget {
final isDarkMode = THelperFunctions.isDarkMode(context); final isDarkMode = THelperFunctions.isDarkMode(context);
return Scaffold( return Scaffold(
// Use dynamic background color from theme
body: SafeArea( body: SafeArea(
child: SingleChildScrollView( child: Center(
child: Padding( child: SingleChildScrollView(
padding: const EdgeInsets.all(24.0), child: Padding(
child: Form( padding: const EdgeInsets.all(24.0),
key: formKey, child: Form(
child: Column( key: formKey,
crossAxisAlignment: CrossAxisAlignment.start, child: Column(
children: [ mainAxisSize: MainAxisSize.min,
const SizedBox(height: 16), crossAxisAlignment: CrossAxisAlignment.center,
children: [
// Header - pass isDarkMode to AuthHeader if needed // Logo centered at the top
const AuthHeader( SvgPicture.asset(
title: 'Welcome Back', TImages.lightAppBgLogo,
subtitle: 'Sign in to your account to continue', height: 100,
), width: 100,
// Email field
Obx(
() => CustomTextField(
label: 'Email',
controller: controller.email,
validator: TValidators.validateEmail,
keyboardType: TextInputType.emailAddress,
errorText: controller.emailError.value,
textInputAction: TextInputAction.next,
), ),
), const SizedBox(height: 16),
// Password field // Header centered
Obx( const AuthHeader(
() => PasswordField( crossAxisAlignment: CrossAxisAlignment.center,
label: 'Password', title: 'Welcome Back',
controller: controller.password, subtitle: 'Sign in to your account to continue',
validator: TValidators.validatePassword,
isVisible: controller.isPasswordVisible,
errorText: controller.passwordError.value,
onToggleVisibility: controller.togglePasswordVisibility,
), ),
),
// Forgot password const SizedBox(height: 24),
Align(
alignment: Alignment.centerRight, // Email field with icon
child: TextButton( Obx(
onPressed: controller.goToForgotPassword, () => CustomTextField(
child: Text( label: 'Email',
'Forgot Password?', controller: controller.email,
style: TextStyle( validator: TValidators.validateEmail,
color: Theme.of(context).primaryColor, keyboardType: TextInputType.emailAddress,
fontWeight: FontWeight.w500, errorText: controller.emailError.value,
), textInputAction: TextInputAction.next,
prefixIcon: const Icon(TablerIcons.mail, size: 20),
), ),
), ),
),
const SizedBox(height: 16), // Password field with icon
Obx(
// Sign in button () => PasswordField(
Obx( label: 'Password',
() => AuthButton( controller: controller.password,
text: 'Sign In', validator: TValidators.validatePassword,
onPressed: () => controller.signIn(formKey), isVisible: controller.isPasswordVisible,
isLoading: controller.isLoading.value, errorText: controller.passwordError.value,
), onToggleVisibility: controller.togglePasswordVisibility,
), prefixIcon: const Icon(TablerIcons.lock, size: 20),
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),
), ),
TextButton( ),
onPressed: controller.goToSignUp,
// Forgot password
Align(
alignment: Alignment.centerRight,
child: TextButton(
onPressed: controller.goToForgotPassword,
child: Text( child: Text(
'Sign Up', 'Forgot Password?',
style: TextStyle( style: TextStyle(
color: Theme.of(context).primaryColor, color: Theme.of(context).primaryColor,
fontWeight: FontWeight.w500, 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,
),
),
),
],
),
],
),
), ),
), ),
), ),

View File

@ -1,5 +1,7 @@
import 'package:flutter/material.dart'; 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/constants/sizes.dart';
import 'package:sigap/src/utils/helpers/helper_functions.dart';
class AuthButton extends StatelessWidget { class AuthButton extends StatelessWidget {
final String text; final String text;
@ -19,6 +21,7 @@ class AuthButton extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isDark = THelperFunctions.isDarkMode(context);
return SizedBox( return SizedBox(
width: double.infinity, width: double.infinity,
height: 55, height: 55,
@ -26,7 +29,8 @@ class AuthButton extends StatelessWidget {
onPressed: isLoading ? null : onPressed, onPressed: isLoading ? null : onPressed,
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: backgroundColor ?? Theme.of(context).primaryColor, backgroundColor: backgroundColor ?? Theme.of(context).primaryColor,
foregroundColor: textColor ?? Theme.of(context).colorScheme.onPrimary, foregroundColor:
textColor ?? (isDark ? TColors.primary : TColors.accent),
elevation: 1, elevation: 1,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(TSizes.buttonRadius), borderRadius: BorderRadius.circular(TSizes.buttonRadius),

View File

@ -5,15 +5,21 @@ import 'package:sigap/src/utils/helpers/helper_functions.dart';
class AuthHeader extends StatelessWidget { class AuthHeader extends StatelessWidget {
final String title; final String title;
final String subtitle; 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final dark = THelperFunctions.isDarkMode(context); final dark = THelperFunctions.isDarkMode(context);
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: crossAxisAlignment,
children: [ children: [
Text( Text(
title, title,

View File

@ -1,8 +1,10 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:sigap/src/utils/constants/sizes.dart'; import 'package:sigap/src/utils/constants/sizes.dart';
enum SocialButtonShapeType { defaultShape, rounded, circular }
class SocialButton extends StatelessWidget { class SocialButton extends StatelessWidget {
final String text; final String? text;
final Icon? icon; final Icon? icon;
final String? iconImage; final String? iconImage;
final double? iconImageWidth; final double? iconImageWidth;
@ -12,10 +14,11 @@ class SocialButton extends StatelessWidget {
final Color? foregroundColor; final Color? foregroundColor;
final Color? borderColor; final Color? borderColor;
final bool isVisible; final bool isVisible;
final SocialButtonShapeType shapeType;
const SocialButton({ const SocialButton({
super.key, super.key,
required this.text, this.text,
this.icon, this.icon,
this.iconImage, this.iconImage,
this.iconImageWidth, this.iconImageWidth,
@ -25,53 +28,81 @@ class SocialButton extends StatelessWidget {
this.foregroundColor, this.foregroundColor,
this.borderColor, this.borderColor,
this.isVisible = true, this.isVisible = true,
this.shapeType = SocialButtonShapeType.defaultShape,
}); });
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (!isVisible) return const SizedBox.shrink(); 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( return SizedBox(
width: double.infinity, width: shapeType == SocialButtonShapeType.circular ? 48 : double.infinity,
height: shapeType == SocialButtonShapeType.circular ? 48 : null,
child: OutlinedButton( child: OutlinedButton(
onPressed: onPressed, onPressed: onPressed,
style: OutlinedButton.styleFrom( style: OutlinedButton.styleFrom(
backgroundColor: backgroundColor ?? Colors.white, backgroundColor: backgroundColor ?? Colors.white,
foregroundColor: foregroundColor ?? Colors.black, foregroundColor: foregroundColor ?? Colors.black,
side: BorderSide(color: borderColor ?? Colors.grey.shade300), side: BorderSide(color: borderColor ?? Colors.grey.shade300),
shape: RoundedRectangleBorder( shape: shape,
borderRadius: BorderRadius.circular(TSizes.buttonRadius), padding: padding,
),
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,
),
),
],
), ),
child: child,
), ),
); );
} }

View File

@ -2,6 +2,7 @@ import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.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/cores/services/location_service.dart';
import 'package:sigap/src/features/onboarding/presentasion/pages/role-selection/role_signup_pageview.dart'; import 'package:sigap/src/features/onboarding/presentasion/pages/role-selection/role_signup_pageview.dart';
import 'package:sigap/src/utils/constants/image_strings.dart'; import 'package:sigap/src/utils/constants/image_strings.dart';
@ -11,6 +12,8 @@ class CheckLocationController extends GetxController
final VoidCallback? onSuccess; final VoidCallback? onSuccess;
final LocationService _locationService = LocationService.instance; final LocationService _locationService = LocationService.instance;
final storage = GetStorage();
// Reactive variables // Reactive variables
final RxInt currentPage = 0.obs; final RxInt currentPage = 0.obs;
final RxBool isLoading = false.obs; final RxBool isLoading = false.obs;
@ -146,6 +149,8 @@ class CheckLocationController extends GetxController
// Navigate to communication slide before zooming // Navigate to communication slide before zooming
_navigateToCommunicationSlide(); _navigateToCommunicationSlide();
storage.write('isFirstTime', false);
// Give time to see the success message before animating // Give time to see the success message before animating
await Future.delayed(Duration(milliseconds: 800)); await Future.delayed(Duration(milliseconds: 800));
animController animController

View File

@ -81,16 +81,16 @@ class OnboardingController extends GetxController
} }
} }
// Method to skip to welcome screen // Method to skip to check location screen
void skipToWelcomeScreen() { void skipToCheckLocation() {
skipOnboarding(); skipOnboarding();
} }
// Method to navigate to welcome screen // Method to navigate to check location screen
void skipOnboarding() { void skipOnboarding() {
// Mark onboarding as completed in storage // Mark onboarding as completed in storage
storage.write('isFirstTime', true); storage.write('isFirstTime', false);
Get.offAllNamed(AppRoutes.welcome); Get.offAllNamed(AppRoutes.checkLocation);
} }
// Method to check location validity and proceed with auth flow // Method to check location validity and proceed with auth flow
@ -136,7 +136,7 @@ class OnboardingController extends GetxController
} }
void goToSignIn() { void goToSignIn() {
storage.write('isFirstTime', true); storage.write('isFirstTime', false);
Get.offAllNamed(AppRoutes.signIn); Get.offAllNamed(AppRoutes.signIn);
} }

View File

@ -47,7 +47,7 @@ class TLoaders {
isDismissible: true, isDismissible: true,
shouldIconPulse: true, shouldIconPulse: true,
colorText: Colors.white, colorText: Colors.white,
backgroundColor: TColors.primary, backgroundColor: TColors.success,
snackPosition: SnackPosition.TOP, snackPosition: SnackPosition.TOP,
duration: Duration(seconds: duration), duration: Duration(seconds: duration),
margin: const EdgeInsets.all(10), margin: const EdgeInsets.all(10),