auth UI and Api

This commit is contained in:
mohammad
2024-07-30 16:36:12 +03:00
parent 3eb3ed10c9
commit 7e9b24a95b
34 changed files with 1773 additions and 510 deletions

View File

@ -0,0 +1,195 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/auth/forget_password/bloc/forget_password_event.dart';
import 'package:syncrow_web/pages/auth/forget_password/bloc/forget_password_state.dart';
import 'package:syncrow_web/services/auth_api.dart';
class ForgetPasswordBloc
extends Bloc<ForgetPasswordEvent, ForgetPasswordState> {
ForgetPasswordBloc() : super(InitialForgetState()) {
on<ChangePasswordEvent>(changePassword);
on<StartTimerEvent>(_onStartTimer);
on<StopTimerEvent>(_onStopTimer);
on<UpdateTimerEvent>(_onUpdateTimer);
}
void _onUpdateTimer(UpdateTimerEvent event, Emitter<ForgetPasswordState> emit) {
emit(TimerState(
isButtonEnabled: event.isButtonEnabled,
remainingTime: event.remainingTime));
}
final TextEditingController emailController = TextEditingController();
final TextEditingController passwordController = TextEditingController();
final TextEditingController otp = TextEditingController();
final formKey = GlobalKey<FormState>();
Timer? _timer;
int _remainingTime = 0;
Future<void> _onStartTimer(
StartTimerEvent event, Emitter<ForgetPasswordState> emit) async {
if (_validateInputs(emit)) return;
print("StartTimerEvent received");
if (_timer != null && _timer!.isActive) {
print("Timer is already active");
return;
}
_remainingTime = 60;
add(UpdateTimerEvent(
remainingTime: _remainingTime, isButtonEnabled: false));
print("Timer started, initial remaining time: $_remainingTime");
await AuthenticationAPI.sendOtp(email: emailController.text);
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
_remainingTime--;
print("Timer tick, remaining time: $_remainingTime"); // Debug print
if (_remainingTime <= 0) {
_timer?.cancel();
add(const UpdateTimerEvent(remainingTime: 0, isButtonEnabled: true));
print("Timer finished"); // Debug print
} else {
add(UpdateTimerEvent(remainingTime: _remainingTime, isButtonEnabled: false));
}
});
}
void _onStopTimer(StopTimerEvent event, Emitter<ForgetPasswordState> emit) {
_timer?.cancel();
emit(TimerState(isButtonEnabled: true, remainingTime: 0));
}
Future<void> changePassword(
ChangePasswordEvent event, Emitter<ForgetPasswordState> emit) async {
try {
emit(LoadingForgetState());
await AuthenticationAPI.forgetPassword(
password: passwordController.text, email: emailController.text);
emit(SuccessForgetState());
} catch (failure) {
print(failure);
emit(FailureForgetState(error: failure.toString()));
}
}
String? validateEmail(String? value) {
if (value == null || value.isEmpty) {
return 'Email is required';
} else if (!RegExp(r'^[^@]+@[^@]+\.[^@]+').hasMatch(value)) {
return 'Enter a valid email address';
}
return null;
}
String? validateRegion(String? value) {
if (value == null || value.isEmpty) {
return 'Please select a region';
}
return null;
}
String? otpValidate(String? value) {
if (value == null || value.isEmpty) {
return 'Please select a region';
}
return null;
}
String? passwordValidator(String? value) {
if (value == null || value.isEmpty) {
return 'Please enter your password';
}
List<String> validationErrors = [];
if (!RegExp(r'^(?=.*[a-z])').hasMatch(value)) {
validationErrors.add(' - one lowercase letter');
}
if (!RegExp(r'^(?=.*[A-Z])').hasMatch(value)) {
validationErrors.add(' - one uppercase letter');
}
if (!RegExp(r'^(?=.*\d)').hasMatch(value)) {
validationErrors.add(' - one number');
}
if (!RegExp(r'^(?=.*[@$!%*?&])').hasMatch(value)) {
validationErrors.add(' - one special character');
}
if (value.length < 8) {
validationErrors.add(' - minimum 8 characters');
}
if (validationErrors.isNotEmpty) {
return 'Password must contain at least:\n${validationErrors.join('\n')}';
}
return null;
}
final List<String> regions = [
'North America',
'South America',
'Europe',
'Asia',
'Africa',
'Australia',
'Antarctica',
];
bool _validateInputs(Emitter<ForgetPasswordState> emit) {
emit(LoadingForgetState());
final nameError = validateEmail(emailController.text);
if (nameError != null) {
emit(FailureForgetState(error:nameError )) ;
// CustomSnackBar.displaySnackBar(nameError);
return true;
}
return false;
}
}
//
//
// _login(LoginButtonPressed event, Emitter<LoginState> emit) async {
// emit(LoginLoading());
// if(isChecked) {
// try {
// if (event.username.isEmpty || event.password.isEmpty) {
// CustomSnackBar.displaySnackBar('Please enter your credentials');
// emit(const LoginFailure(error: 'Something went wrong'));
// return;
// }
// token = await AuthenticationAPI.loginWithEmail(
// model: LoginWithEmailModel(
// email: event.username,
// password: event.password,
// ),
// );
// }
// catch (failure) {
// emit(const LoginFailure(error: 'Something went wrong'));
// // emit(LoginFailure(error: failure.toString()));
// return;
// }
// if (token.accessTokenIsNotEmpty) {
// debugPrint('token: ${token.accessToken}');
// FlutterSecureStorage storage = const FlutterSecureStorage();
// await storage.write(
// key: Token.loginAccessTokenKey, value: token.accessToken);
//
// const FlutterSecureStorage().write(
// key: UserModel.userUuidKey,
// value: Token.decodeToken(token.accessToken)['uuid'].toString()
// );
// user = UserModel.fromToken(token);
// emailController.clear();
// passwordController.clear();
// emit(LoginSuccess());
// } else {
// emit(const LoginFailure(error: 'Something went wrong'));
// }
// }
// else{
// emit(const LoginFailure(error: 'Accept terms and condition'));
// }
// }

View File

