auth UI and Api

This commit is contained in:
mohammad
2024-07-31 16:02:05 +03:00
parent 69abad24b7
commit 1d65617d18
13 changed files with 405 additions and 228 deletions

View File

@ -1,32 +1,46 @@
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart';
import 'package:syncrow_web/pages/auth/view/login_page.dart'; import 'package:syncrow_web/pages/auth/view/login_page.dart';
import 'package:syncrow_web/pages/home/view/home_page.dart';
import 'package:syncrow_web/services/locator.dart'; import 'package:syncrow_web/services/locator.dart';
void main() {
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
initialSetup(); initialSetup(); // Perform initial setup, e.g., dependency injection
runApp(const MyApp()); String res = await AuthBloc.getTokenAndValidate();
runApp(MyApp(
isLoggedIn: res,
));
} }
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
const MyApp({super.key}); final dynamic isLoggedIn;
const MyApp({
super.key,
required this.isLoggedIn,
});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( return MaterialApp(
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false, // Hide debug banner
scrollBehavior: const MaterialScrollBehavior().copyWith( scrollBehavior: const MaterialScrollBehavior().copyWith(
dragDevices: { dragDevices: {
PointerDeviceKind.mouse, PointerDeviceKind.mouse,
PointerDeviceKind.touch, PointerDeviceKind.touch,
PointerDeviceKind.stylus, PointerDeviceKind.stylus,
PointerDeviceKind.unknown PointerDeviceKind.unknown,
}, },
), ),
theme: ThemeData( theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), colorScheme: ColorScheme.fromSeed(
useMaterial3: true, seedColor: Colors.deepPurple), // Set up color scheme
useMaterial3: true, // Enable Material 3
), ),
home: const LoginPage(), home: isLoggedIn == 'Success'?
const HomePage()
:
const LoginPage(),
); );
} }
} }

View File

