mirror of
https://github.com/SyncrowIOT/syncrow-app.git
synced 2025-11-26 22:54:55 +00:00
change_password
This commit is contained in:
207
lib/features/menu/view/widgets/securty/bloc/security_bloc.dart
Normal file
207
lib/features/menu/view/widgets/securty/bloc/security_bloc.dart
Normal file
@ -0,0 +1,207 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
|
||||
import 'package:syncrow_app/features/menu/view/widgets/securty/bloc/security_event.dart';
|
||||
import 'package:syncrow_app/features/menu/view/widgets/securty/bloc/security_state.dart';
|
||||
import 'package:syncrow_app/services/api/authentication_api.dart';
|
||||
|
||||
class SecurityBloc extends Bloc<SecurityEvent, SecurityState> {
|
||||
bool _isPasswordVisible = false;
|
||||
String otpCode = '';
|
||||
String validate = '';
|
||||
SecurityBloc() : super(PasswordVisibilityState(false)) {
|
||||
on<SetPassword>(_onSetPassword);
|
||||
on<TogglePasswordVisibility>(_onTogglePasswordVisibility);
|
||||
on<StartTimerEvent>(_onStartTimer);
|
||||
on<StopTimerEvent>(_onStopTimer);
|
||||
on<UpdateTimerEvent>(_onUpdateTimer);
|
||||
on<VerifyPassCodeEvent>(verifyCode);
|
||||
on<ChangePasswordEvent>(changePassword);
|
||||
}
|
||||
|
||||
void _onSetPassword(SetPassword event, Emitter<SecurityState> emit) {
|
||||
if (event.password.length < 6) {
|
||||
emit(PasswordErrorState('Password must be at least 6 characters long.'));
|
||||
} else {
|
||||
emit(PasswordSetState('Password successfully set.'));
|
||||
}
|
||||
}
|
||||
|
||||
void _onTogglePasswordVisibility(
|
||||
TogglePasswordVisibility event, Emitter<SecurityState> emit) {
|
||||
_isPasswordVisible = !_isPasswordVisible;
|
||||
emit(PasswordVisibilityState(_isPasswordVisible));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
TextEditingController newPassword = TextEditingController();
|
||||
|
||||
Timer? _timer;
|
||||
int _remainingTime = 0;
|
||||
|
||||
Future _onStartTimer(
|
||||
StartTimerEvent event, Emitter<SecurityState> emit) async {
|
||||
if (_timer != null && _timer!.isActive) {
|
||||
return;
|
||||
}
|
||||
_remainingTime = 1;
|
||||
add(UpdateTimerEvent(
|
||||
remainingTime: _remainingTime, isButtonEnabled: false));
|
||||
try {
|
||||
_remainingTime = 30;
|
||||
var res = (await AuthenticationAPI.sendOtp(body: {
|
||||
'email': HomeCubit.user!.email,
|
||||
'type': 'VERIFICATION',
|
||||
if (HomeCubit.user!.regionUuid != null)
|
||||
'regionUuid': HomeCubit.user!.regionUuid
|
||||
}));
|
||||
_remainingTime = res['cooldown'];
|
||||
} on DioException catch (e) {
|
||||
if (e.response!.statusCode == 400) {
|
||||
final errorData = e.response!.data;
|
||||
String errorMessage = errorData['message'];
|
||||
if (errorMessage == 'User not found') {
|
||||
validate = 'Invalid Credential';
|
||||
emit(AuthInitialState());
|
||||
return 1;
|
||||
} else {
|
||||
validate = '';
|
||||
_remainingTime = errorData['data']['cooldown'] ?? 1;
|
||||
emit(AuthInitialState());
|
||||
}
|
||||
} else {
|
||||
emit(AuthInitialState());
|
||||
|
||||
return 1;
|
||||
}
|
||||
emit(AuthInitialState());
|
||||
} catch (e) {
|
||||
emit(AuthInitialState());
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
||||
_remainingTime--;
|
||||
if (_remainingTime <= 0) {
|
||||
_timer?.cancel();
|
||||
add(UpdateTimerEvent(remainingTime: 0, isButtonEnabled: true));
|
||||
} else {
|
||||
add(UpdateTimerEvent(
|
||||
remainingTime: _remainingTime, isButtonEnabled: false));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _onStopTimer(StopTimerEvent event, Emitter<SecurityState> emit) {
|
||||
_timer?.cancel();
|
||||
emit(TimerState(isButtonEnabled: true, remainingTime: 0));
|
||||
}
|
||||
|
||||
void _onUpdateTimer(UpdateTimerEvent event, Emitter<SecurityState> emit) {
|
||||
emit(TimerState(
|
||||
isButtonEnabled: event.isButtonEnabled,
|
||||
remainingTime: event.remainingTime));
|
||||
}
|
||||
|
||||
String formattedTime(int time) {
|
||||
final int days = (time / 86400).floor(); // 86400 seconds in a day
|
||||
final int hours = ((time % 86400) / 3600).floor();
|
||||
final int minutes = (((time % 86400) % 3600) / 60).floor();
|
||||
final int seconds = (((time % 86400) % 3600) % 60).floor();
|
||||
|
||||
final String formattedTime = [
|
||||
if (days > 0) '${days}d', // Append 'd' for days
|
||||
if (days > 0 || hours > 0)
|
||||
hours
|
||||
.toString()
|
||||
.padLeft(2, '0'), // Show hours if there are days or hours
|
||||
minutes.toString().padLeft(2, '0'),
|
||||
seconds.toString().padLeft(2, '0'),
|
||||
].join(':');
|
||||
|
||||
return formattedTime;
|
||||
}
|
||||
|
||||
Future<void> verifyCode(
|
||||
VerifyPassCodeEvent event, Emitter<SecurityState> emit) async {
|
||||
emit(LoadingForgetState());
|
||||
try {
|
||||
final response = await AuthenticationAPI.verifyPassCode(body: {
|
||||
'email': HomeCubit.user!.email!,
|
||||
'type': 'VERIFICATION',
|
||||
'otpCode': otpCode
|
||||
});
|
||||
//929971
|
||||
print('response=-=-=-${response}');
|
||||
if (response['statusCode'] == 200) {
|
||||
_timer?.cancel();
|
||||
emit(SuccessForgetState());
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
final errorData = e.response!.data;
|
||||
String errorMessage =
|
||||
errorData['error']['message'] ?? 'something went wrong';
|
||||
validate = errorMessage;
|
||||
emit(AuthInitialState());
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> changePassword(
|
||||
ChangePasswordEvent event, Emitter<SecurityState> emit) async {
|
||||
emit(LoadingForgetState());
|
||||
try {
|
||||
print('response=-=-=-${event.otpCode}');
|
||||
|
||||
final response = await AuthenticationAPI.forgetPassword(
|
||||
email: HomeCubit.user!.email!,
|
||||
otpCode: event.otpCode,
|
||||
password: newPassword.text,
|
||||
);
|
||||
//537580
|
||||
print('response=-=-=-${response}');
|
||||
// if (response['statusCode'] == 200) {
|
||||
// _timer?.cancel();
|
||||
// emit(ChangedPassState());
|
||||
// }
|
||||
emit(ChangedPassState());
|
||||
} on DioException catch (e) {
|
||||
final errorData = e.response!.data;
|
||||
String errorMessage =
|
||||
errorData['error']['message'] ?? 'something went wrong';
|
||||
validate = errorMessage;
|
||||
emit(AuthInitialState());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
abstract class SecurityEvent {}
|
||||
|
||||
class SetPassword extends SecurityEvent {
|
||||
final String password;
|
||||
|
||||
SetPassword(this.password);
|
||||
}
|
||||
|
||||
class TogglePasswordVisibility extends SecurityEvent {}
|
||||
|
||||
class SubmitEvent extends SecurityEvent {}
|
||||
|
||||
class StartTimerEvent extends SecurityEvent {}
|
||||
|
||||
class StopTimerEvent extends SecurityEvent {}
|
||||
|
||||
class UpdateTimerEvent extends SecurityEvent {
|
||||
final int remainingTime;
|
||||
final bool isButtonEnabled;
|
||||
UpdateTimerEvent(
|
||||
{required this.remainingTime, required this.isButtonEnabled});
|
||||
}
|
||||
|
||||
class ChangePasswordEvent extends SecurityEvent {
|
||||
final String otpCode;
|
||||
ChangePasswordEvent({
|
||||
required this.otpCode,
|
||||
});
|
||||
}
|
||||
|
||||
class VerifyPassCodeEvent extends SecurityEvent {}
|
||||
@ -0,0 +1,44 @@
|
||||
abstract class SecurityState {}
|
||||
|
||||
class InitialState extends SecurityState {}
|
||||
|
||||
class PasswordVisibilityState extends SecurityState {
|
||||
final bool isVisible;
|
||||
|
||||
PasswordVisibilityState(this.isVisible);
|
||||
}
|
||||
|
||||
class PasswordSetState extends SecurityState {
|
||||
final String message;
|
||||
|
||||
PasswordSetState(this.message);
|
||||
}
|
||||
|
||||
class PasswordErrorState extends SecurityState {
|
||||
final String error;
|
||||
|
||||
PasswordErrorState(this.error);
|
||||
}
|
||||
|
||||
class AuthTokenLoading extends SecurityState {}
|
||||
|
||||
class AuthLoading extends SecurityState {}
|
||||
|
||||
class AuthInitialState extends SecurityState {}
|
||||
|
||||
class TimerState extends SecurityState {
|
||||
final bool isButtonEnabled;
|
||||
final int remainingTime;
|
||||
|
||||
TimerState({required this.isButtonEnabled, required this.remainingTime});
|
||||
|
||||
@override
|
||||
List<Object> get props => [isButtonEnabled, remainingTime];
|
||||
}
|
||||
class InitialForgetState extends SecurityState {}
|
||||
|
||||
class LoadingForgetState extends SecurityState {}
|
||||
|
||||
class SuccessForgetState extends SecurityState {}
|
||||
class ChangedPassState extends SecurityState {}
|
||||
|
||||
@ -0,0 +1,88 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:syncrow_app/features/menu/view/widgets/securty/bloc/security_bloc.dart';
|
||||
import 'package:syncrow_app/features/menu/view/widgets/securty/view/verification_code_page.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart';
|
||||
import 'package:syncrow_app/generated/assets.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
|
||||
|
||||
class ChangePasswordPage extends StatelessWidget {
|
||||
const ChangePasswordPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DefaultScaffold(
|
||||
title: 'Change Password',
|
||||
bottomNavBar: SizedBox(
|
||||
height: 150,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () {
|
||||
// In your parent widget or navigator
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => BlocProvider(
|
||||
create: (_) => SecurityBloc(), // Provide the Bloc
|
||||
child: const VerificationCodePage(),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
height: 50,
|
||||
margin: const EdgeInsets.only(right: 20, left: 20),
|
||||
decoration: const BoxDecoration(
|
||||
color: ColorsManager.blueColor,
|
||||
borderRadius: BorderRadius.all(Radius.circular(20))),
|
||||
child: const Center(
|
||||
child: Text(
|
||||
'Get Verification Code',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: ColorsManager.onPrimaryColor),
|
||||
)),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 80, bottom: 30),
|
||||
child: SvgPicture.asset(Assets.verificationIcon),
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(8.0),
|
||||
child: BodyMedium(
|
||||
text: 'Account Verification',
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
const BodyMedium(
|
||||
text: 'Click here to send a verification ',
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 16,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
const BodyMedium(
|
||||
text: 'code to your email: test@test.com',
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 16,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_app/features/menu/view/widgets/securty/view/change_password_page.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart';
|
||||
@ -23,7 +24,11 @@ class SecurtyView extends StatelessWidget {
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () {},
|
||||
onTap: () {
|
||||
Navigator.of(context).push(MaterialPageRoute(
|
||||
builder: (context) => const ChangePasswordPage(),
|
||||
));
|
||||
},
|
||||
child: const Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@ -0,0 +1,239 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:syncrow_app/features/auth/bloc/auth_cubit.dart';
|
||||
import 'package:syncrow_app/features/devices/view/device_settings/update_dialog.dart';
|
||||
import 'package:syncrow_app/features/menu/view/widgets/securty/bloc/security_bloc.dart';
|
||||
import 'package:syncrow_app/features/menu/view/widgets/securty/bloc/security_event.dart';
|
||||
import 'package:syncrow_app/features/menu/view/widgets/securty/bloc/security_state.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart';
|
||||
import 'package:syncrow_app/generated/assets.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
|
||||
|
||||
class SetPasswordPage extends StatelessWidget {
|
||||
String? otpCode;
|
||||
SetPasswordPage({super.key, this.otpCode});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider<SecurityBloc>(
|
||||
create: (context) => SecurityBloc(),
|
||||
child: BlocConsumer<SecurityBloc, SecurityState>(
|
||||
listener: (context, state) {
|
||||
if (state is SuccessForgetState) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Change Password Successfully '),
|
||||
),
|
||||
);
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
if (state is ChangedPassState) {
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
content: SizedBox(
|
||||
height: MediaQuery.of(context).size.height * 0.2,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
BodyLarge(
|
||||
text: 'Password Changed',
|
||||
fontWeight: FontWeight.w700,
|
||||
fontColor:
|
||||
ColorsManager.switchButton.withOpacity(0.6),
|
||||
fontSize: 16,
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(left: 15, right: 15),
|
||||
child: Divider(
|
||||
color: ColorsManager.textGray,
|
||||
),
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: 15, right: 20, top: 15, bottom: 20),
|
||||
child: Column(
|
||||
children: [
|
||||
Center(
|
||||
child: Text(
|
||||
'Your password has been',
|
||||
textAlign: TextAlign.center,
|
||||
)),
|
||||
Center(
|
||||
child: Text(
|
||||
'successfully updated.',
|
||||
textAlign: TextAlign.center,
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(
|
||||
left: BorderSide(
|
||||
color: ColorsManager.textGray,
|
||||
width: 0.5,
|
||||
),
|
||||
top: BorderSide(
|
||||
color: ColorsManager.textGray,
|
||||
width: 1.0,
|
||||
),
|
||||
)),
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
AuthCubit.get(context).logout();
|
||||
},
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.only(top: 15, bottom: 15),
|
||||
child: Center(
|
||||
child: Text(
|
||||
'Done',
|
||||
style: TextStyle(
|
||||
color: ColorsManager.switchButton
|
||||
.withOpacity(0.6),
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400),
|
||||
),
|
||||
),
|
||||
)),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}, builder: (context, state) {
|
||||
final _bloc = BlocProvider.of<SecurityBloc>(context);
|
||||
|
||||
return DefaultScaffold(
|
||||
title: 'Change Password',
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
children: [
|
||||
const SizedBox(height: 55),
|
||||
const Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(8.0),
|
||||
child: BodyMedium(
|
||||
text: 'Set Password',
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
),
|
||||
const Center(
|
||||
child: BodyMedium(
|
||||
text: 'Secure your account with a',
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
const Center(
|
||||
child: BodyMedium(
|
||||
text: 'strong password',
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 40),
|
||||
BlocBuilder<SecurityBloc, SecurityState>(
|
||||
builder: (context, state) {
|
||||
if (state is PasswordErrorState) {
|
||||
return Center(
|
||||
child: Text(
|
||||
state.error,
|
||||
style: const TextStyle(color: Colors.red),
|
||||
),
|
||||
);
|
||||
} else if (state is PasswordSetState) {
|
||||
return Center(
|
||||
child: Text(
|
||||
state.message,
|
||||
style: const TextStyle(color: Colors.green),
|
||||
),
|
||||
);
|
||||
}
|
||||
return const SizedBox.shrink();
|
||||
},
|
||||
),
|
||||
PasswordInputField(controller: _bloc.newPassword),
|
||||
const SizedBox(height: 55),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
_bloc.add(ChangePasswordEvent(otpCode: otpCode.toString()));
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.only(
|
||||
right: 20, left: 20, top: 15, bottom: 15),
|
||||
decoration: const BoxDecoration(
|
||||
color: ColorsManager.blueColor,
|
||||
borderRadius: BorderRadius.all(Radius.circular(20)),
|
||||
),
|
||||
child: const Center(
|
||||
child: Text(
|
||||
"Done",
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
class PasswordInputField extends StatelessWidget {
|
||||
PasswordInputField({this.controller});
|
||||
TextEditingController? controller = TextEditingController();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<SecurityBloc, SecurityState>(
|
||||
builder: (context, state) {
|
||||
bool isPasswordVisible = false;
|
||||
if (state is PasswordVisibilityState) {
|
||||
isPasswordVisible = state.isVisible;
|
||||
}
|
||||
|
||||
return TextField(
|
||||
controller: controller,
|
||||
obscureText: !isPasswordVisible,
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Password',
|
||||
suffixIcon: IconButton(
|
||||
icon: SvgPicture.asset(isPasswordVisible
|
||||
? Assets.passwordVisibility
|
||||
: Assets.passwordUnvisibility),
|
||||
onPressed: () {
|
||||
context.read<SecurityBloc>().add(TogglePasswordVisibility());
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
controller!.dispose();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,181 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
|
||||
import 'package:syncrow_app/features/menu/view/widgets/securty/bloc/security_bloc.dart';
|
||||
import 'package:syncrow_app/features/menu/view/widgets/securty/bloc/security_event.dart';
|
||||
import 'package:syncrow_app/features/menu/view/widgets/securty/bloc/security_state.dart';
|
||||
import 'package:syncrow_app/features/menu/view/widgets/securty/view/set_password_page.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
|
||||
import 'package:pin_code_fields/pin_code_fields.dart';
|
||||
|
||||
class VerificationCodePage extends StatelessWidget {
|
||||
const VerificationCodePage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
String otp = '';
|
||||
return BlocProvider<SecurityBloc>(
|
||||
create: (context) => SecurityBloc()..add(StartTimerEvent()),
|
||||
child: BlocConsumer<SecurityBloc, SecurityState>(
|
||||
listener: (context, state) {
|
||||
if (state is SuccessForgetState) {
|
||||
Navigator.of(context).push(MaterialPageRoute(
|
||||
builder: (context) => SetPasswordPage(
|
||||
otpCode: otp,
|
||||
),
|
||||
));
|
||||
}
|
||||
},
|
||||
builder: (context, state) {
|
||||
final _bloc = BlocProvider.of<SecurityBloc>(context);
|
||||
|
||||
return DefaultScaffold(
|
||||
title: 'Change Password',
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(height: 55),
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(8.0),
|
||||
child: BodyMedium(
|
||||
text: 'Verification Code',
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
const BodyMedium(
|
||||
text: 'We have sent the verification code',
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 16,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const BodyMedium(
|
||||
text: 'to ',
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 14,
|
||||
),
|
||||
BodyMedium(
|
||||
text: HomeCubit.user!.email!,
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 14,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 40),
|
||||
BlocBuilder<SecurityBloc, SecurityState>(
|
||||
builder: (context, state) {
|
||||
return Center(
|
||||
child: PinCodeTextField(
|
||||
hintCharacter: '0',
|
||||
appContext: context,
|
||||
length: 6,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
backgroundColor: Colors.transparent,
|
||||
enableActiveFill: true,
|
||||
pinTheme: PinTheme(
|
||||
shape: PinCodeFieldShape.box,
|
||||
activeColor: Colors.white,
|
||||
selectedColor: Colors.white,
|
||||
inactiveColor: Colors.white,
|
||||
inactiveFillColor: Colors.white70,
|
||||
selectedFillColor: Colors.white70,
|
||||
activeFillColor: Colors.white,
|
||||
errorBorderColor: Colors.white,
|
||||
fieldHeight: 55.0,
|
||||
fieldWidth: 55.0,
|
||||
fieldOuterPadding: const EdgeInsets.only(right: 8),
|
||||
borderRadius: BorderRadius.circular(17),
|
||||
borderWidth: 1,
|
||||
),
|
||||
cursorWidth: 1,
|
||||
keyboardType: TextInputType.number,
|
||||
onChanged: (value) {
|
||||
// Update OTP code in the bloc when user enters a pin
|
||||
_bloc.otpCode = value;
|
||||
otp = value;
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 55),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: InkWell(
|
||||
onTap: state is TimerState &&
|
||||
!state.isButtonEnabled &&
|
||||
state.remainingTime != 1
|
||||
? null
|
||||
: () {
|
||||
_bloc.add(StartTimerEvent());
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.only(
|
||||
right: 20, left: 20, top: 15, bottom: 15),
|
||||
decoration: BoxDecoration(
|
||||
color: state is TimerState && !state.isButtonEnabled
|
||||
? ColorsManager.blueButton
|
||||
: ColorsManager.blueColor,
|
||||
borderRadius:
|
||||
BorderRadius.all(Radius.circular(20))),
|
||||
child: Center(
|
||||
child: Center(
|
||||
child: Text(
|
||||
'${state is TimerState && !state.isButtonEnabled && state.remainingTime != 1 ? "${_bloc.formattedTime(state.remainingTime)} " : "Resend"}',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: state is TimerState &&
|
||||
!state.isButtonEnabled
|
||||
? Colors.white
|
||||
: ColorsManager.onPrimaryColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)),
|
||||
const SizedBox(width: 20),
|
||||
Expanded(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
context
|
||||
.read<SecurityBloc>()
|
||||
.add(VerifyPassCodeEvent());
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.only(
|
||||
right: 20, left: 20, top: 15, bottom: 15),
|
||||
decoration: const BoxDecoration(
|
||||
color: ColorsManager.blueColor,
|
||||
borderRadius:
|
||||
BorderRadius.all(Radius.circular(20))),
|
||||
child: const Center(
|
||||
child: Text(
|
||||
"Verify",
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.w700),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user