diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 89e2ee5..aa006ec 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -5,6 +5,9 @@ + + + + NSLocationWhenInUseUsageDescription + This app needs access to location when open. + NSLocationAlwaysUsageDescription + This app needs access to location when in the background. + NSLocationTemporaryUsageDescriptionDictionary CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName diff --git a/lib/apis/models/entities/login_info.dart b/lib/apis/models/entities/login_info.dart new file mode 100644 index 0000000..973e413 --- /dev/null +++ b/lib/apis/models/entities/login_info.dart @@ -0,0 +1,30 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'login_info.g.dart'; + +@JsonSerializable() +class LoginInfo { + LoginInfo({ + required this.id, + this.user, + this.devices, + required this.createdAt, + }); + + factory LoginInfo.fromJson(Map json) => + _$LoginInfoFromJson(json); + + Map toJson() => _$LoginInfoToJson(this); + + @JsonKey(name: '_id') + String id; + + @JsonKey(name: 'user') + String? user; + + @JsonKey(name: 'devices') + List>? devices; + + @JsonKey(name: 'createdAt') + DateTime createdAt; +} diff --git a/lib/apis/models/entities/login_info.g.dart b/lib/apis/models/entities/login_info.g.dart new file mode 100644 index 0000000..e76dbf9 --- /dev/null +++ b/lib/apis/models/entities/login_info.g.dart @@ -0,0 +1,23 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'login_info.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +LoginInfo _$LoginInfoFromJson(Map json) => LoginInfo( + id: json['_id'] as String, + user: json['user'] as String?, + devices: (json['devices'] as List?) + ?.map((e) => e as Map) + .toList(), + createdAt: DateTime.parse(json['createdAt'] as String), + ); + +Map _$LoginInfoToJson(LoginInfo instance) => { + '_id': instance.id, + 'user': instance.user, + 'devices': instance.devices, + 'createdAt': instance.createdAt.toIso8601String(), + }; diff --git a/lib/apis/models/entities/notification.dart b/lib/apis/models/entities/notification.dart index cf0e146..185f161 100644 --- a/lib/apis/models/entities/notification.dart +++ b/lib/apis/models/entities/notification.dart @@ -17,9 +17,9 @@ class ApiNotification { }); factory ApiNotification.fromJson(Map json) => - _$NotificationFromJson(json); + _$ApiNotificationFromJson(json); - Map toJson() => _$NotificationToJson(this); + Map toJson() => _$ApiNotificationToJson(this); @JsonKey(name: '_id') String id; diff --git a/lib/apis/models/entities/notification.g.dart b/lib/apis/models/entities/notification.g.dart index 92131a9..cfe7c75 100644 --- a/lib/apis/models/entities/notification.g.dart +++ b/lib/apis/models/entities/notification.g.dart @@ -6,7 +6,7 @@ part of 'notification.dart'; // JsonSerializableGenerator // ************************************************************************** -ApiNotification _$NotificationFromJson(Map json) => +ApiNotification _$ApiNotificationFromJson(Map json) => ApiNotification( id: json['_id'] as String, owner: User.fromJson(json['owner'] as Map), @@ -18,7 +18,7 @@ ApiNotification _$NotificationFromJson(Map json) => createdAt: DateTime.parse(json['createdAt'] as String), ); -Map _$NotificationToJson(ApiNotification instance) => +Map _$ApiNotificationToJson(ApiNotification instance) => { '_id': instance.id, 'owner': instance.owner, diff --git a/lib/apis/models/responses/login_info_response.dart b/lib/apis/models/responses/login_info_response.dart new file mode 100644 index 0000000..0c5f98c --- /dev/null +++ b/lib/apis/models/responses/login_info_response.dart @@ -0,0 +1,23 @@ +import 'package:json_annotation/json_annotation.dart'; +import 'package:social_media_app/apis/models/entities/login_info.dart'; + +part 'login_info_response.g.dart'; + +@JsonSerializable() +class LoginInfoResponse { + LoginInfoResponse({ + this.success, + this.result, + }); + + factory LoginInfoResponse.fromJson(Map json) => + _$LoginInfoResponseFromJson(json); + + Map toJson() => _$LoginInfoResponseToJson(this); + + @JsonKey(name: 'success') + bool? success; + + @JsonKey(name: 'result') + LoginInfo? result; +} diff --git a/lib/apis/models/responses/login_info_response.g.dart b/lib/apis/models/responses/login_info_response.g.dart new file mode 100644 index 0000000..3f8f654 --- /dev/null +++ b/lib/apis/models/responses/login_info_response.g.dart @@ -0,0 +1,21 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'login_info_response.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +LoginInfoResponse _$LoginInfoResponseFromJson(Map json) => + LoginInfoResponse( + success: json['success'] as bool?, + result: json['result'] == null + ? null + : LoginInfo.fromJson(json['result'] as Map), + ); + +Map _$LoginInfoResponseToJson(LoginInfoResponse instance) => + { + 'success': instance.success, + 'result': instance.result, + }; diff --git a/lib/apis/providers/api_provider.dart b/lib/apis/providers/api_provider.dart index 67d8e9a..f987cd2 100644 --- a/lib/apis/providers/api_provider.dart +++ b/lib/apis/providers/api_provider.dart @@ -342,6 +342,33 @@ class ApiProvider { }, ); + return response; + } + +// LOGIN INFO + Future saveLoginInfo( + String token, Map body) async { + final response = await _client.post( + Uri.parse('${baseUrl!}${AppUrls.saveLoginInfo}'), + headers: { + "content-type": "application/json", + "authorization": "Bearer $token", + }, + body: jsonEncode(body), + ); + + return response; + } + + Future getLoginInfo(String token) async { + final response = await _client.get( + Uri.parse('${baseUrl!}${AppUrls.getLoginInfo}'), + headers: { + "content-type": "application/json", + "authorization": "Bearer $token", + }, + ); + return response; } } diff --git a/lib/apis/services/auth_service.dart b/lib/apis/services/auth_service.dart index 4d85187..c51d041 100644 --- a/lib/apis/services/auth_service.dart +++ b/lib/apis/services/auth_service.dart @@ -1,23 +1,36 @@ import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; import 'package:connectivity/connectivity.dart'; +import 'package:device_info_plus/device_info_plus.dart'; +import 'package:geocoding/geocoding.dart'; +import 'package:geolocator/geolocator.dart'; import 'package:get/get.dart'; +import 'package:http/http.dart' as http; import 'package:social_media_app/apis/models/responses/login_response.dart'; +import 'package:social_media_app/apis/providers/api_provider.dart'; import 'package:social_media_app/constants/strings.dart'; +import 'package:social_media_app/helpers/permissions.dart'; import 'package:social_media_app/helpers/utils.dart'; import 'package:social_media_app/routes/route_management.dart'; class AuthService extends GetxService { static AuthService get find => Get.find(); + final _apiProvider = ApiProvider(http.Client()); + StreamSubscription? _streamSubscription; String _token = ''; String _expiresAt = ''; + String _deviceId = ''; LoginResponse _loginData = LoginResponse(); String get token => _token; + String get deviceId => _deviceId; + String get expiresAt => _expiresAt; LoginResponse get loginData => _loginData; @@ -50,6 +63,102 @@ class AuthService extends GetxService { ); } + Future getCurrentLocation() async { + var hasPerm = await AppPermissions.checkLocationPermission(); + if (!hasPerm) { + return; + } + var position = await Geolocator.getCurrentPosition(); + var loc = + await placemarkFromCoordinates(position.latitude, position.longitude); + var locationInfo = { + "street": loc.first.street, + "locality": loc.first.locality, + "city": loc.first.subAdministrativeArea, + "state": loc.first.administrativeArea, + "country": loc.first.country, + "countryCode": loc.first.isoCountryCode, + "pincode": loc.first.postalCode + }; + AppUtils.printLog("Location Info: $locationInfo"); + return locationInfo; + } + + Future getDeviceInfo() async { + var deviceInfoPlugin = DeviceInfoPlugin(); + Map deviceInfo; + if (GetPlatform.isIOS) { + var iosInfo = await deviceInfoPlugin.iosInfo; + var deviceBrand = iosInfo.utsname.machine; + var deviceModel = iosInfo.model; + var deviceSystemVersion = iosInfo.utsname.release; + var deviceId = iosInfo.identifierForVendor; + + deviceInfo = { + "deviceId": deviceId, + "model": deviceModel, + "brand": deviceBrand, + "osVersion": deviceSystemVersion + }; + } else { + var androidInfo = await deviceInfoPlugin.androidInfo; + var deviceBrand = androidInfo.brand; + var deviceModel = androidInfo.model; + var deviceSystemVersion = androidInfo.version.release; + var deviceId = androidInfo.androidId; + + deviceInfo = { + "deviceId": deviceId, + "model": deviceModel, + "brand": deviceBrand, + "osVersion": deviceSystemVersion + }; + } + + return deviceInfo; + } + + Future saveLoginInfo() async { + var deviceInfo = await getDeviceInfo(); + _deviceId = deviceInfo['deviceId']; + var locationInfo = await getCurrentLocation(); + + final body = { + 'deviceInfo': deviceInfo, + 'locationInfo': locationInfo, + 'lastActive': DateTime.now().toIso8601String(), + }; + + AppUtils.printLog("Save LoginInfo Request..."); + + try { + final response = await _apiProvider.saveLoginInfo(_token, body); + + final decodedData = jsonDecode(utf8.decode(response.bodyBytes)); + + if (response.statusCode == 200) { + AppUtils.printLog(decodedData[StringValues.message]); + } else { + AppUtils.printLog(decodedData[StringValues.message]); + } + } on SocketException { + AppUtils.printLog(StringValues.internetConnError); + AppUtils.showSnackBar(StringValues.internetConnError, StringValues.error); + } on TimeoutException { + AppUtils.printLog(StringValues.connTimedOut); + AppUtils.printLog(StringValues.connTimedOut); + AppUtils.showSnackBar(StringValues.connTimedOut, StringValues.error); + } on FormatException catch (e) { + AppUtils.printLog(StringValues.formatExcError); + AppUtils.printLog(e); + AppUtils.showSnackBar(StringValues.errorOccurred, StringValues.error); + } catch (exc) { + AppUtils.printLog(StringValues.errorOccurred); + AppUtils.printLog(exc); + AppUtils.showSnackBar(StringValues.errorOccurred, StringValues.error); + } + } + void autoLogout() async { if (_expiresAt.isNotEmpty) { var currentTimestamp = diff --git a/lib/helpers/permissions.dart b/lib/helpers/permissions.dart index a44bedc..5664418 100644 --- a/lib/helpers/permissions.dart +++ b/lib/helpers/permissions.dart @@ -1,7 +1,7 @@ import 'package:permission_handler/permission_handler.dart'; import 'package:social_media_app/helpers/utils.dart'; -abstract class Permissions { +abstract class AppPermissions { static Future checkStoragePermission() async { var status = await Permission.storage.status; if (status.isDenied) { @@ -40,4 +40,23 @@ abstract class Permissions { } return true; } + + static Future checkLocationPermission() async { + var status = await Permission.location.status; + if (status.isDenied) { + if (await Permission.location.request().isGranted) { + return true; + } + return false; + } + if (status.isRestricted) { + AppUtils.showError('Location Permission Error'); + return false; + } + if (status.isPermanentlyDenied) { + await openAppSettings(); + return false; + } + return true; + } } diff --git a/lib/helpers/utils.dart b/lib/helpers/utils.dart index f9ff9bd..08e8df4 100644 --- a/lib/helpers/utils.dart +++ b/lib/helpers/utils.dart @@ -249,7 +249,7 @@ abstract class AppUtils { static void printLog(message) { debugPrint( "======================================================================="); - debugPrint(message.toString()); + debugPrint(message.toString(), wrapWidth: 1024); debugPrint( "======================================================================="); } diff --git a/lib/main.dart b/lib/main.dart index 03fafd6..a13e740 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -38,6 +38,7 @@ Future initServices() async { var hasData = await Get.find().getProfileDetails(); if (hasData) { isLogin = true; + await Get.find().saveLoginInfo(); } } isLogin diff --git a/lib/modules/auth/controllers/login_controller.dart b/lib/modules/auth/controllers/login_controller.dart index caf5d56..138c7f4 100644 --- a/lib/modules/auth/controllers/login_controller.dart +++ b/lib/modules/auth/controllers/login_controller.dart @@ -86,6 +86,7 @@ class LoginController extends GetxController { _auth.setExpiresAt = expiresAt; _auth.autoLogout(); await _profile.fetchProfileDetails(); + await _auth.saveLoginInfo(); _clearLoginTextControllers(); diff --git a/lib/modules/home/views/widgets/notification_widget.dart b/lib/modules/home/views/widgets/notification_widget.dart index 267eaf3..6399804 100644 --- a/lib/modules/home/views/widgets/notification_widget.dart +++ b/lib/modules/home/views/widgets/notification_widget.dart @@ -35,11 +35,17 @@ class NotificationWidget extends StatelessWidget { children: [ TextSpan( text: notification.user.uname, - style: AppStyles.style14Bold, + style: AppStyles.style14Bold.copyWith( + color: + Theme.of(Get.context!).textTheme.bodyText1!.color, + ), ), TextSpan( text: " ${notification.body}", - style: AppStyles.style14Normal, + style: AppStyles.style14Normal.copyWith( + color: + Theme.of(Get.context!).textTheme.bodyText1!.color, + ), ), ], ), diff --git a/lib/modules/settings/bindings/security_settings_binding.dart b/lib/modules/settings/bindings/security_settings_binding.dart new file mode 100644 index 0000000..cf1b705 --- /dev/null +++ b/lib/modules/settings/bindings/security_settings_binding.dart @@ -0,0 +1,9 @@ +import 'package:get/get.dart'; +import 'package:social_media_app/modules/settings/controllers/security_settings_controller.dart'; + +class SecuritySettingBinding extends Bindings { + @override + void dependencies() { + Get.lazyPut(SecuritySettingsController.new); + } +} diff --git a/lib/modules/settings/controllers/security_settings_controller.dart b/lib/modules/settings/controllers/security_settings_controller.dart new file mode 100644 index 0000000..aab2936 --- /dev/null +++ b/lib/modules/settings/controllers/security_settings_controller.dart @@ -0,0 +1,88 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; + +import 'package:get/get.dart'; +import 'package:http/http.dart' as http; +import 'package:social_media_app/apis/models/responses/login_info_response.dart'; +import 'package:social_media_app/apis/providers/api_provider.dart'; +import 'package:social_media_app/apis/services/auth_service.dart'; +import 'package:social_media_app/constants/strings.dart'; +import 'package:social_media_app/helpers/utils.dart'; + +class SecuritySettingsController extends GetxController { + static SecuritySettingsController get find => Get.find(); + + final _auth = AuthService.find; + + final _apiProvider = ApiProvider(http.Client()); + + final _loginInfo = LoginInfoResponse().obs; + final _isLoading = false.obs; + + bool get isLoading => _isLoading.value; + + LoginInfoResponse? get loginInfo => _loginInfo.value; + + set setLoginInfoData(LoginInfoResponse value) { + _loginInfo.value = value; + } + + Future _getLoginInfo() async { + AppUtils.printLog("Get LoginInfo Request..."); + _isLoading.value = true; + update(); + + try { + final response = await _apiProvider.getLoginInfo(_auth.token); + + final decodedData = jsonDecode(utf8.decode(response.bodyBytes)); + + if (response.statusCode == 200) { + setLoginInfoData = LoginInfoResponse.fromJson(decodedData); + _isLoading.value = false; + update(); + } else { + _isLoading.value = false; + update(); + AppUtils.showSnackBar( + decodedData[StringValues.message], + StringValues.error, + ); + } + } on SocketException { + _isLoading.value = false; + update(); + AppUtils.printLog(StringValues.internetConnError); + AppUtils.showSnackBar(StringValues.internetConnError, StringValues.error); + } on TimeoutException { + _isLoading.value = false; + update(); + AppUtils.printLog(StringValues.connTimedOut); + AppUtils.printLog(StringValues.connTimedOut); + AppUtils.showSnackBar(StringValues.connTimedOut, StringValues.error); + } on FormatException catch (e) { + _isLoading.value = false; + update(); + AppUtils.printLog(StringValues.formatExcError); + AppUtils.printLog(e); + AppUtils.showSnackBar(StringValues.errorOccurred, StringValues.error); + } catch (exc) { + _isLoading.value = false; + update(); + AppUtils.printLog(StringValues.errorOccurred); + AppUtils.printLog(exc); + AppUtils.showSnackBar(StringValues.errorOccurred, StringValues.error); + } + } + + Future getLoginInfo() async { + await _getLoginInfo(); + } + + @override + void onInit() async { + await _getLoginInfo(); + super.onInit(); + } +} diff --git a/lib/modules/settings/views/pages/security/login_activity_view.dart b/lib/modules/settings/views/pages/security/login_activity_view.dart new file mode 100644 index 0000000..402a117 --- /dev/null +++ b/lib/modules/settings/views/pages/security/login_activity_view.dart @@ -0,0 +1,109 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:social_media_app/apis/services/auth_service.dart'; +import 'package:social_media_app/common/custom_app_bar.dart'; +import 'package:social_media_app/common/custom_list_tile.dart'; +import 'package:social_media_app/constants/colors.dart'; +import 'package:social_media_app/constants/dimens.dart'; +import 'package:social_media_app/constants/strings.dart'; +import 'package:social_media_app/constants/styles.dart'; +import 'package:social_media_app/modules/settings/controllers/security_settings_controller.dart'; + +class LoginActivityView extends StatelessWidget { + const LoginActivityView({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: SafeArea( + child: SizedBox( + width: Dimens.screenWidth, + height: Dimens.screenHeight, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const NxAppBar( + title: StringValues.security, + ), + _buildBody(), + ], + ), + ), + ), + ); + } + + Widget _buildBody() { + return Expanded( + child: Padding( + padding: Dimens.edgeInsets8, + child: GetBuilder( + builder: (logic) { + if (logic.isLoading) { + return const Center(child: CircularProgressIndicator()); + } + + return SingleChildScrollView( + physics: const BouncingScrollPhysics(), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text( + "Where You're Logged In", + style: AppStyles.style16Bold, + ), + Dimens.boxHeight8, + Column( + children: logic.loginInfo!.result!.devices! + .map( + (e) => NxListTile( + padding: Dimens.edgeInsetsOnlyBottom16, + leading: Icon( + Icons.location_on_outlined, + size: Dimens.twentyEight, + ), + title: Row( + children: [ + Text( + e['locationInfo']['city'] + + ", " + + e['locationInfo']['country'], + style: AppStyles.style16Normal, + ), + if (e['deviceInfo']['deviceId'] == + AuthService.find.deviceId) + Dimens.boxWidth8, + if (e['deviceInfo']['deviceId'] == + AuthService.find.deviceId) + Text( + "This Device", + style: AppStyles.style16Normal.copyWith( + color: ColorValues.successColor, + ), + ) + ], + ), + subtitle: Text( + e['deviceInfo']['model'], + style: AppStyles.style14Normal.copyWith( + color: Theme.of(Get.context!) + .textTheme + .subtitle1! + .color, + ), + ), + ), + ) + .toList(), + ), + Dimens.boxHeight20, + ], + ), + ); + }, + ), + ), + ); + } +} diff --git a/lib/modules/settings/views/pages/security_settings_view.dart b/lib/modules/settings/views/pages/security_settings_view.dart index 69d7106..8901f71 100644 --- a/lib/modules/settings/views/pages/security_settings_view.dart +++ b/lib/modules/settings/views/pages/security_settings_view.dart @@ -64,6 +64,7 @@ class SecuritySettingsView extends StatelessWidget { "${StringValues.login} ${StringValues.activity}", style: AppStyles.style16Normal, ), + onTap: RouteManagement.goToLoginActivityView, ), Dimens.boxHeight20, NxListTile( diff --git a/lib/routes/app_pages.dart b/lib/routes/app_pages.dart index 44bc809..caba30c 100644 --- a/lib/routes/app_pages.dart +++ b/lib/routes/app_pages.dart @@ -32,11 +32,13 @@ import 'package:social_media_app/modules/profile/views/edit_username_view.dart'; import 'package:social_media_app/modules/profile/views/followers_list_view.dart'; import 'package:social_media_app/modules/profile/views/following_list_view.dart'; import 'package:social_media_app/modules/settings/bindings/privacy_settings_binding.dart'; +import 'package:social_media_app/modules/settings/bindings/security_settings_binding.dart'; import 'package:social_media_app/modules/settings/bindings/setting_bindings.dart'; import 'package:social_media_app/modules/settings/views/pages/about_settings_view.dart'; import 'package:social_media_app/modules/settings/views/pages/account_settings_view.dart'; import 'package:social_media_app/modules/settings/views/pages/help_settings_view.dart'; import 'package:social_media_app/modules/settings/views/pages/privacy_settings_view.dart'; +import 'package:social_media_app/modules/settings/views/pages/security/login_activity_view.dart'; import 'package:social_media_app/modules/settings/views/pages/security_settings_view.dart'; import 'package:social_media_app/modules/settings/views/pages/theme_settings_view.dart'; import 'package:social_media_app/modules/settings/views/settings_view.dart'; @@ -201,6 +203,7 @@ abstract class AppPages { GetPage( name: _Routes.securitySettings, page: SecuritySettingsView.new, + binding: SecuritySettingBinding(), transitionDuration: transitionDuration, transition: Transition.downToUp, ), @@ -229,5 +232,14 @@ abstract class AppPages { transitionDuration: transitionDuration, transition: Transition.downToUp, ), + + // SECURITY SETTINGS + GetPage( + name: _Routes.loginActivitySettings, + page: LoginActivityView.new, + binding: SecuritySettingBinding(), + transitionDuration: transitionDuration, + transition: Transition.downToUp, + ), ]; } diff --git a/lib/routes/app_routes.dart b/lib/routes/app_routes.dart index f10fa99..1673514 100644 --- a/lib/routes/app_routes.dart +++ b/lib/routes/app_routes.dart @@ -16,6 +16,8 @@ abstract class AppRoutes { static const aboutSettings = _Routes.aboutSettings; static const themeSettings = _Routes.themeSettings; + static const loginActivitySettings = _Routes.loginActivitySettings; + static const forgotPassword = _Routes.forgotPassword; static const resetPassword = _Routes.resetPassword; static const changePassword = _Routes.changePassword; @@ -52,6 +54,8 @@ abstract class _Routes { static const aboutSettings = "/about_settings"; static const themeSettings = "/theme_settings"; + static const loginActivitySettings = '/login_activity'; + static const forgotPassword = '/forgot_password'; static const resetPassword = '/reset_password'; static const changePassword = '/change_password'; diff --git a/lib/routes/route_management.dart b/lib/routes/route_management.dart index 95af4e3..e3bf974 100644 --- a/lib/routes/route_management.dart +++ b/lib/routes/route_management.dart @@ -119,6 +119,11 @@ abstract class RouteManagement { Get.toNamed(AppRoutes.themeSettings); } + // SECURITY SETTINGS + static void goToLoginActivityView() { + Get.toNamed(AppRoutes.loginActivitySettings); + } + static void goToBack() { Get.back(); } diff --git a/pubspec.lock b/pubspec.lock index 1744021..2c38712 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -439,7 +439,7 @@ packages: name: flutter_native_splash url: "https://pub.dartlang.org" source: hosted - version: "2.2.0" + version: "2.2.0+1" flutter_plugin_android_lifecycle: dependency: transitive description: @@ -492,6 +492,62 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.7.3+2" + geocoding: + dependency: "direct main" + description: + name: geocoding + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.4" + geocoding_platform_interface: + dependency: transitive + description: + name: geocoding_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + geolocator: + dependency: "direct main" + description: + name: geolocator + url: "https://pub.dartlang.org" + source: hosted + version: "8.2.1" + geolocator_android: + dependency: transitive + description: + name: geolocator_android + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.8" + geolocator_apple: + dependency: transitive + description: + name: geolocator_apple + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.3" + geolocator_platform_interface: + dependency: transitive + description: + name: geolocator_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.5" + geolocator_web: + dependency: transitive + description: + name: geolocator_web + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.5" + geolocator_windows: + dependency: transitive + description: + name: geolocator_windows + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.1" get: dependency: "direct main" description: @@ -1294,7 +1350,7 @@ packages: name: win32 url: "https://pub.dartlang.org" source: hosted - version: "2.5.2" + version: "2.6.0" xdg_directories: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index a0e7a5d..92e86e1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,6 +15,8 @@ dependencies: cupertino_icons: ^1.0.4 device_info_plus: ^3.2.3 + geolocator: ^8.2.1 + geocoding: ^2.0.4 image_picker: ^0.8.5+3 image_cropper: ^2.0.2 @@ -62,7 +64,7 @@ dev_dependencies: flutter_lints: ^2.0.1 flutter_launcher_icons: ^0.9.2 - flutter_native_splash: ^2.2.0 + flutter_native_splash: ^2.2.0+1 build_runner: ^2.1.10 json_serializable: ^6.2.0