Merge branch 'main' into meet

This commit is contained in:
meet2711
2024-06-03 15:25:41 +05:30
committed by GitHub
44 changed files with 802 additions and 57 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1018 B

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@@ -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';
@@ -36,5 +37,4 @@ class RouteName {
//Biometric
static const String otpScreen = 'otpScreen';
}

View File

@@ -16,6 +16,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
@@ -110,7 +111,6 @@ final goRouter = GoRouter(
),
],
),
// GoRoute(
// path: '/profile/:userId',
// builder: (context, state) {

View File

@@ -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);
}

View File

@@ -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";
@@ -81,5 +94,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:";
}

View File

@@ -34,7 +34,7 @@ class CountrySelectionList extends StatelessWidget {
height: 24,
),
const Gap(10),
TextWidget().tex14W500(countryName[index],
TextWidget().text14W500(countryName[index],
clr: AppColor.charcoalColor),
],
),

View File

@@ -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)),
),
@@ -104,7 +104,7 @@ class BottomSection extends StatelessWidget {
"fromScreentype": "login",
});
},
text: TextWidget().tex14W700(AppText.signUpText,
text: TextWidget().text14W700(AppText.signUpText,
clr: AppColor.textLabelColor,
textDecoration: TextDecoration.underline)),
],

View File

@@ -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,
),
),
],
),

View File

@@ -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,
),

View File

@@ -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<TimerEvent, TimerState> {
Timer? _timer;
TimerBloc() : super(const TimerInitial()) {
on<StartTimer>(_onStartTimer);
on<Tick>(_onTick);
}
void _onStartTimer(StartTimer event, Emitter<TimerState> emit) {
const int duration = 300; // 5 minutes in seconds
emit(const TimerRunInProgress(duration));
_startTicker(duration);
}
void _onTick(Tick event, Emitter<TimerState> 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<void> close() {
_timer?.cancel();
return super.close();
}
}

View File

@@ -0,0 +1,20 @@
// timer_event.dart
import 'package:equatable/equatable.dart';
abstract class TimerEvent extends Equatable {
const TimerEvent();
@override
List<Object> get props => [];
}
class StartTimer extends TimerEvent {}
class Tick extends TimerEvent {
final int duration;
const Tick(this.duration);
@override
List<Object> get props => [duration];
}

View File

@@ -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<Object> 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);
}

View File

@@ -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(),
],
));
}

View File

@@ -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(),
),

View File

@@ -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) {

View File

@@ -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,
),

View File

@@ -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<TimerBloc, TimerState>(
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,
),
)
],
),
);
},
);
}
}

View File

@@ -8,7 +8,6 @@ class RegisterBloc extends Bloc<RegisterEvent, RegisterState> {
final TextEditingController countrySelectionTextField =
TextEditingController();
final TextEditingController phoneNumberTextField = TextEditingController();
final TextEditingController passwordTextField = TextEditingController();
GlobalKey<FormState> getFormKey() {
return formKey;
@@ -16,7 +15,7 @@ class RegisterBloc extends Bloc<RegisterEvent, RegisterState> {
RegisterBloc() : super(RegisterInitial()) {
phoneNumberTextField.addListener(_onFormFieldChanged);
passwordTextField.addListener(_onFormFieldChanged);
countrySelectionTextField.addListener(_onFormFieldChanged);
on<RegisterFormChanged>(_onLoginFormChanged);
on<RegisterSubmitted>((event, emit) async {
@@ -28,8 +27,8 @@ class RegisterBloc extends Bloc<RegisterEvent, RegisterState> {
// 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<RegisterEvent, RegisterState> {
// Mock API function, replace with actual API call
Future<bool> _mockLoginApi(
String phoneNumber,
String password,
String countryResidence,
) async {
return true;
@@ -67,7 +65,7 @@ class RegisterBloc extends Bloc<RegisterEvent, RegisterState> {
@override
Future<void> close() {
phoneNumberTextField.dispose();
passwordTextField.dispose();
countrySelectionTextField.dispose();
return super.close();
}

View File

@@ -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<Object> get props => [phoneNumber, password, countryResidence];
List<Object> get props => [phoneNumber, countryResidence];
}
class RegisterFormChanged extends RegisterEvent {

View File

@@ -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<RegisterUserEvent, RegisterUserState> {
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
final TextEditingController firstNameTextField = TextEditingController();
final TextEditingController lastNameTextField = TextEditingController();
final TextEditingController emailTextField = TextEditingController();
final TextEditingController passwordTextField = TextEditingController();
final TextEditingController repeatPasswordTextField = TextEditingController();
GlobalKey<FormState> getFormKey() {
return formKey;
}
RegisterUserBloc() : super(RegisterUserInitial()) {
firstNameTextField.addListener(_onFormFieldChanged);
passwordTextField.addListener(_onFormFieldChanged);
lastNameTextField.addListener(_onFormFieldChanged);
emailTextField.addListener(_onFormFieldChanged);
repeatPasswordTextField.addListener(_onFormFieldChanged);
on<RegisterFormChanged>(_onLoginFormChanged);
on<RegisterUserSubmitted>((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<RegisterUserState> 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<bool> _mockLoginApi(
String firstName,
String password,
String confirmPassword,
String email,
String lastName,
) async {
return true;
}
@override
Future<void> close() {
firstNameTextField.dispose();
passwordTextField.dispose();
lastNameTextField.dispose();
emailTextField.dispose();
repeatPasswordTextField.dispose();
return super.close();
}
}

View File

@@ -0,0 +1,48 @@
import 'package:equatable/equatable.dart';
abstract class RegisterUserEvent extends Equatable {
const RegisterUserEvent();
@override
List<Object> 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<Object> 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<Object> get props =>
[firstName, lastName, email, confirmPassword, password];
}

View File

@@ -0,0 +1,32 @@
import 'package:equatable/equatable.dart';
abstract class RegisterUserState extends Equatable {
const RegisterUserState();
@override
List<Object> 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<Object> get props => [error];
}
class RegisterUserFieldsState extends RegisterUserState {
final bool areFieldsFilled;
const RegisterUserFieldsState(this.areFieldsFilled);
@override
List<Object> get props => [areFieldsFilled];
}

View File

@@ -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(),
],
));
}
}

View File

@@ -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(),
),
);
}
}

View File

@@ -75,10 +75,6 @@ class RegisterBottomSection extends StatelessWidget {
.read<RegisterBloc>()
.phoneNumberTextField
.text,
context
.read<RegisterBloc>()
.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)),
],

View File

@@ -42,7 +42,7 @@ class RegisterStepBottomSection extends StatelessWidget {
"fromScreen": "registerStep",
});
},
text: TextWidget().tex14W700(
text: TextWidget().text14W700(
AppText.loginText,
clr: AppColor.darkGreyColor,
textDecoration: TextDecoration.underline,

View File

@@ -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,

View File

@@ -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,
),

View File

@@ -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,
),

