portfolio done pics2
This commit is contained in:
@@ -9,6 +9,10 @@ class RouteName {
|
||||
//Login
|
||||
static const String loginScreen = 'loginScreen';
|
||||
|
||||
//Register
|
||||
static const String registerStepScreen = 'registerStepScreen';
|
||||
static const String registerScreen = 'registerScreen';
|
||||
|
||||
//No Internet
|
||||
static const String noInternetScreen = 'noInternet';
|
||||
|
||||
@@ -23,4 +27,11 @@ class RouteName {
|
||||
|
||||
//Portfolio details
|
||||
static const String porfolioDetails = 'porfolioDetails';
|
||||
|
||||
//Biometric
|
||||
static const String biometricScreen = 'biometricScreen';
|
||||
|
||||
//Biometric
|
||||
static const String otpScreen = 'otpScreen';
|
||||
|
||||
}
|
||||
|
||||
@@ -3,8 +3,15 @@
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:tanami_app/core/routes/route_name.dart';
|
||||
import 'package:tanami_app/features/MainScreens/MainScreen.dart';
|
||||
|
||||
import 'package:tanami_app/features/MainScreens/Portfolio/presentation/pages/detailsScreen.dart';
|
||||
|
||||
import 'package:tanami_app/features/biometric/presentation/pages/biometric_screen.dart';
|
||||
|
||||
import 'package:tanami_app/features/countrySelection/presentation/pages/choose_country_screen.dart';
|
||||
import 'package:tanami_app/features/otpVerification/presentation/pages/otp_screen.dart';
|
||||
import 'package:tanami_app/features/register/presentation/pages/register_screen.dart';
|
||||
import 'package:tanami_app/features/register/presentation/pages/register_step_screen.dart';
|
||||
import 'package:tanami_app/features/welcome/presentation/pages/weclome_screen.dart';
|
||||
|
||||
import '../../features/login/presentation/pages/login_screen.dart';
|
||||
@@ -20,55 +27,80 @@ final goRouter = GoRouter(
|
||||
//errorBuilder: (context, state) => ErrorScreen(state.error),
|
||||
routes: [
|
||||
GoRoute(
|
||||
name: "splash",
|
||||
path: RouteName.splashScreen,
|
||||
builder: (context, state) {
|
||||
return const SplashScreen();
|
||||
},
|
||||
// redirect: (context, state) {
|
||||
// if (true) {
|
||||
// return "/login";
|
||||
// }
|
||||
// return "/";
|
||||
// },
|
||||
routes: [
|
||||
GoRoute(
|
||||
name: RouteName.loginScreen,
|
||||
path: RouteName.loginScreen,
|
||||
builder: (context, state) {
|
||||
return const LoginScreen();
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
name: RouteName.welcomeScreen,
|
||||
path: RouteName.welcomeScreen,
|
||||
builder: (context, state) {
|
||||
return const WelcomeScreen();
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
name: RouteName.chooseCountryScreen,
|
||||
path: RouteName.chooseCountryScreen,
|
||||
builder: (context, state) {
|
||||
return const ChooseCountryScreen();
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
name: RouteName.mainScreen,
|
||||
path: RouteName.mainScreen,
|
||||
builder: (context, state) {
|
||||
return const MainScreen();
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
name: "splash",
|
||||
path: RouteName.splashScreen,
|
||||
builder: (context, state) {
|
||||
return const SplashScreen();
|
||||
},
|
||||
routes: [
|
||||
GoRoute(
|
||||
name: RouteName.loginScreen,
|
||||
path: "${RouteName.loginScreen}/:fromScreen",
|
||||
builder: (context, state) {
|
||||
return LoginScreen(
|
||||
fromScreen: state.pathParameters["fromScreen"]!,
|
||||
);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
name: RouteName.registerStepScreen,
|
||||
path: "${RouteName.registerStepScreen}/:fromScreentype",
|
||||
builder: (context, state) {
|
||||
return RegisterStepScreen(
|
||||
fromScreen: state.pathParameters["fromScreentype"]!,
|
||||
);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
name: RouteName.welcomeScreen,
|
||||
path: RouteName.welcomeScreen,
|
||||
builder: (context, state) {
|
||||
return const WelcomeScreen();
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
name: RouteName.chooseCountryScreen,
|
||||
path: RouteName.chooseCountryScreen,
|
||||
builder: (context, state) {
|
||||
return const ChooseCountryScreen();
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
name: RouteName.mainScreen,
|
||||
path: RouteName.mainScreen,
|
||||
builder: (context, state) {
|
||||
return const MainScreen();
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
name: RouteName.biometricScreen,
|
||||
path: RouteName.biometricScreen,
|
||||
builder: (context, state) {
|
||||
return const BiometricScreen();
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
name: RouteName.registerScreen,
|
||||
path: RouteName.registerScreen,
|
||||
builder: (context, state) {
|
||||
return const RegisterScreen();
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
name: RouteName.otpScreen,
|
||||
path: RouteName.otpScreen,
|
||||
builder: (context, state) {
|
||||
return const OtpScreen();
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
name: RouteName.porfolioDetails,
|
||||
path: RouteName.porfolioDetails,
|
||||
builder: (context, state) {
|
||||
return const DetailsScreen();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
]),
|
||||
|
||||
// GoRoute(
|
||||
// path: '/profile/:userId',
|
||||
|
||||
@@ -36,4 +36,9 @@ class AppColor {
|
||||
|
||||
//Radio Color
|
||||
static const Color radioActiveColor = Color(0xFF0B8933);
|
||||
|
||||
//Otp Color
|
||||
static const Color strokeColor = Color(0xFFB4B4B4);
|
||||
static const Color otpTextColor = Color(0xFF191B1E);
|
||||
static const Color fillColor = Color(0xFFF6F6F6);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,16 @@ class AppImages {
|
||||
"assets/images/auth_screen/svg/hide_password.svg";
|
||||
static const String showPassword =
|
||||
"assets/images/auth_screen/svg/show_password.svg";
|
||||
static const String step1Image =
|
||||
"assets/images/auth_screen/svg/first_step.svg";
|
||||
static const String step2Image =
|
||||
"assets/images/auth_screen/svg/second_step.svg";
|
||||
static const String step3Image =
|
||||
"assets/images/auth_screen/svg/third_step.svg";
|
||||
static const String stage1Image =
|
||||
"assets/images/auth_screen/png/stage_one.png";
|
||||
static const String stage2Image =
|
||||
"assets/images/auth_screen/png/stage_two.png";
|
||||
|
||||
//Country Flag
|
||||
static const String bahrainFlag =
|
||||
@@ -35,4 +45,15 @@ class AppImages {
|
||||
"assets/images/country_flag/png/saudi_arabia_flag.png";
|
||||
static const String unitedArabEmiratesFlag =
|
||||
"assets/images/country_flag/png/uae_flag.png";
|
||||
|
||||
//Biometric Screen
|
||||
static const String biometricBg =
|
||||
"assets/images/biometric_screen/svg/biometric_bg.svg";
|
||||
static const String biometricFingerprint =
|
||||
"assets/images/biometric_screen/png/biometric_fingerprint.png";
|
||||
static const String biometricFace =
|
||||
"assets/images/biometric_screen/png/biomertric_face.png";
|
||||
|
||||
//Dialog
|
||||
static const String exitAppIcon = "assets/images/dialog/svg/exit_icon.svg";
|
||||
}
|
||||
|
||||
@@ -29,6 +29,26 @@ class AppText {
|
||||
static const String invalidPassword = "Invalid Password";
|
||||
static const String forgorPassword = "Forgot Password";
|
||||
|
||||
//Register
|
||||
static const String getStartedToday = "Get started today";
|
||||
static const String setupYourTanamiAccountToBegin =
|
||||
"Setup your Tanami account to begin investing in a few simple steps";
|
||||
static const String step1 = "Step 1";
|
||||
static const String step2 = "Step 2";
|
||||
static const String step3 = "Step 3";
|
||||
static const String enterYourCountryOfResidence =
|
||||
"Enter your country of residence and mobile number";
|
||||
static const String enterNameEmailPassword =
|
||||
"Enter your name, email and password";
|
||||
static const String enableBiometricAuthentication =
|
||||
"Enable biometric authentication and select a unique pin code for easy access";
|
||||
static const String getStarted = "Get started";
|
||||
static const String welcome = "Welcome!";
|
||||
static const String selectYourCountryOfResidence =
|
||||
"Select your country of residence and enter your mobile number";
|
||||
static const String nextText = "Next";
|
||||
static const String backText = "Back";
|
||||
|
||||
//Country Name
|
||||
static const String bahrainCountryText = "Bahrain";
|
||||
static const String kuwaitCountryText = "Kuwait";
|
||||
@@ -45,4 +65,17 @@ class AppText {
|
||||
static const String totalreturn = "Total return";
|
||||
static const String disttodate = "Distributions to date";
|
||||
static const String includeddocs = "Included Documents";
|
||||
|
||||
//Dialog
|
||||
static const String exitText = "Exit";
|
||||
static const String cancelText = "Cancel";
|
||||
static const String areYouSureYouWantToExitText =
|
||||
"Are you sure you want to Exit?";
|
||||
|
||||
//OTP
|
||||
static const String checkYourMessages = "Check your messages";
|
||||
static const String referToSameOtpMessage =
|
||||
"Please refer to the same OTP message shown below";
|
||||
static const String resendSms = "Resend SMS";
|
||||
|
||||
}
|
||||
|
||||
20
lib/core/utils/constant/register_step_data.dart
Normal file
20
lib/core/utils/constant/register_step_data.dart
Normal file
@@ -0,0 +1,20 @@
|
||||
import 'package:tanami_app/core/styles/app_images.dart';
|
||||
import 'package:tanami_app/core/styles/app_text.dart';
|
||||
|
||||
List<String> title = [
|
||||
AppText.step1,
|
||||
AppText.step2,
|
||||
AppText.step3,
|
||||
];
|
||||
|
||||
List<String> description = [
|
||||
AppText.enterYourCountryOfResidence,
|
||||
AppText.enterNameEmailPassword,
|
||||
AppText.enableBiometricAuthentication,
|
||||
];
|
||||
|
||||
List<String> stepImage = [
|
||||
AppImages.step1Image,
|
||||
AppImages.step2Image,
|
||||
AppImages.step3Image,
|
||||
];
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
53
lib/features/otpVerification/presentation/bloc/otp_bloc.dart
Normal file
53
lib/features/otpVerification/presentation/bloc/otp_bloc.dart
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -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 {}
|
||||
@@ -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];
|
||||
}
|
||||
@@ -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(),
|
||||
],
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -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(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
74
lib/features/register/presentation/bloc/register_bloc.dart
Normal file
74
lib/features/register/presentation/bloc/register_bloc.dart
Normal 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();
|
||||
}
|
||||
}
|
||||
33
lib/features/register/presentation/bloc/register_event.dart
Normal file
33
lib/features/register/presentation/bloc/register_event.dart
Normal 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];
|
||||
}
|
||||
32
lib/features/register/presentation/bloc/register_state.dart
Normal file
32
lib/features/register/presentation/bloc/register_state.dart
Normal 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];
|
||||
}
|
||||
@@ -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(),
|
||||
],
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -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(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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(),
|
||||
],
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -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(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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)),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
))
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
))
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -6,6 +6,7 @@ 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
|
||||
@@ -53,7 +54,10 @@ class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
|
||||
providers: [
|
||||
BlocProvider(
|
||||
create: (context) => RadioBloc(),
|
||||
)
|
||||
),
|
||||
BlocProvider(
|
||||
create: (context) => PasswordVisibilityBloc(),
|
||||
),
|
||||
],
|
||||
child: ScreenUtilInit(
|
||||
builder: (BuildContext context, Widget? child) => MaterialApp.router(
|
||||
|
||||
119
lib/shared/components/exit_app_dialog.dart
Normal file
119
lib/shared/components/exit_app_dialog.dart
Normal file
@@ -0,0 +1,119 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.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';
|
||||
|
||||
exitAppDialog(
|
||||
context,
|
||||
) {
|
||||
return showDialog(
|
||||
context: context,
|
||||
builder: (context) => Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
AlertDialog(
|
||||
insetPadding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
backgroundColor: const Color(0XFFFFFFFF),
|
||||
//contentPadding: EdgeInsets.fromLTRB(96, 32, 96, 28),
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(20)),
|
||||
side: BorderSide(color: Color(0XFFFFFFFF)),
|
||||
),
|
||||
content: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
//sizedBoxHeight(32.h),
|
||||
Align(
|
||||
alignment: Alignment.center,
|
||||
child: CircleAvatar(
|
||||
radius: 25,
|
||||
backgroundColor: AppColor.inactiveBtnColor,
|
||||
child: Center(
|
||||
child: SvgPicture.asset(
|
||||
AppImages.exitAppIcon,
|
||||
width: 30,
|
||||
height: 30,
|
||||
fit: BoxFit.contain,
|
||||
color: AppColor.primaryColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 22.h,
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
AppText.areYouSureYouWantToExitText,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: Colors.black,
|
||||
fontSize: 22.sp,
|
||||
//fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Gap(21.h),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Container(
|
||||
height: 48.h,
|
||||
width: 140.w,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: AppColor.primaryColor,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(10.h),
|
||||
color: AppColor.plainWhite,
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
AppText.cancelText,
|
||||
style: TextStyle(
|
||||
color: AppColor.primaryColor, fontSize: 18.sp),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Gap(15.w),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
SystemNavigator.pop();
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Container(
|
||||
height: 48.h,
|
||||
width: 140.w,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10.h),
|
||||
color: AppColor.primaryColor),
|
||||
child: Center(
|
||||
child: Text(
|
||||
AppText.exitText,
|
||||
style: TextStyle(
|
||||
color: AppColor.plainWhite, fontSize: 18.sp),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -13,23 +13,15 @@ class TextWidget {
|
||||
}
|
||||
|
||||
//Text Size 14
|
||||
Widget tex14W700(String text, {Color? clr, TextDecoration? textDecoration}) {
|
||||
return Text(text,
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.dmSans(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w700,
|
||||
decoration: textDecoration ?? TextDecoration.none,
|
||||
color: clr ?? AppColor.plainWhite));
|
||||
}
|
||||
|
||||
Widget tex14W500(
|
||||
String text, {
|
||||
Color? clr,
|
||||
TextDecoration? textDecoration,
|
||||
TextAlign? txtAlign,
|
||||
}) {
|
||||
return Text(text,
|
||||
textAlign: TextAlign.center,
|
||||
textAlign: txtAlign ?? TextAlign.center,
|
||||
style: GoogleFonts.dmSans(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
@@ -37,6 +29,17 @@ class TextWidget {
|
||||
color: clr ?? AppColor.plainWhite));
|
||||
}
|
||||
|
||||
Widget tex14W700(String text,
|
||||
{Color? clr, TextDecoration? textDecoration, TextAlign? txtAlign}) {
|
||||
return Text(text,
|
||||
textAlign: txtAlign ?? TextAlign.center,
|
||||
style: GoogleFonts.dmSans(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w700,
|
||||
decoration: textDecoration ?? TextDecoration.none,
|
||||
color: clr ?? AppColor.plainWhite));
|
||||
}
|
||||
|
||||
//Text Size 15
|
||||
Widget tex15W500(
|
||||
String text, {
|
||||
|
||||
Reference in New Issue
Block a user