@ -0,0 +1,26 @@
import 'package:equatable/equatable.dart';
abstract class ForgetPasswordEvent extends Equatable {
const ForgetPasswordEvent();
@override
List<Object> get props => [];
}
class GetCodeEvent extends ForgetPasswordEvent{}
class SubmitEvent extends ForgetPasswordEvent{}
class StartTimerEvent extends ForgetPasswordEvent{}
class StopTimerEvent extends ForgetPasswordEvent{}
class UpdateTimerEvent extends ForgetPasswordEvent {
final int remainingTime;
final bool isButtonEnabled;
const UpdateTimerEvent({required this.remainingTime, required this.isButtonEnabled});
}
class ChangePasswordEvent extends ForgetPasswordEvent{}
class SendOtpEvent extends ForgetPasswordEvent{}

View File

@ -0,0 +1,35 @@
import 'package:equatable/equatable.dart';
class ForgetPasswordState extends Equatable{
const ForgetPasswordState();
@override
List<Object> get props => [];
}
class InitialForgetState extends ForgetPasswordState{}
class LoadingForgetState extends ForgetPasswordState{}
class SuccessForgetState extends ForgetPasswordState{}
class FailureForgetState extends ForgetPasswordState {
final String error;
const FailureForgetState({required this.error});
@override
List<Object> get props => [error];
}
class TimerState extends ForgetPasswordState {
final bool isButtonEnabled ;
final int remainingTime;
const TimerState({required this.isButtonEnabled, required this.remainingTime});
@override
List<Object> get props => [isButtonEnabled, remainingTime];
}

View File

@ -0,0 +1,11 @@
import 'package:flutter/material.dart';
class ForgetPasswordMobilePage extends StatelessWidget {
const ForgetPasswordMobilePage({super.key});
@override
Widget build(BuildContext context) {
return const Placeholder();
}
}

View File

@ -0,0 +1,20 @@
import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/auth/forget_password/view/forget_password_mobile_page.dart';
import 'package:syncrow_web/pages/auth/forget_password/view/forget_password_web_page.dart';
import 'package:syncrow_web/utils/responsive_layout.dart';
class ForgetPasswordPage extends StatelessWidget {
const ForgetPasswordPage({super.key});
@override
Widget build(BuildContext context) {
return const ResponsiveLayout(
desktopBody: ForgetPasswordWebPage(),
mobileBody:ForgetPasswordMobilePage()
);
}
}

View File

@ -0,0 +1,271 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/pages/auth/forget_password/bloc/forget_password_bloc.dart';
import 'package:syncrow_web/pages/auth/forget_password/bloc/forget_password_event.dart';
import 'package:syncrow_web/pages/auth/forget_password/bloc/forget_password_state.dart';
import 'package:syncrow_web/pages/auth/login/view/login_page.dart';
import 'package:syncrow_web/pages/common/default_button.dart';
import 'package:syncrow_web/pages/common/first_layer.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/style.dart';
class ForgetPasswordWebPage extends StatelessWidget {
const ForgetPasswordWebPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: BlocProvider(
create: (context) => ForgetPasswordBloc(),
child: BlocConsumer<ForgetPasswordBloc, ForgetPasswordState>(
listener: (context, state) {
if (state is LoadingForgetState) {
} else if (state is FailureForgetState) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(state.error),
),
);
}
},
builder: (context, state) {
if (state is LoadingForgetState) {
return const Center(child: CircularProgressIndicator());
} else {
return _buildForm(context, state);
}
},
),
),
);
}
Widget _buildForm(BuildContext context, ForgetPasswordState state) {
final forgetBloc = BlocProvider.of<ForgetPasswordBloc>(context);
return FirstLayer(
second: Container(
margin: const EdgeInsets.all(50),
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.3),
borderRadius: const BorderRadius.all(Radius.circular(20)),
),
child: Center(
child: ListView(
shrinkWrap: true,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Spacer(),
Expanded(
flex: 2,
child: SvgPicture.asset(
Assets.loginLogo,
),
),
const Spacer(),
Container(
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.1),
borderRadius: const BorderRadius.all(Radius.circular(30)),
border: Border.all(color: ColorsManager.graysColor.withOpacity(0.2)),
),
child: Form(
key: forgetBloc.formKey,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 50, vertical: 20),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
const SizedBox(height: 15),
const Text(
'Forget Password',
style: TextStyle(
color: Colors.white,
fontSize: 24,
fontWeight: FontWeight.bold),
),
Text(
'Please fill in your account information to retrieve your password',
style: smallTextStyle,
),
const SizedBox(height: 30),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
"Country/Region",
style: smallTextStyle,
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.2,
child: DropdownButtonFormField<String>(
validator: forgetBloc.validateRegion,
icon: const Icon(
Icons.keyboard_arrow_down_outlined,
),
decoration: textBoxDecoration()!.copyWith(
hintText: null,
),
hint: const Align(
alignment: Alignment.centerLeft,
child: Text(
'Select your region/country',
textAlign: TextAlign.center,
),
),
isDense: true,
style: const TextStyle(color: Colors.black),
items: forgetBloc.regions.map((String region) {
return DropdownMenuItem<String>(
value: region,
child: Text(region),
);
}).toList(),
onChanged: (String? value) {
print(value);
},
),
)
],
),
const SizedBox(height: 20.0),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
"Account",
style: smallTextStyle,
),
SizedBox(
width: MediaQuery.sizeOf(context).width * 0.2,
child: TextFormField(
validator: forgetBloc.validateEmail,
controller: forgetBloc.emailController,
decoration: textBoxDecoration()!.copyWith(hintText: 'Enter your email'),
style: const TextStyle(color: Colors.black),
),
),
],
),
const SizedBox(height: 20.0),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text("One Time Password",
style: smallTextStyle,),
SizedBox(
width: MediaQuery.sizeOf(context).width * 0.2,
child: TextFormField(
validator: forgetBloc.otpValidate,
keyboardType: TextInputType.visiblePassword,
controller: forgetBloc.otp,
decoration: textBoxDecoration()!.copyWith(
hintText: 'Enter Code',
suffixIcon: SizedBox(
width: 100,
child: Center(
child: InkWell(
onTap: () {
BlocProvider.of<ForgetPasswordBloc>(context).add(StartTimerEvent());
},
child: Text(
'Get Code ${state is TimerState && !state.isButtonEnabled ? "(${state.remainingTime.toString()})" : ""}',
style: TextStyle(
color: state is TimerState && !state.isButtonEnabled
? Colors.grey
: ColorsManager.btnColor,
),
),
),
),
),
),
style: const TextStyle(color: Colors.black),
),
),
],
),
const SizedBox(height: 20.0),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text("Password",
style: smallTextStyle,),
SizedBox(
width: MediaQuery.sizeOf(context).width * 0.2,
child: TextFormField(
validator: forgetBloc.passwordValidator,
keyboardType: TextInputType.visiblePassword,
controller: forgetBloc.passwordController,
decoration: textBoxDecoration()!.copyWith(
hintText: 'At least 8 characters',
),
style: const TextStyle(color: Colors.black),
),
),
],
),
const SizedBox(
height: 10,
),
const SizedBox(height: 20.0),
SizedBox(
width: MediaQuery.sizeOf(context).width * 0.2,
child: DefaultButton(
backgroundColor: ColorsManager.btnColor,
child: const Text('Submit'),
onPressed: () {
if (forgetBloc.formKey.currentState!.validate()) {
forgetBloc.add(ChangePasswordEvent());
}
},
),
),
SizedBox(height: 10,),
SizedBox(
width: MediaQuery.sizeOf(context).width * 0.2,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Flexible(
child: Text(
"Do you have an account? ",
style: TextStyle(color: Colors.white),
)),
InkWell(
onTap: () {
Navigator.pop(context);
},
child: const Flexible(
child: Text(
"Sign in",
)),
),
],
),
)
],
),
),
),
),
const Spacer(),
],
),
],
),
),
),
);
}
}

