first commit

This commit is contained in:
2026-02-07 15:57:09 +07:00
commit 157096f164
1153 changed files with 415766 additions and 0 deletions

View File

@@ -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 {}
}
}

View 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),
),
],
)
],
)
],
),
),
),
),
);
}
}

View 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,
// ),
// ),
// ),
// ],
// )
],
),
),
),
);
}
}