@ -9,6 +9,8 @@ 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/token.dart';
import 'package:syncrow_web/pages/auth/model/user_model.dart'; import 'package:syncrow_web/pages/auth/model/user_model.dart';
import 'package:syncrow_web/services/auth_api.dart'; import 'package:syncrow_web/services/auth_api.dart';
import 'package:syncrow_web/utils/constants/strings_manager.dart';
import 'package:syncrow_web/utils/helpers/shared_preferences_helper.dart';
import 'package:syncrow_web/utils/snack_bar.dart'; import 'package:syncrow_web/utils/snack_bar.dart';
class AuthBloc extends Bloc<AuthEvent, AuthState> { class AuthBloc extends Bloc<AuthEvent, AuthState> {
@ -20,16 +22,16 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
on<StopTimerEvent>(_onStopTimer); on<StopTimerEvent>(_onStopTimer);
on<UpdateTimerEvent>(_onUpdateTimer); on<UpdateTimerEvent>(_onUpdateTimer);
on<PasswordVisibleEvent>(_passwordVisible); on<PasswordVisibleEvent>(_passwordVisible);
} }
////////////////////////////// forget password ////////////////////////////////// ////////////////////////////// forget password //////////////////////////////////
final TextEditingController forgetEmailController = TextEditingController(); final TextEditingController forgetEmailController = TextEditingController();
final TextEditingController forgetPasswordController = TextEditingController(); final TextEditingController forgetPasswordController = TextEditingController();
final TextEditingController forgetOtp = TextEditingController(); final TextEditingController forgetOtp = TextEditingController();
final forgetFormKey = GlobalKey<FormState>(); final forgetFormKey = GlobalKey<FormState>();
Timer? _timer; Timer? _timer;
int _remainingTime = 0; int _remainingTime = 0;
@ -53,7 +55,8 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
add(const UpdateTimerEvent(remainingTime: 0, isButtonEnabled: true)); add(const UpdateTimerEvent(remainingTime: 0, isButtonEnabled: true));
print("Timer finished"); // Debug print print("Timer finished"); // Debug print
} else { } else {
add(UpdateTimerEvent(remainingTime: _remainingTime, isButtonEnabled: false)); add(UpdateTimerEvent(
remainingTime: _remainingTime, isButtonEnabled: false));
} }
}); });
} }
@ -67,40 +70,49 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
ChangePasswordEvent event, Emitter<AuthState> emit) async { ChangePasswordEvent event, Emitter<AuthState> emit) async {
try { try {
emit(LoadingForgetState()); emit(LoadingForgetState());
bool response = await AuthenticationAPI.verifyOtp(
email: forgetEmailController.text, otpCode: forgetOtp.text);
if (response == true) {
await AuthenticationAPI.forgetPassword( await AuthenticationAPI.forgetPassword(
password: forgetPasswordController.text, email: forgetEmailController.text); password: forgetPasswordController.text,
email: forgetEmailController.text);
_timer?.cancel();
emit(const TimerState(isButtonEnabled: true, remainingTime: 0));
}
emit(SuccessForgetState()); emit(SuccessForgetState());
} catch (failure) { } catch (failure) {
print(failure);
emit(FailureForgetState(error: failure.toString())); emit(FailureForgetState(error: failure.toString()));
} }
} }
void _onUpdateTimer(UpdateTimerEvent event, Emitter<AuthState> emit) { void _onUpdateTimer(UpdateTimerEvent event, Emitter<AuthState> emit) {
emit(TimerState( emit(TimerState(
isButtonEnabled: event.isButtonEnabled, isButtonEnabled: event.isButtonEnabled,
remainingTime: event.remainingTime)); remainingTime: event.remainingTime));
} }
///////////////////////////////////// login ///////////////////////////////////// ///////////////////////////////////// login /////////////////////////////////////
final TextEditingController loginEmailController = TextEditingController(); final TextEditingController loginEmailController = TextEditingController();
final TextEditingController loginPasswordController = TextEditingController(); final TextEditingController loginPasswordController = TextEditingController();
final loginFormKey = GlobalKey<FormState>(); final loginFormKey = GlobalKey<FormState>();
bool isChecked = false; bool isChecked = false;
bool obscureText = false; bool obscureText = true;
String newPassword = ''; String newPassword = '';
String maskedEmail = ''; String maskedEmail = '';
String otpCode = ''; String otpCode = '';
static Token token = Token.emptyConstructor(); static Token token = Token.emptyConstructor();
static UserModel? user; static UserModel? user;
bool showValidationMessage = false; bool showValidationMessage = false;
void _login(LoginButtonPressed event, Emitter<AuthState> emit) async { void _login(LoginButtonPressed event, Emitter<AuthState> emit) async {
emit(LoginLoading()); emit(LoginLoading());
if(isChecked) { if (isChecked) {
try { try {
if (event.username.isEmpty || event.password.isEmpty) { if (event.username.isEmpty || event.password.isEmpty) {
CustomSnackBar.displaySnackBar('Please enter your credentials'); CustomSnackBar.displaySnackBar('Please enter your credentials');
@ -113,8 +125,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
password: event.password, password: event.password,
), ),
); );
} } catch (failure) {
catch (failure) {
emit(const LoginFailure(error: 'Something went wrong')); emit(const LoginFailure(error: 'Something went wrong'));
// emit(LoginFailure(error: failure.toString())); // emit(LoginFailure(error: failure.toString()));
return; return;
@ -124,11 +135,9 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
FlutterSecureStorage storage = const FlutterSecureStorage(); FlutterSecureStorage storage = const FlutterSecureStorage();
await storage.write( await storage.write(
key: Token.loginAccessTokenKey, value: token.accessToken); key: Token.loginAccessTokenKey, value: token.accessToken);
const FlutterSecureStorage().write( const FlutterSecureStorage().write(
key: UserModel.userUuidKey, key: UserModel.userUuidKey,
value: Token.decodeToken(token.accessToken)['uuid'].toString() value: Token.decodeToken(token.accessToken)['uuid'].toString());
);
user = UserModel.fromToken(token); user = UserModel.fromToken(token);
loginEmailController.clear(); loginEmailController.clear();
loginPasswordController.clear(); loginPasswordController.clear();
@ -136,18 +145,24 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
} else { } else {
emit(const LoginFailure(error: 'Something went wrong')); emit(const LoginFailure(error: 'Something went wrong'));
} }
} } else {
else{
emit(const LoginFailure(error: 'Accept terms and condition')); emit(const LoginFailure(error: 'Accept terms and condition'));
} }
} }
checkBoxToggle(CheckBoxEvent event, Emitter<AuthState> emit,){ checkBoxToggle(CheckBoxEvent event, Emitter<AuthState> emit,) {
emit(LoginLoading()); emit(LoginLoading());
isChecked = event.newValue!; isChecked = event.newValue!;
emit(LoginInitial()); emit(LoginInitial());
} }
checkOtpCode(ChangePasswordEvent event, Emitter<AuthState> emit,) async {
emit(LoadingForgetState());
await AuthenticationAPI.verifyOtp(
email: forgetEmailController.text, otpCode: forgetOtp.text);
emit(SuccessForgetState());
}
void _passwordVisible(PasswordVisibleEvent event, Emitter<AuthState> emit) { void _passwordVisible(PasswordVisibleEvent event, Emitter<AuthState> emit) {
emit(LoginLoading()); emit(LoginLoading());
obscureText = !event.newValue!; obscureText = !event.newValue!;
@ -161,8 +176,8 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
} }
/////////////////////////////////////VALIDATORS/////////////////////////////////////
/////////////////////////////////////VALIDATORS/////////////////////////////////////
String? validatePassword(String? value) { String? validatePassword(String? value) {
if (value == null || value.isEmpty) { if (value == null || value.isEmpty) {
return 'Password is required'; return 'Password is required';
@ -192,8 +207,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
emit(LoadingForgetState()); emit(LoadingForgetState());
final nameError = validateEmail(forgetEmailController.text); final nameError = validateEmail(forgetEmailController.text);
if (nameError != null) { if (nameError != null) {
emit(FailureForgetState(error:nameError )) ; emit(FailureForgetState(error: nameError));
// CustomSnackBar.displaySnackBar(nameError);
return true; return true;
} }
return false; return false;
@ -210,7 +224,6 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
if (value == null || value.isEmpty) { if (value == null || value.isEmpty) {
return 'Please enter your password'; return 'Please enter your password';
} }
List<String> validationErrors = []; List<String> validationErrors = [];
if (!RegExp(r'^(?=.*[a-z])').hasMatch(value)) { if (!RegExp(r'^(?=.*[a-z])').hasMatch(value)) {
@ -230,73 +243,30 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
} }
if (validationErrors.isNotEmpty) { if (validationErrors.isNotEmpty) {
return 'Password must contain at least:\n' + validationErrors.join('\n'); return 'Password must contain at least:\n${validationErrors.join('\n')}';
} }
return null; return null;
} }
String? fullNameValidator(String? value) { String? fullNameValidator(String? value) {
if (value == null) return 'Full name is required'; if (value == null) return 'Full name is required';
final withoutExtraSpaces = value.replaceAll(RegExp(r"\s+"), ' ').trim(); final withoutExtraSpaces = value.replaceAll(RegExp(r"\s+"), ' ').trim();
if (withoutExtraSpaces.length < 2 || withoutExtraSpaces.length > 30) { if (withoutExtraSpaces.length < 2 || withoutExtraSpaces.length > 30) {
return 'Full name must be between 2 and 30 characters long'; return 'Full name must be between 2 and 30 characters long';
} }
// Test if it contains anything but alphanumeric spaces and single quote
if (RegExp(r"/[^ a-zA-Z0-9-\']/").hasMatch(withoutExtraSpaces)) { if (RegExp(r"/[^ a-zA-Z0-9-\']/").hasMatch(withoutExtraSpaces)) {
return 'Only alphanumeric characters, space, dash and single quote are allowed'; return 'Only alphanumeric characters, space, dash and single quote are allowed';
} }
final parts = withoutExtraSpaces.split(' '); final parts = withoutExtraSpaces.split(' ');
if (parts.length < 2) return 'Full name must contain first and last names'; if (parts.length < 2) return 'Full name must contain first and last names';
if (parts.length > 3) return 'Full name can at most contain 3 parts'; if (parts.length > 3) return 'Full name can at most contain 3 parts';
if (parts.any((part) => part.length < 2 || part.length > 30)) { if (parts.any((part) => part.length < 2 || part.length > 30)) {
return 'Full name parts must be between 2 and 30 characters long'; return 'Full name parts must be between 2 and 30 characters long';
} }
return null; return null;
} }
String? reEnterPasswordCheckForgetPass(String? value) {
passwordValidator(value);
if (newPassword == value) {
return null;
} else {
return 'Passwords do not match';
}
}
String? emailAddressValidator(String? value) {
if (value != null && value.isNotEmpty && value != "") {
if (checkValidityOfEmail(value)) {
return null;
} else {
return 'Please enter a valid email';
}
} else {
return 'Email address is required';
}
}
bool checkValidityOfEmail(String? email) {
if (email != null) {
return RegExp(
r"^[a-zA-Z0-9]+([.!#$%&'*+/=?^_`{|}~-]?[a-zA-Z0-9]+)*@[a-zA-Z0-9]+([.-]?[a-zA-Z0-9]+)*\.[a-zA-Z0-9]{2,}$")
.hasMatch(email);
} else {
return false;
}
}
String maskEmail(String email) { String maskEmail(String email) {
final emailParts = email.split('@'); final emailParts = email.split('@');
if (emailParts.length != 2) return email; if (emailParts.length != 2) return email;
@ -324,4 +294,36 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
]; ];
static Future<String> getTokenAndValidate() async {
try {
const storage = FlutterSecureStorage();
final firstLaunch = await SharedPreferencesHelper.readBoolFromSP(StringsManager.firstLaunch) ?? true;
if (firstLaunch) {
storage.deleteAll();
}
await SharedPreferencesHelper.saveBoolToSP(StringsManager.firstLaunch, false);
final value = await storage.read(key: Token.loginAccessTokenKey) ?? '';
if (value.isEmpty) {
return 'Token not found';
}
final tokenData = Token.decodeToken(value);
if (tokenData.containsKey('exp')) {
final exp = tokenData['exp'] ?? 0;
final currentTime = DateTime.now().millisecondsSinceEpoch ~/ 1000;
if (currentTime < exp) {
return 'Success';
} else {
return 'expired';
}
} else {
return 'Something went wrong';
}
} catch (_) {
return 'Something went wrong';
}
}
} }