View File

@ -1,58 +1,156 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:syncrow_web/pages/auth/bloc/login_event.dart';
import 'package:syncrow_web/pages/auth/bloc/login_state.dart';
import 'package:syncrow_web/pages/auth/model/login_with_email_model.dart';
import 'package:syncrow_web/pages/auth/model/token.dart';
import 'package:syncrow_web/pages/auth/model/user_model.dart';
import 'package:syncrow_web/pages/auth/login/bloc/login_event.dart';
import 'package:syncrow_web/pages/auth/login/bloc/login_state.dart';
import 'package:syncrow_web/pages/auth/login/model/login_with_email_model.dart';
import 'package:syncrow_web/pages/auth/login/model/token.dart';
import 'package:syncrow_web/pages/auth/login/model/user_model.dart';
import 'package:syncrow_web/services/auth_api.dart';
import 'package:syncrow_web/utils/snack_bar.dart';
class LoginBloc extends Bloc<LoginEvent, LoginState> {
LoginBloc() : super(LoginInitial()) {
on<LoginButtonPressed>(_login);
on<CheckBoxEvent>(checkBoxTgl);
}
final TextEditingController emailController = TextEditingController();
final TextEditingController passwordController = TextEditingController();
final formKey = GlobalKey<FormState>();
bool isChecked = false;
bool obscureText = true;
_login(LoginButtonPressed event, Emitter<LoginState> emit) async {
emit(LoginLoading());
if(isChecked) {
try {
if (event.username.isEmpty || event.password.isEmpty) {
CustomSnackBar.displaySnackBar('Please enter your credentials');
emit(const LoginFailure(error: 'Something went wrong'));
return;
}
token = await AuthenticationAPI.loginWithEmail(
model: LoginWithEmailModel(
email: event.username,
password: event.password,
),
);
}
catch (failure) {
emit(const LoginFailure(error: 'Something went wrong'));
// emit(LoginFailure(error: failure.toString()));
return;
}
if (token.accessTokenIsNotEmpty) {
debugPrint('token: ${token.accessToken}');
FlutterSecureStorage storage = const FlutterSecureStorage();
await storage.write(
key: Token.loginAccessTokenKey, value: token.accessToken);
const FlutterSecureStorage().write(
key: UserModel.userUuidKey,
value: Token.decodeToken(token.accessToken)['uuid'].toString()
);
user = UserModel.fromToken(token);
emailController.clear();
passwordController.clear();
emit(LoginSuccess());
} else {
emit(const LoginFailure(error: 'Something went wrong'));
}
}
else{
emit(const LoginFailure(error: 'Accept terms and condition'));
}
}
checkBoxTgl(CheckBoxEvent event, Emitter<LoginState> emit,){
emit(LoginLoading());
isChecked = event.newValue!;
emit(LoginInitial());
}
void launchURL(String url) {
if (kDebugMode) {
print('Launching URL: $url');
}
}
String fullName = '';
String email = '';
String forgetPasswordEmail = '';
String signUpPassword = '';
String newPassword = '';
String maskedEmail = '';
String otpCode = '';
final loginFormKey = GlobalKey<FormState>();
final signUpFormKey = GlobalKey<FormState>();
final checkEmailFormKey = GlobalKey<FormState>();
final createNewPasswordKey = GlobalKey<FormState>();
static Token token = Token.emptyConstructor();
static UserModel? user;
bool isPasswordVisible = false;
bool showValidationMessage = false;
/////////////////////////////////////VALIDATORS/////////////////////////////////////
String? passwordValidator(String? value) {
if (value != null) {
if (value.isEmpty) {
return 'Please enter your password';
}
if (value.isNotEmpty) {
if (!RegExp(r'^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$')
.hasMatch(value)) {
return 'Password must contain at least:\n - one uppercase letter.\n - one lowercase letter.\n - one number. \n - special character';
}
}
String? validatePassword(String? value) {
if (value == null || value.isEmpty) {
return 'Password is required';
} else if (value.length < 8) {
return 'Password must be at least 8 characters';
}
return null;
}
String? validateEmail(String? value) {
if (value == null || value.isEmpty) {
return 'Email is required';
} else if (!RegExp(r'^[^@]+@[^@]+\.[^@]+').hasMatch(value)) {
return 'Enter a valid email address';
}
return null;
}
String? validateRegion(String? value) {
if (value == null || value.isEmpty) {
return 'Please select a region';
}
return null;
}
String? passwordValidator(String? value) {
if (value == null || value.isEmpty) {
return 'Please enter your password';
}
List<String> validationErrors = [];
if (!RegExp(r'^(?=.*[a-z])').hasMatch(value)) {
validationErrors.add(' - one lowercase letter');
}
if (!RegExp(r'^(?=.*[A-Z])').hasMatch(value)) {
validationErrors.add(' - one uppercase letter');
}
if (!RegExp(r'^(?=.*\d)').hasMatch(value)) {
validationErrors.add(' - one number');
}
if (!RegExp(r'^(?=.*[@$!%*?&])').hasMatch(value)) {
validationErrors.add(' - one special character');
}
if (value.length < 8) {
validationErrors.add(' - minimum 8 characters');
}
if (validationErrors.isNotEmpty) {
return 'Password must contain at least:\n' + validationErrors.join('\n');
}
return null;
}
String? fullNameValidator(String? value) {
if (value == null) return 'Full name is required';
@ -80,14 +178,7 @@ class LoginBloc extends Bloc<LoginEvent, LoginState> {
return null;
}
String? reEnterPasswordCheck(String? value) {
passwordValidator(value);
if (signUpPassword == value) {
return null;
} else {
return 'Passwords do not match';
}
}
String? reEnterPasswordCheckForgetPass(String? value) {
passwordValidator(value);
@ -136,43 +227,16 @@ class LoginBloc extends Bloc<LoginEvent, LoginState> {
return '$maskedLocalPart@$domainPart';
}
_login(LoginButtonPressed event, Emitter<LoginState> emit) async {
emit(LoginLoading());
try {
if (event.username.isEmpty ||event.password.isEmpty) {
CustomSnackBar.displaySnackBar('Please enter your credentials');
emit(const LoginFailure(error: 'Something went wrong'));
return;
}
token = await AuthenticationAPI.loginWithEmail(
model: LoginWithEmailModel(
email: event.username,
password: event.password,
),
);
} catch (failure) {
emit( LoginFailure(error: failure.toString()));
return;
}
if (token.accessTokenIsNotEmpty) {
debugPrint('token: ${token.accessToken}');
FlutterSecureStorage storage = const FlutterSecureStorage();
await storage.write(key: Token.loginAccessTokenKey, value: token.accessToken);
const FlutterSecureStorage().write(
key: UserModel.userUuidKey,
value: Token.decodeToken(token.accessToken)['uuid'].toString()
);
user = UserModel.fromToken(token);
emailController.clear();
passwordController.clear();
emit(LoginSuccess());
} else {
emit(const LoginFailure(error: 'Something went wrong'));
}
}
final List<String> regions = [
'North America',
'South America',
'Europe',
'Asia',
'Africa',
'Australia',
'Antarctica',
];
// signUp() async {
// emit(LoginLoading());
@ -198,4 +262,7 @@ class LoginBloc extends Bloc<LoginEvent, LoginState> {
// }
// }
}

View File

@ -16,3 +16,13 @@ class LoginButtonPressed extends LoginEvent {
@override
List<Object> get props => [username, password];
}
class CheckBoxEvent extends LoginEvent {
final bool? newValue;
const CheckBoxEvent({required this.newValue,});
@override
List<Object> get props => [newValue!,];
}

View File

@ -21,3 +21,19 @@ class LoginFailure extends LoginState {
@override
List<Object> get props => [error];
}
class LoginValid extends LoginState {}
class LoginInvalid extends LoginState {
final String error;
const LoginInvalid({required this.error});
@override
List<Object> get props => [error];
}
// class LoginState extends LoginState {}

View File

@ -1,4 +1,5 @@
import 'package:syncrow_web/pages/auth/model/token.dart';
import 'package:syncrow_web/pages/auth/login/model/token.dart';
class UserModel {
static String userUuidKey = 'userUuid';

View File

@ -0,0 +1,426 @@
import 'dart:ui';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart';
import 'package:syncrow_web/pages/auth/forget_password/view/forget_password_page.dart';
import 'package:syncrow_web/pages/auth/login/bloc/login_bloc.dart';
import 'package:syncrow_web/pages/auth/login/bloc/login_event.dart';
import 'package:syncrow_web/pages/auth/login/bloc/login_state.dart';
import 'package:syncrow_web/pages/common/default_button.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/pages/home/view/home_page.dart';
import 'package:syncrow_web/utils/style.dart';
class LoginMobilePage extends StatelessWidget {
const LoginMobilePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: BlocProvider(
create: (context) => LoginBloc(),
child: BlocConsumer<LoginBloc, LoginState>(
listener: (context, state) {
if (state is LoginSuccess) {
// Navigate to home screen after successful login
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => const HomePage()),
);
} else if (state is LoginFailure) {
// Show error message
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(state.error),
),
);
}
},
builder: (context, state) {
if (state is LoginLoading) {
return const Center(child: CircularProgressIndicator());
} else {
return _buildLoginForm(context);
}
},
),
),
);
}
Widget _buildLoginForm(BuildContext context) {
final loginBloc = BlocProvider.of<LoginBloc>(context);
final TextEditingController _usernameController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
return Center(
child: Stack(
children: [
Container(
width: MediaQuery.sizeOf(context).width,
height: MediaQuery.sizeOf(context).height,
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage(
Assets.background,
),
fit: BoxFit.cover,
),
),
),
Container(
width: MediaQuery.sizeOf(context).width,
height: MediaQuery.sizeOf(context).height,
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage(Assets.vector),
fit: BoxFit.cover,
opacity: 0.9,
),
),
),
SingleChildScrollView(
child: Container(
margin: const EdgeInsets.all(50),
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.3),
borderRadius: const BorderRadius.all(Radius.circular(20))),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(15.0),
child: SvgPicture.asset(
Assets.loginLogo,
),
),
Container(
margin: EdgeInsets.all(30),
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.1),
borderRadius:
const BorderRadius.all(Radius.circular(30)),
border: Border.all(
color:
ColorsManager.graysColor.withOpacity(0.2))),
child: Form(
key: loginBloc.formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
const SizedBox(height: 15),
const Text(
'Login',
style: TextStyle(
color: Colors.white,
fontSize: 24,
fontWeight: FontWeight.bold),
),
const SizedBox(height: 30),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
"Country/Region",
style: smallTextStyle,
),
SizedBox(
child: DropdownButtonFormField<String>(
validator: loginBloc.validateRegion,
icon: const Icon(
Icons.keyboard_arrow_down_outlined,
),
decoration: textBoxDecoration()!.copyWith(
hintText: null,
),
hint: const Align(
alignment: Alignment.centerLeft,
child: Text(
'Select your region/country',
textAlign: TextAlign.center,
),
),
isDense: true,
style: const TextStyle(color: Colors.black),
items:
loginBloc.regions.map((String region) {
return DropdownMenuItem<String>(
value: region,
child: Text(region),
);
}).toList(),
onChanged: (String? value) {
print(value);
},
),
)
],
),
const SizedBox(height: 20.0),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
"Email",
style: smallTextStyle,
),
SizedBox(
child: TextFormField(
validator: loginBloc.validateEmail,
controller: loginBloc.emailController,
decoration: textBoxDecoration()!
.copyWith(hintText: 'Enter your email'),
style: const TextStyle(color: Colors.black),
),
),
],
),
const SizedBox(height: 20.0),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
"Password",
style: smallTextStyle,
),
SizedBox(
child: TextFormField(
validator: loginBloc.validatePassword,
obscureText: loginBloc.obscureText,
keyboardType: TextInputType.visiblePassword,
controller: loginBloc.passwordController,
decoration: textBoxDecoration()!.copyWith(
hintText: 'At least 8 characters',
),
style: const TextStyle(color: Colors.black),
),
),
],
),
const SizedBox(
height: 10,
),
SizedBox(
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
InkWell(
onTap: () {
Navigator.of(context)
.push(MaterialPageRoute(
builder: (context) =>
const ForgetPasswordPage(),
));
},
child: Text(
"Forgot Password?",
style: smallTextStyle,
),
),
],
),
),
Row(
children: [
Transform.scale(
scale: 1.2, // Adjust the scale as needed
child: Checkbox(
fillColor: MaterialStateProperty.all<Color>(
Colors.white),
activeColor: Colors.white,
value: loginBloc.isChecked,
checkColor: Colors.black,
shape: const CircleBorder(),
onChanged: (bool? newValue) {
loginBloc.add(
CheckBoxEvent(newValue: newValue));
},
),
),
SizedBox(
width: MediaQuery.sizeOf(context).width * 0.5,
child: RichText(
text: TextSpan(
text: 'Agree to ',
style:
const TextStyle(color: Colors.white),
children: [
TextSpan(
text: '(Terms of Service)',
style: const TextStyle(
color: Colors.black),
recognizer: TapGestureRecognizer()
..onTap = () {
loginBloc.launchURL(
'https://example.com/terms');
},
),
TextSpan(
text: ' (Legal Statement)',
style: const TextStyle(
color: Colors.black),
recognizer: TapGestureRecognizer()
..onTap = () {
loginBloc.launchURL(
'https://example.com/legal');
},
),
TextSpan(
text: ' (Privacy Statement)',
style: const TextStyle(
color: Colors.black),
recognizer: TapGestureRecognizer()
..onTap = () {
loginBloc.launchURL(
'https://example.com/privacy');
},
),
],
),
),
),
],
),
const SizedBox(height: 20.0),
SizedBox(
child: DefaultButton(
backgroundColor: loginBloc.isChecked
? ColorsManager.btnColor
: ColorsManager.grayColor,
child: const Text('Sign in'),
onPressed: () {
if (loginBloc.formKey.currentState!
.validate()) {
loginBloc.add(
LoginButtonPressed(
username:
loginBloc.emailController.text,
password:
loginBloc.passwordController.text,
),
);
}
},
),
),
// Padding(
// padding: const EdgeInsets.all(5.0),
// child: SizedBox(
// width: MediaQuery.sizeOf(context).width * 0.2,
// child: Row(
// mainAxisAlignment: MainAxisAlignment.center,
// crossAxisAlignment: CrossAxisAlignment.center,
// children: [
// Expanded(child: Image.asset(Assets.liftLine)),
// Expanded(
// child: Padding(
// padding: const EdgeInsets.all(5.0),
// child: Text('Or sign in with',
// style: smallTextStyle.copyWith(fontSize: 10),
// ),
// )
// ),
// Expanded(child: Image.asset(Assets.rightLine)),
// ],
// ),
// ),
// ),
// SizedBox(
// width: MediaQuery.sizeOf(context).width * 0.2,
// child: Row(
// crossAxisAlignment: CrossAxisAlignment.center,
// children: [
// Expanded(
// child: Container(
// decoration: containerDecoration,
// child:InkWell(
// child: Padding(
// padding: const EdgeInsets.all(8.0),
// child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceAround,
// crossAxisAlignment: CrossAxisAlignment.center,
// children: [
// SvgPicture.asset(
// Assets.google,
// fit: BoxFit.cover,
// ),
// const Flexible(
// child: Text('Google',
// style: TextStyle(color: Colors.black),
// ),
// ),
// ],
// ),
// ),
// onTap: () {},
// ),
// ),
// ),
// SizedBox(width: 10,),
// Expanded(
// child: Container(
// decoration: containerDecoration,
// child:InkWell(
// child: Padding(
// padding: const EdgeInsets.all(8.0),
// child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceAround,
// crossAxisAlignment: CrossAxisAlignment.center,
// children: [
// SvgPicture.asset(
// Assets.facebook,
// fit: BoxFit.cover,
// ),
// const Flexible(
// child: Text('Facebook',
// style: TextStyle(color: Colors.black),
// ),
// ),
// ],
// ),
// ),
// onTap: () {},
// ),
// ),
// ),
//
// ],
// ),
// ),
const SizedBox(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Flexible(
child: Text(
"Don't you have an account? ",
style: TextStyle(
color: Colors.white, fontSize: 13),
)),
Text(
"Sign up",
),
],
),
)
],
),
)),
],
),
),
),
],
),
);
}
}

