portfolio done pics2

This commit is contained in:
meet2711
2024-05-31 11:06:22 +05:30
57 changed files with 1388 additions and 137 deletions

View File

@@ -0,0 +1,49 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_svg/flutter_svg.dart';
import '../../../../core/styles/app_images.dart';
class BiometricLayout extends StatelessWidget {
const BiometricLayout({super.key});
@override
Widget build(BuildContext context) {
String biometricImage = "";
if (Platform.isIOS) {
biometricImage = AppImages.biometricFace;
} else {
biometricImage = AppImages.biometricFingerprint;
}
return Scaffold(
body: SizedBox(
width: 1.sw,
height: 1.sh,
child: Stack(
children: [
Positioned.fill(
child: SvgPicture.asset(
height: 1.sh,
width: 1.sw,
AppImages.biometricBg,
fit: BoxFit.cover,
),
),
Positioned.fill(
child: Align(
alignment: Alignment.center,
child: Image.asset(
biometricImage,
width: 133,
height: 155,
),
),
),
],
),
),
);
}
}

View File

@@ -0,0 +1,14 @@
import 'package:flutter/material.dart';
import 'package:tanami_app/features/biometric/presentation/pages/biometric_layout.dart';
class BiometricScreen extends StatelessWidget {
const BiometricScreen({super.key});
@override
Widget build(BuildContext context) {
return const Scaffold(
// resizeToAvoidBottomInset: true,
body: BiometricLayout(),
);
}
}

View File