View File

@ -9,6 +9,8 @@ abstract class AuthState extends Equatable {
class LoginInitial extends AuthState {} class LoginInitial extends AuthState {}
class AuthTokenLoading extends AuthState {}
class LoginLoading extends AuthState {} class LoginLoading extends AuthState {}
class LoginSuccess extends AuthState {} class LoginSuccess extends AuthState {}
@ -38,6 +40,7 @@ class InitialForgetState extends AuthState{}
class LoadingForgetState extends AuthState{} class LoadingForgetState extends AuthState{}
class SuccessForgetState extends AuthState{} class SuccessForgetState extends AuthState{}
class PasswordVisibleState extends AuthState{} class PasswordVisibleState extends AuthState{}
class FailureForgetState extends AuthState { class FailureForgetState extends AuthState {
@ -58,8 +61,18 @@ class TimerState extends AuthState {
} }
class AuthError extends AuthState {
final String message;
String? code;
AuthError({required this.message, this.code});
}
class AuthTokenError extends AuthError {
AuthTokenError({required super.message, super.code});
}
class AuthSuccess extends AuthState {}
class AuthTokenSuccess extends AuthSuccess {}
// class AuthState extends AuthState {} // class AuthState extends AuthState {}

View File

@ -9,7 +9,6 @@ import 'package:syncrow_web/pages/common/first_layer.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/style.dart'; import 'package:syncrow_web/utils/style.dart';
class ForgetPasswordWebPage extends StatelessWidget { class ForgetPasswordWebPage extends StatelessWidget {
const ForgetPasswordWebPage({super.key}); const ForgetPasswordWebPage({super.key});
@ -21,7 +20,11 @@ class ForgetPasswordWebPage extends StatelessWidget {
child: BlocConsumer<AuthBloc, AuthState>( child: BlocConsumer<AuthBloc, AuthState>(
listener: (context, state) { listener: (context, state) {
if (state is LoadingForgetState) { if (state is LoadingForgetState) {
} else if (state is FailureForgetState) {
} else if (state is SuccessForgetState){
Navigator.of(context).pop();
}
else if (state is FailureForgetState) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: Text(state.error), content: Text(state.error),
@ -138,10 +141,8 @@ class ForgetPasswordWebPage extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
children: [ children: [
Text( Text("Account",
"Account", style: smallTextStyle,),
style: smallTextStyle,
),
SizedBox( SizedBox(
width: MediaQuery.sizeOf(context).width * 0.2, width: MediaQuery.sizeOf(context).width * 0.2,
child: TextFormField( child: TextFormField(

View File

@ -287,108 +287,7 @@ class LoginWebPage extends StatelessWidget {
}, },
), ),
), ),
// 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",
// )),
// ],
// ),
// )
], ],
), ),
), ),
@ -403,3 +302,105 @@ class LoginWebPage extends StatelessWidget {
); );
} }
} }
// 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",
// )),
// ],
// ),
// )

