From bc1dcf6c3b3927c1052897c030f4c2ca116a55ee Mon Sep 17 00:00:00 2001 From: jayesh Date: Fri, 31 May 2024 17:01:48 +0530 Subject: [PATCH] register user detail --- assets/images/auth_screen/png/stage_two.png | Bin 1018 -> 6283 bytes lib/core/routes/route_name.dart | 2 +- lib/core/routes/routes.dart | 20 ++- lib/core/styles/app_color.dart | 3 + lib/core/styles/app_text.dart | 16 +- .../widgets/country_selection_list.dart | 2 +- .../presentation/widgets/bottom_section.dart | 4 +- .../presentation/widgets/login_form.dart | 14 +- .../presentation/widgets/top_section.dart | 4 +- .../presentation/bloc/timer/timer_bloc.dart | 46 ++++++ .../presentation/bloc/timer/timer_event.dart | 20 +++ .../presentation/bloc/timer/timer_state.dart | 28 ++++ .../presentation/pages/otp_layout.dart | 2 + .../presentation/pages/otp_screen.dart | 6 + .../widgets/otp_fill_section.dart | 11 +- .../presentation/widgets/otp_top_section.dart | 4 +- .../widgets/resend_otp_section.dart | 43 +++++ .../presentation/bloc/register_bloc.dart | 10 +- .../presentation/bloc/register_event.dart | 5 +- .../presentation/bloc/register_user_bloc.dart | 94 +++++++++++ .../bloc/register_user_event.dart | 48 ++++++ .../bloc/register_user_state.dart | 32 ++++ .../pages/register_user_details_layout.dart | 26 +++ .../pages/register_user_details_screen.dart | 32 ++++ .../widgets/register_bottom_section.dart | 6 +- .../widgets/register_step_bottom_section.dart | 2 +- .../widgets/register_step_count.dart | 4 +- .../widgets/register_step_top_section.dart | 4 +- .../widgets/register_top_section.dart | 4 +- .../widgets/register_user_bottom_section.dart | 156 ++++++++++++++++++ .../widgets/register_user_form.dart | 76 +++++++++ .../widgets/register_user_top_section.dart | 34 ++++ .../widgets/build_onboarding_page_widget.dart | 4 +- .../widgets/login_signup_button.dart | 2 +- lib/main.dart | 7 +- lib/shared/components/appbar_widget.dart | 2 +- .../bloc/checkbox/checkbox_bloc.dart | 18 ++ .../bloc/checkbox/checkbox_event.dart | 10 ++ .../bloc/checkbox/checkbox_state.dart | 12 ++ lib/shared/components/button_widget.dart | 2 +- lib/shared/components/checkbox_widget.dart | 42 +++++ .../components/form_label_textfield.dart | 4 +- lib/shared/components/text_widget.dart | 12 +- lib/shared/components/toast_message.dart | 4 +- 44 files changed, 815 insertions(+), 62 deletions(-) create mode 100644 lib/features/otpVerification/presentation/bloc/timer/timer_bloc.dart create mode 100644 lib/features/otpVerification/presentation/bloc/timer/timer_event.dart create mode 100644 lib/features/otpVerification/presentation/bloc/timer/timer_state.dart create mode 100644 lib/features/otpVerification/presentation/widgets/resend_otp_section.dart create mode 100644 lib/features/register/presentation/bloc/register_user_bloc.dart create mode 100644 lib/features/register/presentation/bloc/register_user_event.dart create mode 100644 lib/features/register/presentation/bloc/register_user_state.dart create mode 100644 lib/features/register/presentation/pages/register_user_details_layout.dart create mode 100644 lib/features/register/presentation/pages/register_user_details_screen.dart create mode 100644 lib/features/register/presentation/widgets/register_user_bottom_section.dart create mode 100644 lib/features/register/presentation/widgets/register_user_form.dart create mode 100644 lib/features/register/presentation/widgets/register_user_top_section.dart create mode 100644 lib/shared/components/bloc/checkbox/checkbox_bloc.dart create mode 100644 lib/shared/components/bloc/checkbox/checkbox_event.dart create mode 100644 lib/shared/components/bloc/checkbox/checkbox_state.dart create mode 100644 lib/shared/components/checkbox_widget.dart diff --git a/assets/images/auth_screen/png/stage_two.png b/assets/images/auth_screen/png/stage_two.png index e55880e37ee1363b33d08859327ffb9fdd9858f8..354c49427ef8f57d51cd95a2c423e7dd93d4f205 100644 GIT binary patch literal 6283 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 diff --git a/lib/core/routes/route_name.dart b/lib/core/routes/route_name.dart index 89a8b68..0a58eb5 100644 --- a/lib/core/routes/route_name.dart +++ b/lib/core/routes/route_name.dart @@ -12,6 +12,7 @@ class RouteName { //Register static const String registerStepScreen = 'registerStepScreen'; static const String registerScreen = 'registerScreen'; + static const String registerUserDetailsScreen = 'registerUserDetailsScreen'; //No Internet static const String noInternetScreen = 'noInternet'; @@ -33,5 +34,4 @@ class RouteName { //Biometric static const String otpScreen = 'otpScreen'; - } diff --git a/lib/core/routes/routes.dart b/lib/core/routes/routes.dart index 2272075..5841606 100644 --- a/lib/core/routes/routes.dart +++ b/lib/core/routes/routes.dart @@ -15,6 +15,7 @@ import 'package:tanami_app/features/register/presentation/pages/register_step_sc import 'package:tanami_app/features/welcome/presentation/pages/weclome_screen.dart'; import '../../features/login/presentation/pages/login_screen.dart'; +import '../../features/register/presentation/pages/register_user_details_screen.dart'; import '../../features/splash/presentation/pages/splash_screen.dart'; /* CREATED BY - JAYESH JAIN @@ -94,12 +95,19 @@ final goRouter = GoRouter( }, ), GoRoute( - name: RouteName.porfolioDetails, - path: RouteName.porfolioDetails, - builder: (context, state) { - return const DetailsScreen(); - }, - ), + name: RouteName.porfolioDetails, + path: RouteName.porfolioDetails, + builder: (context, state) { + return const DetailsScreen(); + }, + ), + GoRoute( + name: RouteName.registerUserDetailsScreen, + path: RouteName.registerUserDetailsScreen, + builder: (context, state) { + return const RegisterUserDetailsScreen(); + }, + ), ]), // GoRoute( diff --git a/lib/core/styles/app_color.dart b/lib/core/styles/app_color.dart index ac91fbe..22e7280 100644 --- a/lib/core/styles/app_color.dart +++ b/lib/core/styles/app_color.dart @@ -41,4 +41,7 @@ class AppColor { static const Color strokeColor = Color(0xFFB4B4B4); static const Color otpTextColor = Color(0xFF191B1E); static const Color fillColor = Color(0xFFF6F6F6); + + //CheckBox Color + static const Color checkBoxActiveColor = Color(0xFF09622E); } diff --git a/lib/core/styles/app_text.dart b/lib/core/styles/app_text.dart index 1b154bc..efc5536 100644 --- a/lib/core/styles/app_text.dart +++ b/lib/core/styles/app_text.dart @@ -40,6 +40,8 @@ class AppText { "Enter your country of residence and mobile number"; static const String enterNameEmailPassword = "Enter your name, email and password"; + static const String enterNameEmailUniquePassword = + "Enter your name and email and choose a unique password"; static const String enableBiometricAuthentication = "Enable biometric authentication and select a unique pin code for easy access"; static const String getStarted = "Get started"; @@ -48,6 +50,17 @@ class AppText { "Select your country of residence and enter your mobile number"; static const String nextText = "Next"; static const String backText = "Back"; + static const String firstNameText = "First Name"; + static const String lastNameText = "Last Name"; + static const String emailText = "Email"; + static const String repeatPasswordText = "Repeat Password"; + static const String min8CharactersSpecialCharctersText = + "Min 8 characters, special characters required"; + static const String enterFirstName = "Enter First Name"; + static const String enterLastName = "Enter Last Name"; + static const String enterEmail = "Enter Email"; + static const String cantBeEmptyText = "Can't Be Empty"; + static const String passwordMismatch = "Password Mismatch"; //Country Name static const String bahrainCountryText = "Bahrain"; @@ -76,5 +89,6 @@ class AppText { static const String referToSameOtpMessage = "Please refer to the same OTP message shown below"; static const String resendSms = "Resend SMS"; - + static const String otpVerifiedSucessfully = "OTP Verified Successfully!"; + static const String otpVerifiedFailed = "OTP Verification Failed:"; } diff --git a/lib/features/countrySelection/presentation/widgets/country_selection_list.dart b/lib/features/countrySelection/presentation/widgets/country_selection_list.dart index 5f11f8c..4c31feb 100644 --- a/lib/features/countrySelection/presentation/widgets/country_selection_list.dart +++ b/lib/features/countrySelection/presentation/widgets/country_selection_list.dart @@ -34,7 +34,7 @@ class CountrySelectionList extends StatelessWidget { height: 24, ), const Gap(10), - TextWidget().tex14W500(countryName[index], + TextWidget().text14W500(countryName[index], clr: AppColor.charcoalColor), ], ), diff --git a/lib/features/login/presentation/widgets/bottom_section.dart b/lib/features/login/presentation/widgets/bottom_section.dart index 8fc6017..d3f3afb 100644 --- a/lib/features/login/presentation/widgets/bottom_section.dart +++ b/lib/features/login/presentation/widgets/bottom_section.dart @@ -31,7 +31,7 @@ class BottomSection extends StatelessWidget { function: () { goRouter.goNamed(RouteName.loginScreen); }, - text: TextWidget().tex15W400(AppText.forgorPassword, + text: TextWidget().text15W400(AppText.forgorPassword, clr: AppColor.forgotPassButtonColor, textDecoration: TextDecoration.underline)), ), @@ -103,7 +103,7 @@ class BottomSection extends StatelessWidget { "fromScreentype": "login", }); }, - text: TextWidget().tex14W700(AppText.signUpText, + text: TextWidget().text14W700(AppText.signUpText, clr: AppColor.textLabelColor, textDecoration: TextDecoration.underline)), ], diff --git a/lib/features/login/presentation/widgets/login_form.dart b/lib/features/login/presentation/widgets/login_form.dart index e8f5537..d933d3a 100644 --- a/lib/features/login/presentation/widgets/login_form.dart +++ b/lib/features/login/presentation/widgets/login_form.dart @@ -4,6 +4,7 @@ import 'package:gap/gap.dart'; import 'package:tanami_app/core/styles/app_text.dart'; import 'package:tanami_app/core/utils/constant/country_flag_data.dart'; +import '../../../../shared/components/bloc/password_field/password_visibility_bloc.dart'; import '../../../../shared/components/form_label_textfield.dart'; import '../../../countrySelection/presentation/bloc/choose_country_bloc.dart'; import '../../../countrySelection/presentation/bloc/choose_country_state.dart'; @@ -70,11 +71,14 @@ class LoginForm extends StatelessWidget { textEditingController: loginBloc.phoneNumberTextField, ), const Gap(20), - FormLabelTextField( - hintText: AppText.enterPassword, - title: AppText.password, - type: "password", - textEditingController: loginBloc.passwordTextField, + BlocProvider( + create: (_) => PasswordVisibilityBloc(), + child: FormLabelTextField( + hintText: AppText.enterPassword, + title: AppText.password, + type: "password", + textEditingController: loginBloc.passwordTextField, + ), ), ], ), diff --git a/lib/features/login/presentation/widgets/top_section.dart b/lib/features/login/presentation/widgets/top_section.dart index 73cf478..e447561 100644 --- a/lib/features/login/presentation/widgets/top_section.dart +++ b/lib/features/login/presentation/widgets/top_section.dart @@ -21,14 +21,14 @@ class TopSection extends StatelessWidget { ), ), const Gap(60), - TextWidget().tex20W700( + TextWidget().text20W700( AppText.welcomeText, clr: AppColor.charcoalColor, ), const Gap(10), Padding( padding: const EdgeInsets.symmetric(horizontal: 75), - child: TextWidget().tex14W500( + child: TextWidget().text14W500( AppText.pleaseEnterLoginDetails, clr: AppColor.smokeGrayColor, ), diff --git a/lib/features/otpVerification/presentation/bloc/timer/timer_bloc.dart b/lib/features/otpVerification/presentation/bloc/timer/timer_bloc.dart new file mode 100644 index 0000000..acbe5a3 --- /dev/null +++ b/lib/features/otpVerification/presentation/bloc/timer/timer_bloc.dart @@ -0,0 +1,46 @@ +// timer_bloc.dart +import 'dart:async'; +import 'package:bloc/bloc.dart'; +import 'timer_event.dart'; +import 'timer_state.dart'; + +class TimerBloc extends Bloc { + Timer? _timer; + + TimerBloc() : super(const TimerInitial()) { + on(_onStartTimer); + on(_onTick); + } + + void _onStartTimer(StartTimer event, Emitter emit) { + const int duration = 300; // 5 minutes in seconds + emit(const TimerRunInProgress(duration)); + _startTicker(duration); + } + + void _onTick(Tick event, Emitter emit) { + if (event.duration > 0) { + emit(TimerRunInProgress(event.duration)); + } else { + emit(const TimerRunComplete()); + } + } + + void _startTicker(int duration) { + _timer?.cancel(); + _timer = Timer.periodic(const Duration(seconds: 1), (timer) { + if (duration - timer.tick > 0) { + add(Tick(duration - timer.tick)); + } else { + timer.cancel(); + add(const Tick(0)); + } + }); + } + + @override + Future close() { + _timer?.cancel(); + return super.close(); + } +} diff --git a/lib/features/otpVerification/presentation/bloc/timer/timer_event.dart b/lib/features/otpVerification/presentation/bloc/timer/timer_event.dart new file mode 100644 index 0000000..302880e --- /dev/null +++ b/lib/features/otpVerification/presentation/bloc/timer/timer_event.dart @@ -0,0 +1,20 @@ +// timer_event.dart +import 'package:equatable/equatable.dart'; + +abstract class TimerEvent extends Equatable { + const TimerEvent(); + + @override + List get props => []; +} + +class StartTimer extends TimerEvent {} + +class Tick extends TimerEvent { + final int duration; + + const Tick(this.duration); + + @override + List get props => [duration]; +} diff --git a/lib/features/otpVerification/presentation/bloc/timer/timer_state.dart b/lib/features/otpVerification/presentation/bloc/timer/timer_state.dart new file mode 100644 index 0000000..5410e76 --- /dev/null +++ b/lib/features/otpVerification/presentation/bloc/timer/timer_state.dart @@ -0,0 +1,28 @@ +// timer_state.dart +import 'package:equatable/equatable.dart'; + +class TimerState extends Equatable { + final int duration; + const TimerState(this.duration); + + String get formattedDuration { + final minutes = (duration ~/ 60).toString().padLeft(2, '0'); + final seconds = (duration % 60).toString().padLeft(2, '0'); + return '$minutes:$seconds'; + } + + @override + List get props => [duration]; +} + +class TimerInitial extends TimerState { + const TimerInitial() : super(300); // Initial state with 5 minutes +} + +class TimerRunInProgress extends TimerState { + const TimerRunInProgress(int duration) : super(duration); +} + +class TimerRunComplete extends TimerState { + const TimerRunComplete() : super(0); +} diff --git a/lib/features/otpVerification/presentation/pages/otp_layout.dart b/lib/features/otpVerification/presentation/pages/otp_layout.dart index 46cd8a6..658a4e3 100644 --- a/lib/features/otpVerification/presentation/pages/otp_layout.dart +++ b/lib/features/otpVerification/presentation/pages/otp_layout.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:tanami_app/features/otpVerification/presentation/widgets/otp_fill_section.dart'; import '../widgets/otp_top_section.dart'; +import '../widgets/resend_otp_section.dart'; class OtpLayout extends StatelessWidget { const OtpLayout({super.key}); @@ -13,6 +14,7 @@ class OtpLayout extends StatelessWidget { children: const [ OtpTopSection(), OtpFillSection(), + ResendOtpSection(), ], )); } diff --git a/lib/features/otpVerification/presentation/pages/otp_screen.dart b/lib/features/otpVerification/presentation/pages/otp_screen.dart index fbfc730..7a9e994 100644 --- a/lib/features/otpVerification/presentation/pages/otp_screen.dart +++ b/lib/features/otpVerification/presentation/pages/otp_screen.dart @@ -4,6 +4,8 @@ import 'package:tanami_app/features/otpVerification/presentation/bloc/otp_bloc.d import 'package:tanami_app/features/otpVerification/presentation/pages/otp_layout.dart'; import '../bloc/otp_event.dart'; +import '../bloc/timer/timer_bloc.dart'; +import '../bloc/timer/timer_event.dart'; class OtpScreen extends StatelessWidget { const OtpScreen({super.key}); @@ -18,6 +20,10 @@ class OtpScreen extends StatelessWidget { // Create an instance of the OnboardingBloc create: (context) => OtpBloc()..add(StartListeningForOtp()), ), + BlocProvider( + create: (context) => + TimerBloc()..add(StartTimer()), // Start the timer here + ), ], child: const OtpLayout(), ), diff --git a/lib/features/otpVerification/presentation/widgets/otp_fill_section.dart b/lib/features/otpVerification/presentation/widgets/otp_fill_section.dart index 2bf78f3..417e1f9 100644 --- a/lib/features/otpVerification/presentation/widgets/otp_fill_section.dart +++ b/lib/features/otpVerification/presentation/widgets/otp_fill_section.dart @@ -2,8 +2,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:sms_autofill/sms_autofill.dart'; +import 'package:tanami_app/core/routes/route_name.dart'; import 'package:tanami_app/core/routes/routes.dart'; import 'package:tanami_app/core/styles/app_color.dart'; +import 'package:tanami_app/core/styles/app_text.dart'; import 'package:tanami_app/shared/components/loader.dart'; import 'package:tanami_app/shared/components/toast_message.dart'; @@ -22,10 +24,15 @@ class OtpFillSection extends StatelessWidget { Loader.loader(context); } else if (state is OtpSubmissionSuccess) { goRouter.pop(); - successToastMessage(context, 'OTP Verified Successfully!'); + successToastMessage( + context, + AppText.otpVerifiedSucessfully, + ); + goRouter.pushNamed(RouteName.registerUserDetailsScreen); } else if (state is OtpSubmissionFailure) { goRouter.pop(); - errorToastMessage(context, 'OTP Verification Failed: ${state.error}'); + errorToastMessage( + context, '${AppText.otpVerifiedFailed} ${state.error}'); } }, builder: (context, state) { diff --git a/lib/features/otpVerification/presentation/widgets/otp_top_section.dart b/lib/features/otpVerification/presentation/widgets/otp_top_section.dart index ff3df0f..c1d85f9 100644 --- a/lib/features/otpVerification/presentation/widgets/otp_top_section.dart +++ b/lib/features/otpVerification/presentation/widgets/otp_top_section.dart @@ -22,14 +22,14 @@ class OtpTopSection extends StatelessWidget { ), ), const Gap(125), - TextWidget().tex20W700( + TextWidget().text20W700( AppText.checkYourMessages, clr: AppColor.charcoalColor, ), const Gap(25), Padding( padding: const EdgeInsets.symmetric(horizontal: 35), - child: TextWidget().tex14W500( + child: TextWidget().text14W500( AppText.referToSameOtpMessage, clr: AppColor.smokeGrayColor, ), diff --git a/lib/features/otpVerification/presentation/widgets/resend_otp_section.dart b/lib/features/otpVerification/presentation/widgets/resend_otp_section.dart new file mode 100644 index 0000000..20204a0 --- /dev/null +++ b/lib/features/otpVerification/presentation/widgets/resend_otp_section.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:tanami_app/core/styles/app_color.dart'; +import 'package:tanami_app/core/styles/app_text.dart'; +import 'package:tanami_app/shared/components/text_widget.dart'; +import 'package:tanami_app/shared/components/toast_message.dart'; + +import '../bloc/timer/timer_bloc.dart'; +import '../bloc/timer/timer_state.dart'; + +class ResendOtpSection extends StatelessWidget { + const ResendOtpSection({super.key}); + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + TextWidget().text14W500( + state.formattedDuration, + clr: AppColor.plainBlack, + ), + InkWell( + onTap: () { + successToastMessage(context, "OTP Resend Sucessfully !"); + }, + child: TextWidget().text14W500( + AppText.resendSms, + clr: AppColor.plainBlack, + textDecoration: TextDecoration.underline, + ), + ) + ], + ), + ); + }, + ); + } +} diff --git a/lib/features/register/presentation/bloc/register_bloc.dart b/lib/features/register/presentation/bloc/register_bloc.dart index 13d3edf..7811fe6 100644 --- a/lib/features/register/presentation/bloc/register_bloc.dart +++ b/lib/features/register/presentation/bloc/register_bloc.dart @@ -8,7 +8,6 @@ class RegisterBloc extends Bloc { final TextEditingController countrySelectionTextField = TextEditingController(); final TextEditingController phoneNumberTextField = TextEditingController(); - final TextEditingController passwordTextField = TextEditingController(); GlobalKey getFormKey() { return formKey; @@ -16,7 +15,7 @@ class RegisterBloc extends Bloc { RegisterBloc() : super(RegisterInitial()) { phoneNumberTextField.addListener(_onFormFieldChanged); - passwordTextField.addListener(_onFormFieldChanged); + countrySelectionTextField.addListener(_onFormFieldChanged); on(_onLoginFormChanged); on((event, emit) async { @@ -28,8 +27,8 @@ class RegisterBloc extends Bloc { // Simulate API call await Future.delayed(const Duration(seconds: 2)); // Replace the next line with actual API call - final isSuccess = await _mockLoginApi( - event.phoneNumber, event.password, event.countryResidence); + final isSuccess = + await _mockLoginApi(event.phoneNumber, event.countryResidence); if (isSuccess) { emit(RegisterSuccess()); } else { @@ -58,7 +57,6 @@ class RegisterBloc extends Bloc { // Mock API function, replace with actual API call Future _mockLoginApi( String phoneNumber, - String password, String countryResidence, ) async { return true; @@ -67,7 +65,7 @@ class RegisterBloc extends Bloc { @override Future close() { phoneNumberTextField.dispose(); - passwordTextField.dispose(); + countrySelectionTextField.dispose(); return super.close(); } diff --git a/lib/features/register/presentation/bloc/register_event.dart b/lib/features/register/presentation/bloc/register_event.dart index d7a46e1..2481914 100644 --- a/lib/features/register/presentation/bloc/register_event.dart +++ b/lib/features/register/presentation/bloc/register_event.dart @@ -9,17 +9,16 @@ abstract class RegisterEvent extends Equatable { class RegisterSubmitted extends RegisterEvent { final String phoneNumber; - final String password; + final String countryResidence; const RegisterSubmitted( this.phoneNumber, - this.password, this.countryResidence, ); @override - List get props => [phoneNumber, password, countryResidence]; + List get props => [phoneNumber, countryResidence]; } class RegisterFormChanged extends RegisterEvent { diff --git a/lib/features/register/presentation/bloc/register_user_bloc.dart b/lib/features/register/presentation/bloc/register_user_bloc.dart new file mode 100644 index 0000000..937a3a2 --- /dev/null +++ b/lib/features/register/presentation/bloc/register_user_bloc.dart @@ -0,0 +1,94 @@ +import 'package:bloc/bloc.dart'; +import 'package:flutter/material.dart'; + +import 'register_user_event.dart'; +import 'register_user_state.dart'; + +class RegisterUserBloc extends Bloc { + final GlobalKey formKey = GlobalKey(); + final TextEditingController firstNameTextField = TextEditingController(); + final TextEditingController lastNameTextField = TextEditingController(); + final TextEditingController emailTextField = TextEditingController(); + final TextEditingController passwordTextField = TextEditingController(); + final TextEditingController repeatPasswordTextField = TextEditingController(); + + GlobalKey getFormKey() { + return formKey; + } + + RegisterUserBloc() : super(RegisterUserInitial()) { + firstNameTextField.addListener(_onFormFieldChanged); + passwordTextField.addListener(_onFormFieldChanged); + lastNameTextField.addListener(_onFormFieldChanged); + emailTextField.addListener(_onFormFieldChanged); + repeatPasswordTextField.addListener(_onFormFieldChanged); + + on(_onLoginFormChanged); + on((event, emit) async { + if (!formKey.currentState!.validate()) { + return; + } + emit(RegisterUserLoading()); + try { + // Simulate API call + await Future.delayed(const Duration(seconds: 2)); + // Replace the next line with actual API call + final isSuccess = await _mockLoginApi( + event.firstName, + event.password, + event.lastName, + event.confirmPassword, + event.email, + ); + if (isSuccess) { + emit(RegisterUserSuccess()); + } else { + emit(const RegisterUserFailure( + "Register failed. Please check your credentials.")); + } + } catch (e) { + emit(RegisterUserFailure(e.toString())); + } + }); + } + void _onFormFieldChanged() { + add(RegisterFormChanged( + firstNameTextField.text, + lastNameTextField.text, + emailTextField.text, + passwordTextField.text, + repeatPasswordTextField.text, + )); + } + + void _onLoginFormChanged( + RegisterFormChanged event, Emitter emit) { + final areFieldsFilled = event.firstName.isNotEmpty && + event.lastName.isNotEmpty && + event.email.isNotEmpty && + event.password.isNotEmpty && + event.confirmPassword.isNotEmpty; + emit(RegisterUserFieldsState(areFieldsFilled)); + } + + // Mock API function, replace with actual API call + Future _mockLoginApi( + String firstName, + String password, + String confirmPassword, + String email, + String lastName, + ) async { + return true; + } + + @override + Future close() { + firstNameTextField.dispose(); + passwordTextField.dispose(); + lastNameTextField.dispose(); + emailTextField.dispose(); + repeatPasswordTextField.dispose(); + return super.close(); + } +} diff --git a/lib/features/register/presentation/bloc/register_user_event.dart b/lib/features/register/presentation/bloc/register_user_event.dart new file mode 100644 index 0000000..b48da92 --- /dev/null +++ b/lib/features/register/presentation/bloc/register_user_event.dart @@ -0,0 +1,48 @@ +import 'package:equatable/equatable.dart'; + +abstract class RegisterUserEvent extends Equatable { + const RegisterUserEvent(); + + @override + List get props => []; +} + +class RegisterUserSubmitted extends RegisterUserEvent { + final String firstName; + final String lastName; + final String email; + final String password; + final String confirmPassword; + + const RegisterUserSubmitted( + this.firstName, + this.password, + this.lastName, + this.confirmPassword, + this.email, + ); + + @override + List get props => + [firstName, lastName, email, confirmPassword, password]; +} + +class RegisterFormChanged extends RegisterUserEvent { + final String firstName; + final String lastName; + final String email; + final String password; + final String confirmPassword; + + const RegisterFormChanged( + this.firstName, + this.password, + this.lastName, + this.confirmPassword, + this.email, + ); + + @override + List get props => + [firstName, lastName, email, confirmPassword, password]; +} diff --git a/lib/features/register/presentation/bloc/register_user_state.dart b/lib/features/register/presentation/bloc/register_user_state.dart new file mode 100644 index 0000000..6364781 --- /dev/null +++ b/lib/features/register/presentation/bloc/register_user_state.dart @@ -0,0 +1,32 @@ +import 'package:equatable/equatable.dart'; + +abstract class RegisterUserState extends Equatable { + const RegisterUserState(); + + @override + List get props => []; +} + +class RegisterUserInitial extends RegisterUserState {} + +class RegisterUserLoading extends RegisterUserState {} + +class RegisterUserSuccess extends RegisterUserState {} + +class RegisterUserFailure extends RegisterUserState { + final String error; + + const RegisterUserFailure(this.error); + + @override + List get props => [error]; +} + +class RegisterUserFieldsState extends RegisterUserState { + final bool areFieldsFilled; + + const RegisterUserFieldsState(this.areFieldsFilled); + + @override + List get props => [areFieldsFilled]; +} diff --git a/lib/features/register/presentation/pages/register_user_details_layout.dart b/lib/features/register/presentation/pages/register_user_details_layout.dart new file mode 100644 index 0000000..2a49b8f --- /dev/null +++ b/lib/features/register/presentation/pages/register_user_details_layout.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; +import 'package:tanami_app/features/register/presentation/widgets/register_user_bottom_section.dart'; + +import '../../../../shared/components/appbar_widget.dart'; +import '../widgets/register_user_form.dart'; +import '../widgets/register_user_top_section.dart'; + +class RegisterUserDetailsLayout extends StatelessWidget { + const RegisterUserDetailsLayout({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: const AppBarWidget( + height: 75, + titleTxt: "", + ), + body: ListView( + children: const [ + RegisterUserTopSection(), + RegisterUserForm(), + RegisterUserBottomSection(), + ], + )); + } +} diff --git a/lib/features/register/presentation/pages/register_user_details_screen.dart b/lib/features/register/presentation/pages/register_user_details_screen.dart new file mode 100644 index 0000000..93ae371 --- /dev/null +++ b/lib/features/register/presentation/pages/register_user_details_screen.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:tanami_app/features/register/presentation/pages/register_user_details_layout.dart'; +import 'package:tanami_app/shared/components/bloc/checkbox/checkbox_bloc.dart'; + +import '../bloc/register_user_bloc.dart'; + +class RegisterUserDetailsScreen extends StatelessWidget { + const RegisterUserDetailsScreen({ + super.key, + }); + + @override + Widget build(BuildContext context) { + return Scaffold( + resizeToAvoidBottomInset: true, + body: MultiBlocProvider( + providers: [ + BlocProvider( + // Create an instance of the OnboardingBloc + create: (context) => RegisterUserBloc(), + ), + BlocProvider( + // Create an instance of the OnboardingBloc + create: (context) => CheckboxBloc(), + ), + ], + child: const RegisterUserDetailsLayout(), + ), + ); + } +} diff --git a/lib/features/register/presentation/widgets/register_bottom_section.dart b/lib/features/register/presentation/widgets/register_bottom_section.dart index df9c3bb..a3b6b51 100644 --- a/lib/features/register/presentation/widgets/register_bottom_section.dart +++ b/lib/features/register/presentation/widgets/register_bottom_section.dart @@ -75,10 +75,6 @@ class RegisterBottomSection extends StatelessWidget { .read() .phoneNumberTextField .text, - context - .read() - .passwordTextField - .text, ""), ) : null; @@ -97,7 +93,7 @@ class RegisterBottomSection extends StatelessWidget { radioBloc.resetSelection(); goRouter.pop(); }, - text: TextWidget().tex14W700(AppText.backText, + text: TextWidget().text14W700(AppText.backText, clr: AppColor.textLabelColor, textDecoration: TextDecoration.underline)), ], diff --git a/lib/features/register/presentation/widgets/register_step_bottom_section.dart b/lib/features/register/presentation/widgets/register_step_bottom_section.dart index e1bcd60..d76b878 100644 --- a/lib/features/register/presentation/widgets/register_step_bottom_section.dart +++ b/lib/features/register/presentation/widgets/register_step_bottom_section.dart @@ -42,7 +42,7 @@ class RegisterStepBottomSection extends StatelessWidget { "fromScreen": "registerStep", }); }, - text: TextWidget().tex14W700( + text: TextWidget().text14W700( AppText.loginText, clr: AppColor.darkGreyColor, textDecoration: TextDecoration.underline, diff --git a/lib/features/register/presentation/widgets/register_step_count.dart b/lib/features/register/presentation/widgets/register_step_count.dart index 528596f..d4a2595 100644 --- a/lib/features/register/presentation/widgets/register_step_count.dart +++ b/lib/features/register/presentation/widgets/register_step_count.dart @@ -21,12 +21,12 @@ class RegisterStepCount extends StatelessWidget { child: ListTile( isThreeLine: true, leading: SvgPicture.asset(stepImage[index]), - title: TextWidget().tex14W700( + title: TextWidget().text14W700( title[index], clr: AppColor.textLabelColor, txtAlign: TextAlign.start, ), - subtitle: TextWidget().tex14W500( + subtitle: TextWidget().text14W500( description[index], clr: AppColor.textLabelColor, txtAlign: TextAlign.start, diff --git a/lib/features/register/presentation/widgets/register_step_top_section.dart b/lib/features/register/presentation/widgets/register_step_top_section.dart index 7f4bb24..fee10b5 100644 --- a/lib/features/register/presentation/widgets/register_step_top_section.dart +++ b/lib/features/register/presentation/widgets/register_step_top_section.dart @@ -21,14 +21,14 @@ class RegisterStepTopSection extends StatelessWidget { ), ), const Gap(30), - TextWidget().tex20W700( + TextWidget().text20W700( AppText.getStarted, clr: AppColor.charcoalColor, ), const Gap(10), Padding( padding: const EdgeInsets.symmetric(horizontal: 75), - child: TextWidget().tex14W500( + child: TextWidget().text14W500( AppText.setupYourTanamiAccountToBegin, clr: AppColor.smokeGrayColor, ), diff --git a/lib/features/register/presentation/widgets/register_top_section.dart b/lib/features/register/presentation/widgets/register_top_section.dart index d6e71c0..3917f5f 100644 --- a/lib/features/register/presentation/widgets/register_top_section.dart +++ b/lib/features/register/presentation/widgets/register_top_section.dart @@ -21,14 +21,14 @@ class RegisterTopSection extends StatelessWidget { ), ), const Gap(60), - TextWidget().tex20W700( + TextWidget().text20W700( AppText.welcome, clr: AppColor.charcoalColor, ), const Gap(10), Padding( padding: const EdgeInsets.symmetric(horizontal: 75), - child: TextWidget().tex14W500( + child: TextWidget().text14W500( AppText.selectYourCountryOfResidence, clr: AppColor.smokeGrayColor, ), diff --git a/lib/features/register/presentation/widgets/register_user_bottom_section.dart b/lib/features/register/presentation/widgets/register_user_bottom_section.dart new file mode 100644 index 0000000..64f8beb --- /dev/null +++ b/lib/features/register/presentation/widgets/register_user_bottom_section.dart @@ -0,0 +1,156 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:gap/gap.dart'; +import 'package:tanami_app/core/styles/app_images.dart'; +import 'package:tanami_app/shared/components/checkbox_widget.dart'; +import 'package:tanami_app/shared/components/loader.dart'; +import 'package:tanami_app/shared/components/toast_message.dart'; + +import '../../../../core/routes/route_name.dart'; +import '../../../../core/routes/routes.dart'; +import '../../../../core/styles/app_color.dart'; +import '../../../../core/styles/app_text.dart'; +import '../../../../shared/components/button_widget.dart'; +import '../../../../shared/components/text_widget.dart'; +import '../bloc/register_user_bloc.dart'; +import '../bloc/register_user_event.dart'; +import '../bloc/register_user_state.dart'; + +class RegisterUserBottomSection extends StatelessWidget { + const RegisterUserBottomSection({super.key}); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + const Gap(40), + Row( + children: [ + const CheckBoxWidget(), + Container( + width: 0.8.sw, + child: Row( + children: [ + const Text("I agree to the "), + InkWell( + onTap: () { + // Handle Terms & Conditions tap + }, + child: const Text( + "Terms & Conditions", + style: TextStyle( + color: Colors.blue, + decoration: TextDecoration.underline, + ), + ), + ), + const Text(" and the "), + InkWell( + onTap: () { + // Handle Privacy Policy tap + }, + child: const Text( + "Privacy Policy", + style: TextStyle( + color: Colors.blue, + decoration: TextDecoration.underline, + ), + ), + ), + ], + ), + ) + ], + ), + const Gap(10), + Image.asset( + AppImages.stage2Image, + width: 75, + height: 12, + ), + const Gap(36), + BlocConsumer( + listener: (context, state) { + if (state is RegisterUserLoading) { + Loader.loader(context); + } else if (state is RegisterUserSuccess) { + successToastMessage(context, "successful !"); + goRouter.pop(); + + goRouter.pushNamed(RouteName.otpScreen); + } else if (state is RegisterUserFailure) { + goRouter.pop(); + errorToastMessage( + context, + state.error, + ); + } + }, + builder: (context, state) { + bool isButtonEnabled = false; + if (state is RegisterUserFieldsState) { + isButtonEnabled = state.areFieldsFilled; + } else if (state is RegisterUserSuccess || + state is RegisterUserFailure) { + isButtonEnabled = true; + } + return Container( + margin: const EdgeInsets.symmetric( + horizontal: 16, + ), + width: 1.sw, + height: 56.h, + child: ButtonWidget().elevatedBtn( + txtClr: isButtonEnabled + ? AppColor.plainWhite + : AppColor.inactiveBtnTxtColor, + function: () { + isButtonEnabled + ? context.read().add( + RegisterUserSubmitted( + context + .read() + .firstNameTextField + .text, + context + .read() + .passwordTextField + .text, + context + .read() + .lastNameTextField + .text, + context + .read() + .repeatPasswordTextField + .text, + context + .read() + .emailTextField + .text, + ), + ) + : null; + }, + text: AppText.nextText, + clr: isButtonEnabled + ? AppColor.primaryColor2 + : AppColor.inactiveBtnColor, + ), + ); + }, + ), + const Gap(5), + ButtonWidget().textBtn( + function: () { + goRouter.pop(); + }, + text: TextWidget().text14W700(AppText.backText, + clr: AppColor.textLabelColor, + textDecoration: TextDecoration.underline)), + ], + ); + } +} diff --git a/lib/features/register/presentation/widgets/register_user_form.dart b/lib/features/register/presentation/widgets/register_user_form.dart new file mode 100644 index 0000000..d4e077c --- /dev/null +++ b/lib/features/register/presentation/widgets/register_user_form.dart @@ -0,0 +1,76 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:gap/gap.dart'; +import 'package:tanami_app/core/styles/app_text.dart'; + +import '../../../../shared/components/bloc/password_field/password_visibility_bloc.dart'; +import '../../../../shared/components/form_label_textfield.dart'; +import '../bloc/register_user_bloc.dart'; + +class RegisterUserForm extends StatelessWidget { + const RegisterUserForm({super.key}); + + @override + Widget build(BuildContext context) { + final registerUserBloc = context.read(); + + return Form( + key: registerUserBloc.formKey, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 14, + ), + child: Align( + alignment: Alignment.topLeft, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Gap(50), + FormLabelTextField( + hintText: AppText.enterFirstName, + title: AppText.firstNameText, + type: AppText.firstNameText, + textEditingController: registerUserBloc.firstNameTextField, + ), + const Gap(12), + FormLabelTextField( + hintText: AppText.enterLastName, + title: AppText.lastNameText, + type: AppText.lastNameText, + textEditingController: registerUserBloc.lastNameTextField, + ), + const Gap(12), + FormLabelTextField( + hintText: AppText.enterEmail, + title: AppText.emailText, + type: AppText.emailText, + textEditingController: registerUserBloc.emailTextField, + ), + const Gap(12), + BlocProvider( + create: (_) => PasswordVisibilityBloc(), + child: FormLabelTextField( + hintText: AppText.enterPassword, + title: AppText.password, + type: AppText.password.toLowerCase(), + textEditingController: registerUserBloc.passwordTextField, + ), + ), + const Gap(12), + BlocProvider( + create: (_) => PasswordVisibilityBloc(), + child: FormLabelTextField( + hintText: AppText.repeatPasswordText, + title: AppText.repeatPasswordText, + type: AppText.password.toLowerCase(), + textEditingController: + registerUserBloc.repeatPasswordTextField, + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/features/register/presentation/widgets/register_user_top_section.dart b/lib/features/register/presentation/widgets/register_user_top_section.dart new file mode 100644 index 0000000..71b8f33 --- /dev/null +++ b/lib/features/register/presentation/widgets/register_user_top_section.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:gap/gap.dart'; +import 'package:tanami_app/core/styles/app_color.dart'; +import 'package:tanami_app/core/styles/app_images.dart'; +import 'package:tanami_app/core/styles/app_text.dart'; +import 'package:tanami_app/shared/components/text_widget.dart'; + +class RegisterUserTopSection extends StatelessWidget { + const RegisterUserTopSection({super.key}); + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const Gap(22), + Center( + child: SvgPicture.asset( + AppImages.weclomeLogo, + ), + ), + const Gap(25), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 75), + child: TextWidget().text14W500( + AppText.enterNameEmailUniquePassword, + clr: AppColor.smokeGrayColor, + ), + ), + ], + ); + } +} diff --git a/lib/features/welcome/presentation/widgets/build_onboarding_page_widget.dart b/lib/features/welcome/presentation/widgets/build_onboarding_page_widget.dart index af4c921..6f96393 100644 --- a/lib/features/welcome/presentation/widgets/build_onboarding_page_widget.dart +++ b/lib/features/welcome/presentation/widgets/build_onboarding_page_widget.dart @@ -32,7 +32,7 @@ Widget buildOnboardingPage( fit: BoxFit.cover, ), const Gap(15), - TextWidget().tex22W700( + TextWidget().text22W700( title, clr: AppColor.primaryColor, ), @@ -41,7 +41,7 @@ Widget buildOnboardingPage( padding: const EdgeInsets.symmetric( horizontal: 50, ), - child: TextWidget().tex15W500( + child: TextWidget().text15W500( description, clr: AppColor.darkGreyColor, ), diff --git a/lib/features/welcome/presentation/widgets/login_signup_button.dart b/lib/features/welcome/presentation/widgets/login_signup_button.dart index ef12d97..6887eb8 100644 --- a/lib/features/welcome/presentation/widgets/login_signup_button.dart +++ b/lib/features/welcome/presentation/widgets/login_signup_button.dart @@ -42,7 +42,7 @@ class LoginSignUpButton extends StatelessWidget { "fromScreen": "welcome", }); }, - text: TextWidget().tex14W700( + text: TextWidget().text14W700( AppText.loginText, clr: AppColor.darkGreyColor, textDecoration: TextDecoration.underline, diff --git a/lib/main.dart b/lib/main.dart index 70a04f9..4d4555d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -6,7 +6,6 @@ import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'core/routes/routes.dart'; import 'core/utils/connectivity/network_connectivity.dart'; import 'features/countrySelection/presentation/bloc/choose_country_bloc.dart'; -import 'shared/components/bloc/password_field/password_visibility_bloc.dart'; /* CREATED BY - JAYESH JAIN DATE - 24-05-2024 @@ -55,9 +54,9 @@ class _MyAppState extends State with WidgetsBindingObserver { BlocProvider( create: (context) => RadioBloc(), ), - BlocProvider( - create: (context) => PasswordVisibilityBloc(), - ), + // BlocProvider( + // create: (context) => PasswordVisibilityBloc(), + // ), ], child: ScreenUtilInit( builder: (BuildContext context, Widget? child) => MaterialApp.router( diff --git a/lib/shared/components/appbar_widget.dart b/lib/shared/components/appbar_widget.dart index bee8ea5..445b197 100644 --- a/lib/shared/components/appbar_widget.dart +++ b/lib/shared/components/appbar_widget.dart @@ -36,7 +36,7 @@ class AppBarWidget extends StatelessWidget implements PreferredSizeWidget { scrolledUnderElevation: 0.0, elevation: 0, centerTitle: true, - title: TextWidget().tex20W700(titleTxt, clr: AppColor.charcoalColor), + title: TextWidget().text20W700(titleTxt, clr: AppColor.charcoalColor), leading: Padding( padding: EdgeInsets.only( left: 16.w, diff --git a/lib/shared/components/bloc/checkbox/checkbox_bloc.dart b/lib/shared/components/bloc/checkbox/checkbox_bloc.dart new file mode 100644 index 0000000..90e68d4 --- /dev/null +++ b/lib/shared/components/bloc/checkbox/checkbox_bloc.dart @@ -0,0 +1,18 @@ +import 'package:bloc/bloc.dart'; + +import 'checkbox_event.dart'; +import 'checkbox_state.dart'; + +class CheckboxBloc extends Bloc { + CheckboxBloc() : super(CheckboxUnchecked()) { + on(_onToggleCheckbox); + } + + void _onToggleCheckbox(ToggleCheckbox event, Emitter emit) { + if (state is CheckboxUnchecked) { + emit(CheckboxChecked()); + } else { + emit(CheckboxUnchecked()); + } + } +} diff --git a/lib/shared/components/bloc/checkbox/checkbox_event.dart b/lib/shared/components/bloc/checkbox/checkbox_event.dart new file mode 100644 index 0000000..346cbbc --- /dev/null +++ b/lib/shared/components/bloc/checkbox/checkbox_event.dart @@ -0,0 +1,10 @@ +import 'package:equatable/equatable.dart'; + +abstract class CheckboxEvent extends Equatable { + const CheckboxEvent(); + + @override + List get props => []; +} + +class ToggleCheckbox extends CheckboxEvent {} diff --git a/lib/shared/components/bloc/checkbox/checkbox_state.dart b/lib/shared/components/bloc/checkbox/checkbox_state.dart new file mode 100644 index 0000000..a40b36b --- /dev/null +++ b/lib/shared/components/bloc/checkbox/checkbox_state.dart @@ -0,0 +1,12 @@ +import 'package:equatable/equatable.dart'; + +abstract class CheckBoxState extends Equatable { + const CheckBoxState(); + + @override + List get props => []; +} + +class CheckboxUnchecked extends CheckBoxState {} + +class CheckboxChecked extends CheckBoxState {} diff --git a/lib/shared/components/button_widget.dart b/lib/shared/components/button_widget.dart index c4069cd..29ee744 100644 --- a/lib/shared/components/button_widget.dart +++ b/lib/shared/components/button_widget.dart @@ -26,7 +26,7 @@ class ButtonWidget { style: ElevatedButton.styleFrom( backgroundColor: clr, ), - child: TextWidget().tex14W700( + child: TextWidget().text14W700( text, clr: txtClr ?? AppColor.plainWhite, ), diff --git a/lib/shared/components/checkbox_widget.dart b/lib/shared/components/checkbox_widget.dart new file mode 100644 index 0000000..81b853d --- /dev/null +++ b/lib/shared/components/checkbox_widget.dart @@ -0,0 +1,42 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:tanami_app/core/styles/app_color.dart'; + +import 'bloc/checkbox/checkbox_bloc.dart'; +import 'bloc/checkbox/checkbox_event.dart'; +import 'bloc/checkbox/checkbox_state.dart'; + +class CheckBoxWidget extends StatelessWidget { + const CheckBoxWidget({super.key}); + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + return Checkbox( + activeColor: AppColor.checkBoxActiveColor, + checkColor: Colors.white, + side: MaterialStateBorderSide.resolveWith( + (states) { + if (states.contains(MaterialState.selected)) { + return const BorderSide( + color: AppColor.checkBoxActiveColor, width: 2); + } + return const BorderSide( + color: AppColor.strokeColor, + width: 2, + ); + }, + ), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(6), // Custom radius + ), + value: state is CheckboxChecked, + onChanged: (value) { + context.read().add(ToggleCheckbox()); + }, + ); + }, + ); + } +} diff --git a/lib/shared/components/form_label_textfield.dart b/lib/shared/components/form_label_textfield.dart index 9beadd5..0dd53b7 100644 --- a/lib/shared/components/form_label_textfield.dart +++ b/lib/shared/components/form_label_textfield.dart @@ -29,7 +29,7 @@ class FormLabelTextField extends StatelessWidget { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - TextWidget().tex14W500( + TextWidget().text14W500( title, clr: AppColor.textLabelColor, ), @@ -56,7 +56,7 @@ class FormLabelTextField extends StatelessWidget { }, texttype: type == "phone number" ? TextInputType.phone - : TextInputType.none, + : TextInputType.name, textEditingController: textEditingController, readonly: type == "country selection" ? true : false, hintText: hintText, diff --git a/lib/shared/components/text_widget.dart b/lib/shared/components/text_widget.dart index 6d79bbc..b54fd6f 100644 --- a/lib/shared/components/text_widget.dart +++ b/lib/shared/components/text_widget.dart @@ -14,7 +14,7 @@ class TextWidget { //Text Size 14 - Widget tex14W500( + Widget text14W500( String text, { Color? clr, TextDecoration? textDecoration, @@ -29,7 +29,7 @@ class TextWidget { color: clr ?? AppColor.plainWhite)); } - Widget tex14W700(String text, + Widget text14W700(String text, {Color? clr, TextDecoration? textDecoration, TextAlign? txtAlign}) { return Text(text, textAlign: txtAlign ?? TextAlign.center, @@ -41,7 +41,7 @@ class TextWidget { } //Text Size 15 - Widget tex15W500( + Widget text15W500( String text, { Color? clr, }) { @@ -53,7 +53,7 @@ class TextWidget { color: clr ?? AppColor.plainWhite)); } - Widget tex15W400( + Widget text15W400( String text, { Color? clr, TextDecoration? textDecoration, @@ -70,7 +70,7 @@ class TextWidget { } //Text Size 22 - Widget tex22W700(String text, {Color? clr}) { + Widget text22W700(String text, {Color? clr}) { return Text(text, style: GoogleFonts.dmSans( fontSize: 22, @@ -79,7 +79,7 @@ class TextWidget { } //Text Size 20 - Widget tex20W700(String text, {Color? clr}) { + Widget text20W700(String text, {Color? clr}) { return Text(text, style: GoogleFonts.dmSans( fontSize: 20, diff --git a/lib/shared/components/toast_message.dart b/lib/shared/components/toast_message.dart index 89a7e6c..3986f34 100644 --- a/lib/shared/components/toast_message.dart +++ b/lib/shared/components/toast_message.dart @@ -14,7 +14,7 @@ ToastificationItem successToastMessage( icon: const Icon(Icons.done), type: ToastificationType.success, context: context, - title: TextWidget().tex15W400( + title: TextWidget().text15W400( textV, clr: AppColor.darkGreyColor, txtAlign: TextAlign.start, @@ -33,7 +33,7 @@ ToastificationItem errorToastMessage( icon: const Icon(Icons.error), type: ToastificationType.error, context: context, - title: TextWidget().tex15W400( + title: TextWidget().text15W400( textV, clr: AppColor.darkGreyColor, txtAlign: TextAlign.start,