@@ -6,12 +6,22 @@ import 'choose_country_state.dart';
class RadioBloc extends Bloc<RadioEvent, RadioState> {
RadioBloc() : super(RadioInitial()) {
on<RadioSelected>(_onRadioSelected);
on<ResetRadioSelection>(_onResetRadioSelection);
}
void _onRadioSelected(RadioSelected event, Emitter<RadioState> emit) {
emit(RadioSelectionChanged(event.selectedIndex));
}
void _onResetRadioSelection(
ResetRadioSelection event, Emitter<RadioState> emit) {
emit(RadioInitial());
}
void resetSelection() {
add(ResetRadioSelection());
}
int get selectedCountry {
if (state is RadioSelectionChanged) {
return (state as RadioSelectionChanged).selectedIndex;

View File

@@ -10,8 +10,10 @@ abstract class RadioEvent extends Equatable {
class RadioSelected extends RadioEvent {
final int selectedIndex;
const RadioSelected(this.selectedIndex);
const RadioSelected([this.selectedIndex = 0]);
@override
List<Object> get props => [selectedIndex];
}
class ResetRadioSelection extends RadioEvent {}

View File

@@ -24,7 +24,6 @@ class ChooseCountryLayout extends StatelessWidget {
child: ButtonWidget().elevatedBtn(
txtClr: AppColor.plainWhite,
function: () {
// radioBloc.add(const BackPressed(true));
goRouter.pop();
},
text: AppText.confirmSelectionText,

View File

@@ -17,7 +17,7 @@ class CountrySelectionList extends StatelessWidget {
final radioBloc = context.read<RadioBloc>();
return BlocBuilder<RadioBloc, RadioState>(
builder: (context, state) {
int selectedIndex = 0;
int selectedIndex = -1;
if (state is RadioSelectionChanged) {
selectedIndex = state.selectedIndex;
}

View File

@@ -56,13 +56,20 @@ class LoginBloc extends Bloc<LoginEvent, LoginState> {
emit(LoginFieldsState(areFieldsFilled));
}
// Method to reset text fields
void resetFields() {
phoneNumberTextField.clear();
passwordTextField.clear();
countrySelectionTextField.clear();
}
// Mock API function, replace with actual API call
Future<bool> _mockLoginApi(
String email,
String phoneNumber,
String password,
String countryResidence,
) async {
return email == "1234567891" && password == "123456";
return phoneNumber == "1234567891" && password == "123456";
}
@override

View File

@@ -1,29 +1,40 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../../../shared/components/bloc/password_field/password_visibility_bloc.dart';
import '../../../../shared/components/exit_app_dialog.dart';
import '../../../countrySelection/presentation/bloc/choose_country_bloc.dart';
import '../bloc/login_bloc.dart';
import 'login_layout.dart';
class LoginScreen extends StatelessWidget {
const LoginScreen({super.key});
final String fromScreen;
const LoginScreen({super.key, required this.fromScreen});
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: true,
body: MultiBlocProvider(
// Define the providers for the OnboardingBloc and other blocs
providers: [
BlocProvider(
// Create an instance of the OnboardingBloc
create: (context) => LoginBloc(),
),
BlocProvider(
create: (context) => PasswordVisibilityBloc(),
),
],
child: const LoginLayout(),
final radioBloc = context.read<RadioBloc>();
return WillPopScope(
onWillPop: () async {
if (fromScreen == "welcome" || fromScreen == "registerStep") {
exitAppDialog(context);
return false;
} else {
radioBloc.resetSelection();
return true;
}
},
child: Scaffold(
resizeToAvoidBottomInset: true,
body: MultiBlocProvider(
// Define the providers for the OnboardingBloc and other blocs
providers: [
BlocProvider(
// Create an instance of the OnboardingBloc
create: (context) => LoginBloc(),
),
],
child: const LoginLayout(),
),
),
);
}

View File

@@ -11,6 +11,7 @@ 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 '../../../countrySelection/presentation/bloc/choose_country_bloc.dart';
import '../bloc/login_bloc.dart';
import '../bloc/login_event.dart';
import '../bloc/login_state.dart';
@@ -20,6 +21,7 @@ class BottomSection extends StatelessWidget {
@override
Widget build(BuildContext context) {
final radioBloc = context.read<RadioBloc>();
return Column(
children: [
const Gap(12),
@@ -39,9 +41,11 @@ class BottomSection extends StatelessWidget {
if (state is LoginLoading) {
Loader.loader(context);
} else if (state is LoginSuccess) {
goRouter.pop();
goRouter.goNamed('mainScreen');
successToastMessage(context, "login successful !");
goRouter.pop();
goRouter.goNamed(RouteName.biometricScreen);
} else if (state is LoginFailure) {
goRouter.pop();
errorToastMessage(
@@ -94,7 +98,10 @@ class BottomSection extends StatelessWidget {
const Gap(5),
ButtonWidget().textBtn(
function: () {
goRouter.goNamed(RouteName.loginScreen);
radioBloc.resetSelection();
goRouter.pushNamed(RouteName.registerStepScreen, pathParameters: {
"fromScreentype": "login",
});
},
text: TextWidget().tex14W700(AppText.signUpText,
clr: AppColor.textLabelColor,

View File

@@ -16,6 +16,9 @@ class LoginForm extends StatelessWidget {
Widget build(BuildContext context) {
final loginBloc = context.read<LoginBloc>();
// Reset fields when the screen is built
loginBloc.resetFields();
return BlocConsumer<RadioBloc, RadioState>(listener: (context, state) {
int selectedCountry = -1;
if (state is RadioSelectionChanged) {

View File

@@ -0,0 +1,53 @@
// otp_bloc.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:sms_autofill/sms_autofill.dart';
import 'otp_event.dart';
import 'otp_state.dart';
class OtpBloc extends Bloc<OtpEvent, OtpState> {
final TextEditingController otpController = TextEditingController();
OtpBloc() : super(OtpInitial()) {
on<StartListeningForOtp>(_onStartListeningForOtp);
on<OtpCodeChanged>(_onOtpCodeChanged);
on<OtpSubmit>(_onOtpSubmit);
}
void _onStartListeningForOtp(
StartListeningForOtp event, Emitter<OtpState> emit) {
_startListening();
}
void _onOtpCodeChanged(OtpCodeChanged event, Emitter<OtpState> emit) {
emit(OtpCodeReceived(event.code));
if (event.code.length == 6) {
add(OtpSubmit(event.code));
}
}
void _onOtpSubmit(OtpSubmit event, Emitter<OtpState> emit) async {
emit(OtpSubmitting());
try {
// Add your OTP verification logic here
await Future.delayed(const Duration(seconds: 2));
if (otpController.text == "123456") {
emit(OtpSubmissionSuccess());
} else {
emit(const OtpSubmissionFailure("Otp Invalid !"));
} // Simulate network call
} catch (e) {
emit(OtpSubmissionFailure(e.toString()));
}
}
void _startListening() {
SmsAutoFill().listenForCode(); // Correctly call listenForCode
}
@override
Future<void> close() {
otpController.dispose();
SmsAutoFill().unregisterListener();
return super.close();
}
}

View File

@@ -0,0 +1,29 @@
// otp_event.dart
import 'package:equatable/equatable.dart';
abstract class OtpEvent extends Equatable {
const OtpEvent();
@override
List<Object> get props => [];
}
class OtpCodeChanged extends OtpEvent {
final String code;
const OtpCodeChanged(this.code);
@override
List<Object> get props => [code];
}
class OtpSubmit extends OtpEvent {
final String code;
const OtpSubmit(this.code);
@override
List<Object> get props => [code];
}
class StartListeningForOtp extends OtpEvent {}

View File

@@ -0,0 +1,33 @@
// otp_state.dart
import 'package:equatable/equatable.dart';
abstract class OtpState extends Equatable {
const OtpState();
@override
List<Object> get props => [];
}
class OtpInitial extends OtpState {}
class OtpCodeReceived extends OtpState {
final String code;
const OtpCodeReceived(this.code);
@override
List<Object> get props => [code];
}
class OtpSubmitting extends OtpState {}
class OtpSubmissionSuccess extends OtpState {}
class OtpSubmissionFailure extends OtpState {
final String error;
const OtpSubmissionFailure(this.error);
@override
List<Object> get props => [error];
}

View File

@@ -0,0 +1,19 @@
import 'package:flutter/material.dart';
import 'package:tanami_app/features/otpVerification/presentation/widgets/otp_fill_section.dart';
import '../widgets/otp_top_section.dart';
class OtpLayout extends StatelessWidget {
const OtpLayout({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
children: const [
OtpTopSection(),
OtpFillSection(),
],
));
}
}

View File

@@ -0,0 +1,26 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:tanami_app/features/otpVerification/presentation/bloc/otp_bloc.dart';
import 'package:tanami_app/features/otpVerification/presentation/pages/otp_layout.dart';
import '../bloc/otp_event.dart';
class OtpScreen extends StatelessWidget {
const OtpScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: true,
body: MultiBlocProvider(
providers: [
BlocProvider(
// Create an instance of the OnboardingBloc
create: (context) => OtpBloc()..add(StartListeningForOtp()),
),
],
child: const OtpLayout(),
),
);
}
}

View File

@@ -0,0 +1,68 @@
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/routes.dart';
import 'package:tanami_app/core/styles/app_color.dart';
import 'package:tanami_app/shared/components/loader.dart';
import 'package:tanami_app/shared/components/toast_message.dart';
import '../bloc/otp_bloc.dart';
import '../bloc/otp_event.dart';
import '../bloc/otp_state.dart';
class OtpFillSection extends StatelessWidget {
const OtpFillSection({super.key});
@override
Widget build(BuildContext context) {
return BlocConsumer<OtpBloc, OtpState>(
listener: (context, state) {
if (state is OtpSubmitting) {
Loader.loader(context);
} else if (state is OtpSubmissionSuccess) {
goRouter.pop();
successToastMessage(context, 'OTP Verified Successfully!');
} else if (state is OtpSubmissionFailure) {
goRouter.pop();
errorToastMessage(context, 'OTP Verification Failed: ${state.error}');
}
},
builder: (context, state) {
final otpBloc = context.read<OtpBloc>();
return Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
PinFieldAutoFill(
controller: otpBloc.otpController,
currentCode: otpBloc.otpController.text,
decoration: BoxLooseDecoration(
textStyle: GoogleFonts.dmSans(
fontSize: 22,
color: AppColor.otpTextColor,
fontWeight: FontWeight.w700,
),
bgColorBuilder: const FixedColorBuilder(AppColor.fillColor),
radius: const Radius.circular(12),
strokeColorBuilder: const FixedColorBuilder(
AppColor.strokeColor,
)),
codeLength: 6,
onCodeChanged: (code) {
if (code != null) {
otpBloc.otpController.text = code;
otpBloc.add(OtpCodeChanged(code));
}
},
autoFocus: true,
),
],
),
);
},
);
}
}

View File

@@ -0,0 +1,40 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:gap/gap.dart';
import '../../../../core/styles/app_color.dart';
import '../../../../core/styles/app_images.dart';
import '../../../../core/styles/app_text.dart';
import '../../../../shared/components/text_widget.dart';
class OtpTopSection extends StatelessWidget {
const OtpTopSection({super.key});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Gap(85),
Center(
child: SvgPicture.asset(
AppImages.weclomeLogo,
),
),
const Gap(125),
TextWidget().tex20W700(
AppText.checkYourMessages,
clr: AppColor.charcoalColor,
),
const Gap(25),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 35),
child: TextWidget().tex14W500(
AppText.referToSameOtpMessage,
clr: AppColor.smokeGrayColor,
),
),
],
);
}
}

View File

@@ -0,0 +1,74 @@
import 'package:bloc/bloc.dart';
import 'package:flutter/material.dart';
import 'register_event.dart';
import 'register_state.dart';
class RegisterBloc extends Bloc<RegisterEvent, RegisterState> {
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
final TextEditingController countrySelectionTextField =
TextEditingController();
final TextEditingController phoneNumberTextField = TextEditingController();
final TextEditingController passwordTextField = TextEditingController();
GlobalKey<FormState> getFormKey() {
return formKey;
}
RegisterBloc() : super(RegisterInitial()) {
phoneNumberTextField.addListener(_onFormFieldChanged);
passwordTextField.addListener(_onFormFieldChanged);
countrySelectionTextField.addListener(_onFormFieldChanged);
on<RegisterFormChanged>(_onLoginFormChanged);
on<RegisterSubmitted>((event, emit) async {
if (!formKey.currentState!.validate()) {
return;
}
emit(RegisterLoading());
try {
// 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);
if (isSuccess) {
emit(RegisterSuccess());
} else {
emit(const RegisterFailure(
"Register failed. Please check your credentials."));
}
} catch (e) {
emit(RegisterFailure(e.toString()));
}
});
}
void _onFormFieldChanged() {
add(RegisterFormChanged(
phoneNumberTextField.text,
countrySelectionTextField.text,
));
}
void _onLoginFormChanged(
RegisterFormChanged event, Emitter<RegisterState> emit) {
final areFieldsFilled =
event.phoneNumber.isNotEmpty && event.country.isNotEmpty;
emit(RegisterFieldsState(areFieldsFilled));
}
// Mock API function, replace with actual API call
Future<bool> _mockLoginApi(
String phoneNumber,
String password,
String countryResidence,
) async {
return true;
}
@override
Future<void> close() {
phoneNumberTextField.dispose();
passwordTextField.dispose();
countrySelectionTextField.dispose();
return super.close();
}
}

View File

@@ -0,0 +1,33 @@
import 'package:equatable/equatable.dart';
abstract class RegisterEvent extends Equatable {
const RegisterEvent();
@override
List<Object> get props => [];
}
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];
}
class RegisterFormChanged extends RegisterEvent {
final String phoneNumber;
final String country;
const RegisterFormChanged(this.phoneNumber, this.country);
@override
List<Object> get props => [phoneNumber, country];
}

View File

@@ -0,0 +1,32 @@
import 'package:equatable/equatable.dart';
abstract class RegisterState extends Equatable {
const RegisterState();
@override
List<Object> get props => [];
}
class RegisterInitial extends RegisterState {}
class RegisterLoading extends RegisterState {}
class RegisterSuccess extends RegisterState {}
class RegisterFailure extends RegisterState {
final String error;
const RegisterFailure(this.error);
@override
List<Object> get props => [error];
}
class RegisterFieldsState extends RegisterState {
final bool areFieldsFilled;
const RegisterFieldsState(this.areFieldsFilled);
@override
List<Object> get props => [areFieldsFilled];
}

View File

@@ -0,0 +1,21 @@
import 'package:flutter/material.dart';
import 'package:tanami_app/features/register/presentation/widgets/register_bottom_section.dart';
import 'package:tanami_app/features/register/presentation/widgets/register_form.dart';
import 'package:tanami_app/features/register/presentation/widgets/register_top_section.dart';
class RegisterLayout extends StatelessWidget {
const RegisterLayout({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
//
children: const [
RegisterTopSection(),
RegisterForm(),
RegisterBottomSection(),
],
));
}
}

View File

@@ -0,0 +1,33 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:tanami_app/features/register/presentation/bloc/register_bloc.dart';
import '../../../countrySelection/presentation/bloc/choose_country_bloc.dart';
import 'register_layout.dart';
class RegisterScreen extends StatelessWidget {
const RegisterScreen({super.key});
@override
Widget build(BuildContext context) {
final radioBloc = context.read<RadioBloc>();
return WillPopScope(
onWillPop: () async {
radioBloc.resetSelection();
return true; // Allow the pop to happen
},
child: Scaffold(
resizeToAvoidBottomInset: true,
body: MultiBlocProvider(
providers: [
BlocProvider(
// Create an instance of the OnboardingBloc
create: (context) => RegisterBloc(),
),
],
child: const RegisterLayout(),
),
),
);
}
}

View File

@@ -0,0 +1,23 @@
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:tanami_app/features/register/presentation/widgets/register_step_count.dart';
import '../widgets/register_step_bottom_section.dart';
import '../widgets/register_step_top_section.dart';
class RegisterStepLayout extends StatelessWidget {
const RegisterStepLayout({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
children: const [
RegisterStepTopSection(),
Gap(20),
RegisterStepCount(),
RegisterStepBottomSection(),
],
));
}
}

View File

@@ -0,0 +1,30 @@
import 'package:flutter/material.dart';
import '../../../../shared/components/exit_app_dialog.dart';
import 'register_step_layout.dart';
class RegisterStepScreen extends StatelessWidget {
final String fromScreen;
const RegisterStepScreen({
super.key,
required this.fromScreen,
});
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
if (fromScreen == "welcome" || fromScreen == "login") {
exitAppDialog(context);
return false;
} else {
return true;
}
},
child: const Scaffold(
resizeToAvoidBottomInset: true,
body: RegisterStepLayout(),
),
);
}
}

View File

@@ -0,0 +1,106 @@
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/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 '../../../countrySelection/presentation/bloc/choose_country_bloc.dart';
import '../bloc/register_bloc.dart';
import '../bloc/register_event.dart';
import '../bloc/register_state.dart';
class RegisterBottomSection extends StatelessWidget {
const RegisterBottomSection({super.key});
@override
Widget build(BuildContext context) {
final radioBloc = context.read<RadioBloc>();
return Column(
children: [
const Gap(90),
Image.asset(
AppImages.stage1Image,
width: 75,
height: 12,
),
const Gap(36),
BlocConsumer<RegisterBloc, RegisterState>(
listener: (context, state) {
if (state is RegisterLoading) {
Loader.loader(context);
} else if (state is RegisterSuccess) {
successToastMessage(context, "successful !");
goRouter.pop();
goRouter.pushNamed(RouteName.otpScreen);
} else if (state is RegisterFailure) {
goRouter.pop();
errorToastMessage(
context,
state.error,
);
}
},
builder: (context, state) {
bool isButtonEnabled = false;
if (state is RegisterFieldsState) {
isButtonEnabled = state.areFieldsFilled;
} else if (state is RegisterSuccess || state is RegisterFailure) {
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<RegisterBloc>().add(
RegisterSubmitted(
context
.read<RegisterBloc>()
.phoneNumberTextField
.text,
context
.read<RegisterBloc>()
.passwordTextField
.text,
""),
)
: null;
},
text: AppText.nextText,
clr: isButtonEnabled
? AppColor.primaryColor2
: AppColor.inactiveBtnColor,
),
);
},
),
const Gap(5),
ButtonWidget().textBtn(
function: () {
radioBloc.resetSelection();
goRouter.pop();
},
text: TextWidget().tex14W700(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 'package:tanami_app/core/utils/constant/country_flag_data.dart';
import '../../../../shared/components/form_label_textfield.dart';
import '../../../countrySelection/presentation/bloc/choose_country_bloc.dart';
import '../../../countrySelection/presentation/bloc/choose_country_state.dart';
import '../bloc/register_bloc.dart';
class RegisterForm extends StatelessWidget {
const RegisterForm({super.key});
@override
Widget build(BuildContext context) {
final loginBloc = context.read<RegisterBloc>();
int selectedCountry = -1;
return BlocConsumer<RadioBloc, RadioState>(listener: (context, state) {
if (state is RadioSelectionChanged) {
selectedCountry = state.selectedIndex;
loginBloc.countrySelectionTextField.text = countryName[selectedCountry];
}
}, builder: (context, state) {
if (state is RadioSelectionChanged) {
selectedCountry = state.selectedIndex;
} else {
selectedCountry = -1;
}
return Form(
key: loginBloc.formKey,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 14,
),
child: Align(
alignment: Alignment.topLeft,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Gap(50),
FormLabelTextField(
prefixWidget: selectedCountry == -1
? null
: Image.asset(
countryFlag[selectedCountry],
width: 20,
height: 20,
),
hintText: AppText.chooseCountry,
title: AppText.countryOfResidence,
type: "country selection",
textEditingController: loginBloc.countrySelectionTextField,
),
const Gap(20),
FormLabelTextField(
prefixWidget: selectedCountry == -1
? null
: Image.asset(
countryFlag[selectedCountry],
width: 20,
height: 20,
),
hintText: "+0 (000) 000 00 00",
title: AppText.phoneNumber,
type: "phone number",
textEditingController: loginBloc.phoneNumberTextField,
),
],
),
),
),
);
});
}
}

View File

@@ -0,0 +1,54 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:gap/gap.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/shared/components/button_widget.dart';
import '../../../../core/styles/app_text.dart';
import '../../../../shared/components/text_widget.dart';
class RegisterStepBottomSection extends StatelessWidget {
const RegisterStepBottomSection({super.key});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 40.0),
child: Column(
children: [
Container(
margin: const EdgeInsets.symmetric(
horizontal: 16,
),
width: 1.sw,
height: 56.h,
child: ButtonWidget().elevatedBtn(
function: () {
goRouter.pushNamed(
RouteName.registerScreen,
);
},
text: AppText.getStarted,
txtClr: AppColor.plainWhite,
clr: AppColor.primaryColor2,
),
),
const Gap(16),
ButtonWidget().textBtn(
function: () {
goRouter.goNamed(RouteName.loginScreen, pathParameters: {
"fromScreen": "registerStep",
});
},
text: TextWidget().tex14W700(
AppText.loginText,
clr: AppColor.darkGreyColor,
textDecoration: TextDecoration.underline,
))
],
),
);
}
}

View File

@@ -0,0 +1,39 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:tanami_app/core/styles/app_color.dart';
import 'package:tanami_app/core/utils/constant/register_step_data.dart';
import 'package:tanami_app/shared/components/text_widget.dart';
class RegisterStepCount extends StatelessWidget {
const RegisterStepCount({super.key});
@override
Widget build(BuildContext context) {
return ListView.builder(
shrinkWrap: true,
itemCount: 3,
itemBuilder: (context, index) {
return Container(
margin: const EdgeInsets.symmetric(
horizontal: 27,
vertical: 5,
),
child: ListTile(
isThreeLine: true,
leading: SvgPicture.asset(stepImage[index]),
title: TextWidget().tex14W700(
title[index],
clr: AppColor.textLabelColor,
txtAlign: TextAlign.start,
),
subtitle: TextWidget().tex14W500(
description[index],
clr: AppColor.textLabelColor,
txtAlign: TextAlign.start,
),
),
);
},
);
}
}

View File

@@ -0,0 +1,39 @@
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 RegisterStepTopSection extends StatelessWidget {
const RegisterStepTopSection({super.key});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Gap(85),
Center(
child: SvgPicture.asset(
AppImages.weclomeLogo,
),
),
const Gap(30),
TextWidget().tex20W700(
AppText.getStarted,
clr: AppColor.charcoalColor,
),
const Gap(10),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 75),
child: TextWidget().tex14W500(
AppText.setupYourTanamiAccountToBegin,
clr: AppColor.smokeGrayColor,
),
),
],
);
}
}

