first commit
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
import '../../../../Const/api_config.dart';
|
||||
import '../../profile_setup_screen.dart';
|
||||
import '../../success_screen.dart';
|
||||
|
||||
class PhoneAuthRepo {
|
||||
Future<bool> sentOTP({
|
||||
required String phoneNumber,
|
||||
required BuildContext context,
|
||||
}) async {
|
||||
final url = Uri.parse('${APIConfig.url}/send-otp');
|
||||
final body = {
|
||||
'phone': phoneNumber,
|
||||
};
|
||||
final headers = {
|
||||
'Accept': 'application/json',
|
||||
};
|
||||
|
||||
try {
|
||||
final response = await http.post(url, headers: headers, body: body);
|
||||
|
||||
final errorData = jsonDecode(response.body);
|
||||
EasyLoading.dismiss();
|
||||
if (response.statusCode == 200) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(errorData['message'])));
|
||||
|
||||
return true;
|
||||
} else {
|
||||
EasyLoading.showError(errorData['message']);
|
||||
}
|
||||
} catch (error) {
|
||||
EasyLoading.showError('Network error: Please try again');
|
||||
// ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Network error: Please try again')));
|
||||
} finally {}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Future<void> submitOTP({
|
||||
required String phoneNumber,
|
||||
required String otp,
|
||||
required BuildContext context,
|
||||
}) async {
|
||||
final url = Uri.parse('${APIConfig.url}/submit-otp');
|
||||
final body = {
|
||||
'phone': phoneNumber,
|
||||
'otp': otp,
|
||||
};
|
||||
final headers = {
|
||||
'Accept': 'application/json',
|
||||
};
|
||||
|
||||
try {
|
||||
final response = await http.post(url, headers: headers, body: body);
|
||||
|
||||
final data = jsonDecode(response.body);
|
||||
print(response.statusCode);
|
||||
|
||||
EasyLoading.dismiss();
|
||||
if (response.statusCode == 200) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(data['message'])));
|
||||
// await saveUserData(userData: data);
|
||||
bool isSetup = data['is_setup'] ?? false;
|
||||
if (isSetup) {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => SuccessScreen(email: 'phone')));
|
||||
} else {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => ProfileSetup()));
|
||||
}
|
||||
} else {
|
||||
EasyLoading.showError(data['message']);
|
||||
}
|
||||
} catch (error) {
|
||||
print(error);
|
||||
EasyLoading.showError('Network error: Please try again');
|
||||
// ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Network error: Please try again')));
|
||||
} finally {}
|
||||
}
|
||||
}
|
||||
211
lib/Screens/Authentication/Phone Auth/phone_OTP_screen.dart
Normal file
211
lib/Screens/Authentication/Phone Auth/phone_OTP_screen.dart
Normal file
@@ -0,0 +1,211 @@
|
||||
// ignore_for_file: file_names
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
import 'package:iconly/iconly.dart';
|
||||
import 'package:mobile_pos/GlobalComponents/button_global.dart';
|
||||
import 'package:mobile_pos/Screens/Authentication/Phone%20Auth/Repo/phone_auth_repo.dart';
|
||||
import 'package:mobile_pos/constant.dart';
|
||||
import 'package:mobile_pos/generated/l10n.dart' as lang;
|
||||
import 'package:pinput/pinput.dart';
|
||||
|
||||
class OTPVerify extends StatefulWidget {
|
||||
const OTPVerify({Key? key, required this.phoneNumber}) : super(key: key);
|
||||
|
||||
final String phoneNumber;
|
||||
|
||||
@override
|
||||
State<OTPVerify> createState() => _OTPVerifyState();
|
||||
}
|
||||
|
||||
class _OTPVerifyState extends State<OTPVerify> {
|
||||
String code = '';
|
||||
FocusNode focusNode = FocusNode();
|
||||
int _start = 60; // 2 minutes in seconds
|
||||
late Timer _timer;
|
||||
|
||||
void _startTimer() {
|
||||
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
||||
setState(() {
|
||||
if (_start == 0) {
|
||||
timer.cancel();
|
||||
} else {
|
||||
_start--;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void _resendOtp() async {
|
||||
_start = 60;
|
||||
_startTimer();
|
||||
PhoneAuthRepo repo = PhoneAuthRepo();
|
||||
await repo.sentOTP(phoneNumber: widget.phoneNumber, context: context);
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_startTimer();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
// TODO: implement dispose
|
||||
super.dispose();
|
||||
_timer.cancel();
|
||||
focusNode.dispose();
|
||||
}
|
||||
|
||||
final GlobalKey<FormState> _key = GlobalKey();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return WillPopScope(
|
||||
onWillPop: () async {
|
||||
return false;
|
||||
},
|
||||
child: Scaffold(
|
||||
extendBodyBehindAppBar: true,
|
||||
backgroundColor: kWhite,
|
||||
body: Container(
|
||||
margin: const EdgeInsets.only(left: 25, right: 25),
|
||||
alignment: Alignment.center,
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const NameWithLogo(),
|
||||
const SizedBox(height: 25),
|
||||
Text(
|
||||
lang.S.of(context).phoneVerification,
|
||||
style: const TextStyle(fontSize: 22, fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
//lang.S.of(context)
|
||||
lang.S.of(context).weSentAnOTPInYourPhoneNumber,
|
||||
// 'We sent an OTP in your phone number',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
widget.phoneNumber,
|
||||
style: const TextStyle(fontSize: 16, color: kMainColor),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
const Icon(
|
||||
IconlyLight.edit_square,
|
||||
size: 16,
|
||||
color: kMainColor,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Form(
|
||||
key: _key,
|
||||
child: Pinput(
|
||||
focusNode: focusNode,
|
||||
keyboardType: TextInputType.number,
|
||||
errorPinTheme: PinTheme(
|
||||
width: 50,
|
||||
height: 50,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.red.shade200, borderRadius: const BorderRadius.all(Radius.circular(8)))),
|
||||
validator: (value) {
|
||||
// if (value.isEmptyOrNull) {
|
||||
// //return 'Please enter the OTP';
|
||||
// return lang.S.of(context).pleaseEnterTheOTP;
|
||||
// }
|
||||
if (value == null || value.isEmpty) {
|
||||
return lang.S.of(context).pleaseEnterTheOTP;
|
||||
}
|
||||
|
||||
if (value!.length < 4) {
|
||||
//return 'Enter a valid OTP';
|
||||
return lang.S.of(context).enterAValidOTP;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
length: 4,
|
||||
showCursor: true,
|
||||
onCompleted: (pin) {
|
||||
code = pin;
|
||||
}),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
height: 45,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: kMainColor,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10))),
|
||||
onPressed: () async {
|
||||
focusNode.unfocus();
|
||||
|
||||
if (_key.currentState?.validate() ?? false) {
|
||||
EasyLoading.show();
|
||||
|
||||
PhoneAuthRepo repo = PhoneAuthRepo();
|
||||
|
||||
await repo.submitOTP(phoneNumber: widget.phoneNumber, otp: code, context: context);
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
lang.S.of(context).verify,
|
||||
// 'Verify',
|
||||
style: const TextStyle(color: Colors.white),
|
||||
)),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
_start == 0
|
||||
? GestureDetector(
|
||||
onTap: _resendOtp,
|
||||
child: Text(
|
||||
//'Resend OTP',
|
||||
lang.S.of(context).resendOTP,
|
||||
style: const TextStyle(color: kMainColor),
|
||||
))
|
||||
: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
lang.S.of(context).resendIn,
|
||||
//'Resend OTP in '
|
||||
),
|
||||
Text(
|
||||
'${_start.toString()} ${lang.S.of(context).seconds}',
|
||||
style: const TextStyle(color: Colors.grey),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
214
lib/Screens/Authentication/Phone Auth/phone_auth_screen.dart
Normal file
214
lib/Screens/Authentication/Phone Auth/phone_auth_screen.dart
Normal file
@@ -0,0 +1,214 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
import 'package:intl_phone_field/intl_phone_field.dart';
|
||||
import 'package:mobile_pos/GlobalComponents/button_global.dart';
|
||||
import 'package:mobile_pos/Screens/Authentication/Phone%20Auth/phone_OTP_screen.dart';
|
||||
import 'package:mobile_pos/constant.dart';
|
||||
import 'package:mobile_pos/generated/l10n.dart' as lang;
|
||||
|
||||
import 'Repo/phone_auth_repo.dart';
|
||||
|
||||
class PhoneAuth extends StatefulWidget {
|
||||
const PhoneAuth({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<PhoneAuth> createState() => _PhoneAuthState();
|
||||
}
|
||||
|
||||
class _PhoneAuthState extends State<PhoneAuth> {
|
||||
String? phoneNumber;
|
||||
|
||||
bool phoneFieldValid = true;
|
||||
late StreamSubscription subscription;
|
||||
bool isDeviceConnected = false;
|
||||
bool isAlertSet = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: kWhite,
|
||||
body: Container(
|
||||
margin: const EdgeInsets.only(left: 25, right: 25),
|
||||
alignment: Alignment.center,
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
const NameWithLogo(),
|
||||
const SizedBox(height: 25),
|
||||
Text(
|
||||
lang.S.of(context).phoneVerification,
|
||||
style: const TextStyle(fontSize: 22, fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
lang.S.of(context).registerTitle,
|
||||
style: const TextStyle(fontSize: 16),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
IntlPhoneField(
|
||||
decoration: InputDecoration(
|
||||
//labelText: 'Phone Number',
|
||||
labelText: lang.S.of(context).phoneNumber,
|
||||
border: const OutlineInputBorder(borderSide: BorderSide(), borderRadius: BorderRadius.all(Radius.circular(15))),
|
||||
),
|
||||
initialCountryCode: 'BD',
|
||||
onChanged: (phone) {
|
||||
phoneNumber = phone.completeNumber;
|
||||
},
|
||||
inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r'^\d*\.?\d{0,2}'))],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
// Container(
|
||||
// height: 55,
|
||||
// decoration:
|
||||
// BoxDecoration(border: Border.all(width: phoneFieldValid ? 1 : 2, color: phoneFieldValid ? Colors.grey : Colors.red), borderRadius: BorderRadius.circular(10)),
|
||||
// child: Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.center,
|
||||
// children: [
|
||||
// const SizedBox(width: 10),
|
||||
// SizedBox(
|
||||
// width: 40,
|
||||
// child: TextField(
|
||||
// controller: countryController,
|
||||
// keyboardType: TextInputType.number,
|
||||
// decoration: const InputDecoration(
|
||||
// border: InputBorder.none,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// const Text(
|
||||
// "|",
|
||||
// style: TextStyle(fontSize: 33, color: Colors.grey),
|
||||
// ),
|
||||
// const SizedBox(width: 10),
|
||||
// Expanded(
|
||||
// child: Form(
|
||||
// key: _key,
|
||||
// child: TextFormField(
|
||||
// controller: phoneNumberController,
|
||||
// inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r'^\d*\.?\d'))],
|
||||
// validator: (value) {
|
||||
// if (value.isEmptyOrNull) {
|
||||
// setState(() {
|
||||
// phoneFieldValid = false;
|
||||
// });
|
||||
// return null;
|
||||
// }
|
||||
// if (value!.length < 8) {
|
||||
// setState(() {
|
||||
// phoneFieldValid = false;
|
||||
// });
|
||||
// return null;
|
||||
// } else {
|
||||
// setState(() {
|
||||
// phoneFieldValid = true;
|
||||
// });
|
||||
//
|
||||
// return null;
|
||||
// }
|
||||
// },
|
||||
// keyboardType: TextInputType.phone,
|
||||
// decoration: const InputDecoration(
|
||||
// border: InputBorder.none,
|
||||
// hintText: "Phone Number",
|
||||
// ),
|
||||
// ),
|
||||
// ))
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// Visibility(
|
||||
// visible: !phoneFieldValid,
|
||||
// child: const Padding(
|
||||
// padding: EdgeInsets.only(top: 4, left: 2),
|
||||
// child: Text(
|
||||
// 'Enter a valid phone number',
|
||||
// style: TextStyle(color: Colors.red),
|
||||
// ),
|
||||
// )),
|
||||
// const SizedBox(height: 20),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
height: 45,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(backgroundColor: kMainColor, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10))),
|
||||
// onPressed: () async {
|
||||
// // const OTPVerify().launch(context);
|
||||
// _key.currentState?.validate();
|
||||
//
|
||||
// if (phoneFieldValid) {
|
||||
// EasyLoading.show();
|
||||
// PhoneAuthRepo repo = PhoneAuthRepo();
|
||||
//
|
||||
// if (await repo.sentOTP(phoneNumber: countryController.text + phoneNumberController.text, context: context)) {
|
||||
// OTPVerify(phoneNumber: countryController.text + phoneNumberController.text).launch(context);
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
onPressed: () async {
|
||||
if ((phoneNumber?.length ?? 0) > 8) {
|
||||
EasyLoading.show();
|
||||
PhoneAuthRepo repo = PhoneAuthRepo();
|
||||
|
||||
if (await repo.sentOTP(phoneNumber: phoneNumber!, context: context)) {
|
||||
// OTPVerify(phoneNumber: phoneNumber!).launch(context);
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => OTPVerify(phoneNumber: phoneNumber!)));
|
||||
}
|
||||
} else {
|
||||
EasyLoading.showError(
|
||||
lang.S.of(context).pleaseEnterAValidPhoneNumber,
|
||||
//'Enter a valid Phone Number'
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
lang.S.of(context).sendCode,
|
||||
style: const TextStyle(color: Colors.white),
|
||||
)),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
// Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
// children: [
|
||||
// TextButton(
|
||||
// onPressed: () {
|
||||
// const LoginForm(isEmailLogin: false).launch(context);
|
||||
// },
|
||||
// child: Text(lang.S.of(context).staffLogin),
|
||||
// ),
|
||||
// Flexible(
|
||||
// child: TextButton(
|
||||
// onPressed: () {
|
||||
// const LoginForm(isEmailLogin: true).launch(context);
|
||||
// },
|
||||
// child: Text(
|
||||
// lang.S.of(context).logInWithMail,
|
||||
// overflow: TextOverflow.ellipsis,
|
||||
// maxLines: 1,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// )
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
16
lib/Screens/Authentication/Repo/licnese_repo.dart
Normal file
16
lib/Screens/Authentication/Repo/licnese_repo.dart
Normal file
@@ -0,0 +1,16 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io'; // Required for SocketException
|
||||
|
||||
import 'package:http/http.dart' as http;
|
||||
// import '../../../constant.dart'; // Keep your constant import
|
||||
|
||||
class PurchaseModel {
|
||||
final String validProductCode = '53621221';
|
||||
final String apiToken = 'orZoxiU81Ok7kxsE0FvfraaO0vDW5tiz';
|
||||
|
||||
// Added 'purchaseCode' as a parameter to the function
|
||||
Future<bool> isActiveBuyer(String purchaseCode) async {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
31
lib/Screens/Authentication/Repo/logout_repo.dart
Normal file
31
lib/Screens/Authentication/Repo/logout_repo.dart
Normal file
@@ -0,0 +1,31 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:restart_app/restart_app.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import '../../../Const/api_config.dart';
|
||||
import '../../../Repository/constant_functions.dart';
|
||||
import '../../../currency.dart';
|
||||
|
||||
class LogOutRepo {
|
||||
Future<void> signOut() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.remove("token");
|
||||
await prefs.remove("hasShownExpiredDialog");
|
||||
CurrencyMethods().removeCurrencyFromLocalDatabase();
|
||||
EasyLoading.showSuccess('Successfully Logged Out');
|
||||
Restart.restartApp();
|
||||
}
|
||||
|
||||
Future<void> signOutApi() async {
|
||||
final uri = Uri.parse('${APIConfig.url}/sign-out');
|
||||
|
||||
await http.get(uri, headers: {
|
||||
'Accept': 'application/json',
|
||||
'Authorization': await getAuthToken(),
|
||||
});
|
||||
await signOut();
|
||||
}
|
||||
}
|
||||
48
lib/Screens/Authentication/Repo/otp_settings_repo.dart
Normal file
48
lib/Screens/Authentication/Repo/otp_settings_repo.dart
Normal file
@@ -0,0 +1,48 @@
|
||||
import 'dart:convert';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
import '../../../Const/api_config.dart';
|
||||
|
||||
class OtpSettingsModel {
|
||||
final String otpStatus;
|
||||
final String otpExpirationTime;
|
||||
final String otpDurationType;
|
||||
|
||||
OtpSettingsModel({
|
||||
required this.otpStatus,
|
||||
required this.otpExpirationTime,
|
||||
required this.otpDurationType,
|
||||
});
|
||||
|
||||
factory OtpSettingsModel.fromJson(Map<String, dynamic> json) {
|
||||
return OtpSettingsModel(
|
||||
otpStatus: json['otp_status'] ?? '',
|
||||
otpExpirationTime: json['otp_expiration_time'] ?? '',
|
||||
otpDurationType: json['otp_duration_type'] ?? '',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class OtpSettingsRepo {
|
||||
Future<OtpSettingsModel?> fetchOtpSettings() async {
|
||||
try {
|
||||
final response = await http.get(
|
||||
Uri.parse("${APIConfig.url}/otp-settings"),
|
||||
headers: {
|
||||
"Accept": "application/json",
|
||||
},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final Map<String, dynamic> decoded = jsonDecode(response.body);
|
||||
final data = decoded['data'];
|
||||
return OtpSettingsModel.fromJson(data);
|
||||
} else {
|
||||
throw Exception("Failed to load OTP settings");
|
||||
}
|
||||
} catch (e) {
|
||||
print("Error fetching OTP settings: $e");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
class LogInResponseModel {
|
||||
LogInResponseModel({
|
||||
this.message,
|
||||
this.data,
|
||||
});
|
||||
|
||||
LogInResponseModel.fromJson(dynamic json) {
|
||||
message = json['message'];
|
||||
data = json['data'] != null ? Data.fromJson(json['data']) : null;
|
||||
}
|
||||
|
||||
String? message;
|
||||
Data? data;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['message'] = message;
|
||||
if (data != null) {
|
||||
map['data'] = data?.toJson();
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
class Data {
|
||||
Data({
|
||||
this.name,
|
||||
this.email,
|
||||
this.isSetupped,
|
||||
this.token,
|
||||
});
|
||||
|
||||
Data.fromJson(dynamic json) {
|
||||
name = json['name'];
|
||||
email = json['email'];
|
||||
isSetupped = json['is_setupped'];
|
||||
token = json['token'];
|
||||
}
|
||||
|
||||
String? name;
|
||||
String? email;
|
||||
bool? isSetupped;
|
||||
String? token;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['name'] = name;
|
||||
map['email'] = email;
|
||||
map['is_setupped'] = isSetupped;
|
||||
map['token'] = token;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
82
lib/Screens/Authentication/Sign In/Repo/sign_in_repo.dart
Normal file
82
lib/Screens/Authentication/Sign In/Repo/sign_in_repo.dart
Normal file
@@ -0,0 +1,82 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
import '../../../../Const/api_config.dart';
|
||||
import '../../../../Repository/constant_functions.dart';
|
||||
import '../../../../currency.dart';
|
||||
import '../../../Home/home.dart';
|
||||
import '../../Sign Up/verify_email.dart';
|
||||
import '../../profile_setup_screen.dart';
|
||||
|
||||
class LogInRepo {
|
||||
Future<bool> logIn({
|
||||
required String email,
|
||||
required String password,
|
||||
required BuildContext context,
|
||||
}) async {
|
||||
final url = Uri.parse('${APIConfig.url}/sign-in');
|
||||
|
||||
final body = {
|
||||
'email': email,
|
||||
'password': password,
|
||||
};
|
||||
final headers = {
|
||||
'Accept': 'application/json',
|
||||
};
|
||||
|
||||
try {
|
||||
final response = await http.post(url, headers: headers, body: body);
|
||||
|
||||
final responseData = jsonDecode(response.body);
|
||||
EasyLoading.dismiss();
|
||||
print('Signin ${response.statusCode}');
|
||||
print('Signin ${response.body}');
|
||||
if (response.statusCode == 200) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(responseData['message'])));
|
||||
|
||||
bool isSetupDone = responseData['data']['is_setup'];
|
||||
try {
|
||||
await CurrencyMethods()
|
||||
.saveCurrencyDataInLocalDatabase(selectedCurrencySymbol: responseData['data']['currency']['symbol'], selectedCurrencyName: responseData['data']['currency']['name']);
|
||||
} catch (error) {
|
||||
print(error);
|
||||
}
|
||||
if (!isSetupDone) {
|
||||
Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => const ProfileSetup()));
|
||||
} else {
|
||||
await saveUserData(
|
||||
token: responseData['data']['token'],
|
||||
);
|
||||
Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => const Home()));
|
||||
}
|
||||
|
||||
return true;
|
||||
} else if (response.statusCode == 201) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(responseData['message'])));
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => VerifyEmail(
|
||||
email: email,
|
||||
isFormForgotPass: false,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(responseData['message'])));
|
||||
}
|
||||
} catch (error) {
|
||||
print(error);
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Error: $error')));
|
||||
// ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Network error: Please try again')));
|
||||
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Network error: Please try again')));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
419
lib/Screens/Authentication/Sign In/sign_in_screen.dart
Normal file
419
lib/Screens/Authentication/Sign In/sign_in_screen.dart
Normal file
@@ -0,0 +1,419 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:mobile_pos/Const/api_config.dart';
|
||||
import 'package:mobile_pos/GlobalComponents/button_global.dart';
|
||||
import 'package:mobile_pos/GlobalComponents/glonal_popup.dart';
|
||||
import 'package:mobile_pos/Screens/Authentication/Sign%20In/webview_login.dart';
|
||||
import 'package:mobile_pos/generated/l10n.dart' as lang;
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import '../../../constant.dart';
|
||||
import '../../../service/check_user_role_permission_provider.dart';
|
||||
import '../Sign Up/sign_up_screen.dart';
|
||||
import '../forgot password/forgot_password.dart';
|
||||
import 'Repo/sign_in_repo.dart';
|
||||
import '../../../Repository/check_addon_providers.dart';
|
||||
|
||||
class SignIn extends StatefulWidget {
|
||||
const SignIn({super.key});
|
||||
|
||||
@override
|
||||
State<SignIn> createState() => _SignInState();
|
||||
}
|
||||
|
||||
class _SignInState extends State<SignIn> {
|
||||
bool showPassword = true;
|
||||
bool _isChecked = false;
|
||||
|
||||
///__________variables_____________
|
||||
bool isClicked = false;
|
||||
|
||||
final key = GlobalKey<FormState>();
|
||||
|
||||
TextEditingController emailController = TextEditingController();
|
||||
TextEditingController passwordController = TextEditingController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadUserCredentials();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
emailController.dispose();
|
||||
passwordController.dispose();
|
||||
}
|
||||
|
||||
void _loadUserCredentials() async {
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
setState(() {
|
||||
_isChecked = prefs.getBool('remember_me') ?? false;
|
||||
if (_isChecked) {
|
||||
emailController.text = prefs.getString('email') ?? '';
|
||||
passwordController.text = prefs.getString('password') ?? '';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _saveUserCredentials() async {
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
prefs.setBool('remember_me', _isChecked);
|
||||
if (_isChecked) {
|
||||
prefs.setString('email', emailController.text);
|
||||
prefs.setString('password', passwordController.text);
|
||||
} else {
|
||||
prefs.remove('email');
|
||||
prefs.remove('password');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
TextTheme textTheme = Theme.of(context).textTheme;
|
||||
final _theme = Theme.of(context);
|
||||
return GlobalPopup(
|
||||
child: Consumer(
|
||||
builder: (_, ref, watch) {
|
||||
final socialNetworkProvider = ref.watch(socialLoginCheckProvider);
|
||||
return Scaffold(
|
||||
backgroundColor: kWhite,
|
||||
appBar: AppBar(
|
||||
surfaceTintColor: kWhite,
|
||||
centerTitle: false,
|
||||
automaticallyImplyLeading: false,
|
||||
backgroundColor: kWhite,
|
||||
titleSpacing: 16,
|
||||
title: Text(
|
||||
// 'Sign in',
|
||||
lang.S.of(context).signIn,
|
||||
),
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
|
||||
child: Form(
|
||||
key: key,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
const NameWithLogo(),
|
||||
// const SizedBox(height: 24),
|
||||
// Text(
|
||||
// // 'Welcome back!',f
|
||||
// lang.S.of(context).welcomeBack,
|
||||
// style: textTheme.titleMedium?.copyWith(fontSize: 24.0, fontWeight: FontWeight.w600),
|
||||
// ),
|
||||
// Text(
|
||||
// lang.S.of(context).pleaseEnterYourDetails,
|
||||
// //'Please enter your details.',
|
||||
// style: textTheme.bodyMedium?.copyWith(color: kGreyTextColor, fontSize: 16),
|
||||
// ),
|
||||
const SizedBox(height: 34.0),
|
||||
TextFormField(
|
||||
controller: emailController,
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
decoration: InputDecoration(
|
||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||
// labelText: 'Email',
|
||||
labelText: lang.S.of(context).lableEmail,
|
||||
//hintText: 'Enter email address',
|
||||
hintText: lang.S.of(context).hintEmail,
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
// return 'Email can\'t be empty';
|
||||
return lang.S.of(context).emailCannotBeEmpty;
|
||||
} else if (!value.contains('@')) {
|
||||
//return 'Please enter a valid email';
|
||||
return lang.S.of(context).pleaseEnterAValidEmail;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 20.0),
|
||||
TextFormField(
|
||||
controller: passwordController,
|
||||
keyboardType: TextInputType.text,
|
||||
obscureText: showPassword,
|
||||
decoration: InputDecoration(
|
||||
//labelText: 'Password',
|
||||
labelText: lang.S.of(context).lablePassword,
|
||||
//hintText: 'Enter password',
|
||||
hintText: lang.S.of(context).hintPassword,
|
||||
suffixIcon: IconButton(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
showPassword = !showPassword;
|
||||
});
|
||||
},
|
||||
icon: Icon(
|
||||
showPassword ? FeatherIcons.eyeOff : FeatherIcons.eye,
|
||||
color: kGreyTextColor,
|
||||
size: 18,
|
||||
),
|
||||
),
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
// return 'Password can\'t be empty';
|
||||
return lang.S.of(context).passwordCannotBeEmpty;
|
||||
} else if (value.length < 6) {
|
||||
//return 'Please enter a bigger password';
|
||||
return lang.S.of(context).pleaseEnterABiggerPassword;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 4.0),
|
||||
Row(
|
||||
children: [
|
||||
Checkbox(
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
checkColor: Colors.white,
|
||||
activeColor: kMainColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(3.0),
|
||||
),
|
||||
fillColor: WidgetStateProperty.all(_isChecked ? kMainColor : Colors.transparent),
|
||||
visualDensity: const VisualDensity(horizontal: -4),
|
||||
side: const BorderSide(color: kGreyTextColor),
|
||||
value: _isChecked,
|
||||
onChanged: (newValue) {
|
||||
setState(() {
|
||||
_isChecked = newValue!;
|
||||
});
|
||||
},
|
||||
),
|
||||
const SizedBox(width: 8.0),
|
||||
Text(
|
||||
lang.S.of(context).rememberMe,
|
||||
//'Remember me',
|
||||
style: textTheme.bodyMedium?.copyWith(color: kGreyTextColor),
|
||||
),
|
||||
const Spacer(),
|
||||
TextButton(
|
||||
style: ButtonStyle(
|
||||
shape: WidgetStateProperty.all(
|
||||
RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(6.0),
|
||||
),
|
||||
),
|
||||
),
|
||||
onPressed: () => Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const ForgotPassword(),
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
lang.S.of(context).forgotPassword,
|
||||
//'Forgot password?',
|
||||
style: textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.bold, fontSize: 14),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 24.0),
|
||||
ElevatedButton(
|
||||
style: OutlinedButton.styleFrom(
|
||||
maximumSize: const Size(double.infinity, 48),
|
||||
minimumSize: const Size(double.infinity, 48),
|
||||
disabledBackgroundColor: _theme.colorScheme.primary.withValues(alpha: 0.15),
|
||||
),
|
||||
onPressed: () async {
|
||||
if (isClicked) {
|
||||
return;
|
||||
}
|
||||
if (key.currentState?.validate() ?? false) {
|
||||
isClicked = true;
|
||||
EasyLoading.show();
|
||||
LogInRepo repo = LogInRepo();
|
||||
if (await repo.logIn(
|
||||
email: emailController.text, password: passwordController.text, context: context)) {
|
||||
_saveUserCredentials();
|
||||
EasyLoading.showSuccess(lang.S.of(context).done);
|
||||
} else {
|
||||
isClicked = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
lang.S.of(context).logIn,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: _theme.textTheme.bodyMedium?.copyWith(
|
||||
color: _theme.colorScheme.primaryContainer,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
InkWell(
|
||||
highlightColor: kMainColor.withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(3.0),
|
||||
onTap: () {
|
||||
Navigator.push(context, MaterialPageRoute(
|
||||
builder: (context) {
|
||||
return const SignUpScreen();
|
||||
},
|
||||
));
|
||||
},
|
||||
hoverColor: kMainColor.withValues(alpha: 0.1),
|
||||
child: RichText(
|
||||
text: TextSpan(
|
||||
text: lang.S.of(context).donNotHaveAnAccount,
|
||||
//'Don’t have an account? ',
|
||||
style: textTheme.bodyMedium?.copyWith(color: kGreyTextColor),
|
||||
children: [
|
||||
TextSpan(
|
||||
text: lang.S.of(context).signUp,
|
||||
// text:'Sign Up',
|
||||
style:
|
||||
textTheme.bodyMedium?.copyWith(color: kMainColor, fontWeight: FontWeight.bold),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
socialNetworkProvider.when(data: (isEnable) {
|
||||
if (isEnable) {
|
||||
return Column(
|
||||
children: [
|
||||
SizedBox(height: 20),
|
||||
// Divider
|
||||
SizedBox(
|
||||
height: 28,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
spacing: 6,
|
||||
children: [
|
||||
Expanded(child: Divider()),
|
||||
Text(
|
||||
lang.S.of(context).orContinueWith,
|
||||
style: _theme.textTheme.bodyLarge,
|
||||
),
|
||||
Expanded(child: Divider()),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox.square(dimension: 30),
|
||||
// Social Login
|
||||
Row(
|
||||
spacing: 16,
|
||||
children: [
|
||||
// Facebook
|
||||
Expanded(
|
||||
child: OutlinedButton.icon(
|
||||
onPressed: () {
|
||||
///_________-This is a _ repo________________________
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => WebViewLogin(
|
||||
loginUrl: "${APIConfig.domain}login/x?platform=app",
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: EdgeInsets.symmetric(horizontal: 8),
|
||||
minimumSize: Size(double.infinity, 48),
|
||||
side: const BorderSide(color: kBorder),
|
||||
foregroundColor: _theme.colorScheme.onPrimaryContainer,
|
||||
),
|
||||
label: Text(
|
||||
lang.S.of(context).loginX,
|
||||
textAlign: TextAlign.center,
|
||||
style: _theme.textTheme.titleMedium?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
icon: Container(
|
||||
height: 26,
|
||||
width: 26,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
image:
|
||||
DecorationImage(fit: BoxFit.cover, image: AssetImage('images/x.png'))),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// Google
|
||||
Expanded(
|
||||
child: OutlinedButton.icon(
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => WebViewLogin(
|
||||
loginUrl: "${APIConfig.domain}login/google?platform=app",
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
),
|
||||
minimumSize: Size(double.infinity, 48),
|
||||
side: const BorderSide(color: kBorder),
|
||||
foregroundColor: _theme.colorScheme.onPrimaryContainer,
|
||||
),
|
||||
label: Text(
|
||||
lang.S.of(context).loginGoogle,
|
||||
textAlign: TextAlign.center,
|
||||
style: _theme.textTheme.titleMedium?.copyWith(
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
icon: SvgPicture.asset(
|
||||
'assets/google.svg',
|
||||
width: 26,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
} else {
|
||||
return SizedBox.shrink();
|
||||
}
|
||||
}, error: (e, stack) {
|
||||
return Center(
|
||||
child: Text(e.toString()),
|
||||
);
|
||||
}, loading: () {
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
109
lib/Screens/Authentication/Sign In/webview_login.dart
Normal file
109
lib/Screens/Authentication/Sign In/webview_login.dart
Normal file
@@ -0,0 +1,109 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:webview_flutter/webview_flutter.dart';
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
import 'package:mobile_pos/generated/l10n.dart' as l;
|
||||
|
||||
import '../../../../Repository/constant_functions.dart'; // Adjust path accordingly
|
||||
import '../../../../currency.dart'; // Adjust path accordingly
|
||||
import '../../Home/home.dart';
|
||||
import '../profile_setup_screen.dart'; // Adjust path accordingly
|
||||
|
||||
class WebViewLogin extends StatefulWidget {
|
||||
final String loginUrl;
|
||||
|
||||
const WebViewLogin({super.key, required this.loginUrl});
|
||||
|
||||
@override
|
||||
_WebViewLoginState createState() => _WebViewLoginState();
|
||||
}
|
||||
|
||||
class _WebViewLoginState extends State<WebViewLogin> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
EasyLoading.show(status: l.S.of(context).loading);
|
||||
}
|
||||
|
||||
void _handleRedirect(String url) async {
|
||||
if (url.contains('/app-login-or-signup')) {
|
||||
final uri = Uri.parse(url);
|
||||
final queryParams = uri.queryParameters;
|
||||
|
||||
final token = queryParams['token'];
|
||||
final isSetup = queryParams['is_setup'] == '1';
|
||||
final status = queryParams['status'];
|
||||
final currency = queryParams['currency'] ?? queryParams['currency_id'];
|
||||
if (status == 'success' && token != null) {
|
||||
await saveUserData(token: token); // Save token
|
||||
if (currency != null) {
|
||||
try {
|
||||
await CurrencyMethods().saveCurrencyDataInLocalDatabase(
|
||||
selectedCurrencySymbol: currency,
|
||||
selectedCurrencyName: currency,
|
||||
);
|
||||
} catch (e) {
|
||||
print('Error saving currency: $e');
|
||||
}
|
||||
}
|
||||
|
||||
if (mounted) {
|
||||
if (isSetup) {
|
||||
Navigator.pushReplacement(
|
||||
context,
|
||||
MaterialPageRoute(builder: (_) => const Home()),
|
||||
);
|
||||
} else {
|
||||
Navigator.pushReplacement(
|
||||
context,
|
||||
MaterialPageRoute(builder: (_) => const ProfileSetup()),
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||
content: Text(l.S.of(context).loginFailedPleaseTryAgain),
|
||||
));
|
||||
Navigator.pop(context); // Close WebView
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SafeArea(
|
||||
child: Scaffold(
|
||||
body: WebViewWidget(
|
||||
controller: WebViewController()
|
||||
..setJavaScriptMode(JavaScriptMode.unrestricted)
|
||||
..setBackgroundColor(const Color(0x00000000))
|
||||
..setNavigationDelegate(
|
||||
NavigationDelegate(
|
||||
// Intercept all navigation requests and load within WebView
|
||||
onNavigationRequest: (request) {
|
||||
return NavigationDecision.navigate;
|
||||
},
|
||||
onPageFinished: (url) {
|
||||
EasyLoading.dismiss();
|
||||
},
|
||||
onPageStarted: (url) {
|
||||
_handleRedirect(url);
|
||||
},
|
||||
onWebResourceError: (error) {
|
||||
EasyLoading.dismiss();
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(l.S.of(context).someThingWithWrongWithTheWebPage)),
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
// Set user agent to mimic a browser
|
||||
..setUserAgent(
|
||||
'Mozilla/5.0 (Linux; Android 10; Mobile) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.120 Mobile Safari/537.36')
|
||||
..loadRequest(Uri.parse(widget.loginUrl)),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
118
lib/Screens/Authentication/Sign Up/repo/sign_up_repo.dart
Normal file
118
lib/Screens/Authentication/Sign Up/repo/sign_up_repo.dart
Normal file
@@ -0,0 +1,118 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
import '../../../../Const/api_config.dart';
|
||||
import '../../../../Repository/constant_functions.dart';
|
||||
import '../../../../currency.dart';
|
||||
|
||||
class SignUpRepo {
|
||||
Future<dynamic> signUp({required String name, required String email, required String password, required BuildContext context}) async {
|
||||
final url = Uri.parse('${APIConfig.url}/sign-up');
|
||||
|
||||
final body = {
|
||||
'name': name,
|
||||
'email': email,
|
||||
'password': password,
|
||||
};
|
||||
final headers = {
|
||||
'Accept': 'application/json',
|
||||
};
|
||||
|
||||
try {
|
||||
final response = await http.post(url, headers: headers, body: body);
|
||||
|
||||
final responseData = jsonDecode(response.body);
|
||||
EasyLoading.dismiss();
|
||||
if (response.statusCode == 200) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(responseData['message'])));
|
||||
|
||||
final token = responseData['token'];
|
||||
if (token != null) {
|
||||
await saveUserData(token: token);
|
||||
return responseData['token'];
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(responseData['message'])));
|
||||
}
|
||||
} catch (error) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Network error: Please try again')));
|
||||
} finally {}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Future<bool> verifyOTP({required String email, required String otp, required BuildContext context}) async {
|
||||
final url = Uri.parse('${APIConfig.url}/submit-otp');
|
||||
|
||||
final body = {
|
||||
'email': email,
|
||||
'otp': otp,
|
||||
};
|
||||
final headers = {
|
||||
'Accept': 'application/json',
|
||||
};
|
||||
|
||||
try {
|
||||
final response = await http.post(url, headers: headers, body: body);
|
||||
|
||||
final responseData = jsonDecode(response.body);
|
||||
EasyLoading.dismiss();
|
||||
if (response.statusCode == 200) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(responseData['message'])));
|
||||
|
||||
String? token = responseData['token'];
|
||||
if (responseData['currency'] != null) {
|
||||
await CurrencyMethods()
|
||||
.saveCurrencyDataInLocalDatabase(selectedCurrencySymbol: responseData['currency']['symbol'], selectedCurrencyName: responseData['currency']['name']);
|
||||
}
|
||||
if (token != null) {
|
||||
await saveUserData(token: responseData['token']);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(responseData['error'])));
|
||||
}
|
||||
} catch (error) {
|
||||
print('Error: $error');
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Error: $error')));
|
||||
// ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Server error: Please try again')));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Future<bool> resendOTP({required String email, required BuildContext context}) async {
|
||||
final url = Uri.parse('${APIConfig.url}/resend-otp');
|
||||
|
||||
final body = {
|
||||
'email': email,
|
||||
};
|
||||
final headers = {
|
||||
'Accept': 'application/json',
|
||||
};
|
||||
|
||||
try {
|
||||
final response = await http.post(url, headers: headers, body: body);
|
||||
|
||||
final responseData = jsonDecode(response.body);
|
||||
EasyLoading.dismiss();
|
||||
if (response.statusCode == 200) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(responseData['message'])));
|
||||
|
||||
return true;
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(responseData['error'])));
|
||||
}
|
||||
} catch (error) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Network error: Please try again')));
|
||||
} finally {}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
260
lib/Screens/Authentication/Sign Up/sign_up_screen.dart
Normal file
260
lib/Screens/Authentication/Sign Up/sign_up_screen.dart
Normal file
@@ -0,0 +1,260 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
import 'package:mobile_pos/Screens/Authentication/Sign%20Up/repo/sign_up_repo.dart';
|
||||
import 'package:mobile_pos/Screens/Authentication/Sign%20Up/verify_email.dart';
|
||||
import 'package:mobile_pos/generated/l10n.dart' as lang;
|
||||
|
||||
import '../../../GlobalComponents/button_global.dart';
|
||||
import '../../../GlobalComponents/glonal_popup.dart';
|
||||
import '../../../constant.dart';
|
||||
import '../Wedgets/check_email_for_otp_popup.dart';
|
||||
import '../profile_setup_screen.dart';
|
||||
|
||||
class SignUpScreen extends StatefulWidget {
|
||||
const SignUpScreen({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<SignUpScreen> createState() => _SignUpScreenState();
|
||||
}
|
||||
|
||||
class _SignUpScreenState extends State<SignUpScreen> {
|
||||
///__________Variables________________________________
|
||||
bool showPassword = true;
|
||||
bool isClicked = false;
|
||||
|
||||
///________Key_______________________________________
|
||||
GlobalKey<FormState> key = GlobalKey<FormState>();
|
||||
|
||||
///___________Controllers______________________________
|
||||
TextEditingController nameTextController = TextEditingController();
|
||||
TextEditingController passwordTextController = TextEditingController();
|
||||
TextEditingController emailTextController = TextEditingController();
|
||||
|
||||
///________Dispose____________________________________
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
nameTextController.dispose();
|
||||
passwordTextController.dispose();
|
||||
emailTextController.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
TextTheme textTheme = Theme.of(context).textTheme;
|
||||
final _theme = Theme.of(context);
|
||||
return GlobalPopup(
|
||||
child: Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
appBar: AppBar(
|
||||
backgroundColor: kWhite,
|
||||
titleSpacing: 16,
|
||||
centerTitle: true,
|
||||
surfaceTintColor: kWhite,
|
||||
title: Text(
|
||||
lang.S.of(context).signUp,
|
||||
//'Sign Up',
|
||||
),
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16.0, 20.0, 16.0, 0.0),
|
||||
child: Form(
|
||||
key: key,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 24,
|
||||
),
|
||||
const NameWithLogo(),
|
||||
const SizedBox(
|
||||
height: 24,
|
||||
),
|
||||
Text(
|
||||
lang.S.of(context).createAFreeAccount,
|
||||
//'Create A Free Account',
|
||||
style: textTheme.titleMedium?.copyWith(fontSize: 24.0, fontWeight: FontWeight.w600),
|
||||
),
|
||||
Text(
|
||||
lang.S.of(context).pleaseEnterYourDetails,
|
||||
//'Please enter your details',
|
||||
style: textTheme.bodyMedium?.copyWith(color: kGreyTextColor, fontSize: 16),
|
||||
),
|
||||
const SizedBox(height: 24.0),
|
||||
|
||||
///____________Name______________________________________________
|
||||
TextFormField(
|
||||
controller: nameTextController,
|
||||
keyboardType: TextInputType.name,
|
||||
decoration: InputDecoration(
|
||||
//labelText: 'Full Name',
|
||||
labelText: lang.S.of(context).fullName,
|
||||
//hintText: 'Enter your full name',
|
||||
hintText: lang.S.of(context).enterYourFullName,
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
//return 'name can\'n be empty';
|
||||
return lang.S.of(context).nameCanNotBeEmpty;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 20.0),
|
||||
|
||||
///__________Email______________________________________________
|
||||
TextFormField(
|
||||
controller: emailTextController,
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
decoration: InputDecoration(
|
||||
// border: OutlineInputBorder(),
|
||||
// labelText: 'email',
|
||||
labelText: lang.S.of(context).lableEmail,
|
||||
//hintText: 'Enter email address',
|
||||
hintText: lang.S.of(context).hintEmail,
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
//return 'Email can\'n be empty';
|
||||
return lang.S.of(context).emailCannotBeEmpty;
|
||||
} else if (!value.contains('@')) {
|
||||
//return 'Please enter a valid email';
|
||||
return lang.S.of(context).pleaseEnterAValidEmail;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 20.0),
|
||||
|
||||
///___________Password_____________________________________________
|
||||
TextFormField(
|
||||
controller: passwordTextController,
|
||||
keyboardType: TextInputType.text,
|
||||
obscureText: showPassword,
|
||||
decoration: InputDecoration(
|
||||
//labelText: 'Password',
|
||||
labelText: lang.S.of(context).lablePassword,
|
||||
// hintText: 'Enter password',
|
||||
hintText: lang.S.of(context).hintPassword,
|
||||
suffixIcon: IconButton(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
showPassword = !showPassword;
|
||||
});
|
||||
},
|
||||
icon: Icon(
|
||||
showPassword ? FeatherIcons.eyeOff : FeatherIcons.eye,
|
||||
color: kGreyTextColor,
|
||||
size: 18,
|
||||
),
|
||||
),
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
//return 'Password can\'t be empty';
|
||||
return lang.S.of(context).passwordCannotBeEmpty;
|
||||
} else if (value.length < 6) {
|
||||
//return 'Please enter a bigger password';
|
||||
return lang.S.of(context).pleaseEnterABiggerPassword;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 24.0),
|
||||
|
||||
///________Button___________________________________________________
|
||||
ElevatedButton(
|
||||
style: OutlinedButton.styleFrom(
|
||||
maximumSize: const Size(double.infinity, 48),
|
||||
minimumSize: const Size(double.infinity, 48),
|
||||
disabledBackgroundColor: _theme.colorScheme.primary.withValues(alpha: 0.15),
|
||||
),
|
||||
onPressed: () async {
|
||||
if (isClicked) {
|
||||
return;
|
||||
}
|
||||
if (key.currentState?.validate() ?? false) {
|
||||
isClicked = true;
|
||||
EasyLoading.show();
|
||||
SignUpRepo repo = SignUpRepo();
|
||||
final result = await repo.signUp(name: nameTextController.text, email: emailTextController.text, password: passwordTextController.text, context: context);
|
||||
if (result is bool && result) {
|
||||
if (result) {
|
||||
if (await checkEmailForCodePupUp(email: emailTextController.text, context: context, textTheme: textTheme)) {
|
||||
Navigator.pushReplacement(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => VerifyEmail(
|
||||
email: emailTextController.text,
|
||||
isFormForgotPass: false,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
isClicked = false;
|
||||
}
|
||||
} else if (result is String) {
|
||||
Navigator.pushReplacement(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const ProfileSetup(),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
isClicked = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
lang.S.of(context).signUp,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: _theme.textTheme.bodyMedium?.copyWith(
|
||||
color: _theme.colorScheme.primaryContainer,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
InkWell(
|
||||
highlightColor: kMainColor.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(3.0),
|
||||
onTap: () => Navigator.pop(context),
|
||||
hoverColor: kMainColor.withOpacity(0.1),
|
||||
child: RichText(
|
||||
text: TextSpan(
|
||||
text: lang.S.of(context).alreadyHaveAnAccount,
|
||||
//'Already have an account? ',
|
||||
style: textTheme.bodyMedium?.copyWith(color: kGreyTextColor),
|
||||
children: [
|
||||
TextSpan(
|
||||
text: lang.S.of(context).signIn,
|
||||
//'Sign In',
|
||||
style: textTheme.bodyMedium?.copyWith(color: kMainColor, fontWeight: FontWeight.bold),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
251
lib/Screens/Authentication/Sign Up/verify_email.dart
Normal file
251
lib/Screens/Authentication/Sign Up/verify_email.dart
Normal file
@@ -0,0 +1,251 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
import 'package:mobile_pos/Screens/Authentication/Sign%20Up/repo/sign_up_repo.dart';
|
||||
import 'package:mobile_pos/constant.dart';
|
||||
import 'package:mobile_pos/generated/l10n.dart' as lang;
|
||||
import 'package:pinput/pinput.dart' as p;
|
||||
import '../../../GlobalComponents/glonal_popup.dart';
|
||||
import '../Repo/otp_settings_repo.dart';
|
||||
import '../forgot password/repo/forgot_pass_repo.dart';
|
||||
import '../forgot password/set_new_password.dart';
|
||||
import '../profile_setup_screen.dart';
|
||||
|
||||
class VerifyEmail extends StatefulWidget {
|
||||
const VerifyEmail({super.key, required this.email, required this.isFormForgotPass});
|
||||
final String email;
|
||||
final bool isFormForgotPass;
|
||||
|
||||
@override
|
||||
State<VerifyEmail> createState() => _VerifyEmailNewState();
|
||||
}
|
||||
|
||||
class _VerifyEmailNewState extends State<VerifyEmail> {
|
||||
bool isClicked = false;
|
||||
|
||||
Timer? _timer;
|
||||
int _start = 180; // default fallback
|
||||
bool _isButtonEnabled = false;
|
||||
|
||||
final pinController = TextEditingController();
|
||||
final focusNode = FocusNode();
|
||||
final _pinputKey = GlobalKey<FormState>();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadOtpSettings();
|
||||
}
|
||||
|
||||
Future<void> _loadOtpSettings() async {
|
||||
EasyLoading.show(status: lang.S.of(context).loadingOtpSetting);
|
||||
final settings = await OtpSettingsRepo().fetchOtpSettings();
|
||||
print(settings?.otpExpirationTime);
|
||||
EasyLoading.dismiss();
|
||||
|
||||
if (settings != null) {
|
||||
int durationInSec = int.parse(settings.otpExpirationTime);
|
||||
|
||||
if (settings.otpDurationType.toLowerCase().contains("minute")) {
|
||||
durationInSec *= 60;
|
||||
} else if (settings.otpDurationType.toLowerCase().contains("hour")) {
|
||||
durationInSec *= 3600;
|
||||
}
|
||||
|
||||
setState(() {
|
||||
_start = durationInSec;
|
||||
});
|
||||
}
|
||||
startTimer();
|
||||
}
|
||||
|
||||
void startTimer() {
|
||||
_isButtonEnabled = false;
|
||||
_timer?.cancel();
|
||||
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
||||
setState(() {
|
||||
if (_start > 0) {
|
||||
_start--;
|
||||
} else {
|
||||
_isButtonEnabled = true;
|
||||
_timer?.cancel();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
pinController.dispose();
|
||||
focusNode.dispose();
|
||||
_timer?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
static const focusedBorderColor = kMainColor;
|
||||
static const fillColor = Color(0xFFF3F3F3);
|
||||
final defaultPinTheme = p.PinTheme(
|
||||
width: 45,
|
||||
height: 52,
|
||||
textStyle: const TextStyle(
|
||||
fontSize: 20,
|
||||
color: kTitleColor,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(color: kBorderColor),
|
||||
),
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
TextTheme textTheme = Theme.of(context).textTheme;
|
||||
return GlobalPopup(
|
||||
child: Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
appBar: AppBar(
|
||||
backgroundColor: kWhite,
|
||||
surfaceTintColor: kWhite,
|
||||
centerTitle: true,
|
||||
titleSpacing: 16,
|
||||
title: Text(lang.S.of(context).verityEmail),
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16.0, 20.0, 16.0, 0.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
lang.S.of(context).verityEmail,
|
||||
style: textTheme.titleMedium?.copyWith(fontSize: 24.0),
|
||||
),
|
||||
const SizedBox(height: 8.0),
|
||||
RichText(
|
||||
textAlign: TextAlign.center,
|
||||
text: TextSpan(
|
||||
text: lang.S.of(context).digits,
|
||||
style: textTheme.bodyMedium?.copyWith(color: kGreyTextColor, fontSize: 16),
|
||||
children: [
|
||||
TextSpan(
|
||||
text: widget.email,
|
||||
style:
|
||||
textTheme.bodyMedium?.copyWith(color: kTitleColor, fontWeight: FontWeight.bold, fontSize: 16),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24.0),
|
||||
Form(
|
||||
key: _pinputKey,
|
||||
child: p.Pinput(
|
||||
length: 6,
|
||||
controller: pinController,
|
||||
focusNode: focusNode,
|
||||
defaultPinTheme: defaultPinTheme,
|
||||
separatorBuilder: (index) => const SizedBox(width: 11),
|
||||
validator: (value) {
|
||||
if ((value?.length ?? 0) < 6) {
|
||||
return lang.S.of(context).enterValidOTP;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
focusedPinTheme: defaultPinTheme.copyWith(
|
||||
decoration: defaultPinTheme.decoration!.copyWith(
|
||||
color: kMainColor.withOpacity(0.1),
|
||||
border: Border.all(color: focusedBorderColor),
|
||||
),
|
||||
),
|
||||
submittedPinTheme: defaultPinTheme.copyWith(
|
||||
decoration: defaultPinTheme.decoration!.copyWith(
|
||||
color: fillColor,
|
||||
border: Border.all(color: kTitleColor),
|
||||
),
|
||||
),
|
||||
errorPinTheme: defaultPinTheme.copyBorderWith(
|
||||
border: Border.all(color: Colors.redAccent),
|
||||
),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 11, bottom: 11),
|
||||
child: Text(
|
||||
_isButtonEnabled
|
||||
? lang.S.of(context).youCanNowResendYourOtp
|
||||
: lang.S.of(context).resendOtpSeconds(_start),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 20),
|
||||
Visibility(
|
||||
visible: _isButtonEnabled,
|
||||
child: TextButton(
|
||||
onPressed: _isButtonEnabled
|
||||
? () async {
|
||||
EasyLoading.show();
|
||||
SignUpRepo repo = SignUpRepo();
|
||||
if (await repo.resendOTP(email: widget.email, context: context)) {
|
||||
_loadOtpSettings();
|
||||
}
|
||||
}
|
||||
: null,
|
||||
child: Text(
|
||||
lang.S.of(context).resendOTP,
|
||||
style: TextStyle(color: kMainColor),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 24.0),
|
||||
ElevatedButton(
|
||||
onPressed: widget.isFormForgotPass
|
||||
? () async {
|
||||
if (isClicked) return;
|
||||
focusNode.unfocus();
|
||||
if (_pinputKey.currentState?.validate() ?? false) {
|
||||
isClicked = true;
|
||||
EasyLoading.show();
|
||||
ForgotPassRepo repo = ForgotPassRepo();
|
||||
if (await repo.verifyOTPForgotPass(
|
||||
email: widget.email, otp: pinController.text, context: context)) {
|
||||
Navigator.pushReplacement(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => SetNewPassword(email: widget.email),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
isClicked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
: () async {
|
||||
if (isClicked) return;
|
||||
focusNode.unfocus();
|
||||
if (_pinputKey.currentState?.validate() ?? false) {
|
||||
isClicked = true;
|
||||
EasyLoading.show();
|
||||
SignUpRepo repo = SignUpRepo();
|
||||
if (await repo.verifyOTP(email: widget.email, otp: pinController.text, context: context)) {
|
||||
Navigator.pushReplacement(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const ProfileSetup(),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
isClicked = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
child: Text(lang.S.of(context).continueE),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mobile_pos/generated/l10n.dart' as lang;
|
||||
|
||||
import '../../../constant.dart';
|
||||
|
||||
Future<dynamic> checkEmailForCodePupUp({required String email, required BuildContext context, required TextTheme textTheme}) {
|
||||
return showDialog(
|
||||
barrierDismissible: false,
|
||||
context: context,
|
||||
builder: (BuildContext contextPopUp) {
|
||||
return WillPopScope(
|
||||
onWillPop: () async => false,
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(sigmaX: 4, sigmaY: 4),
|
||||
child: Dialog(
|
||||
backgroundColor: kWhite,
|
||||
surfaceTintColor: kWhite,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16.0),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
lang.S.of(context).verifyYourEmail,
|
||||
// 'Verify Your Email',
|
||||
style: textTheme.titleMedium?.copyWith(fontSize: 24.0),
|
||||
),
|
||||
const SizedBox(height: 10.0),
|
||||
Text(
|
||||
lang.S.of(context).weHaveSentAConfirmationEmailTo,
|
||||
//'We have sent a confirmation email to',
|
||||
textAlign: TextAlign.center,
|
||||
style: textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.normal, color: kGreyTextColor, fontSize: 16),
|
||||
),
|
||||
Text(
|
||||
email,
|
||||
style: textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.bold, fontSize: 16),
|
||||
),
|
||||
const SizedBox(height: 16.0),
|
||||
Text(
|
||||
lang.S.of(context).folder,
|
||||
// 'It May be that the mail ended up in your spam folder.',
|
||||
textAlign: TextAlign.center,
|
||||
style: textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.normal, color: kGreyTextColor, fontSize: 16),
|
||||
),
|
||||
const SizedBox(height: 17.0),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(contextPopUp, true);
|
||||
},
|
||||
child: Text(lang.S.of(context).gotIt),
|
||||
//'Got It !',
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
import 'package:mobile_pos/Screens/Authentication/change%20password/repo/change_pass_repo.dart';
|
||||
import 'package:mobile_pos/generated/l10n.dart' as lang;
|
||||
import '../../../GlobalComponents/glonal_popup.dart';
|
||||
import '../../../constant.dart';
|
||||
|
||||
class ChangePasswordScreen extends StatefulWidget {
|
||||
const ChangePasswordScreen({super.key});
|
||||
|
||||
@override
|
||||
State<ChangePasswordScreen> createState() => _ChangePasswordScreenState();
|
||||
}
|
||||
|
||||
class _ChangePasswordScreenState extends State<ChangePasswordScreen> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
bool isClicked = false;
|
||||
final TextEditingController _oldPasswordController = TextEditingController();
|
||||
final TextEditingController _newPasswordController = TextEditingController();
|
||||
final TextEditingController _confirmPasswordController = TextEditingController();
|
||||
|
||||
bool showOldPassword = true;
|
||||
bool showPassword = true;
|
||||
bool showConfirmPassword = true;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_newPasswordController.dispose();
|
||||
_confirmPasswordController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
TextTheme textTheme = Theme.of(context).textTheme;
|
||||
final _lang = lang.S.of(context);
|
||||
return GlobalPopup(
|
||||
child: Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
appBar: AppBar(
|
||||
surfaceTintColor: kWhite,
|
||||
backgroundColor: kWhite,
|
||||
centerTitle: true,
|
||||
titleSpacing: 16,
|
||||
title: Text(
|
||||
lang.S.of(context).changePassword,
|
||||
//'Create New Password',
|
||||
style: textTheme.titleMedium?.copyWith(fontSize: 18),
|
||||
),
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16.0, 20.0, 16.0, 0.0),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
// Text(
|
||||
// lang.S.of(context).setUpNewPassword,
|
||||
// // 'Set Up New Password',
|
||||
// style: textTheme.titleMedium?.copyWith(fontSize: 24.0),
|
||||
// ),
|
||||
// const SizedBox(height: 8.0),
|
||||
// Text(
|
||||
// lang.S.of(context).resetPassword,
|
||||
// //'Reset your password to recovery and log in your account',
|
||||
// style: textTheme.bodyMedium?.copyWith(color: kGreyTextColor, fontSize: 16), textAlign: TextAlign.center,
|
||||
// ),
|
||||
// const SizedBox(height: 24.0),
|
||||
TextFormField(
|
||||
controller: _oldPasswordController,
|
||||
keyboardType: TextInputType.text,
|
||||
obscureText: showOldPassword,
|
||||
decoration: kInputDecoration.copyWith(
|
||||
// border: const OutlineInputBorder(),
|
||||
hintText: '********',
|
||||
labelText: _lang.oldPassword,
|
||||
suffixIcon: IconButton(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
showOldPassword = !showOldPassword;
|
||||
});
|
||||
},
|
||||
icon: Icon(
|
||||
showOldPassword ? FeatherIcons.eyeOff : FeatherIcons.eye,
|
||||
color: kGreyTextColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return _lang.oldPasswordCanNotBeEmpty;
|
||||
} else if (value.length < 6) {
|
||||
//return 'Please enter a bigger password';
|
||||
return lang.S.of(context).pleaseEnterABiggerPassword;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 20.0),
|
||||
TextFormField(
|
||||
controller: _newPasswordController,
|
||||
keyboardType: TextInputType.text,
|
||||
obscureText: showPassword,
|
||||
decoration: kInputDecoration.copyWith(
|
||||
// border: const OutlineInputBorder(),
|
||||
hintText: '********',
|
||||
//labelText: 'New Password',
|
||||
labelText: lang.S.of(context).newPassword,
|
||||
suffixIcon: IconButton(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
showPassword = !showPassword;
|
||||
});
|
||||
},
|
||||
icon: Icon(
|
||||
showPassword ? FeatherIcons.eyeOff : FeatherIcons.eye,
|
||||
color: kGreyTextColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
//return 'Password can\'t be empty';
|
||||
return lang.S.of(context).passwordCannotBeEmpty;
|
||||
} else if (value.length < 6) {
|
||||
//return 'Please enter a bigger password';
|
||||
return lang.S.of(context).pleaseEnterABiggerPassword;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 20.0),
|
||||
TextFormField(
|
||||
controller: _confirmPasswordController,
|
||||
keyboardType: TextInputType.text,
|
||||
obscureText: showConfirmPassword,
|
||||
decoration: kInputDecoration.copyWith(
|
||||
border: const OutlineInputBorder(),
|
||||
//labelText: 'Confirm Password',
|
||||
labelText: lang.S.of(context).confirmPassword,
|
||||
hintText: '********',
|
||||
suffixIcon: IconButton(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
showConfirmPassword = !showConfirmPassword;
|
||||
});
|
||||
},
|
||||
icon: Icon(
|
||||
showConfirmPassword ? FeatherIcons.eyeOff : FeatherIcons.eye,
|
||||
color: kGreyTextColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
//return 'Password can\'t be empty';
|
||||
return lang.S.of(context).passwordCannotBeEmpty;
|
||||
} else if (value != _newPasswordController.text) {
|
||||
//return 'Passwords do not match';
|
||||
return lang.S.of(context).passwordsDoNotMatch;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 24.0),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
if (isClicked) {
|
||||
return;
|
||||
}
|
||||
if (_formKey.currentState?.validate() ?? false) {
|
||||
isClicked = true;
|
||||
EasyLoading.show();
|
||||
ChangePassRepo repo = ChangePassRepo();
|
||||
if (await repo.changePass(
|
||||
oldPass: _oldPasswordController.text,
|
||||
newPass: _confirmPasswordController.text,
|
||||
context: context)) {
|
||||
Navigator.pop(context);
|
||||
} else {
|
||||
isClicked = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
child: Text(lang.S.of(context).save),
|
||||
//'Save',
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
import '../../../../Const/api_config.dart';
|
||||
import '../../../../Repository/constant_functions.dart';
|
||||
|
||||
class ChangePassRepo {
|
||||
Future<bool> changePass({required String oldPass, required String newPass, required BuildContext context}) async {
|
||||
final url = Uri.parse('${APIConfig.url}/change-password');
|
||||
|
||||
final body = {
|
||||
'current_password': oldPass,
|
||||
'password': newPass,
|
||||
};
|
||||
final headers = {
|
||||
'Accept': 'application/json',
|
||||
'Authorization': await getAuthToken(),
|
||||
};
|
||||
|
||||
try {
|
||||
final response = await http.post(url, headers: headers, body: body);
|
||||
|
||||
final responseData = jsonDecode(response.body);
|
||||
print('ChangePass: $responseData');
|
||||
EasyLoading.dismiss();
|
||||
if (response.statusCode == 200) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(responseData['message'])));
|
||||
|
||||
return true;
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(responseData['message'])));
|
||||
}
|
||||
} catch (error) {
|
||||
print(error);
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Error: $error')));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
89
lib/Screens/Authentication/check_email.dart
Normal file
89
lib/Screens/Authentication/check_email.dart
Normal file
@@ -0,0 +1,89 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mobile_pos/constant.dart';
|
||||
import 'package:mobile_pos/generated/l10n.dart' as lang;
|
||||
|
||||
class CheckEMail extends StatefulWidget {
|
||||
const CheckEMail({super.key});
|
||||
|
||||
@override
|
||||
// ignore: library_private_types_in_public_api
|
||||
_CheckEMailState createState() => _CheckEMailState();
|
||||
}
|
||||
|
||||
class _CheckEMailState extends State<CheckEMail> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
return SafeArea(
|
||||
child: Scaffold(
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 5,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 100.0,
|
||||
width: 100.0,
|
||||
child: Image(
|
||||
image: AssetImage('images/mailbox.png'),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
lang.S.of(context).gotEmail,
|
||||
style: theme.textTheme.titleLarge?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 25,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
width: MediaQuery.of(context).size.width,
|
||||
child: Text(
|
||||
lang.S.of(context).sendEmail,
|
||||
textAlign: TextAlign.center,
|
||||
style: theme.textTheme.titleLarge?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'example@johndoe.com',
|
||||
style: theme.textTheme.titleLarge,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Column(
|
||||
children: [
|
||||
ElevatedButton(
|
||||
onPressed: null,
|
||||
child: Text(lang.S.of(context).checkEmail),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pushNamed(context, '/otp');
|
||||
},
|
||||
child: Text(
|
||||
lang.S.of(context).checkEmail,
|
||||
style: theme.textTheme.bodyMedium?.copyWith(
|
||||
color: kMainColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
140
lib/Screens/Authentication/forgot password/forgot_password.dart
Normal file
140
lib/Screens/Authentication/forgot password/forgot_password.dart
Normal file
@@ -0,0 +1,140 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
import 'package:mobile_pos/Screens/Authentication/forgot%20password/repo/forgot_pass_repo.dart';
|
||||
import 'package:mobile_pos/generated/l10n.dart' as lang;
|
||||
|
||||
import '../../../GlobalComponents/glonal_popup.dart';
|
||||
import '../../../constant.dart';
|
||||
import '../Sign Up/verify_email.dart';
|
||||
import '../Wedgets/check_email_for_otp_popup.dart';
|
||||
|
||||
class ForgotPassword extends StatefulWidget {
|
||||
const ForgotPassword({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<ForgotPassword> createState() => _ForgotPasswordState();
|
||||
}
|
||||
|
||||
class _ForgotPasswordState extends State<ForgotPassword> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
bool isClicked = false;
|
||||
final TextEditingController _emailController = TextEditingController();
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_emailController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final _theme = Theme.of(context);
|
||||
TextTheme textTheme = Theme.of(context).textTheme;
|
||||
return GlobalPopup(
|
||||
child: Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
appBar: AppBar(
|
||||
titleSpacing: 16,
|
||||
backgroundColor: kWhite,
|
||||
surfaceTintColor: kWhite,
|
||||
centerTitle: true,
|
||||
title: Text(
|
||||
// 'Forgot Password',
|
||||
lang.S.of(context).forgotPassword,
|
||||
style: textTheme.titleMedium?.copyWith(fontSize: 18),
|
||||
),
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16.0, 20.0, 16.0, 0.0),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
// 'Forgot Password',
|
||||
lang.S.of(context).forgotPassword,
|
||||
style: textTheme.titleMedium?.copyWith(fontSize: 24.0),
|
||||
),
|
||||
const SizedBox(height: 8.0),
|
||||
Text(
|
||||
//'Reset password by using your email or phone number',
|
||||
lang.S.of(context).reset,
|
||||
style: textTheme.bodyMedium?.copyWith(color: kGreyTextColor, fontSize: 16),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 24.0),
|
||||
TextFormField(
|
||||
controller: _emailController,
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
decoration: kInputDecoration.copyWith(
|
||||
// labelText: 'Email',
|
||||
labelText: lang.S.of(context).lableEmail,
|
||||
// hintText: 'Enter email address',
|
||||
hintText: lang.S.of(context).hintEmail,
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
//return 'Email can\'t be empty';
|
||||
return lang.S.of(context).emailCannotBeEmpty;
|
||||
} else if (!value.contains('@')) {
|
||||
// return 'Please enter a valid email';
|
||||
return lang.S.of(context).pleaseEnterAValidEmail;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 24.0),
|
||||
ElevatedButton(
|
||||
style: OutlinedButton.styleFrom(
|
||||
maximumSize: const Size(double.infinity, 48),
|
||||
minimumSize: const Size(double.infinity, 48),
|
||||
disabledBackgroundColor: _theme.colorScheme.primary.withValues(alpha: 0.15),
|
||||
),
|
||||
onPressed: () async {
|
||||
if (isClicked) {
|
||||
return;
|
||||
}
|
||||
if (_formKey.currentState?.validate() ?? false) {
|
||||
isClicked = true;
|
||||
EasyLoading.show();
|
||||
ForgotPassRepo repo = ForgotPassRepo();
|
||||
if (await repo.forgotPass(email: _emailController.text, context: context)) {
|
||||
if (await checkEmailForCodePupUp(
|
||||
email: _emailController.text, context: context, textTheme: textTheme)) {
|
||||
Navigator.pushReplacement(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => VerifyEmail(
|
||||
email: _emailController.text,
|
||||
isFormForgotPass: true,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
isClicked = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
lang.S.of(context).continueE,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: _theme.textTheme.bodyMedium?.copyWith(
|
||||
color: _theme.colorScheme.primaryContainer,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
import '../../../../Const/api_config.dart';
|
||||
|
||||
class ForgotPassRepo {
|
||||
Future<bool> forgotPass({required String email, required BuildContext context}) async {
|
||||
final url = Uri.parse('${APIConfig.url}/send-reset-code');
|
||||
|
||||
final body = {
|
||||
'email': email,
|
||||
};
|
||||
final headers = {
|
||||
'Accept': 'application/json',
|
||||
};
|
||||
|
||||
try {
|
||||
final response = await http.post(url, headers: headers, body: body);
|
||||
|
||||
final responseData = jsonDecode(response.body);
|
||||
EasyLoading.dismiss();
|
||||
if (response.statusCode == 200) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(responseData['message'])));
|
||||
|
||||
return true;
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(responseData['message'])));
|
||||
}
|
||||
} catch (error) {
|
||||
print(error);
|
||||
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Network error: Please try again')));
|
||||
} finally {}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Future<bool> verifyOTPForgotPass({required String email, required String otp, required BuildContext context}) async {
|
||||
final url = Uri.parse('${APIConfig.url}/verify-reset-code');
|
||||
|
||||
final body = {
|
||||
'email': email,
|
||||
'code': otp,
|
||||
};
|
||||
final headers = {
|
||||
'Accept': 'application/json',
|
||||
};
|
||||
|
||||
try {
|
||||
final response = await http.post(url, headers: headers, body: body);
|
||||
|
||||
final responseData = jsonDecode(response.body);
|
||||
print(response.body);
|
||||
EasyLoading.dismiss();
|
||||
if (response.statusCode == 200) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(responseData['message'])));
|
||||
|
||||
return true;
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(responseData['error'])));
|
||||
}
|
||||
} catch (error) {
|
||||
print(error);
|
||||
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Network error: Please try again')));
|
||||
} finally {}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Future<bool> resetPass({required String email, required String password, required BuildContext context}) async {
|
||||
final url = Uri.parse('${APIConfig.url}/password-reset');
|
||||
|
||||
final body = {
|
||||
'email': email,
|
||||
"password": password,
|
||||
};
|
||||
final headers = {
|
||||
'Accept': 'application/json',
|
||||
};
|
||||
|
||||
try {
|
||||
final response = await http.post(url, headers: headers, body: body);
|
||||
|
||||
final responseData = jsonDecode(response.body);
|
||||
EasyLoading.dismiss();
|
||||
if (response.statusCode == 200) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(responseData['message'])));
|
||||
|
||||
return true;
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(responseData['message'])));
|
||||
}
|
||||
} catch (error) {
|
||||
print(error);
|
||||
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Network error: Please try again')));
|
||||
} finally {}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
165
lib/Screens/Authentication/forgot password/set_new_password.dart
Normal file
165
lib/Screens/Authentication/forgot password/set_new_password.dart
Normal file
@@ -0,0 +1,165 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
import 'package:mobile_pos/Screens/Authentication/forgot%20password/repo/forgot_pass_repo.dart';
|
||||
import 'package:mobile_pos/generated/l10n.dart' as lang;
|
||||
|
||||
import '../../../GlobalComponents/glonal_popup.dart';
|
||||
import '../../../constant.dart';
|
||||
|
||||
class SetNewPassword extends StatefulWidget {
|
||||
const SetNewPassword({super.key, required this.email});
|
||||
|
||||
final String email;
|
||||
|
||||
@override
|
||||
State<SetNewPassword> createState() => _SetNewPasswordState();
|
||||
}
|
||||
|
||||
class _SetNewPasswordState extends State<SetNewPassword> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
bool isClicked = false;
|
||||
final TextEditingController _passwordController = TextEditingController();
|
||||
final TextEditingController _confirmPasswordController = TextEditingController();
|
||||
|
||||
bool showPassword = true;
|
||||
bool showConfirmPassword = true;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_passwordController.dispose();
|
||||
_confirmPasswordController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
TextTheme textTheme = Theme.of(context).textTheme;
|
||||
return GlobalPopup(
|
||||
child: Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
appBar: AppBar(
|
||||
surfaceTintColor: kWhite,
|
||||
backgroundColor: kWhite,
|
||||
centerTitle: true,
|
||||
titleSpacing: 16,
|
||||
title: Text(
|
||||
lang.S.of(context).createNewPassword,
|
||||
//'Create New Password',
|
||||
style: textTheme.titleMedium?.copyWith(fontSize: 18),
|
||||
),
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16.0, 20.0, 16.0, 0.0),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
lang.S.of(context).setUpNewPassword,
|
||||
// 'Set Up New Password',
|
||||
style: textTheme.titleMedium?.copyWith(fontSize: 24.0),
|
||||
),
|
||||
const SizedBox(height: 8.0),
|
||||
Text(
|
||||
lang.S.of(context).resetPassword,
|
||||
//'Reset your password to recovery and log in your account',
|
||||
style: textTheme.bodyMedium?.copyWith(color: kGreyTextColor, fontSize: 16), textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 24.0),
|
||||
TextFormField(
|
||||
controller: _passwordController,
|
||||
keyboardType: TextInputType.text,
|
||||
obscureText: showPassword,
|
||||
decoration: kInputDecoration.copyWith(
|
||||
// border: const OutlineInputBorder(),
|
||||
hintText: '********',
|
||||
//labelText: 'New Password',
|
||||
labelText: lang.S.of(context).newPassword,
|
||||
suffixIcon: IconButton(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
showPassword = !showPassword;
|
||||
});
|
||||
},
|
||||
icon: Icon(
|
||||
showPassword ? FeatherIcons.eyeOff : FeatherIcons.eye,
|
||||
color: kGreyTextColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
//return 'Password can\'t be empty';
|
||||
return lang.S.of(context).passwordCannotBeEmpty;
|
||||
} else if (value.length < 6) {
|
||||
//return 'Please enter a bigger password';
|
||||
return lang.S.of(context).pleaseEnterABiggerPassword;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 20.0),
|
||||
TextFormField(
|
||||
controller: _confirmPasswordController,
|
||||
keyboardType: TextInputType.text,
|
||||
obscureText: showConfirmPassword,
|
||||
decoration: kInputDecoration.copyWith(
|
||||
border: const OutlineInputBorder(),
|
||||
//labelText: 'Confirm Password',
|
||||
labelText: lang.S.of(context).confirmPassword,
|
||||
hintText: '********',
|
||||
suffixIcon: IconButton(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
showConfirmPassword = !showConfirmPassword;
|
||||
});
|
||||
},
|
||||
icon: Icon(
|
||||
showConfirmPassword ? FeatherIcons.eyeOff : FeatherIcons.eye,
|
||||
color: kGreyTextColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
//return 'Password can\'t be empty';
|
||||
return lang.S.of(context).passwordCannotBeEmpty;
|
||||
} else if (value != _passwordController.text) {
|
||||
//return 'Passwords do not match';
|
||||
return lang.S.of(context).passwordsDoNotMatch;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 24.0),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
if (isClicked) {
|
||||
return;
|
||||
}
|
||||
if (_formKey.currentState?.validate() ?? false) {
|
||||
isClicked = true;
|
||||
EasyLoading.show();
|
||||
ForgotPassRepo repo = ForgotPassRepo();
|
||||
if (await repo.resetPass(email: widget.email, password: _confirmPasswordController.text, context: context)) {
|
||||
Navigator.pop(context);
|
||||
} else {
|
||||
isClicked = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
child: Text(lang.S.of(context).save),
|
||||
//'Save',
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
119
lib/Screens/Authentication/forgot_password.dart
Normal file
119
lib/Screens/Authentication/forgot_password.dart
Normal file
@@ -0,0 +1,119 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mobile_pos/generated/l10n.dart' as lang;
|
||||
|
||||
import '../../constant.dart';
|
||||
|
||||
class ForgotPassword extends StatefulWidget {
|
||||
const ForgotPassword({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
// ignore: library_private_types_in_public_api
|
||||
_ForgotPasswordState createState() => _ForgotPasswordState();
|
||||
}
|
||||
|
||||
class _ForgotPasswordState extends State<ForgotPassword> {
|
||||
bool showProgress = false;
|
||||
late String email;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
return SafeArea(
|
||||
child: Scaffold(
|
||||
body: Center(
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
lang.S.of(context).forgotPassword,
|
||||
style: theme.textTheme.headlineSmall?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Text(
|
||||
lang.S.of(context).enterEmail,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
textAlign: TextAlign.center,
|
||||
style: theme.textTheme.titleLarge?.copyWith(
|
||||
color: kGreyTextColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: TextFormField(
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
email = value;
|
||||
});
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
labelText: lang.S.of(context).email,
|
||||
border: const OutlineInputBorder(),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||
hintText: 'example@example.com'),
|
||||
),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () {},
|
||||
// onPressed: () async {
|
||||
// setState(() {
|
||||
// showProgress = true;
|
||||
// });
|
||||
// try {
|
||||
// await FirebaseAuth.instance.sendPasswordResetEmail(
|
||||
// email: email,
|
||||
// );
|
||||
// // ScaffoldMessenger.of(context).showSnackBar(
|
||||
// // const SnackBar(
|
||||
// // content: Text('Check your Inbox'),
|
||||
// // duration: Duration(seconds: 3),
|
||||
// // ),
|
||||
// // );
|
||||
// if (!mounted) return;
|
||||
// const LoginForm(
|
||||
// isEmailLogin: true,
|
||||
// ).launch(context);
|
||||
// } on FirebaseAuthException catch (e) {
|
||||
// if (e.code == 'user-not-found') {
|
||||
// ScaffoldMessenger.of(context).showSnackBar(
|
||||
// const SnackBar(
|
||||
// content: Text('No user found for that email.'),
|
||||
// duration: Duration(seconds: 3),
|
||||
// ),
|
||||
// );
|
||||
// } else if (e.code == 'wrong-password') {
|
||||
// ScaffoldMessenger.of(context).showSnackBar(
|
||||
// const SnackBar(
|
||||
// content: Text('Wrong password provided for that user.'),
|
||||
// duration: Duration(seconds: 3),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// } catch (e) {
|
||||
// ScaffoldMessenger.of(context).showSnackBar(
|
||||
// SnackBar(
|
||||
// content: Text(e.toString()),
|
||||
// duration: const Duration(seconds: 3),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// setState(
|
||||
// () {
|
||||
// showProgress = false;
|
||||
// },
|
||||
// );
|
||||
// },
|
||||
child: Text(lang.S.of(context).sendLink)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
173
lib/Screens/Authentication/login_form.dart
Normal file
173
lib/Screens/Authentication/login_form.dart
Normal file
@@ -0,0 +1,173 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:mobile_pos/Screens/Authentication/register_screen.dart';
|
||||
import 'package:mobile_pos/generated/l10n.dart' as lang;
|
||||
|
||||
import '../../constant.dart';
|
||||
import 'forgot_password.dart';
|
||||
|
||||
class LoginForm extends StatefulWidget {
|
||||
const LoginForm({Key? key, required this.isEmailLogin}) : super(key: key);
|
||||
|
||||
final bool isEmailLogin;
|
||||
|
||||
@override
|
||||
// ignore: library_private_types_in_public_api
|
||||
_LoginFormState createState() => _LoginFormState();
|
||||
}
|
||||
|
||||
class _LoginFormState extends State<LoginForm> {
|
||||
bool showPassword = true;
|
||||
late String email, password;
|
||||
GlobalKey<FormState> globalKey = GlobalKey<FormState>();
|
||||
|
||||
bool validateAndSave() {
|
||||
final form = globalKey.currentState;
|
||||
if (form!.validate()) {
|
||||
form.save();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
return SafeArea(
|
||||
child: Scaffold(
|
||||
body: Consumer(builder: (context, ref, child) {
|
||||
return Center(
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Image.asset('images/logoandname.png'),
|
||||
const SizedBox(
|
||||
height: 30.0,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: Form(
|
||||
key: globalKey,
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(height: 20),
|
||||
TextFormField(
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(),
|
||||
labelText: lang.S.of(context).emailText,
|
||||
hintText: lang.S.of(context).enterYourEmailAddress,
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
//return 'Email can\'n be empty';
|
||||
return lang.S.of(context).emailCannotBeEmpty;
|
||||
} else if (!value.contains('@')) {
|
||||
//return 'Please enter a valid email';
|
||||
return lang.S.of(context).pleaseEnterAValidEmail;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
onSaved: (value) {
|
||||
// loginProvider.email = value!;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
TextFormField(
|
||||
keyboardType: TextInputType.text,
|
||||
obscureText: showPassword,
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(),
|
||||
labelText: lang.S.of(context).password,
|
||||
hintText: lang.S.of(context).pleaseEnterAPassword,
|
||||
suffixIcon: IconButton(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
showPassword = !showPassword;
|
||||
});
|
||||
},
|
||||
icon: Icon(showPassword ? Icons.visibility_off : Icons.visibility),
|
||||
),
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
//return 'Password can\'t be empty';
|
||||
return lang.S.of(context).passwordCannotBeEmpty;
|
||||
} else if (value.length < 4) {
|
||||
//return 'Please enter a bigger password';
|
||||
return lang.S.of(context).pleaseEnterABiggerPassword;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
onSaved: (value) {
|
||||
// loginProvider.password = value!;
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Visibility(
|
||||
visible: widget.isEmailLogin,
|
||||
child: Row(
|
||||
children: [
|
||||
const Spacer(),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => const ForgotPassword()));
|
||||
// const ForgotPassword().launch(context);
|
||||
},
|
||||
child: Text(
|
||||
lang.S.of(context).forgotPassword,
|
||||
style: theme.textTheme.bodyLarge?.copyWith(
|
||||
color: kGreyTextColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
ElevatedButton(
|
||||
child: Text(lang.S.of(context).logIn),
|
||||
onPressed: () {
|
||||
if (validateAndSave()) {
|
||||
// loginProvider.signIn(context);
|
||||
}
|
||||
},
|
||||
),
|
||||
Visibility(
|
||||
visible: widget.isEmailLogin,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
lang.S.of(context).noAcc,
|
||||
style: theme.textTheme.bodyLarge?.copyWith(color: kGreyTextColor),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
// Navigator.pushNamed(context, '/signup');
|
||||
// const RegisterScreen().launch(context);
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => const RegisterScreen()));
|
||||
},
|
||||
child: Text(
|
||||
lang.S.of(context).register,
|
||||
style: theme.textTheme.titleMedium?.copyWith(
|
||||
color: kMainColor,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
406
lib/Screens/Authentication/profile_setup_screen.dart
Normal file
406
lib/Screens/Authentication/profile_setup_screen.dart
Normal file
@@ -0,0 +1,406 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:mobile_pos/generated/l10n.dart' as lang;
|
||||
import 'package:nb_utils/nb_utils.dart';
|
||||
|
||||
import '../../GlobalComponents/glonal_popup.dart';
|
||||
import '../../Provider/shop_category_provider.dart';
|
||||
import '../../Repository/API/business_setup_repo.dart';
|
||||
import '../../constant.dart';
|
||||
import '../../model/business_category_model.dart';
|
||||
import '../../model/lalnguage_model.dart';
|
||||
|
||||
class ProfileSetup extends StatefulWidget {
|
||||
const ProfileSetup({super.key});
|
||||
|
||||
@override
|
||||
State<ProfileSetup> createState() => _ProfileSetupState();
|
||||
}
|
||||
|
||||
class _ProfileSetupState extends State<ProfileSetup> {
|
||||
@override
|
||||
void initState() {
|
||||
// TODO: implement initState
|
||||
super.initState();
|
||||
}
|
||||
|
||||
// Language? selectedLanguage;
|
||||
BusinessCategory? selectedBusinessCategory;
|
||||
List<Language> language = [];
|
||||
|
||||
final ImagePicker _picker = ImagePicker();
|
||||
XFile? pickedImage;
|
||||
TextEditingController addressController = TextEditingController();
|
||||
TextEditingController openingBalanceController = TextEditingController();
|
||||
TextEditingController phoneController = TextEditingController();
|
||||
TextEditingController nameController = TextEditingController();
|
||||
TextEditingController vatGstTitleController = TextEditingController();
|
||||
TextEditingController vatGstNumberController = TextEditingController();
|
||||
|
||||
DropdownButton<BusinessCategory> getCategory({required List<BusinessCategory> list}) {
|
||||
List<DropdownMenuItem<BusinessCategory>> dropDownItems = [];
|
||||
|
||||
for (BusinessCategory category in list) {
|
||||
var item = DropdownMenuItem(
|
||||
value: category,
|
||||
child: Text(category.name),
|
||||
);
|
||||
dropDownItems.add(item);
|
||||
}
|
||||
return DropdownButton(
|
||||
isExpanded: true,
|
||||
hint: Text(lang.S.of(context).selectBusinessCategory
|
||||
//'Select Business Category'
|
||||
),
|
||||
items: dropDownItems,
|
||||
value: selectedBusinessCategory,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
selectedBusinessCategory = value!;
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
final GlobalKey<FormState> _formKey = GlobalKey();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final _lang = lang.S.of(context);
|
||||
return WillPopScope(
|
||||
onWillPop: () async => false,
|
||||
child: Consumer(builder: (context, ref, __) {
|
||||
final businessCategoryList = ref.watch(businessCategoryProvider);
|
||||
|
||||
return businessCategoryList.when(data: (categoryList) {
|
||||
return GlobalPopup(
|
||||
child: Scaffold(
|
||||
backgroundColor: kWhite,
|
||||
appBar: AppBar(
|
||||
iconTheme: const IconThemeData(color: Colors.black),
|
||||
title: Text(
|
||||
lang.S.of(context).setUpProfile,
|
||||
),
|
||||
centerTitle: true,
|
||||
backgroundColor: Colors.white,
|
||||
elevation: 0.0,
|
||||
),
|
||||
bottomNavigationBar: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: ElevatedButton.icon(
|
||||
iconAlignment: IconAlignment.end,
|
||||
onPressed: () async {
|
||||
if (selectedBusinessCategory != null) {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
try {
|
||||
BusinessSetupRepo businessSetupRepo = BusinessSetupRepo();
|
||||
await businessSetupRepo.businessSetup(
|
||||
context: context,
|
||||
name: nameController.text,
|
||||
phone: phoneController.text,
|
||||
address: addressController.text.isEmptyOrNull ? null : addressController.text,
|
||||
categoryId: selectedBusinessCategory!.id.toString(),
|
||||
image: pickedImage == null ? null : File(pickedImage!.path),
|
||||
vatGstNumber: vatGstNumberController.text,
|
||||
vatGstTitle: vatGstTitleController.text,
|
||||
openingBalance: openingBalanceController.text,
|
||||
);
|
||||
} catch (e) {
|
||||
EasyLoading.dismiss();
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(e.toString())));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Select a Business Category')));
|
||||
}
|
||||
},
|
||||
icon: const Icon(
|
||||
Icons.arrow_forward,
|
||||
color: Colors.white,
|
||||
),
|
||||
label: Text(lang.S.of(context).continueButton),
|
||||
),
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
child: Center(
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
children: [
|
||||
///________Image______________________________
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return Dialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
),
|
||||
// ignore: sized_box_for_whitespace
|
||||
child: Container(
|
||||
height: 200.0,
|
||||
width: MediaQuery.of(context).size.width - 80,
|
||||
child: Center(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () async {
|
||||
pickedImage = await _picker.pickImage(source: ImageSource.gallery);
|
||||
setState(() {});
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.photo_library_rounded,
|
||||
size: 60.0,
|
||||
color: kMainColor,
|
||||
),
|
||||
Text(
|
||||
lang.S.of(context).gallery,
|
||||
style: theme.textTheme.titleMedium?.copyWith(
|
||||
color: kMainColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 40.0),
|
||||
GestureDetector(
|
||||
onTap: () async {
|
||||
pickedImage = await _picker.pickImage(source: ImageSource.camera);
|
||||
setState(() {});
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.camera,
|
||||
size: 60.0,
|
||||
color: kGreyTextColor,
|
||||
),
|
||||
Text(
|
||||
lang.S.of(context).camera,
|
||||
style: theme.textTheme.titleMedium?.copyWith(color: kGreyTextColor),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
},
|
||||
child: Stack(
|
||||
children: [
|
||||
Container(
|
||||
height: 120,
|
||||
width: 120,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
// border: Border.all(color: Colors.black54, width: 1),
|
||||
// borderRadius: const BorderRadius.all(Radius.circular(120)),
|
||||
image: pickedImage == null
|
||||
? const DecorationImage(
|
||||
image: AssetImage('images/noImage.png'),
|
||||
fit: BoxFit.cover,
|
||||
)
|
||||
: DecorationImage(
|
||||
image: FileImage(File(pickedImage!.path)),
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
child: Container(
|
||||
height: 35,
|
||||
width: 35,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.white, width: 2),
|
||||
// borderRadius: const BorderRadius.all(Radius.circular(120)),
|
||||
shape: BoxShape.circle,
|
||||
color: kMainColor,
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.camera_alt_outlined,
|
||||
size: 20,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20.0),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: SizedBox(
|
||||
height: 60.0,
|
||||
child: FormField(
|
||||
builder: (FormFieldState<dynamic> field) {
|
||||
return InputDecorator(
|
||||
decoration: kInputDecoration.copyWith(
|
||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||
labelText: lang.S.of(context).businessCat,
|
||||
border: OutlineInputBorder(borderRadius: BorderRadius.circular(5.0))),
|
||||
child: DropdownButtonHideUnderline(child: getCategory(list: categoryList)),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
///_________Name________________________
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: AppTextField(
|
||||
// Optional
|
||||
textFieldType: TextFieldType.NAME,
|
||||
controller: nameController,
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
// return 'Please enter a valid business name';
|
||||
return lang.S.of(context).pleaseEnterAValidBusinessName;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
decoration: kInputDecoration.copyWith(
|
||||
labelText: lang.S.of(context).businessName,
|
||||
border: const OutlineInputBorder(),
|
||||
//hintText: 'Enter Business/Store Name'
|
||||
hintText: lang.S.of(context).enterBusiness,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
///__________Phone_________________________
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: SizedBox(
|
||||
height: 60.0,
|
||||
child: AppTextField(
|
||||
controller: phoneController,
|
||||
validator: (value) {
|
||||
return null;
|
||||
},
|
||||
textFieldType: TextFieldType.PHONE,
|
||||
decoration: kInputDecoration.copyWith(
|
||||
labelText: lang.S.of(context).phone,
|
||||
hintText: lang.S.of(context).enterYourPhoneNumber,
|
||||
border: const OutlineInputBorder(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
///_________Address___________________________
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: AppTextField(
|
||||
// ignore: deprecated_member_use
|
||||
textFieldType: TextFieldType.ADDRESS,
|
||||
controller: addressController,
|
||||
decoration: kInputDecoration.copyWith(
|
||||
focusedBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(color: kGreyTextColor),
|
||||
),
|
||||
labelText: lang.S.of(context).companyAddress,
|
||||
hintText: lang.S.of(context).enterFullAddress,
|
||||
border: const OutlineInputBorder(),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
///________Opening_balance_______________________
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: AppTextField(
|
||||
validator: (value) {
|
||||
return null;
|
||||
},
|
||||
controller: openingBalanceController, // Optional
|
||||
textFieldType: TextFieldType.PHONE,
|
||||
decoration: kInputDecoration.copyWith(
|
||||
//hintText: 'Enter opening balance',
|
||||
hintText: lang.S.of(context).enterOpeningBalance,
|
||||
labelText: lang.S.of(context).openingBalance,
|
||||
border: const OutlineInputBorder(),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// ///_______Gst_number____________________________
|
||||
// Row(
|
||||
// children: [
|
||||
// ///_______title__________________________________
|
||||
// Expanded(
|
||||
// child: Padding(
|
||||
// padding: const EdgeInsets.only(top: 10, left: 10, bottom: 10),
|
||||
// child: AppTextField(
|
||||
// validator: (value) {
|
||||
// return null;
|
||||
// },
|
||||
// controller: vatGstTitleController,
|
||||
// textFieldType: TextFieldType.NAME,
|
||||
// decoration: kInputDecoration.copyWith(
|
||||
// labelText: _lang.vatGstTitle,
|
||||
// hintText: _lang.enterVatGstTitle,
|
||||
// border: const OutlineInputBorder(),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
//
|
||||
// ///______Vat_and_Gst_Number__________________________________
|
||||
// Expanded(
|
||||
// child: Padding(
|
||||
// padding: const EdgeInsets.all(10.0),
|
||||
// child: AppTextField(
|
||||
// validator: (value) {
|
||||
// return null;
|
||||
// },
|
||||
// controller: vatGstNumberController, // Optional
|
||||
// textFieldType: TextFieldType.NAME,
|
||||
// decoration: kInputDecoration.copyWith(
|
||||
// hintText: _lang.enterVatGstNumber,
|
||||
// labelText: _lang.vatGstNumber,
|
||||
// border: const OutlineInputBorder(),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// )
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}, error: (e, stack) {
|
||||
return Center(
|
||||
child: Text(e.toString()),
|
||||
);
|
||||
}, loading: () {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
});
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
221
lib/Screens/Authentication/register_screen.dart
Normal file
221
lib/Screens/Authentication/register_screen.dart
Normal file
@@ -0,0 +1,221 @@
|
||||
// ignore_for_file: curly_braces_in_flow_control_structures
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:mobile_pos/Screens/Authentication/Phone%20Auth/phone_auth_screen.dart';
|
||||
import 'package:mobile_pos/Screens/Authentication/profile_setup_screen.dart';
|
||||
import 'package:mobile_pos/generated/l10n.dart' as lang;
|
||||
import 'package:nb_utils/nb_utils.dart';
|
||||
|
||||
import '../../Repository/API/register_repo.dart';
|
||||
import '../../constant.dart';
|
||||
import 'login_form.dart';
|
||||
|
||||
class RegisterScreen extends StatefulWidget {
|
||||
const RegisterScreen({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<RegisterScreen> createState() => _RegisterScreenState();
|
||||
}
|
||||
|
||||
class _RegisterScreenState extends State<RegisterScreen> {
|
||||
bool showPass1 = true;
|
||||
bool showPass2 = true;
|
||||
GlobalKey<FormState> globalKey = GlobalKey<FormState>();
|
||||
bool passwordShow = false;
|
||||
String? givenPassword;
|
||||
String? givenPassword2;
|
||||
|
||||
late String email;
|
||||
late String password;
|
||||
late String passwordConfirmation;
|
||||
|
||||
bool validateAndSave() {
|
||||
final form = globalKey.currentState;
|
||||
if (form!.validate() && givenPassword == givenPassword2) {
|
||||
form.save();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
// TODO: implement initState
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
return SafeArea(
|
||||
child: Scaffold(
|
||||
body: Consumer(builder: (context, ref, child) {
|
||||
return Center(
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Image.asset('images/logoandname.png'),
|
||||
const SizedBox(
|
||||
height: 30.0,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: Form(
|
||||
key: globalKey,
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(height: 20),
|
||||
TextFormField(
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(),
|
||||
labelText: lang.S.of(context).emailText,
|
||||
hintText: lang.S.of(context).enterYourEmailAddress,
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
// return 'Email can\'n be empty';
|
||||
return lang.S.of(context).emailCannotBeEmpty;
|
||||
} else if (!value.contains('@')) {
|
||||
//return 'Please enter a valid email';
|
||||
return lang.S.of(context).pleaseEnterAValidEmail;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
onSaved: (value) {
|
||||
email = value!;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
TextFormField(
|
||||
keyboardType: TextInputType.text,
|
||||
obscureText: showPass1,
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(),
|
||||
labelText: lang.S.of(context).password,
|
||||
hintText: lang.S.of(context).pleaseEnterAPassword,
|
||||
suffixIcon: IconButton(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
showPass1 = !showPass1;
|
||||
});
|
||||
},
|
||||
icon: Icon(showPass1 ? Icons.visibility_off : Icons.visibility),
|
||||
),
|
||||
),
|
||||
onChanged: (value) {
|
||||
givenPassword = value;
|
||||
},
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
//return 'Password can\'t be empty';
|
||||
return lang.S.of(context).passwordCannotBeEmpty;
|
||||
} else if (value.length < 4) {
|
||||
//return 'Please enter a bigger password';
|
||||
return lang.S.of(context).pleaseEnterABiggerPassword;
|
||||
} else if (value.length < 4) {
|
||||
//return 'Please enter a bigger password';
|
||||
return lang.S.of(context).pleaseEnterABiggerPassword;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
onSaved: (value) {
|
||||
password = value!;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
TextFormField(
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
obscureText: showPass2,
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(),
|
||||
labelText: lang.S.of(context).confirmPass,
|
||||
hintText: lang.S.of(context).pleaseEnterAConfirmPassword,
|
||||
suffixIcon: IconButton(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
showPass2 = !showPass2;
|
||||
});
|
||||
},
|
||||
icon: Icon(showPass2 ? Icons.visibility_off : Icons.visibility),
|
||||
),
|
||||
),
|
||||
onChanged: (value) {
|
||||
givenPassword2 = value;
|
||||
},
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
//return 'Password can\'t be empty';
|
||||
return lang.S.of(context).passwordCannotBeEmpty;
|
||||
} else if (value.length < 4) {
|
||||
// return 'Please enter a bigger password';
|
||||
return lang.S.of(context).pleaseEnterABiggerPassword;
|
||||
} else if (givenPassword != givenPassword2) {
|
||||
//return 'Password Not mach';
|
||||
return lang.S.of(context).passwordsDoNotMatch;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
if (validateAndSave()) {
|
||||
RegisterRepo reg = RegisterRepo();
|
||||
if (await reg.registerRepo(email: email, context: context, password: password, confirmPassword: password))
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const ProfileSetup(),
|
||||
));
|
||||
// auth.signUp(context);
|
||||
}
|
||||
},
|
||||
child: Text(lang.S.of(context).register),
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
lang.S.of(context).haveAcc,
|
||||
style: theme.textTheme.bodyLarge?.copyWith(
|
||||
color: kMainColor,
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
const LoginForm(
|
||||
isEmailLogin: true,
|
||||
).launch(context);
|
||||
// Navigator.pushNamed(context, '/loginForm');
|
||||
},
|
||||
child: Text(
|
||||
lang.S.of(context).logIn,
|
||||
style: theme.textTheme.titleSmall?.copyWith(
|
||||
color: kMainColor,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
const PhoneAuth().launch(context);
|
||||
},
|
||||
child: Text(lang.S.of(context).loginWithPhone),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
79
lib/Screens/Authentication/success_screen.dart
Normal file
79
lib/Screens/Authentication/success_screen.dart
Normal file
@@ -0,0 +1,79 @@
|
||||
// ignore_for_file: unused_result
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:mobile_pos/Provider/profile_provider.dart';
|
||||
import 'package:mobile_pos/generated/l10n.dart' as lang;
|
||||
import 'package:nb_utils/nb_utils.dart';
|
||||
|
||||
import '../../GlobalComponents/glonal_popup.dart';
|
||||
import '../../constant.dart';
|
||||
import '../Home/home.dart';
|
||||
|
||||
class SuccessScreen extends StatelessWidget {
|
||||
const SuccessScreen({Key? key, required this.email}) : super(key: key);
|
||||
|
||||
final String? email;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
return Consumer(builder: (context, ref, _) {
|
||||
final userRoleData = ref.watch(businessInfoProvider);
|
||||
ref.watch(getExpireDateProvider(ref));
|
||||
return userRoleData.when(data: (data) {
|
||||
return GlobalPopup(
|
||||
child: Scaffold(
|
||||
backgroundColor: kWhite,
|
||||
resizeToAvoidBottomInset: true,
|
||||
body: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Image(image: AssetImage('images/success.png')),
|
||||
const SizedBox(height: 40.0),
|
||||
Text(
|
||||
lang.S.of(context).congratulation,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Text(
|
||||
lang.S.of(context).loremIpsumDolorSitAmetConsecteturElitInterdumCons,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
textAlign: TextAlign.center,
|
||||
style: theme.textTheme.bodyLarge?.copyWith(
|
||||
fontSize: 16.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
const Home().launch(context);
|
||||
// Navigator.pushNamed(context, '/home');
|
||||
},
|
||||
child: Text(lang.S.of(context).continueButton),
|
||||
),
|
||||
)
|
||||
],
|
||||
// ),
|
||||
// bottomNavigationBar: ButtonGlobalWithoutIcon(
|
||||
// buttontext: lang.S.of(context).continueButton,
|
||||
// buttonDecoration: kButtonDecoration.copyWith(color: kMainColor),
|
||||
// onPressed: () {
|
||||
// const Home().launch(context);
|
||||
// // Navigator.pushNamed(context, '/home');
|
||||
// },
|
||||
// buttonTextColor: Colors.white,
|
||||
// ),
|
||||
)),
|
||||
);
|
||||
}, error: (e, stack) {
|
||||
return Text(e.toString());
|
||||
}, loading: () {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user