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