View File

@ -1,3 +1,5 @@
import 'dart:convert';
import 'package:syncrow_web/pages/auth/model/token.dart'; import 'package:syncrow_web/pages/auth/model/token.dart';
import 'package:syncrow_web/services/api/http_service.dart'; import 'package:syncrow_web/services/api/http_service.dart';
import 'package:syncrow_web/utils/constants/api_const.dart'; import 'package:syncrow_web/utils/constants/api_const.dart';
@ -43,17 +45,23 @@ class AuthenticationAPI {
return response; return response;
} }
static Future checkOtp({ required var email}) async { static Future<bool> verifyOtp({ required String email, required String otpCode}) async {
final response = await HTTPService().post( final response = await HTTPService().post(
path: ApiEndpoints.sendOtp, path: ApiEndpoints.verifyOtp,
body: { body: {
"email": email, "email": email,
"type": "VERIFICATION" "type": "VERIFICATION",
"otpCode": otpCode
}, },
showServerMessage: true, showServerMessage: true,
expectedResponseModel: (json) { expectedResponseModel: (json) {
print('json===${json['message']}');
if(json['message']=='Otp Verified Successfully'){
return true;
}else{
return false;
}
}); });
return response; return response;
} }
} }

View File

@ -7,4 +7,5 @@ abstract class ApiEndpoints {
static const String login = '$baseUrl/authentication/user/login'; static const String login = '$baseUrl/authentication/user/login';
static const String forgetPassword = '$baseUrl/authentication/user/forget-password'; static const String forgetPassword = '$baseUrl/authentication/user/forget-password';
static const String sendOtp = '$baseUrl/authentication/user/send-otp'; static const String sendOtp = '$baseUrl/authentication/user/send-otp';
static const String verifyOtp = '$baseUrl/authentication/user/verify-otp';
} }