View File

@ -1,8 +1,8 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:syncrow_web/pages/auth/view/login_mobile_page.dart';
import 'package:syncrow_web/pages/auth/view/login_web_page.dart';
import 'package:syncrow_web/pages/auth/login/view/login_mobile_page.dart';
import 'package:syncrow_web/pages/auth/login/view/login_web_page.dart';
import 'package:syncrow_web/utils/responsive_layout.dart';
class LoginPage extends StatelessWidget {

View File

@ -0,0 +1,393 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart';
import 'package:syncrow_web/pages/auth/login/bloc/login_bloc.dart';
import 'package:syncrow_web/pages/auth/login/bloc/login_event.dart';
import 'package:syncrow_web/pages/auth/login/bloc/login_state.dart';
import 'package:syncrow_web/pages/auth/forget_password/view/forget_password_page.dart';
import 'package:syncrow_web/pages/common/default_button.dart';
import 'package:syncrow_web/pages/common/first_layer.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/pages/home/view/home_page.dart';
import 'package:syncrow_web/utils/style.dart';
class LoginWebPage extends StatelessWidget {
const LoginWebPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: BlocProvider(
create: (context) => LoginBloc(),
child: BlocConsumer<LoginBloc, LoginState>(
listener: (context, state) {
if (state is LoginSuccess) {
// Navigate to home screen after successful login
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => const HomePage()),
);
} else if (state is LoginFailure) {
// Show error message
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(state.error),
),
);
}
},
builder: (context, state) {
if (state is LoginLoading) {
return const Center(child: CircularProgressIndicator());
} else {
return _buildLoginForm(context);
}
},
),
),
);
}
Widget _buildLoginForm(BuildContext context) {
final loginBloc = BlocProvider.of<LoginBloc>(context);
return FirstLayer(
second: Container(
margin: const EdgeInsets.all(50),
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.3),
borderRadius: const BorderRadius.all(Radius.circular(20)),
),
child: Center(
child: ListView(
shrinkWrap: true,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Spacer(),
Expanded(
flex: 2,
child: SvgPicture.asset(
Assets.loginLogo,
),
),
const Spacer(),
Container(
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.1),
borderRadius: const BorderRadius.all(Radius.circular(30)),
border: Border.all(color: ColorsManager.graysColor.withOpacity(0.2))),
child: Form(
key: loginBloc.formKey,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 50, vertical: 20),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
const SizedBox(height: 15),
const Text(
'Login',
style: TextStyle(
color: Colors.white,
fontSize: 24,
fontWeight: FontWeight.bold),
),
const SizedBox(height: 30),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
"Country/Region",
style: smallTextStyle,
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.2,
child: DropdownButtonFormField<String>(
validator:loginBloc.validateRegion ,
icon: const Icon(
Icons.keyboard_arrow_down_outlined,
),
decoration: textBoxDecoration()!.copyWith(
hintText: null,
),
hint: const Align(
alignment: Alignment.centerLeft,
child: Text(
'Select your region/country',
textAlign: TextAlign.center,
),
),
isDense: true,
style: const TextStyle(color: Colors.black),
items:loginBloc.regions.map((String region) {
return DropdownMenuItem<String>(
value: region,
child: Text(region),
);
}).toList(),
onChanged: (String? value) {
print(value);
},
),
)
],
),
const SizedBox(height: 20.0),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text("Email",
style: smallTextStyle,
),
SizedBox(
width: MediaQuery.sizeOf(context).width * 0.2,
child: TextFormField(
validator:loginBloc.validateEmail ,
controller:loginBloc.emailController,
decoration: textBoxDecoration()!.copyWith(hintText: 'Enter your email'),
style: const TextStyle(color: Colors.black),
),
),
],
),
const SizedBox(height: 20.0),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text("Password",
style: smallTextStyle,),
SizedBox(
width: MediaQuery.sizeOf(context).width * 0.2,
child: TextFormField(
validator:loginBloc.validatePassword ,
obscureText:loginBloc.obscureText,
keyboardType: TextInputType.visiblePassword,
controller:loginBloc.passwordController,
decoration: textBoxDecoration()!.copyWith(
hintText: 'At least 8 characters',
),
style: const TextStyle(color: Colors.black),
),
),
],
),
const SizedBox(
height: 10,
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.2,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
InkWell(
onTap: () {
Navigator.of(context).push(MaterialPageRoute(builder: (context) => const ForgetPasswordPage(),));
},
child: Text(
"Forgot Password?",
style: smallTextStyle,
),
),
],
),
),
Row(
children: [
Transform.scale(
scale: 1.2, // Adjust the scale as needed
child: Checkbox(
fillColor: MaterialStateProperty.all<Color>(Colors.white),
activeColor: Colors.white,
value:loginBloc.isChecked,
checkColor: Colors.black,
shape: const CircleBorder(),
onChanged: (bool? newValue) {
loginBloc.add(CheckBoxEvent(newValue: newValue));
},
),
),
SizedBox(
width: MediaQuery.sizeOf(context).width * 0.2,
child: RichText(
text: TextSpan(
text: 'Agree to ',
style: const TextStyle(color: Colors.white),
children: [
TextSpan(
text: '(Terms of Service)',
style: const TextStyle(
color: Colors.black),
recognizer: TapGestureRecognizer()
..onTap = () {
loginBloc.launchURL(
'https://example.com/terms');
},
),
TextSpan(
text: ' (Legal Statement)',
style: const TextStyle(color: Colors.black),
recognizer: TapGestureRecognizer()
..onTap = () {
loginBloc.launchURL(
'https://example.com/legal');
},
),
TextSpan(
text: ' (Privacy Statement)',
style: const TextStyle(
color: Colors.black),
recognizer: TapGestureRecognizer()
..onTap = () {
loginBloc.launchURL(
'https://example.com/privacy');
},
),
],
),
),
),
],
),
const SizedBox(height: 20.0),
SizedBox(
width: MediaQuery.sizeOf(context).width * 0.2,
child: DefaultButton(
backgroundColor: loginBloc.isChecked?
ColorsManager.btnColor:ColorsManager.grayColor,
child: const Text('Sign in'),
onPressed: () {
if (loginBloc.formKey.currentState!.validate()) {
loginBloc.add(LoginButtonPressed(
username: loginBloc.emailController.text,
password: loginBloc.passwordController.text,
),
);
}
},
),
),
// Padding(
// padding: const EdgeInsets.all(5.0),
// child: SizedBox(
// width: MediaQuery.sizeOf(context).width * 0.2,
// child: Row(
// mainAxisAlignment: MainAxisAlignment.center,
// crossAxisAlignment: CrossAxisAlignment.center,
// children: [
// Expanded(child: Image.asset(Assets.liftLine)),
// Expanded(
// child: Padding(
// padding: const EdgeInsets.all(5.0),
// child: Text('Or sign in with',
// style: smallTextStyle.copyWith(fontSize: 10),
// ),
// )
// ),
// Expanded(child: Image.asset(Assets.rightLine)),
// ],
// ),
// ),
// ),
// SizedBox(
// width: MediaQuery.sizeOf(context).width * 0.2,
// child: Row(
// crossAxisAlignment: CrossAxisAlignment.center,
// children: [
// Expanded(
// child: Container(
// decoration: containerDecoration,
// child:InkWell(
// child: Padding(
// padding: const EdgeInsets.all(8.0),
// child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceAround,
// crossAxisAlignment: CrossAxisAlignment.center,
// children: [
// SvgPicture.asset(
// Assets.google,
// fit: BoxFit.cover,
// ),
// const Flexible(
// child: Text('Google',
// style: TextStyle(color: Colors.black),
// ),
// ),
// ],
// ),
// ),
// onTap: () {},
// ),
// ),
// ),
// SizedBox(width: 10,),
// Expanded(
// child: Container(
// decoration: containerDecoration,
// child:InkWell(
// child: Padding(
// padding: const EdgeInsets.all(8.0),
// child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceAround,
// crossAxisAlignment: CrossAxisAlignment.center,
// children: [
// SvgPicture.asset(
// Assets.facebook,
// fit: BoxFit.cover,
// ),
// const Flexible(
// child: Text('Facebook',
// style: TextStyle(color: Colors.black),
// ),
// ),
// ],
// ),
// ),
// onTap: () {},
// ),
// ),
// ),
//
// ],
// ),
// ),
SizedBox(
width: MediaQuery.sizeOf(context).width * 0.2,
child: const Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Flexible(
child: Text(
"Don't you have an account? ",
style: TextStyle(color: Colors.white),
)),
Flexible(
child: Text(
"Sign up",
)),
],
),
)
],
),
),
)
),
const Spacer(),
],
),
],
)),
),
);
}
}