View File

@@ -0,0 +1,39 @@
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 RegisterTopSection extends StatelessWidget {
const RegisterTopSection({super.key});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Gap(85),
Center(
child: SvgPicture.asset(
AppImages.weclomeLogo,
),
),
const Gap(60),
TextWidget().tex20W700(
AppText.welcome,
clr: AppColor.charcoalColor,
),
const Gap(10),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 75),
child: TextWidget().tex14W500(
AppText.selectYourCountryOfResidence,
clr: AppColor.smokeGrayColor,
),
),
],
);
}
}

View File

@@ -25,7 +25,11 @@ class LoginSignUpButton extends StatelessWidget {
width: 1.sw,
height: 56.h,
child: ButtonWidget().elevatedBtn(
function: () {},
function: () {
goRouter.goNamed(RouteName.registerStepScreen, pathParameters: {
"fromScreentype": "welcome",
});
},
text: AppText.signUpText,
txtClr: AppColor.plainWhite,
clr: AppColor.primaryColor,
@@ -34,11 +38,15 @@ class LoginSignUpButton extends StatelessWidget {
const Gap(16),
ButtonWidget().textBtn(
function: () {
goRouter.goNamed(RouteName.loginScreen);
goRouter.goNamed(RouteName.loginScreen, pathParameters: {
"fromScreen": "welcome",
});
},
text: TextWidget().tex14W700(AppText.loginText,
clr: AppColor.darkGreyColor,
textDecoration: TextDecoration.underline))
text: TextWidget().tex14W700(
AppText.loginText,
clr: AppColor.darkGreyColor,
textDecoration: TextDecoration.underline,
))
],
),
);