View File

@ -0,0 +1,43 @@
class StringsManager {
static const noRouteFound = 'No route found';
static const noInternetConnection = 'No internet connection';
static const String dashboard = 'Dashboard';
static const String devices = 'Devices';
static const String routine = 'Routines';
static const String tapToRunRoutine = 'Tap to run routine';
static const String wizard = 'Wizard';
static const String active = 'Active';
static const String current = 'Current';
static const String frequency = 'Frequency';
static const String energyUsage = 'Energy Usage';
static const String totalConsumption = 'Total Consumption';
static const String ACConsumption = 'AC Consumption';
static const String units = 'Units';
static const String emissions = 'Emissions';
static const String reductions = 'Reductions';
static const String winter = 'Winter';
static const String winterMode = 'Winter Mode';
static const String summer = 'Summer';
static const String summerMode = 'Summer Mode';
static const String on = 'ON';
static const String off = 'OFF';
static const String timer = 'Timer';
static const String dimmerAndColor = "Dimmer & color";
static const String recentlyUsed = "Recently used colors";
static const String lightingModes = "Lighting modes";
static const String doze = "Doze";
static const String relax = "Relax";
static const String reading = "Reading";
static const String energizing = "Energizing";
static const String createScene = 'Create Scene';
static const String tapToRun = 'Launch: Tap - To - Run';
static const String turnOffAllLights =
'Example: turn off all lights in the with one tap.';
static const String whenDeviceStatusChanges = 'When device status changes';
static const String whenUnusualActivityIsDetected =
'Example: when an unusual activity is detected.';
static const String functions = "Functions";
static const String firstLaunch = "firstLaunch";
static const String deleteScene = 'Delete Scene';
static const String deleteAutomation = 'Delete Automation';
}

View File

