diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index db43866a43039c3b7eb40641c2a5693e2ef00e24..4578376caa47b5977db1eff17be30b90a04a5bd1 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -25,6 +25,11 @@ <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> + + <activity + android:name="com.yalantis.ucrop.UCropActivity" + android:screenOrientation="portrait" + android:theme="@style/Theme.AppCompat.Light.NoActionBar"/> <!-- Don't delete the meta-data below. This is used by the Flutter tool to generate GeneratedPluginRegistrant.java --> <meta-data diff --git a/lib/database/offline_model.dart b/lib/database/offline_model.dart index 6ad7747188bc563354692fb07376fa8d740275d5..666f3da80869617cd2f76d07979a5943aa5ee43c 100644 --- a/lib/database/offline_model.dart +++ b/lib/database/offline_model.dart @@ -123,11 +123,13 @@ class OfflineModel { where: 'username = ?', whereArgs: whereArgs); } - static Future<Map> getPinDetails(String pin) async { + static Future<Map> getPinDetails(String username, String pin) async { final db = await OfflineModel.database(); - List<dynamic> whereArgs = [pin]; + List<dynamic> whereArgs = [username, pin]; List<Map> rows = await db.query(AppDatabase.loginPinsTable, - where: 'pin = ?', orderBy: 'id DESC', whereArgs: whereArgs); + where: 'username = ? AND pin = ?', + orderBy: 'id DESC', + whereArgs: whereArgs); return rows.isNotEmpty ? rows[0] : {}; } } diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index d6391bbe63a68056577ff2b42241f24a9b208dc8..f8919c23a69681627877f03713853b7f746b4ac3 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -3,7 +3,7 @@ "logout": "Logout", "emailId": "Email Id", "enterHere": "Enter here", - "orEnterPin": "Or, Enter your 4 Digit Login Pin", + "enterPin": "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", @@ -19,6 +19,9 @@ "pleaseMatchPin": "Please match pin to proceed", "pleaseEnterValidPin": "Please enter valid pin to proceed", "pinGeneratedMessage": "Pin generated successfully, please login", + "moreLoginOptions": "More login options", + "loginWithPin": "Login with your PIN", + "createResetPin": "Create/reset PIN", "signIn": "SIGN IN", "goBack": "Go back", "goBackText": "Go back, re-enter the email", @@ -55,5 +58,8 @@ "dataSynchoronizedText": "Data has been synchoronized", "formNotAvailable": "Form not available", "failedToGetUniqueIdentifier": "Failed to get Unique Identifier", - "invalidResponse": "Invalid response recieved from the server" + "invalidResponse": "Invalid response recieved from the server", + "takeAPicture": "Take a picture", + "goToFiles": "Go to files", + "cropImage": "Crop image" } \ No newline at end of file diff --git a/lib/pages/create_pin_page.dart b/lib/pages/create_pin_page.dart index 87284176a2885cbb27b97c8e82f66af312ee22ff..f8b8aa75f1a3b408984891f39ec5cc28eb29f5f7 100644 --- a/lib/pages/create_pin_page.dart +++ b/lib/pages/create_pin_page.dart @@ -78,7 +78,7 @@ class _CreatePinPageState extends State<CreatePinPage> { return; } if (_pin1 == _pin2 && _pin1.length == 4) { - Map pinDetails = await OfflineModel.getPinDetails(_pin1); + Map pinDetails = await OfflineModel.getPinDetails(email, _pin1); if (pinDetails['username'] != null) { Helper.toastMessage(AppLocalizations.of(context)!.pinAlreadyExists); return; diff --git a/lib/pages/login_email_page.dart b/lib/pages/login_email_page.dart index 17c2d7c997448f9ce90fd787f8eea2302708c031..497031984e54c09ecd67150f61800733104cc09f 100644 --- a/lib/pages/login_email_page.dart +++ b/lib/pages/login_email_page.dart @@ -1,18 +1,15 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; -import 'package:smf_mobile/constants/app_constants.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/create_pin_page.dart'; -import 'package:smf_mobile/pages/home_page.dart'; import 'package:smf_mobile/pages/login_otp_page.dart'; +import 'package:smf_mobile/pages/login_pin_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:smf_mobile/widgets/otp_input_field.dart'; import 'package:unique_identifier/unique_identifier.dart'; import 'dart:async'; import 'package:email_validator/email_validator.dart'; @@ -31,51 +28,12 @@ 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; - 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(); - // _connectivity.initialise(); - // _connectivity.myStream.listen((source) { - // if (mounted) { - // setState(() { - // _source = source; - // }); - // } - // }); - _fieldOne.addListener(_validatePin); - _fieldTwo.addListener(_validatePin); - _fieldThree.addListener(_validatePin); - _fieldFour.addListener(_validatePin); - initUniqueIdentifierState(); - } - - Future<void> initUniqueIdentifierState() async { - String? identifier; - try { - identifier = await UniqueIdentifier.serial; - } on PlatformException { - identifier = AppLocalizations.of(context)!.failedToGetUniqueIdentifier; - } - - if (!mounted) return; - - setState(() { - _identifier = identifier!; - }); } Future<void> _generateOtp() async { @@ -103,59 +61,15 @@ class _LoginEmailPageState extends State<LoginEmailPage> { } } - Future<void> _validatePin() async { - 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 { - _fieldOne.text = ''; - _fieldTwo.text = ''; - _fieldThree.text = ''; - _fieldFour.text = ''; - Helper.toastMessage(AppLocalizations.of(context)!.inValidPin); - } - } catch (err) { - throw Exception(err); - } - } - } - _validateEmail() { String email = _emailController.text; - final bool isValid = EmailValidator.validate(email); - SystemChannels.textInput.invokeMethod('TextInput.hide'); - if (!isValid) { - _emailController.text = ''; - Helper.toastMessage(AppLocalizations.of(context)!.inValidEmailId); + if (email != '') { + final bool isValid = EmailValidator.validate(email); + SystemChannels.textInput.invokeMethod('TextInput.hide'); + if (!isValid) { + _emailController.text = ''; + Helper.toastMessage(AppLocalizations.of(context)!.inValidEmailId); + } } } @@ -188,7 +102,7 @@ class _LoginEmailPageState extends State<LoginEmailPage> { ), Container( padding: - const EdgeInsets.only(left: 40, right: 40, bottom: 40), + const EdgeInsets.only(left: 40, right: 40, bottom: 50), margin: const EdgeInsets.only(left: 20, right: 20), decoration: BoxDecoration( borderRadius: BorderRadius.circular(4), @@ -340,72 +254,65 @@ class _LoginEmailPageState extends State<LoginEmailPage> { )), ]))), ), - Padding( - padding: const EdgeInsets.only( - top: 20, - ), - child: Text( - AppLocalizations.of(context)!.orEnterPin, - textAlign: TextAlign.left, - style: GoogleFonts.lato( - color: AppColors.black87, - fontSize: 16, - letterSpacing: - 0.25 /*percentages not used in flutter. defaulting to zero*/, - fontWeight: FontWeight.w400, - height: 1.4), - )), - Container( - // 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, + ]), + ), + const Spacer(), + Column( + children: [ + Padding( + padding: const EdgeInsets.only(top: 10, bottom: 20), + child: Text( + AppLocalizations.of(context)!.moreLoginOptions, + style: GoogleFonts.lato( + color: AppColors.black87, + fontSize: 16, + letterSpacing: + 0.25 /*percentages not used in flutter. defaulting to zero*/, + fontWeight: FontWeight.w400, + height: 1.4), + ), + ), + Container( + width: double.infinity, + color: AppColors.primaryBlue, + padding: const EdgeInsets.only(top: 15, bottom: 15), + child: InkWell( + onTap: () => Navigator.of(context) + .pushReplacement(MaterialPageRoute( + builder: (context) => const LoginPinPage(), + )), + child: Text( + AppLocalizations.of(context)!.loginWithPin, + textAlign: TextAlign.center, + style: GoogleFonts.lato( + color: Colors.white, + fontSize: 16, + letterSpacing: + 0.25 /*percentages not used in flutter. defaulting to zero*/, + fontWeight: FontWeight.w400, + height: 1.4), ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - OtpInputField(_fieldOne, true, false), - OtpInputField(_fieldTwo, false, false), - OtpInputField(_fieldThree, false, false), - OtpInputField(_fieldFour, false, true) - ], + )), + Padding( + padding: const EdgeInsets.only(top: 20, bottom: 30), + child: InkWell( + onTap: () => Navigator.of(context) + .pushReplacement(MaterialPageRoute( + builder: (context) => const CreatePinPage(), + )), + child: Text( + AppLocalizations.of(context)!.createResetPin, + style: GoogleFonts.lato( + color: AppColors.primaryBlue, + fontSize: 16, + letterSpacing: + 0.25 /*percentages not used in flutter. defaulting to zero*/, + fontWeight: FontWeight.w400, + height: 1.4), ), - ), - const Padding( - padding: EdgeInsets.only( - top: 20, - ), - child: Divider( - color: AppColors.black16, - )), - Center( - child: Container( - padding: const EdgeInsets.only( - top: 10, - ), - child: InkWell( - onTap: () => Navigator.of(context) - .pushReplacement(MaterialPageRoute( - builder: (context) => - const CreatePinPage(), - )), - child: Text( - AppLocalizations.of(context)!.createPin, - style: GoogleFonts.lato( - color: AppColors.primaryBlue, - fontSize: 14, - letterSpacing: - 0.5 /*percentages not used in flutter. defaulting to zero*/, - fontWeight: FontWeight.w700, - height: 1.4), - ))), - ) - ]), - ) + )), + ], + ), ], )), )); diff --git a/lib/pages/login_otp_page.dart b/lib/pages/login_otp_page.dart index 0f633f8fe7d4c333169ffb67526d670f6dbcb707..ee3b7b219989812980e1687830475db2ba729fb8 100644 --- a/lib/pages/login_otp_page.dart +++ b/lib/pages/login_otp_page.dart @@ -67,7 +67,7 @@ class _LoginOtpPageState extends State<LoginOtpPage> { try { identifier = await UniqueIdentifier.serial; } on PlatformException { - identifier = 'Failed to get Unique Identifier'; + identifier = AppLocalizations.of(context)!.failedToGetUniqueIdentifier; } if (!mounted) return; diff --git a/lib/pages/login_pin_page.dart b/lib/pages/login_pin_page.dart new file mode 100644 index 0000000000000000000000000000000000000000..17231a948c119a8e431d7f49cfb12529c7d5a44a --- /dev/null +++ b/lib/pages/login_pin_page.dart @@ -0,0 +1,361 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:provider/provider.dart'; +import 'package:smf_mobile/constants/app_constants.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/home_page.dart'; +import 'package:smf_mobile/pages/login_email_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:smf_mobile/widgets/otp_input_field.dart'; +import 'package:unique_identifier/unique_identifier.dart'; +import 'dart:async'; +import 'package:email_validator/email_validator.dart'; + +class LoginPinPage extends StatefulWidget { + static const route = AppUrl.loginEmailPage; + + const LoginPinPage({Key? key}) : super(key: key); + @override + _LoginPinPageState createState() => _LoginPinPageState(); +} + +class _LoginPinPageState extends State<LoginPinPage> { + final TextEditingController _emailController = TextEditingController(); + final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>(); + + String _errorMessage = ''; + late Locale locale; + late String _identifier; + + final TextEditingController _fieldOne = TextEditingController(); + final TextEditingController _fieldTwo = TextEditingController(); + final TextEditingController _fieldThree = TextEditingController(); + final TextEditingController _fieldFour = TextEditingController(); + + @override + void initState() { + super.initState(); + initUniqueIdentifierState(); + } + + Future<void> initUniqueIdentifierState() async { + String? identifier; + try { + identifier = await UniqueIdentifier.serial; + } on PlatformException { + identifier = AppLocalizations.of(context)!.failedToGetUniqueIdentifier; + } + + if (!mounted) return; + + setState(() { + _identifier = identifier!; + }); + } + + Future<void> _validatePin() async { + final email = _emailController.text.trim(); + if (email == '') { + Helper.toastMessage(AppLocalizations.of(context)!.pleaseEnterEmail); + return; + } + String pin = + '${_fieldOne.text}${_fieldTwo.text}${_fieldThree.text}${_fieldFour.text}'; + bool isInternetConnected = await Helper.isInternetConnected(); + // await Future.delayed(const Duration(milliseconds: 10)); + try { + Map pinDetails = await OfflineModel.getPinDetails(email, pin); + 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 { + _fieldOne.text = ''; + _fieldTwo.text = ''; + _fieldThree.text = ''; + _fieldFour.text = ''; + Helper.toastMessage(AppLocalizations.of(context)!.inValidPin); + } + } catch (err) { + throw Exception(err); + } + } + + _validateEmail() { + String email = _emailController.text.trim(); + if (email != '') { + final bool isValid = EmailValidator.validate(email); + SystemChannels.textInput.invokeMethod('TextInput.hide'); + if (!isValid) { + _emailController.text = ''; + Helper.toastMessage(AppLocalizations.of(context)!.inValidEmailId); + } + } + } + + @override + void dispose() { + // _connectivity.disposeStream(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + key: _scaffoldKey, + body: SingleChildScrollView( + reverse: true, + child: SizedBox( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + child: Column( + children: [ + Container( + padding: const EdgeInsets.only(top: 60, bottom: 60), + child: Center( + child: Image.asset( + 'assets/images/logo.png', + // width: 50.0, + // height: 50.0, + ), + ), + ), + Container( + padding: + const EdgeInsets.only(left: 40, right: 40, bottom: 40), + margin: const EdgeInsets.only(left: 20, right: 20), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(4), + color: Colors.white, + boxShadow: const [ + BoxShadow( + color: AppColors.black16, + offset: Offset(0, 2), + blurRadius: 2) + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(top: 40), + child: Align( + alignment: Alignment.center, + child: Text( + AppLocalizations.of(context)!.login, + textAlign: TextAlign.center, + style: GoogleFonts.lato( + color: AppColors.black87, + fontSize: 20, + letterSpacing: + 0.25 /*percentages not used in flutter. defaulting to zero*/, + fontWeight: FontWeight.w600, + height: 1.4), + ))), + Padding( + padding: const EdgeInsets.only( + top: 50, + ), + child: Text( + AppLocalizations.of(context)!.emailId, + textAlign: TextAlign.left, + style: GoogleFonts.lato( + color: AppColors.black87, + fontSize: 16, + letterSpacing: + 0.25 /*percentages not used in flutter. defaulting to zero*/, + fontWeight: FontWeight.w400, + height: 1.4), + )), + Container( + width: double.infinity, + margin: const EdgeInsets.only( + top: 0, + ), + child: Padding( + padding: const EdgeInsets.only(top: 5), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 10.0), + Container( + alignment: Alignment.centerLeft, + height: 50.0, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(4), + border: + Border.all(color: AppColors.black16), + color: Colors.white, + ), + child: Focus( + child: TextFormField( + onEditingComplete: () => + _validateEmail(), + textCapitalization: + TextCapitalization.none, + textInputAction: TextInputAction.done, + controller: _emailController, + style: GoogleFonts.lato( + color: AppColors.black40, + fontSize: 16, + letterSpacing: + 0 /*percentages not used in flutter. defaulting to zero*/, + fontWeight: FontWeight.normal, + height: + 1.5 /*PERCENT not supported*/ + ), + keyboardType: + TextInputType.emailAddress, + decoration: const InputDecoration( + contentPadding: EdgeInsets.fromLTRB( + 20.0, 0.0, 20.0, 0.0), + border: InputBorder.none, + hintText: 'Enter here', + hintStyle: TextStyle( + color: AppColors.black40, + fontSize: 14, + letterSpacing: + 0 /*percentages not used in flutter. defaulting to zero*/, + fontWeight: FontWeight.normal, + height: + 1.5 /*PERCENT not supported*/ + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.transparent, + width: 2.0), + ), + ), + ), + ), + ) + ], + ), + ), + ), + Padding( + padding: const EdgeInsets.only( + top: 20, + ), + child: Text( + AppLocalizations.of(context)!.enterPin, + textAlign: TextAlign.left, + style: GoogleFonts.lato( + color: AppColors.black87, + fontSize: 16, + letterSpacing: + 0.25 /*percentages not used in flutter. defaulting to zero*/, + fontWeight: FontWeight.w400, + height: 1.4), + )), + Container( + // 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: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + OtpInputField(_fieldOne, true, false), + OtpInputField(_fieldTwo, false, false), + OtpInputField(_fieldThree, false, false), + OtpInputField(_fieldFour, false, true) + ], + ), + ), + Padding( + padding: const EdgeInsets.only(top: 30, bottom: 10), + child: InkWell( + // ignore: avoid_print + onTap: () => _validatePin(), + child: SizedBox( + width: + MediaQuery.of(context).size.width - 80, + height: 50, + child: Stack(children: <Widget>[ + Align( + alignment: Alignment.bottomCenter, + child: Container( + width: MediaQuery.of(context) + .size + .width - + 80, + height: 50, + decoration: BoxDecoration( + color: AppColors.primaryBlue, + borderRadius: + BorderRadius.circular(4), + ))), + Align( + alignment: Alignment.center, + child: Text( + AppLocalizations.of(context)!.login, + textAlign: TextAlign.center, + style: GoogleFonts.lato( + color: Colors.white, + fontSize: 17, + letterSpacing: + 0 /*percentages not used in flutter. defaulting to zero*/, + fontWeight: FontWeight.normal, + height: + 1.5 /*PERCENT not supported*/ + ), + )), + ]))), + ), + Center( + child: Container( + padding: const EdgeInsets.only( + top: 10, + ), + child: InkWell( + onTap: () => Navigator.of(context) + .pushReplacement(MaterialPageRoute( + builder: (context) => + const LoginEmailPage(), + )), + child: Text( + AppLocalizations.of(context)!.goBack, + style: GoogleFonts.lato( + color: AppColors.primaryBlue, + fontSize: 14, + letterSpacing: + 0.5 /*percentages not used in flutter. defaulting to zero*/, + fontWeight: FontWeight.w700, + height: 1.4), + ))), + ) + ]), + ) + ], + )), + )); + } +} diff --git a/lib/repositories/login_repository.dart b/lib/repositories/login_repository.dart index c7eecafd5b73b155bea02f5687058fffc491dde2..bbcdfacdb9140d5eb3efdb01c1494929d164fdad 100644 --- a/lib/repositories/login_repository.dart +++ b/lib/repositories/login_repository.dart @@ -58,6 +58,7 @@ class LoginRespository with ChangeNotifier { 'pin': pin, }; } + // print(requestData); final request = await LoginService.validateOtp(requestData); _data = json.decode(request.body); } catch (_) { diff --git a/lib/services/application_service.dart b/lib/services/application_service.dart index dc1be0cda39d4f4d94fb5ab2dbbbd56b763fb06a..8deb5807bca0b5a5f017da54e0c73179c626dc9d 100644 --- a/lib/services/application_service.dart +++ b/lib/services/application_service.dart @@ -4,7 +4,7 @@ import 'package:http/http.dart' as http; import 'package:smf_mobile/constants/api_endpoints.dart'; import 'package:smf_mobile/constants/app_constants.dart'; import 'package:smf_mobile/services/base_service.dart'; -// import 'dart:developer' as developer; +import 'dart:developer' as developer; class ApplicationService extends BaseService { ApplicationService(HttpClient client) : super(client); @@ -26,9 +26,9 @@ class ApplicationService extends BaseService { final response = await http.post(Uri.parse(ApiUrl.submitInspection), headers: headers, body: body); - // developer.log(ApiUrl.submitInspection); - // developer.log(body); - // developer.log(response.body); + developer.log(ApiUrl.submitInspection); + developer.log(body); + developer.log(response.body); return response; } diff --git a/lib/util/notification_helper.dart b/lib/util/notification_helper.dart index 95aea227620a13eed603fdb1d394446bd8115e8e..980d9066c1a71a8e487f14a3bad7026872673417 100644 --- a/lib/util/notification_helper.dart +++ b/lib/util/notification_helper.dart @@ -1,8 +1,7 @@ // import 'dart:convert'; // import 'package:crypto/crypto.dart'; import 'dart:convert'; - -import 'package:flutter/material.dart'; +// import 'package:flutter/material.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; // import 'package:smf_mobile/pages/home_page.dart'; diff --git a/lib/widgets/application_card.dart b/lib/widgets/application_card.dart index b5e34ef5930693f5d32faed1970d330106639673..31b865fb5314dfcc7763d3b8813e9acad90023e5 100644 --- a/lib/widgets/application_card.dart +++ b/lib/widgets/application_card.dart @@ -94,37 +94,37 @@ class _ApplicationCardState extends State<ApplicationCard> { fontWeight: FontWeight.w400, )), ), - Row( - children: [ - Padding( - padding: const EdgeInsets.only(bottom: 10), - child: Text(AppLocalizations.of(context)!.status + ': ', - style: GoogleFonts.lato( - color: AppColors.black60, - fontSize: 14.0, - letterSpacing: 0.12, - fontWeight: FontWeight.w400, - )), - ), - Padding( - padding: const EdgeInsets.only(bottom: 10), - child: Text( - Helper.getInspectionStatus( - context, - _getApplicationStatus( - widget.application.inspectionStatus, - widget.application.status)), - style: GoogleFonts.lato( - color: Helper.getColorByStatus( - widget.application.inspectionStatus, - widget.application.status), - fontSize: 14.0, - letterSpacing: 0.12, - fontWeight: FontWeight.w700, - )), - ), - ], - ) + // Row( + // children: [ + // Padding( + // padding: const EdgeInsets.only(bottom: 10), + // child: Text(AppLocalizations.of(context)!.status + ': ', + // style: GoogleFonts.lato( + // color: AppColors.black60, + // fontSize: 14.0, + // letterSpacing: 0.12, + // fontWeight: FontWeight.w400, + // )), + // ), + // Padding( + // padding: const EdgeInsets.only(bottom: 10), + // child: Text( + // Helper.getInspectionStatus( + // context, + // _getApplicationStatus( + // widget.application.inspectionStatus, + // widget.application.status)), + // style: GoogleFonts.lato( + // color: Helper.getColorByStatus( + // widget.application.inspectionStatus, + // widget.application.status), + // fontSize: 14.0, + // letterSpacing: 0.12, + // fontWeight: FontWeight.w700, + // )), + // ), + // ], + // ) ], ), )); diff --git a/lib/widgets/lead_inspector_application_field.dart b/lib/widgets/lead_inspector_application_field.dart index 7e73ae7880f61d87d7c18e653dacea5d9e5bfda8..59011fc8b80eb151c8ad5fc0ca1a97c31d4b36cd 100644 --- a/lib/widgets/lead_inspector_application_field.dart +++ b/lib/widgets/lead_inspector_application_field.dart @@ -1,9 +1,12 @@ +import 'dart:io'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:smf_mobile/constants/app_constants.dart'; import 'package:smf_mobile/constants/color_constants.dart'; import 'package:smf_mobile/widgets/lead_inspector_dialog.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:image_picker/image_picker.dart'; +import 'package:image_cropper/image_cropper.dart'; class LeadInspectorApplicationField extends StatefulWidget { final String fieldName; @@ -33,6 +36,9 @@ class _LeadInspectorApplicationFieldState String _inspectionValue = ''; String _summaryText = ''; final List<String> _options = [FieldValue.correct, FieldValue.inCorrect]; + late File _selectedFile; + final _picker = ImagePicker(); + bool _inProcess = false; @override void initState() { @@ -112,12 +118,123 @@ class _LeadInspectorApplicationFieldState })); } + Future<dynamic> _photoOptions(contextMain) { + return showDialog( + context: context, + builder: (context) => Stack( + children: [ + Positioned( + child: Align( + alignment: FractionalOffset.bottomCenter, + child: Container( + padding: const EdgeInsets.all(20), + width: double.infinity, + height: 120.0, + color: Colors.white, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: GestureDetector( + onTap: () { + Navigator.of(context).pop(true); + _getImage(ImageSource.camera); + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + const Icon( + Icons.photo_camera, + color: AppColors.primaryBlue, + ), + Padding( + padding: + const EdgeInsets.only(left: 8.0), + child: Text( + 'Take a picture', + style: GoogleFonts.montserrat( + decoration: TextDecoration.none, + color: Colors.black87, + fontSize: 16, + fontWeight: FontWeight.w500), + ), + ), + ], + ), + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: GestureDetector( + onTap: () { + Navigator.of(context).pop(true); + _getImage(ImageSource.gallery); + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + const Icon(Icons.photo), + Padding( + padding: + const EdgeInsets.only(left: 8.0), + child: Text( + 'Go to files', + style: GoogleFonts.montserrat( + decoration: TextDecoration.none, + color: Colors.black87, + fontSize: 16, + fontWeight: FontWeight.w500), + ), + ), + ], + ), + ), + ), + ], + ), + ))) + ], + )); + } + + Future<dynamic> _getImage(ImageSource source) async { + _inProcess = true; + XFile? image = await _picker.pickImage(source: source); + + if (image != null) { + try { + File? cropped = await ImageCropper().cropImage( + sourcePath: image.path, + aspectRatio: const CropAspectRatio(ratioX: 1, ratioY: 1), + compressQuality: 100, + maxWidth: 700, + maxHeight: 700, + compressFormat: ImageCompressFormat.jpg, + androidUiSettings: AndroidUiSettings( + toolbarColor: AppColors.primaryBlue, + toolbarTitle: 'Crop image', + toolbarWidgetColor: Colors.white, + statusBarColor: Colors.grey.shade900, + backgroundColor: Colors.white, + )); + print(cropped!.path); + setState(() { + _selectedFile = cropped; + _inProcess = false; + }); + } catch (e) { + print(e); + } + } else { + setState(() { + _inProcess = false; + }); + } + } + @override Widget build(BuildContext context) { - // if (_data != widget.fieldData[widget.fieldData.keys.elementAt(0)]) { - // _radioValue = _data[_data.keys.elementAt(0)]; - // _summaryText = _data[_data.keys.elementAt(1)]; - // } return SingleChildScrollView( reverse: true, child: Container( @@ -240,9 +357,9 @@ class _LeadInspectorApplicationFieldState }, child: Container( padding: const EdgeInsets.only( - right: 15), + right: 10), margin: const EdgeInsets.only( - right: 15), + right: 10), decoration: BoxDecoration( color: _radioValue == _options[i] @@ -261,6 +378,10 @@ class _LeadInspectorApplicationFieldState child: Row(children: [ Radio( value: _options[i], + visualDensity: + const VisualDensity( + horizontal: -2.5), + // dense: true, groupValue: _radioValue, activeColor: AppColors.primaryBlue, @@ -297,7 +418,7 @@ class _LeadInspectorApplicationFieldState color: AppColors.black87, fontWeight: FontWeight.w400, - fontSize: 14.0, + fontSize: 13.0, letterSpacing: 0.25, ), ), @@ -331,6 +452,30 @@ class _LeadInspectorApplicationFieldState color: AppColors.black40, ), ), + ), + Padding( + padding: const EdgeInsets.only(left: 0), + child: IconButton( + onPressed: () { + // if (widget.applicationStatus != + // InspectionStatus + // .inspectionCompleted && + // _radioValue != + // FieldValue.correct) { + _photoOptions(context); + // } + }, + icon: + _radioValue != FieldValue.correct + ? const Icon( + Icons.camera_alt, + color: AppColors.black40, + ) + : const Icon( + Icons.camera_alt_outlined, + color: AppColors.black40, + ), + ), ) ], )), @@ -396,9 +541,9 @@ class _LeadInspectorApplicationFieldState ), ), ) - : const Center() + : const Center(), ], - ))) + ))), ]))); } } diff --git a/lib/widgets/radio_question.dart b/lib/widgets/radio_question.dart index 50e59f056c4981e9d9c4df4e0e3ddd9f635f7c56..f58c83b089da307c1d3383469bc37b71956a78f5 100644 --- a/lib/widgets/radio_question.dart +++ b/lib/widgets/radio_question.dart @@ -19,7 +19,7 @@ class RadioQuestion extends StatefulWidget { class _RadioQuestionState extends State<RadioQuestion> { int _radioValue = 0; - final List<String> _options = ['Correct', 'Incorrect']; + // final List<String> _options = ['Correct', 'Incorrect']; @override void initState() { diff --git a/pubspec.lock b/pubspec.lock index 1e10ec0db7a43659f067c773ae4b3ddf4a7d1058..7fbb4152118d06ed5541edcfb8376ce44cec2845 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -113,6 +113,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.2.0" + cross_file: + dependency: transitive + description: + name: cross_file + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.2" crypto: dependency: transitive description: @@ -249,6 +256,13 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.5" flutter_secure_storage: dependency: "direct main" description: @@ -336,6 +350,34 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.1.1" + image_cropper: + dependency: "direct main" + description: + name: image_cropper + url: "https://pub.dartlang.org" + source: hosted + version: "1.5.0" + image_picker: + dependency: "direct main" + description: + name: image_picker + url: "https://pub.dartlang.org" + source: hosted + version: "0.8.4+11" + image_picker_for_web: + dependency: transitive + description: + name: image_picker_for_web + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.6" + image_picker_platform_interface: + dependency: transitive + description: + name: image_picker_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.4.4" intl: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 6bf454c66bc81188c6a1524dc0f2643af84cf4de..49851ba51170db0cc7612c6eb51445bf590158b0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -52,6 +52,8 @@ dependencies: connectivity_plus: ^2.2.1 change_app_package_name: ^1.0.0 email_validator: ^2.0.1 + image_picker: ^0.8.4+11 + image_cropper: ^1.5.0 dev_dependencies: flutter_test: