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,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// )
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user