View File

@@ -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<RegisterUserBloc, RegisterUserState>(
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<RegisterUserBloc>().add(
RegisterUserSubmitted(
context
.read<RegisterUserBloc>()
.firstNameTextField
.text,
context
.read<RegisterUserBloc>()
.passwordTextField
.text,
context
.read<RegisterUserBloc>()
.lastNameTextField
.text,
context
.read<RegisterUserBloc>()
.repeatPasswordTextField
.text,
context
.read<RegisterUserBloc>()
.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)),
],
);
}
}

View File

@@ -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<RegisterUserBloc>();
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,
),
),
],
),
),
),
);
}
}

View File

@@ -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,
),
),
],
);
}
}

View File

@@ -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,
),

View File

@@ -42,7 +42,7 @@ class LoginSignUpButton extends StatelessWidget {
"fromScreen": "welcome",
});
},
text: TextWidget().tex14W700(
text: TextWidget().text14W700(
AppText.loginText,
clr: AppColor.darkGreyColor,
textDecoration: TextDecoration.underline,

View File

@@ -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<MyApp> with WidgetsBindingObserver {
BlocProvider(
create: (context) => RadioBloc(),
),
BlocProvider(
create: (context) => PasswordVisibilityBloc(),
),
// BlocProvider(
// create: (context) => PasswordVisibilityBloc(),
// ),
],
child: ScreenUtilInit(
builder: (BuildContext context, Widget? child) => MaterialApp.router(

View File

@@ -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,

View File

@@ -0,0 +1,18 @@
import 'package:bloc/bloc.dart';
import 'checkbox_event.dart';
import 'checkbox_state.dart';
class CheckboxBloc extends Bloc<CheckboxEvent, CheckBoxState> {
CheckboxBloc() : super(CheckboxUnchecked()) {
on<ToggleCheckbox>(_onToggleCheckbox);
}
void _onToggleCheckbox(ToggleCheckbox event, Emitter<CheckBoxState> emit) {
if (state is CheckboxUnchecked) {
emit(CheckboxChecked());
} else {
emit(CheckboxUnchecked());
}
}
}

View File

@@ -0,0 +1,10 @@
import 'package:equatable/equatable.dart';
abstract class CheckboxEvent extends Equatable {
const CheckboxEvent();
@override
List<Object> get props => [];
}
class ToggleCheckbox extends CheckboxEvent {}

View File

@@ -0,0 +1,12 @@
import 'package:equatable/equatable.dart';
abstract class CheckBoxState extends Equatable {
const CheckBoxState();
@override
List<Object> get props => [];
}
class CheckboxUnchecked extends CheckBoxState {}
class CheckboxChecked extends CheckBoxState {}

View File

@@ -26,7 +26,7 @@ class ButtonWidget {
style: ElevatedButton.styleFrom(
backgroundColor: clr,
),
child: TextWidget().tex14W700(
child: TextWidget().text14W700(
text,
clr: txtClr ?? AppColor.plainWhite,
),

View File

@@ -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<CheckboxBloc, CheckBoxState>(
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<CheckboxBloc>().add(ToggleCheckbox());
},
);
},
);
}
}

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,