View File

@ -1,220 +0,0 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/pages/auth/bloc/login_bloc.dart';
import 'package:syncrow_web/pages/auth/bloc/login_event.dart';
import 'package:syncrow_web/pages/auth/bloc/login_state.dart';
import 'package:syncrow_web/pages/home/view/home_page.dart';
class LoginMobilePage extends StatelessWidget {
const LoginMobilePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: BlocProvider(
create: (context) => LoginBloc(),
child: BlocConsumer<LoginBloc, LoginState>(
listener: (context, state) {
if (state is LoginSuccess) {
// Navigate to home screen after successful login
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => const HomePage()),
);
} else if (state is LoginFailure) {
// Show error message
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(state.error),
),
);
}
},
builder: (context, state) {
if (state is LoginLoading) {
return const Center(child: CircularProgressIndicator());
} else {
return _buildLoginForm(context);
}
},
),
),
);
}
Widget _buildLoginForm(BuildContext context) {
final loginBloc = BlocProvider.of<LoginBloc>(context);
final TextEditingController _usernameController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
return Center(
child: Stack(
children: [
Container(
width: MediaQuery.sizeOf(context).width,
height: MediaQuery.sizeOf(context).height,
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage(
Assets.background,
),
fit: BoxFit.cover,
),
),
),
Container(
width: MediaQuery.sizeOf(context).width,
height: MediaQuery.sizeOf(context).height,
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage(Assets.vector),
fit: BoxFit.cover,
opacity: 0.9,
),
),
),
SingleChildScrollView(
child: Container(
margin: const EdgeInsets.all(50),
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.3),
borderRadius: const BorderRadius.all(Radius.circular(20))),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(15.0),
child: SvgPicture.asset(
Assets.loginLogo,
),
),
Container(
margin: const EdgeInsets.all(15),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.3),
borderRadius: const BorderRadius.all(Radius.circular(30))),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 50, vertical: 20),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
const SizedBox(height: 15),
const Text(
'Login',
style: TextStyle(
color: Colors.white,
fontSize: 24,
fontWeight: FontWeight.bold),
),
const SizedBox(height: 15),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
const Text(
"Email",
style: TextStyle(
color: Colors.white,
fontSize: 13,
fontWeight: FontWeight.bold),
),
SizedBox(
child: TextFormField(
decoration: InputDecoration(
labelText: 'Email',
labelStyle: TextStyle(color: Colors.white),
hintText: 'username@gmail.com',
hintStyle: TextStyle(color: Colors.grey),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: BorderSide(color: Colors.white),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: BorderSide(color: Colors.white),
),
filled: true,
fillColor: Colors.white,
contentPadding: EdgeInsets.symmetric(
vertical: 16.0, horizontal: 12.0),
),
style: TextStyle(color: Colors.black),
)),
],
),
const SizedBox(height: 15.0),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
const Text(
"Password",
style: TextStyle(
color: Colors.white,
fontSize: 13,
fontWeight: FontWeight.bold),
),
SizedBox(
child: TextFormField(
decoration: InputDecoration(
labelText: 'Password',
labelStyle: TextStyle(color: Colors.white),
hintText: 'Password',
hintStyle: TextStyle(color: Colors.grey),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: BorderSide(color: Colors.white),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: BorderSide(color: Colors.white),
),
filled: true,
fillColor: Colors.white,
contentPadding: EdgeInsets.symmetric(
vertical: 16.0, horizontal: 12.0),
),
style: TextStyle(color: Colors.black),
)),
const SizedBox(height: 20.0),
const Text(
"Forgot Password?",
style: TextStyle(
color: Colors.white,
fontSize: 13,
fontWeight: FontWeight.bold),
),
],
),
const SizedBox(height: 20.0),
ElevatedButton(
onPressed: () async {
// Trigger login event
loginBloc.add(
LoginButtonPressed(
username: _usernameController.text,
password: _passwordController.text,
),
);
},
child: const Text('Login'),
),
],
),
),
),
],
),
),
),
],
),
);
}
}