@ -0,0 +1,58 @@
import 'package:shared_preferences/shared_preferences.dart';
class SharedPreferencesHelper {
static saveStringToSP(String key, String value) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString(key, value);
}
static saveBoolToSP(String key, bool value) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setBool(key, value);
}
static saveIntToSP(String key, int value) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setInt(key, value);
}
static saveDoubleToSP(String key, double value) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setDouble(key, value);
}
static saveStringListToSP(String key, List<String> value) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setStringList(key, value);
}
static Future<String> readStringFromSP(String key) async {
final prefs = await SharedPreferences.getInstance();
String value = prefs.getString(key) ?? '';
return value;
}
static Future<bool?> readBoolFromSP(String key) async {
final prefs = await SharedPreferences.getInstance();
bool? value = prefs.getBool(key);
return value;
}
static Future<int> readIntFromSP(String key) async {
final prefs = await SharedPreferences.getInstance();
int value = prefs.getInt(key) ?? 0;
return value;
}
static Future<List<String>> readStringListFromSP(String key) async {
final prefs = await SharedPreferences.getInstance();
List<String>? value = prefs.getStringList(key) ?? [];
return value;
}
static Future<bool> removeValueFromSP(String key) async {
final prefs = await SharedPreferences.getInstance();
await prefs.remove(key);
return true;
}
}

View File

@ -7,8 +7,10 @@ import Foundation
import flutter_secure_storage_macos import flutter_secure_storage_macos
import path_provider_foundation import path_provider_foundation
import shared_preferences_foundation
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
} }

View File

@ -105,6 +105,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.2" version: "2.1.2"
file:
dependency: transitive
description:
name: file
sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
url: "https://pub.dev"
source: hosted
version: "7.0.0"
flutter: flutter:
dependency: "direct main" dependency: "direct main"
description: flutter description: flutter
@ -392,6 +400,62 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.1.2" version: "6.1.2"
shared_preferences:
dependency: "direct main"
description:
name: shared_preferences
sha256: c3f888ba2d659f3e75f4686112cc1e71f46177f74452d40d8307edc332296ead
url: "https://pub.dev"
source: hosted
version: "2.3.0"
shared_preferences_android:
dependency: transitive
description:
name: shared_preferences_android
sha256: "041be4d9d2dc6079cf342bc8b761b03787e3b71192d658220a56cac9c04a0294"
url: "https://pub.dev"
source: hosted
version: "2.3.0"
shared_preferences_foundation:
dependency: transitive
description:
name: shared_preferences_foundation
sha256: "671e7a931f55a08aa45be2a13fe7247f2a41237897df434b30d2012388191833"
url: "https://pub.dev"
source: hosted
version: "2.5.0"
shared_preferences_linux:
dependency: transitive
description:
name: shared_preferences_linux
sha256: "2ba0510d3017f91655b7543e9ee46d48619de2a2af38e5c790423f7007c7ccc1"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
shared_preferences_platform_interface:
dependency: transitive
description:
name: shared_preferences_platform_interface
sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
shared_preferences_web:
dependency: transitive
description:
name: shared_preferences_web
sha256: "3a293170d4d9403c3254ee05b84e62e8a9b3c5808ebd17de6a33fe9ea6457936"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
shared_preferences_windows:
dependency: transitive
description:
name: shared_preferences_windows
sha256: "398084b47b7f92110683cac45c6dc4aae853db47e470e5ddcd52cab7f7196ab2"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
sky_engine: sky_engine:
dependency: transitive dependency: transitive
description: flutter description: flutter
@ -497,10 +561,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: web name: web
sha256: d43c1d6b787bf0afad444700ae7f4db8827f701bc61c255ac8d328c6f4d52062 sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.0" version: "0.5.1"
win32: win32:
dependency: transitive dependency: transitive
description: description:

View File

@ -42,7 +42,7 @@ dependencies:
dio: ^5.5.0+1 dio: ^5.5.0+1
get_it: ^7.6.7 get_it: ^7.6.7
flutter_secure_storage: ^9.2.2 flutter_secure_storage: ^9.2.2
shared_preferences: ^2.3.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:

View File

@ -1,30 +0,0 @@
// This is a basic Flutter widget test.
//
// To perform an interaction with a widget in your test, use the WidgetTester
// utility in the flutter_test package. For example, you can send tap and scroll
// gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct.
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:syncrow_web/main.dart';
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(const MyApp());
// Verify that our counter starts at 0.
expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsNothing);
// Tap the '+' icon and trigger a frame.
await tester.tap(find.byIcon(Icons.add));
await tester.pump();
// Verify that our counter has incremented.
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsOneWidget);
});
}