feat: sending otp and resend otp done

This commit is contained in:
jahongireshonqulov
2025-10-29 20:20:06 +05:00
parent d31b68cde2
commit eb7459c13f
19 changed files with 591 additions and 78 deletions

View File

@@ -3,5 +3,6 @@ abstract class TimeDelayConst {
static const Duration durationMill300 = Duration(milliseconds: 300);
static const Duration durationMill800 = Duration(milliseconds: 800);
static const Duration durationMill3500 = Duration(milliseconds: 3500);
static const Duration duration1 = Duration(seconds: 1);
static const Duration duration3 = Duration(seconds: 3);
}

View File

@@ -30,6 +30,10 @@ import '../../feature/auth/domain/usecases/verify_phone_register_usecase.dart'
as _i664;
import '../../feature/auth/presentation/blocs/login_bloc/login_bloc.dart'
as _i1065;
import '../../feature/auth/presentation/blocs/verify_otp_bloc/verify_otp_bloc.dart'
as _i323;
import '../../feature/auth/presentation/blocs/verify_phone_bloc/verify_phone_bloc.dart'
as _i224;
import '../../feature/basket/presentation/blocs/basket_bloc.dart' as _i728;
import '../../feature/browse/presentation/blocs/browse_bloc/browse_bloc.dart'
as _i991;
@@ -101,6 +105,20 @@ extension GetItInjectableX on _i174.GetIt {
gh.factory<_i406.VerifyOtpCodeRegisterUseCase>(
() => _i406.VerifyOtpCodeRegisterUseCase(gh<_i884.AuthRepository>()),
);
gh.factory<_i323.VerifyOtpBloc>(
() => _i323.VerifyOtpBloc(
gh<_i406.VerifyOtpCodeRegisterUseCase>(),
gh<_i318.VerifyOtpCodeForgotPasswordUseCase>(),
gh<_i664.VerifyPhoneRegisterUseCase>(),
gh<_i801.VerifyPhoneNumberLoginUseCase>(),
),
);
gh.factory<_i224.VerifyPhoneBloc>(
() => _i224.VerifyPhoneBloc(
gh<_i801.VerifyPhoneNumberLoginUseCase>(),
gh<_i664.VerifyPhoneRegisterUseCase>(),
),
);
gh.factory<_i1065.LoginBloc>(
() => _i1065.LoginBloc(
gh<_i241.LoginUseCase>(),

View File

@@ -0,0 +1,8 @@
abstract class TimeFormatters {
static String formatMinutesToTime(int totalMinutes) {
final hours = totalMinutes ~/ 60;
final minutes = totalMinutes % 60;
return '${hours.toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}';
}
}

View File

@@ -1,4 +1,5 @@
import 'package:flutter/cupertino.dart';
import 'package:food_delivery_client/feature/auth/domain/usecases/verify_otp_code_login_usecase.dart';
import 'package:food_delivery_client/feature/auth/presentation/pages/forgot_password_page/forgot_password_page.dart';
import 'package:food_delivery_client/feature/auth/presentation/pages/login_page/login_page.dart';
import 'package:food_delivery_client/feature/auth/presentation/pages/register_page/register_page.dart';
@@ -25,7 +26,9 @@ class AppRoutes {
GoRoute(
path: Routes.register,
pageBuilder: (context, state) => CupertinoPage(child: RegisterPage()),
pageBuilder: (context, state) => CupertinoPage(
child: RegisterPage(phoneNumber: state.extra as String),
),
),
GoRoute(
path: Routes.forgotPassword,
@@ -42,14 +45,15 @@ class AppRoutes {
GoRoute(
path: Routes.verifyOtpCode,
pageBuilder: (context, state) => CupertinoPage(
child: VerifyOtpCodePage(isRegister: state.extra as bool),
child: VerifyOtpCodePage(params: state.extra as OtpCodePageParams),
),
),
GoRoute(
path: Routes.resetPassword,
pageBuilder: (context, state) =>
CupertinoPage(child: ResetPasswordPage()),
pageBuilder: (context, state) => CupertinoPage(
child: ResetPasswordPage(phoneNumber: state.extra as String),
),
),
GoRoute(

View File

@@ -6,13 +6,12 @@ import 'package:food_delivery_client/food_delivery_client.dart';
import '../../../../core/usecase/usecase.dart';
@injectable
class VerifyOtpCodeForgotPasswordUseCase
class VerifyOtpCodeForgotPasswordUseCase
implements UseCase<SuccessModel, VerifyOtpCodeParams> {
final AuthRepository _authRepository;
VerifyOtpCodeForgotPasswordUseCase(this._authRepository);
@override
Future<Either<Failure, SuccessModel>> call(VerifyOtpCodeParams params) async {
return _authRepository.verifyOtpCodeResetPassword(params: params);
@@ -25,3 +24,10 @@ class VerifyOtpCodeParams {
VerifyOtpCodeParams({required this.otpCode, required this.phoneNumber});
}
class OtpCodePageParams {
final String phoneNumber;
final bool isRegister;
OtpCodePageParams({required this.phoneNumber, required this.isRegister});
}

View File

@@ -0,0 +1,138 @@
import 'package:food_delivery_client/feature/auth/domain/usecases/verify_otp_code_login_usecase.dart';
import 'package:food_delivery_client/feature/auth/domain/usecases/verify_otp_code_register_usecase.dart';
import 'package:food_delivery_client/feature/auth/domain/usecases/verify_phone_login_usecase.dart';
import 'package:food_delivery_client/feature/auth/domain/usecases/verify_phone_register_usecase.dart';
import 'package:food_delivery_client/feature/common/presentation/widgets/w_toastification.dart';
import 'package:food_delivery_client/food_delivery_client.dart';
part 'verify_otp_event.dart';
part 'verify_otp_state.dart';
part 'verify_otp_bloc.freezed.dart';
@injectable
class VerifyOtpBloc extends Bloc<VerifyOtpEvent, VerifyOtpState> {
final VerifyOtpCodeRegisterUseCase _registerUseCase;
final VerifyOtpCodeForgotPasswordUseCase _passwordUseCase;
final VerifyPhoneRegisterUseCase _phoneRegisterUseCase;
final VerifyPhoneNumberLoginUseCase _phoneNumberLoginUseCase;
VerifyOtpBloc(
this._registerUseCase,
this._passwordUseCase,
this._phoneRegisterUseCase,
this._phoneNumberLoginUseCase,
) : super(const VerifyOtpState()) {
Timer? timer1;
on<_CancelTimer>((event, emit) {
timer1?.cancel();
});
on<_Started>((event, emit) {
int seconds = state.time;
emit(state.copyWith(time: seconds));
timer1 = Timer.periodic(TimeDelayConst.duration1, (timer) {
if (seconds == 0) {
timer.cancel();
} else {
seconds--;
add(VerifyOtpEvent.ticked(seconds));
}
});
});
on<_Ticked>(_onTicked);
on<_VerifyOtpReset>(_onVerifyOtpReset);
on<_VerifyOtpRegister>(_onVerifyOtpRegister);
on<_ResendRegister>(_onResendVerifyPhoneRegister);
on<_ResendForgot>(_onResendVerifyPhoneForgot);
}
void _onTicked(_Ticked event, Emitter<VerifyOtpState> emit) {
emit(state.copyWith(time: event.seconds));
}
Future<void> _onVerifyOtpReset(
_VerifyOtpReset event,
Emitter<VerifyOtpState> emit,
) async {
emit(state.copyWith(status: RequestStatus.loading));
final response = await _passwordUseCase.call(event.params);
response.fold(
(l) {
showErrorToast(l.errorMessage);
emit(state.copyWith(status: RequestStatus.error));
},
(r) {
emit(state.copyWith(status: RequestStatus.loaded));
add(VerifyOtpEvent.cancelTimer());
},
);
}
Future<void> _onVerifyOtpRegister(
_VerifyOtpRegister event,
Emitter<VerifyOtpState> emit,
) async {
emit(state.copyWith(status: RequestStatus.loading));
final response = await _registerUseCase.call(event.params);
response.fold(
(l) {
showErrorToast(l.errorMessage);
emit(state.copyWith(status: RequestStatus.error));
},
(r) {
emit(state.copyWith(status: RequestStatus.loaded));
add(VerifyOtpEvent.cancelTimer());
},
);
}
Future<void> _onResendVerifyPhoneRegister(
_ResendRegister event,
Emitter<VerifyOtpState> emit,
) async {
emit(state.copyWith(resendStatus: RequestStatus.loading));
final response = await _phoneRegisterUseCase.call(
VerifyPhoneNumberParams(phoneNumber: event.phoneNUmber),
);
response.fold(
(l) {
showErrorToast(l.errorMessage);
emit(state.copyWith(resendStatus: RequestStatus.error));
},
(r) {
emit(state.copyWith(resendStatus: RequestStatus.loaded));
add(VerifyOtpEvent.ticked(120));
add(VerifyOtpEvent.started());
},
);
}
Future<void> _onResendVerifyPhoneForgot(
_ResendForgot event,
Emitter<VerifyOtpState> emit,
) async {
emit(state.copyWith(resendStatus: RequestStatus.loading));
final response = await _phoneNumberLoginUseCase.call(
VerifyPhoneNumberParams(phoneNumber: event.phoneNUmber),
);
response.fold(
(l) {
showErrorToast(l.errorMessage);
emit(state.copyWith(resendStatus: RequestStatus.error));
},
(r) {
emit(state.copyWith(resendStatus: RequestStatus.loaded));
add(VerifyOtpEvent.ticked(120));
add(VerifyOtpEvent.started());
},
);
}
}

View File

@@ -0,0 +1,19 @@
part of 'verify_otp_bloc.dart';
@freezed
class VerifyOtpEvent with _$VerifyOtpEvent {
const factory VerifyOtpEvent.started() = _Started;
const factory VerifyOtpEvent.cancelTimer() = _CancelTimer;
const factory VerifyOtpEvent.resendRegister(String phoneNUmber) =
_ResendRegister;
const factory VerifyOtpEvent.resendForgot(String phoneNUmber) = _ResendForgot;
const factory VerifyOtpEvent.ticked(int seconds) = _Ticked;
const factory VerifyOtpEvent.verifyOtpReset(VerifyOtpCodeParams params) =
_VerifyOtpReset;
const factory VerifyOtpEvent.verifyOtpRegister(VerifyOtpCodeParams params) =
_VerifyOtpRegister;
}

View File

@@ -0,0 +1,10 @@
part of 'verify_otp_bloc.dart';
@freezed
abstract class VerifyOtpState with _$VerifyOtpState {
const factory VerifyOtpState({
@Default(RequestStatus.initial) RequestStatus status,
@Default(RequestStatus.initial) RequestStatus resendStatus,
@Default(120) int time,
}) = _VerifyOtpState;
}

View File

@@ -0,0 +1,60 @@
import 'package:food_delivery_client/feature/auth/domain/usecases/verify_phone_login_usecase.dart';
import 'package:food_delivery_client/feature/auth/domain/usecases/verify_phone_register_usecase.dart';
import 'package:food_delivery_client/feature/common/presentation/widgets/w_toastification.dart';
import 'package:food_delivery_client/food_delivery_client.dart';
part 'verify_phone_event.dart';
part 'verify_phone_state.dart';
part 'verify_phone_bloc.freezed.dart';
@injectable
class VerifyPhoneBloc extends Bloc<VerifyPhoneEvent, VerifyPhoneState> {
final VerifyPhoneNumberLoginUseCase _loginUseCase;
final VerifyPhoneRegisterUseCase _registerUseCase;
VerifyPhoneBloc(this._loginUseCase, this._registerUseCase)
: super(const VerifyPhoneState()) {
on<_VerifyPhoneRegister>(_onVerifyPhoneNumberRegister);
on<_VerifyPhoneReset>(_onVerifyPhoneReset);
}
Future<void> _onVerifyPhoneNumberRegister(
_VerifyPhoneRegister event,
Emitter<VerifyPhoneState> emit,
) async {
emit(state.copyWith(status: RequestStatus.loading));
final response = await _registerUseCase.call(event.params);
response.fold(
(l) {
showErrorToast(l.errorMessage);
emit(state.copyWith(status: RequestStatus.error));
},
(r) {
showSuccessToast(r.message);
emit(state.copyWith(status: RequestStatus.loaded));
},
);
}
Future<void> _onVerifyPhoneReset(
_VerifyPhoneReset event,
Emitter<VerifyPhoneState> emit,
) async {
emit(state.copyWith(status: RequestStatus.loading));
final response = await _loginUseCase.call(event.params);
response.fold(
(l) {
showErrorToast(l.errorMessage);
emit(state.copyWith(status: RequestStatus.error));
},
(r) {
showSuccessToast(r.message);
emit(state.copyWith(status: RequestStatus.loaded));
},
);
}
}

View File

@@ -0,0 +1,14 @@
part of 'verify_phone_bloc.dart';
@freezed
class VerifyPhoneEvent with _$VerifyPhoneEvent {
const factory VerifyPhoneEvent.started() = _Started;
const factory VerifyPhoneEvent.verifyPhoneRegister(
VerifyPhoneNumberParams params,
) = _VerifyPhoneRegister;
const factory VerifyPhoneEvent.verifyPhoneReset(
VerifyPhoneNumberParams params,
) = _VerifyPhoneReset;
}

View File

@@ -0,0 +1,8 @@
part of 'verify_phone_bloc.dart';
@freezed
abstract class VerifyPhoneState with _$VerifyPhoneState {
const factory VerifyPhoneState({
@Default(RequestStatus.initial) RequestStatus status,
}) = _VerifyPhoneState;
}

View File

@@ -1,6 +1,5 @@
import 'package:food_delivery_client/core/helpers/formatters.dart';
import 'package:food_delivery_client/core/helpers/validator_helpers.dart';
import 'package:food_delivery_client/feature/auth/domain/usecases/login_usecase.dart';
import 'package:food_delivery_client/feature/auth/presentation/widgets/w_auth_background.dart';
import '../../../../../../food_delivery_client.dart';
import '../../../blocs/login_bloc/login_bloc.dart';
@@ -110,7 +109,7 @@ class _WLoginBodyState extends State<WLoginBody> {
alignment: AlignmentGeometry.centerRight,
child: TextButton(
onPressed: () {
// context.push(Routes.verifyPhoneNumber, extra: false);
context.push(Routes.verifyPhoneNumber, extra: false);
},
child: Text(
context.loc.forgot_password,
@@ -154,7 +153,7 @@ class _WLoginBodyState extends State<WLoginBody> {
),
TextButton(
onPressed: () {
// context.push(Routes.verifyPhoneNumber, extra: true);
context.push(Routes.verifyPhoneNumber, extra: true);
},
child: Text(
context.loc.sign_up,

View File

@@ -3,12 +3,12 @@ import 'package:food_delivery_client/feature/auth/presentation/pages/register_pa
import '../../../../../food_delivery_client.dart';
class RegisterPage extends StatelessWidget {
const RegisterPage({super.key});
const RegisterPage({super.key, required this.phoneNumber});
final String phoneNumber;
@override
Widget build(BuildContext context) {
return WLayout(
top: false,
child: Scaffold(body: WRegisterBody()));
return WLayout(top: false, child: Scaffold(body: WRegisterBody()));
}
}

View File

@@ -172,7 +172,10 @@ class _WRegisterBodyState extends State<WRegisterBody> {
AppIcons.icArrowRightLight,
).paddingOnly(left: 8),
onPressed: () {
if (_formKey.currentState?.validate() ?? false) {}
if (_formKey.currentState?.validate() ?? false) {
}
},
),
20.verticalSpace,

View File

@@ -3,8 +3,8 @@ import 'package:food_delivery_client/feature/auth/presentation/widgets/w_auth_ba
import '../../../../../food_delivery_client.dart';
class ResetPasswordPage extends StatelessWidget {
const ResetPasswordPage({super.key});
const ResetPasswordPage({super.key, required this.phoneNumber});
final String phoneNumber;
@override
Widget build(BuildContext context) {
return WLayout(

View File

@@ -1,17 +1,181 @@
import 'package:food_delivery_client/core/helpers/time_formatters.dart';
import 'package:food_delivery_client/feature/auth/domain/usecases/verify_otp_code_login_usecase.dart';
import 'package:food_delivery_client/feature/auth/presentation/blocs/verify_otp_bloc/verify_otp_bloc.dart';
import 'package:food_delivery_client/feature/auth/presentation/widgets/w_auth_background.dart';
import 'package:pinput/pinput.dart';
import '../../../../../food_delivery_client.dart';
class VerifyOtpCodePage extends StatelessWidget {
const VerifyOtpCodePage({super.key, required this.isRegister});
class VerifyOtpCodePage extends StatefulWidget {
const VerifyOtpCodePage({super.key, required this.params});
final bool isRegister;
final OtpCodePageParams params;
@override
State<VerifyOtpCodePage> createState() => _VerifyOtpCodePageState();
}
class _VerifyOtpCodePageState extends State<VerifyOtpCodePage> {
late TextEditingController _controller;
@override
void initState() {
_controller = TextEditingController();
super.initState();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
final defaultPinTheme = PinTheme(
height: 56,
width: 56,
textStyle: AppTextStyles.size18Medium,
decoration: BoxDecoration(
borderRadius: AppUtils.kBorderRadius8,
border: Border.all(color: Color.fromRGBO(234, 239, 243, 1)),
color: Color.fromRGBO(234, 234, 243, 1),
),
);
@override
Widget build(BuildContext context) {
return WLayout(
top: false,
child: Scaffold(body: WAuthBackground(child: Column())),
return BlocProvider(
create: (context) => sl<VerifyOtpBloc>()..add(VerifyOtpEvent.started()),
child: BlocConsumer<VerifyOtpBloc, VerifyOtpState>(
listener: (context, state) {
if (state.status.isLoaded()) {
if (widget.params.isRegister) {
context.push(Routes.register, extra: widget.params.phoneNumber);
} else {
context.push(
Routes.resetPassword,
extra: widget.params.phoneNumber,
);
}
}
},
builder: (context, state) {
return WLayout(
top: false,
child: Scaffold(
body: WAuthBackground(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
40.verticalSpace,
Text(
"Enter the 5-digit code sent to you at ${widget.params.phoneNumber}",
style: AppTextStyles.size20Medium,
),
20.verticalSpace,
Pinput(
length: 5,
enabled: true,
controller: _controller,
keyboardType: TextInputType.number,
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
crossAxisAlignment: CrossAxisAlignment.center,
autofocus: true,
showCursor: true,
defaultPinTheme: defaultPinTheme,
focusedPinTheme: defaultPinTheme.copyWith(
decoration: defaultPinTheme.decoration!.copyWith(
color: Color.fromRGBO(234, 239, 243, 1),
),
),
submittedPinTheme: defaultPinTheme.copyWith(
decoration: defaultPinTheme.decoration!.copyWith(
color: Color.fromRGBO(234, 239, 243, 1),
),
),
onChanged: (value) {
if (value.length == 5 && state.time > 0) {}
},
),
10.verticalSpace,
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("You can resend otp after"),
if (state.time != 0)
Text(TimeFormatters.formatMinutesToTime(state.time)),
if (state.time == 0)
state.resendStatus.isLoading()
? CircularProgressIndicator.adaptive()
: TextButton(
onPressed: () {
if (widget.params.isRegister) {
context.read<VerifyOtpBloc>().add(
VerifyOtpEvent.resendRegister(
widget.params.phoneNumber,
),
);
} else {
context.read<VerifyOtpBloc>().add(
VerifyOtpEvent.resendForgot(
widget.params.phoneNumber,
),
);
}
},
child: Text("Resend"),
),
],
),
20.verticalSpace,
AppButton(
name: context.loc.continue_str,
onPressed: () {
if (_controller.text.trim().length == 5 &&
state.time > 0) {
if (widget.params.isRegister) {
context.read<VerifyOtpBloc>().add(
VerifyOtpEvent.verifyOtpRegister(
VerifyOtpCodeParams(
phoneNumber: widget.params.phoneNumber,
otpCode: _controller.text.trim().substring(
0,
4,
),
),
),
);
} else {
context.read<VerifyOtpBloc>().add(
VerifyOtpEvent.verifyOtpReset(
VerifyOtpCodeParams(
phoneNumber: widget.params.phoneNumber,
otpCode: _controller.text.trim().substring(
0,
4,
),
),
),
);
}
}
},
backgroundColor: AppColors.c34A853,
borderRadius: 15,
trailing: SvgPicture.asset(
AppIcons.icArrowRightLight,
).paddingOnly(left: 8),
),
],
).paddingSymmetric(horizontal: 16),
),
),
);
},
),
);
}
}

View File

@@ -1,5 +1,8 @@
import 'package:food_delivery_client/core/helpers/formatters.dart';
import 'package:food_delivery_client/core/helpers/validator_helpers.dart';
import 'package:food_delivery_client/feature/auth/domain/usecases/verify_otp_code_login_usecase.dart';
import 'package:food_delivery_client/feature/auth/domain/usecases/verify_phone_login_usecase.dart';
import 'package:food_delivery_client/feature/auth/presentation/blocs/verify_phone_bloc/verify_phone_bloc.dart';
import 'package:food_delivery_client/feature/auth/presentation/widgets/w_auth_background.dart';
import '../../../../../food_delivery_client.dart';
@@ -31,65 +34,106 @@ class _VerifyPhoneNumberPageState extends State<VerifyPhoneNumberPage> {
@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: WLayout(
top: false,
child: Scaffold(
body: WAuthBackground(
child: SizedBox(
width: context.w,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
50.verticalSpace,
Text(
context.loc.enter_phone_number,
style: AppTextStyles.size20Medium,
),
20.verticalSpace,
AppTextFormField(
controller: _phoneNumberController,
borderRadius: AppUtils.kBorderRadius8,
hintText: context.loc.phone_number,
keyBoardType: TextInputType.phone,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
Formatters.phoneFormatter,
LengthLimitingTextInputFormatter(12),
],
return BlocProvider(
create: (context) => sl<VerifyPhoneBloc>(),
child: BlocConsumer<VerifyPhoneBloc, VerifyPhoneState>(
listener: (context, state) {
if (state.status.isLoaded()) {
context.push(
Routes.verifyOtpCode,
extra: OtpCodePageParams(
phoneNumber:
"+998${_phoneNumberController.text.trim().replaceAll(" ", "")}",
isRegister: widget.isRegister,
),
);
}
},
builder: (context, state) {
return Form(
key: _formKey,
child: WLayout(
top: false,
child: Scaffold(
body: WAuthBackground(
child: SizedBox(
width: context.w,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
50.verticalSpace,
Text(
context.loc.enter_phone_number,
style: AppTextStyles.size20Medium,
),
20.verticalSpace,
AppTextFormField(
controller: _phoneNumberController,
borderRadius: AppUtils.kBorderRadius8,
hintText: context.loc.phone_number,
keyBoardType: TextInputType.phone,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
Formatters.phoneFormatter,
LengthLimitingTextInputFormatter(12),
],
prefixIcon: Text(
"+ 998",
style: AppTextStyles.size16Regular,
),
validator: (value) {
return Validators.validatePhoneNumber(
_phoneNumberController.text.trim(),
);
},
prefixIcon: Text(
"+ 998",
style: AppTextStyles.size16Regular,
),
validator: (value) {
return Validators.validatePhoneNumber(
_phoneNumberController.text.trim(),
);
},
),
25.verticalSpace,
AppButton(
onPressed: () {
if (_formKey.currentState?.validate() ?? false) {
if (widget.isRegister) {
context.read<VerifyPhoneBloc>().add(
VerifyPhoneEvent.verifyPhoneRegister(
VerifyPhoneNumberParams(
phoneNumber:
"+998${_phoneNumberController.text.trim().replaceAll(" ", "")}",
),
),
);
} else {
context.read<VerifyPhoneBloc>().add(
VerifyPhoneEvent.verifyPhoneReset(
VerifyPhoneNumberParams(
phoneNumber:
"+998${_phoneNumberController.text.trim().replaceAll(" ", "")}",
),
),
);
}
}
},
name: context.loc.continue_str,
borderRadius: 15,
backgroundColor: AppColors.c34A853,
),
10.verticalSpace,
Text(
context.loc.consent_message("Felix Eats"),
style: AppTextStyles.size12Medium.copyWith(
color: AppColors.c888888,
),
),
],
).paddingSymmetric(horizontal: 16),
),
25.verticalSpace,
AppButton(
onPressed: () {
if (_formKey.currentState?.validate() ?? false) {}
},
name: context.loc.continue_str,
borderRadius: 15,
backgroundColor: AppColors.c34A853,
),
10.verticalSpace,
Text(
context.loc.consent_message("Felix Eats"),
style: AppTextStyles.size12Medium.copyWith(
color: AppColors.c888888,
),
),
],
).paddingSymmetric(horizontal: 16),
),
),
),
),
),
);
},
),
);
}