View File

@ -1,194 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/pages/auth/bloc/login_bloc.dart';
import 'package:syncrow_web/pages/auth/bloc/login_event.dart';
import 'package:syncrow_web/pages/auth/bloc/login_state.dart';
import 'package:syncrow_web/pages/home/view/home_page.dart';
import 'package:syncrow_web/utils/style.dart';
class LoginWebPage extends StatelessWidget {
const LoginWebPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: BlocProvider(
create: (context) => LoginBloc(),
child: BlocConsumer<LoginBloc, LoginState>(
listener: (context, state) {
if (state is LoginSuccess) {
// Navigate to home screen after successful login
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => const HomePage()),
);
} else if (state is LoginFailure) {
// Show error message
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(state.error),
),
);
}
},
builder: (context, state) {
if (state is LoginLoading) {
return const Center(child: CircularProgressIndicator());
} else {
return _buildLoginForm(context);
}
},
),
),
);
}
Widget _buildLoginForm(BuildContext context) {
final loginBloc = BlocProvider.of<LoginBloc>(context);
final TextEditingController _usernameController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
return Center(
child: Stack(
children: [
Container(
width: MediaQuery.sizeOf(context).width,
height: MediaQuery.sizeOf(context).height,
child: SvgPicture.asset(
Assets.webBackground,
fit: BoxFit.cover,
),
),
Container(
width: MediaQuery.sizeOf(context).width,
height: MediaQuery.sizeOf(context).height,
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage(Assets.vector),
fit: BoxFit.cover,
opacity: 0.9,
),
),
),
Container(
margin: const EdgeInsets.all(50),
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.3),
borderRadius: const BorderRadius.all(Radius.circular(20)),
),
child:Center(
child: ListView(
shrinkWrap: true,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Spacer(),
Expanded(
flex: 2,
child: SvgPicture.asset(
Assets.loginLogo,
),
),
const Spacer(),
Container(
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.3),
borderRadius: const BorderRadius.all(Radius.circular(30)),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 50, vertical: 20),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
const SizedBox(height: 15),
const Text(
'Login',
style: TextStyle(
color: Colors.white,
fontSize: 24,
fontWeight: FontWeight.bold),
),
const SizedBox(height: 30),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
const Text(
"Email",
style: TextStyle(
color: Colors.white,
fontSize: 13,
fontWeight: FontWeight.bold),
),
SizedBox(
width: MediaQuery.sizeOf(context).width * 0.2,
child: TextFormField(
controller: _usernameController,
decoration: textBoxDecoration()!.copyWith(hintText: 'Email'),
style: const TextStyle(color: Colors.black),
),
),
],
),
const SizedBox(height: 20.0),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
const Text(
"Password",
style: TextStyle(
color: Colors.white,
fontSize: 13,
fontWeight: FontWeight.bold),
),
SizedBox(
width: MediaQuery.sizeOf(context).width * 0.2,
child: TextFormField(
controller: _passwordController,
decoration: textBoxDecoration()!.copyWith(hintText: 'Password' ,),
style: const TextStyle(color: Colors.black),
),
),
const SizedBox(height: 20.0),
const Text(
"Forgot Password?",
style: TextStyle(
color: Colors.white,
fontSize: 13,
fontWeight: FontWeight.bold),
),
],
),
const SizedBox(height: 20.0),
ElevatedButton(
onPressed: () async {
// Trigger login event
loginBloc.add(
LoginButtonPressed(
username: _usernameController.text,
password: _passwordController.text,
),
);
},
child: const Text('Login'),
),
],
),
),
),
const Spacer(),
],
),
],
)),
),
],
),
);
}
}

