mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-09 14:47:23 +00:00
auth UI and Api
This commit is contained in:
@ -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(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,33 +70,42 @@ 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;
|
||||||
@ -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,8 +145,7 @@ 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'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -148,6 +156,13 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
|||||||
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';
|
||||||
@ -193,7 +208,6 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
|||||||
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';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -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 {}
|
||||||
|
@ -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(
|
||||||
|
@ -287,6 +287,21 @@ class LoginWebPage extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
// Padding(
|
// Padding(
|
||||||
// padding: const EdgeInsets.all(5.0),
|
// padding: const EdgeInsets.all(5.0),
|
||||||
// child: SizedBox(
|
// child: SizedBox(
|
||||||
@ -389,17 +404,3 @@ class LoginWebPage extends StatelessWidget {
|
|||||||
// ],
|
// ],
|
||||||
// ),
|
// ),
|
||||||
// )
|
// )
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
),
|
|
||||||
const Spacer(),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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';
|
||||||
}
|
}
|
||||||
|
43
lib/utils/constants/strings_manager.dart
Normal file
43
lib/utils/constants/strings_manager.dart
Normal 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';
|
||||||
|
}
|
58
lib/utils/helpers/shared_preferences_helper.dart
Normal file
58
lib/utils/helpers/shared_preferences_helper.dart
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@ -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"))
|
||||||
}
|
}
|
||||||
|
68
pubspec.lock
68
pubspec.lock
@ -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:
|
||||||
|
@ -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:
|
||||||
|
@ -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);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
Reference in New Issue
Block a user