From a5f063f42d990493690c42a08e752d2a7c858d97 Mon Sep 17 00:00:00 2001 From: shoaib-mohmad <shoaib.mohmad@tarento.com> Date: Wed, 16 Mar 2022 18:08:51 +0530 Subject: [PATCH] Fixed issues in the login & create pin flow. Created customized widget for the pin, existing one was having issues. --- lib/l10n/app_en.arb | 2 + lib/pages/create_pin_page.dart | 106 +++++++++--------- lib/pages/login_email_page.dart | 108 ++++++++++--------- lib/pages/login_otp_page.dart | 83 +++++++++----- lib/repositories/application_repository.dart | 1 + lib/widgets/otp_input_field.dart | 47 ++++++++ 6 files changed, 219 insertions(+), 128 deletions(-) create mode 100644 lib/widgets/otp_input_field.dart diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index ae93053..4bc5dad 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -6,6 +6,7 @@ "orEnterPin": "Or, Enter your 4 Digit Login Pin", "enterPin": "Enter 4 Digit Login Pin", "confirmPin": "Confirm 4 Digit Login Pin", + "pinAlreadyExists": "Pin already exists, please choose different one", "createPin": "Create PIN", "inValidPin": "Invalid PIN", "getOtp": "GET OTP", @@ -13,6 +14,7 @@ "otpFieldDescription": "Enter the 6 digit OTP sent to your email address.", "pleaseEnterEmail": "Please enter email id", "pleaseEnterPin": "Please enter pin to proceed", + "pleaseEnterOtp": "Please enter otp to proceed", "pleaseMatchPin": "Please match pin to proceed", "pleaseEnterValidPin": "Please enter valid pin to proceed", "pinGeneratedMessage": "Pin generated successfully, please login", diff --git a/lib/pages/create_pin_page.dart b/lib/pages/create_pin_page.dart index f9a270e..e3eda0e 100644 --- a/lib/pages/create_pin_page.dart +++ b/lib/pages/create_pin_page.dart @@ -1,16 +1,16 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:otp_text_field/style.dart'; import 'package:provider/provider.dart'; import 'package:smf_mobile/constants/app_urls.dart'; import 'package:smf_mobile/constants/color_constants.dart'; import 'package:google_fonts/google_fonts.dart'; +import 'package:smf_mobile/database/offline_model.dart'; import 'package:smf_mobile/pages/login_email_page.dart'; import 'package:smf_mobile/pages/login_otp_page.dart'; import 'package:smf_mobile/repositories/login_repository.dart'; import 'package:smf_mobile/util/helper.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import 'package:otp_text_field/otp_field.dart'; +import 'package:smf_mobile/widgets/otp_input_field.dart'; class CreatePinPage extends StatefulWidget { static const route = AppUrl.loginEmailPage; @@ -25,12 +25,37 @@ class _CreatePinPageState extends State<CreatePinPage> { final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>(); String _errorMessage = ''; late Locale locale; - String _pin1 = ''; - String _pin2 = ''; + + final TextEditingController _pinOneFieldOne = TextEditingController(); + final TextEditingController _pinOneFieldTwo = TextEditingController(); + final TextEditingController _pinOneFieldThree = TextEditingController(); + final TextEditingController _pinOneFieldFour = TextEditingController(); + final TextEditingController _pinTwoFieldOne = TextEditingController(); + final TextEditingController _pinTwoFieldTwo = TextEditingController(); + final TextEditingController _pinTwoFieldThree = TextEditingController(); + final TextEditingController _pinTwoFieldFour = TextEditingController(); + String _pin1 = '', _pin2 = ''; @override void initState() { super.initState(); + _pinOneFieldOne.addListener(_setPin); + _pinOneFieldTwo.addListener(_setPin); + _pinOneFieldThree.addListener(_setPin); + _pinOneFieldFour.addListener(_setPin); + _pinTwoFieldOne.addListener(_setPin); + _pinTwoFieldTwo.addListener(_setPin); + _pinTwoFieldThree.addListener(_setPin); + _pinTwoFieldFour.addListener(_setPin); + } + + void _setPin() { + setState(() { + _pin1 = + '${_pinOneFieldOne.text}${_pinOneFieldTwo.text}${_pinOneFieldThree.text}${_pinOneFieldFour.text}'; + _pin2 = + '${_pinTwoFieldOne.text}${_pinTwoFieldTwo.text}${_pinTwoFieldThree.text}${_pinTwoFieldFour.text}'; + }); } Future<void> _generateOtp() async { @@ -51,6 +76,13 @@ class _CreatePinPageState extends State<CreatePinPage> { Helper.toastMessage(AppLocalizations.of(context)!.pleaseMatchPin); return; } + if (_pin1 == _pin2 && _pin1.length == 4) { + Map pinDetails = await OfflineModel.getPinDetails(_pin1); + if (pinDetails['username'] != null) { + Helper.toastMessage(AppLocalizations.of(context)!.pinAlreadyExists); + return; + } + } SystemChannels.textInput.invokeMethod('TextInput.hide'); try { final responseCode = @@ -228,34 +260,23 @@ class _CreatePinPageState extends State<CreatePinPage> { )), Container( alignment: Alignment.centerLeft, - height: 30.0, - margin: const EdgeInsets.only(top: 5), + height: 45.0, + margin: const EdgeInsets.only(top: 10), decoration: BoxDecoration( borderRadius: BorderRadius.circular(4), // border: // Border.all(color: AppColors.black16), color: Colors.white, ), - child: OTPTextField( - length: 4, - width: MediaQuery.of(context).size.width / 2, - fieldWidth: 38, - style: const TextStyle(fontSize: 14), - textFieldAlignment: - MainAxisAlignment.spaceAround, - fieldStyle: FieldStyle.underline, - onCompleted: (pin) { - setState(() { - _pin1 = pin; - }); - }, - onChanged: (String? pin) { - if (pin?.length == 4) { - setState(() { - _pin1 = pin.toString(); - }); - } - }), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + OtpInputField(_pinOneFieldOne, true), + OtpInputField(_pinOneFieldTwo, false), + OtpInputField(_pinOneFieldThree, false), + OtpInputField(_pinOneFieldFour, false) + ], + ), ), Padding( padding: const EdgeInsets.only( @@ -274,34 +295,23 @@ class _CreatePinPageState extends State<CreatePinPage> { )), Container( alignment: Alignment.centerLeft, - height: 30.0, - margin: const EdgeInsets.only(top: 5), + height: 45.0, + margin: const EdgeInsets.only(top: 10), decoration: BoxDecoration( borderRadius: BorderRadius.circular(4), // border: // Border.all(color: AppColors.black16), color: Colors.white, ), - child: OTPTextField( - length: 4, - width: MediaQuery.of(context).size.width / 2, - fieldWidth: 38, - style: const TextStyle(fontSize: 14), - textFieldAlignment: - MainAxisAlignment.spaceAround, - fieldStyle: FieldStyle.underline, - onCompleted: (pin) { - setState(() { - _pin2 = pin; - }); - }, - onChanged: (String? pin) { - if (pin?.length == 4) { - setState(() { - _pin2 = pin.toString(); - }); - } - }), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + OtpInputField(_pinTwoFieldOne, true), + OtpInputField(_pinTwoFieldTwo, false), + OtpInputField(_pinTwoFieldThree, false), + OtpInputField(_pinTwoFieldFour, false) + ], + ), ), Padding( padding: const EdgeInsets.only(top: 40), diff --git a/lib/pages/login_email_page.dart b/lib/pages/login_email_page.dart index e0adf51..7d55324 100644 --- a/lib/pages/login_email_page.dart +++ b/lib/pages/login_email_page.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:otp_text_field/style.dart'; import 'package:provider/provider.dart'; import 'package:smf_mobile/constants/app_constants.dart'; import 'package:smf_mobile/constants/app_urls.dart'; @@ -13,7 +12,7 @@ import 'package:smf_mobile/pages/login_otp_page.dart'; import 'package:smf_mobile/repositories/login_repository.dart'; import 'package:smf_mobile/util/helper.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import 'package:otp_text_field/otp_field.dart'; +import 'package:smf_mobile/widgets/otp_input_field.dart'; import 'package:unique_identifier/unique_identifier.dart'; import 'dart:async'; // import 'package:connectivity_plus/connectivity_plus.dart'; @@ -30,13 +29,19 @@ class LoginEmailPage extends StatefulWidget { class _LoginEmailPageState extends State<LoginEmailPage> { final TextEditingController _emailController = TextEditingController(); final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>(); + // Map _source = {ConnectivityResult.none: false}; // final MyConnectivity _connectivity = MyConnectivity.instance; String _errorMessage = ''; late Locale locale; - String _pin = ''; late String _identifier; + final TextEditingController _fieldOne = TextEditingController(); + final TextEditingController _fieldTwo = TextEditingController(); + final TextEditingController _fieldThree = TextEditingController(); + final TextEditingController _fieldFour = TextEditingController(); + String _oldPin = ''; + @override void initState() { super.initState(); @@ -48,6 +53,10 @@ class _LoginEmailPageState extends State<LoginEmailPage> { // }); // } // }); + _fieldOne.addListener(_validatePin); + _fieldTwo.addListener(_validatePin); + _fieldThree.addListener(_validatePin); + _fieldFour.addListener(_validatePin); initUniqueIdentifierState(); } @@ -92,37 +101,44 @@ class _LoginEmailPageState extends State<LoginEmailPage> { } Future<void> _validatePin() async { - bool isInternetConnected = await Helper.isInternetConnected(); - // await Future.delayed(const Duration(milliseconds: 10)); - try { - Map pinDetails = await OfflineModel.getPinDetails(_pin); - // print(pinDetails); - if (isInternetConnected && pinDetails['username'] != null) { - final responseCode = await Provider.of<LoginRespository>(context, - listen: false) - .validateOtp( - context, pinDetails['username'], '', _identifier, _pin, false); - if (responseCode == 200) { + String pin = + '${_fieldOne.text}${_fieldTwo.text}${_fieldThree.text}${_fieldFour.text}'; + // print(_oldPin + ", " + pin); + if (pin.length == 4 && pin != _oldPin) { + _oldPin = pin; + bool isInternetConnected = await Helper.isInternetConnected(); + // await Future.delayed(const Duration(milliseconds: 10)); + try { + Map pinDetails = await OfflineModel.getPinDetails(pin); + // print(pinDetails); + if (isInternetConnected && pinDetails['username'] != null) { + final responseCode = await Provider.of<LoginRespository>(context, + listen: false) + .validateOtp( + context, pinDetails['username'], '', _identifier, pin, false); + if (responseCode == 200) { + Navigator.of(context).pushReplacement(MaterialPageRoute( + builder: (context) => const HomePage(), + )); + } else { + _errorMessage = + Provider.of<LoginRespository>(context, listen: false) + .errorMessage; + Helper.toastMessage(_errorMessage); + } + } else if (pinDetails['username'] != null) { + Helper.setUser(Storage.username, pinDetails['username']); + Helper.setUser(Storage.authtoken, ''); + Helper.toastMessage(AppLocalizations.of(context)!.youAreOffline); Navigator.of(context).pushReplacement(MaterialPageRoute( builder: (context) => const HomePage(), )); } else { - _errorMessage = Provider.of<LoginRespository>(context, listen: false) - .errorMessage; - Helper.toastMessage(_errorMessage); + Helper.toastMessage(AppLocalizations.of(context)!.inValidPin); } - } else if (pinDetails['username'] != null) { - Helper.setUser(Storage.username, pinDetails['username']); - Helper.setUser(Storage.authtoken, ''); - Helper.toastMessage(AppLocalizations.of(context)!.youAreOffline); - Navigator.of(context).pushReplacement(MaterialPageRoute( - builder: (context) => const HomePage(), - )); - } else { - Helper.toastMessage(AppLocalizations.of(context)!.inValidPin); + } catch (err) { + throw Exception(err); } - } catch (err) { - throw Exception(err); } } @@ -321,36 +337,24 @@ class _LoginEmailPageState extends State<LoginEmailPage> { height: 1.4), )), Container( - alignment: Alignment.centerLeft, - height: 30.0, - margin: const EdgeInsets.only(top: 5), + // alignment: Alignment.centerLeft, + height: 45.0, + margin: const EdgeInsets.only(top: 10), decoration: BoxDecoration( borderRadius: BorderRadius.circular(4), // border: // Border.all(color: AppColors.black16), color: Colors.white, ), - child: OTPTextField( - length: 4, - width: MediaQuery.of(context).size.width / 2, - fieldWidth: 38, - style: const TextStyle(fontSize: 14), - textFieldAlignment: - MainAxisAlignment.spaceAround, - fieldStyle: FieldStyle.underline, - onCompleted: (pin) { - setState(() { - _pin = pin; - }); - }, - onChanged: (String? pin) { - if (pin?.length == 4) { - setState(() { - _pin = pin.toString(); - }); - _validatePin(); - } - }), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + OtpInputField(_fieldOne, true), + OtpInputField(_fieldTwo, false), + OtpInputField(_fieldThree, false), + OtpInputField(_fieldFour, false) + ], + ), ), const Padding( padding: EdgeInsets.only( diff --git a/lib/pages/login_otp_page.dart b/lib/pages/login_otp_page.dart index ec000e7..3237596 100644 --- a/lib/pages/login_otp_page.dart +++ b/lib/pages/login_otp_page.dart @@ -6,11 +6,10 @@ import 'package:smf_mobile/database/offline_model.dart'; import 'package:smf_mobile/pages/home_page.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:smf_mobile/pages/login_email_page.dart'; -import 'package:otp_text_field/otp_field.dart'; -import 'package:otp_text_field/style.dart'; import 'package:smf_mobile/repositories/login_repository.dart'; import 'package:smf_mobile/util/helper.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:smf_mobile/widgets/otp_input_field.dart'; import 'package:unique_identifier/unique_identifier.dart'; import 'package:flutter/services.dart'; @@ -34,15 +33,35 @@ class LoginOtpPage extends StatefulWidget { class _LoginOtpPageState extends State<LoginOtpPage> { final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>(); String _errorMessage = ''; - String _otp = ''; late String _identifier; + final TextEditingController _fieldOne = TextEditingController(); + final TextEditingController _fieldTwo = TextEditingController(); + final TextEditingController _fieldThree = TextEditingController(); + final TextEditingController _fieldFour = TextEditingController(); + final TextEditingController _fieldFive = TextEditingController(); + final TextEditingController _fieldSix = TextEditingController(); + String _otp = ''; + @override void initState() { super.initState(); + _fieldOne.addListener(_setOtp); + _fieldTwo.addListener(_setOtp); + _fieldThree.addListener(_setOtp); + _fieldFour.addListener(_setOtp); + _fieldFive.addListener(_setOtp); + _fieldSix.addListener(_setOtp); initUniqueIdentifierState(); } + void _setOtp() { + setState(() { + _otp = + '${_fieldOne.text}${_fieldTwo.text}${_fieldThree.text}${_fieldFour.text}${_fieldFive.text}${_fieldSix.text}'; + }); + } + Future<void> initUniqueIdentifierState() async { String? identifier; try { @@ -59,17 +78,27 @@ class _LoginOtpPageState extends State<LoginOtpPage> { } Future<void> _validateOtp() async { - String otp = _otp; try { + if (_otp == '') { + Helper.toastMessage(AppLocalizations.of(context)!.pleaseEnterOtp); + return; + } if (widget.loginRequest) { final responseCode = await Provider.of<LoginRespository>(context, listen: false) - .validateOtp(context, widget.username, otp, _identifier, '', true); + .validateOtp(context, widget.username, _otp, _identifier, '', true); if (responseCode == 200) { Navigator.of(context).pushReplacement(MaterialPageRoute( builder: (context) => const HomePage(), )); } else { + _fieldOne.text = ''; + _fieldTwo.text = ''; + _fieldThree.text = ''; + _fieldFour.text = ''; + _fieldFive.text = ''; + _fieldSix.text = ''; + _otp = ''; _errorMessage = Provider.of<LoginRespository>(context, listen: false) .errorMessage; Helper.toastMessage(_errorMessage); @@ -77,7 +106,7 @@ class _LoginOtpPageState extends State<LoginOtpPage> { } else { final responseData = await Provider.of<LoginRespository>(context, listen: false) - .generatePin(widget.username, widget.pin, otp); + .generatePin(widget.username, widget.pin, _otp); if (responseData) { Helper.toastMessage( AppLocalizations.of(context)!.pinGeneratedMessage); @@ -91,6 +120,13 @@ class _LoginOtpPageState extends State<LoginOtpPage> { builder: (context) => const LoginEmailPage(), )); } else { + _fieldOne.text = ''; + _fieldTwo.text = ''; + _fieldThree.text = ''; + _fieldFour.text = ''; + _fieldFive.text = ''; + _fieldSix.text = ''; + _otp = ''; _errorMessage = Provider.of<LoginRespository>(context, listen: false) .errorMessage; Helper.toastMessage(_errorMessage); @@ -195,28 +231,19 @@ class _LoginOtpPageState extends State<LoginOtpPage> { // Border.all(color: AppColors.black16), color: Colors.white, ), - child: OTPTextField( - length: 6, - width: - MediaQuery.of(context).size.width, - fieldWidth: 38, - style: const TextStyle(fontSize: 14), - textFieldAlignment: - MainAxisAlignment.spaceAround, - fieldStyle: FieldStyle.underline, - onCompleted: (pin) { - setState(() { - _otp = pin; - }); - }, - onChanged: (String? pin) { - if (pin?.length == 6) { - setState(() { - _otp = pin.toString(); - }); - } - }), - ) + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceEvenly, + children: [ + OtpInputField(_fieldOne, true), + OtpInputField(_fieldTwo, false), + OtpInputField(_fieldThree, false), + OtpInputField(_fieldFour, false), + OtpInputField(_fieldFive, false), + OtpInputField(_fieldSix, false) + ], + ), + ), ], ), ), diff --git a/lib/repositories/application_repository.dart b/lib/repositories/application_repository.dart index f0063a7..16914e3 100644 --- a/lib/repositories/application_repository.dart +++ b/lib/repositories/application_repository.dart @@ -68,6 +68,7 @@ class ApplicationRespository with ChangeNotifier { if (applications[i]['applicationId'] == data['applicationId']) { applications[i]['inspection']['status'] = InspectionStatus.leadInspectorCompleted; + applications[i]['inspectorDataObject'] = data; } } applicationFieldData['responseData'] = applications; diff --git a/lib/widgets/otp_input_field.dart b/lib/widgets/otp_input_field.dart new file mode 100644 index 0000000..8bf0c15 --- /dev/null +++ b/lib/widgets/otp_input_field.dart @@ -0,0 +1,47 @@ +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:smf_mobile/constants/color_constants.dart'; + +class OtpInputField extends StatefulWidget { + final TextEditingController controller; + final bool autoFocus; + const OtpInputField(this.controller, this.autoFocus, {Key? key}) + : super(key: key); + @override + _OtpInputFieldState createState() => _OtpInputFieldState(); +} + +class _OtpInputFieldState extends State<OtpInputField> { + @override + Widget build(BuildContext context) { + return SizedBox( + height: 40, + width: 40, + child: TextField( + // autofocus: widget.autoFocus, + textAlign: TextAlign.center, + keyboardType: TextInputType.number, + controller: widget.controller, + maxLength: 1, + cursorColor: AppColors.black08, + style: GoogleFonts.lato( + color: AppColors.black40, + fontSize: 16, + letterSpacing: + 0 /*percentages not used in flutter. defaulting to zero*/, + fontWeight: FontWeight.normal, + height: 1 /*PERCENT not supported*/ + ), + decoration: const InputDecoration( + border: OutlineInputBorder(), + counterText: '', + hintStyle: TextStyle(color: AppColors.black40, fontSize: 20.0)), + onChanged: (value) { + if (value.length == 1) { + FocusScope.of(context).nextFocus(); + } + }, + ), + ); + } +} -- GitLab