View File

@ -0,0 +1,106 @@
import 'package:flutter/material.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/style.dart';
class DefaultButton extends StatelessWidget {
const DefaultButton({
super.key,
this.enabled = true,
this.onPressed,
required this.child,
this.isSecondary = false,
this.isLoading = false,
this.isDone = false,
this.customTextStyle,
this.customButtonStyle,
this.backgroundColor,
this.foregroundColor,
this.borderRadius,
this.height,
this.padding,
});
final void Function()? onPressed;
final Widget child;
final double? height;
final bool isSecondary;
final double? borderRadius;
final bool enabled;
final double? padding;
final bool isDone;
final bool isLoading;
final TextStyle? customTextStyle;
final ButtonStyle? customButtonStyle;
final Color? backgroundColor;
final Color? foregroundColor;
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: enabled ? onPressed : null,
style: isSecondary
? null
: customButtonStyle ??
ButtonStyle(
textStyle: MaterialStateProperty.all(
customTextStyle
?? smallTextStyle.copyWith(
fontSize: 13,
color: foregroundColor,
fontWeight: FontWeight.normal
),
),
foregroundColor: MaterialStateProperty.all(
isSecondary
? Colors.black
: enabled
? foregroundColor ?? Colors.white
: Colors.black,
),
backgroundColor: MaterialStateProperty.resolveWith<Color>(
(Set<MaterialState> states) {
return enabled
? backgroundColor ?? ColorsManager.primaryColor
: Colors.grey;
}),
shape: MaterialStateProperty.all(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(borderRadius ?? 20),
),
),
fixedSize: MaterialStateProperty.all(
const Size.fromHeight(50),
),
padding: MaterialStateProperty.all(
EdgeInsets.all(padding ?? 10),
),
minimumSize: MaterialStateProperty.all(
const Size.fromHeight(50),
),
),
child: SizedBox(
height: height ?? 50,
child: Center(
child: isLoading
? const SizedBox.square(
dimension: 24,
child: CircularProgressIndicator(
color: Colors.white,
),
)
: isDone
? const Icon(
Icons.check_circle_outline,
color: Colors.white,
)
: child,
),
),
);
}
}

View File

@ -0,0 +1,38 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
class FirstLayer extends StatelessWidget {
final Widget? second;
const FirstLayer({super.key,this.second});
@override
Widget build(BuildContext context) {
return Center(
child: Stack(
children: [
SizedBox(
width: MediaQuery.sizeOf(context).width,
height: MediaQuery.sizeOf(context).height,
child: SvgPicture.asset(
Assets.webBackground,
fit: BoxFit.cover,
),
),
Container(
width: MediaQuery.sizeOf(context).width,
height: MediaQuery.sizeOf(context).height,
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage(Assets.vector),
fit: BoxFit.cover,
opacity: 0.9,
),
),
),
second!
],
),
);
}
}