From 6855d7bc735425e7c14f60df09e284f6aa02971f Mon Sep 17 00:00:00 2001 From: Aritra Date: Mon, 24 Jun 2024 21:48:52 +0530 Subject: [PATCH] feat: Custom KML generation and KMl rendering done. --- android/app/src/main/AndroidManifest.xml | 2 + lib/components/apimanager_block.dart | 61 +++++++++- lib/components/navisland.dart | 4 +- lib/main.dart | 4 +- lib/screens/dashboard.dart | 19 +-- lib/screens/kml_builder.dart | 26 +++- lib/screens/settings.dart | 11 +- lib/screens/test.dart | 31 +++++ lib/tabs/sshcommands_tab.dart | 5 +- lib/utils/api_manager.dart | 80 ++++++++----- lib/utils/autocomplete_controller.dart | 3 +- lib/utils/kmlgenerator.dart | 112 +++++++++++++++++- lib/utils/lg_connection.dart | 62 ++++++++-- macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.lock | 24 ++++ pubspec.yaml | 1 + 16 files changed, 386 insertions(+), 61 deletions(-) create mode 100644 lib/screens/test.dart diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 06129cc..6ead5ca 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -3,6 +3,8 @@ + + diff --git a/lib/components/apimanager_block.dart b/lib/components/apimanager_block.dart index 1a751d5..0d86329 100644 --- a/lib/components/apimanager_block.dart +++ b/lib/components/apimanager_block.dart @@ -1,7 +1,11 @@ import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:super_liquid_galaxy_controller/utils/api_manager.dart'; +import '../generated/assets.dart'; +import 'custom_dialog.dart'; import 'galaxytextfield.dart'; +import 'package:lottie/lottie.dart'; +import 'package:get/get.dart'; import 'package:shared_preferences/shared_preferences.dart'; class ApiManagerBlock extends StatefulWidget { @@ -31,7 +35,16 @@ class ApiManagerBlock extends StatefulWidget { State createState() => _ApiManagerBlockState(); } + + class _ApiManagerBlockState extends State { + + @override + void initState() { + loadSetValues(); + super.initState(); + } + @override Widget build(BuildContext context) { return Padding( @@ -179,10 +192,56 @@ class _ApiManagerBlockState extends State { ))) ])))); } + void loadSetValues() async { + + try { + SharedPreferences preferences = await SharedPreferences.getInstance(); + var key = preferences.getString("places_apikey") ?? ""; + key = key.trim(); + widget.keyController.text = key; + } + catch(e) + { + print(e); + } + + } + void saveApiKey(String prefKey) async { SharedPreferences preferences = await SharedPreferences.getInstance(); await preferences.setString(prefKey, widget.keyController.text); - ApiManager.instance; + ApiManager apiClient = Get.find(); + await apiClient.testApiKey(); + var dialog = apiClient.isConnected.value + ? CustomDialog( + content: Text("All Api Services now available!"), + title: Text("API KEY VALIDATED",style: TextStyle(color: Colors.green.shade500,fontSize: 25.0,fontWeight: FontWeight.bold),), + firstColor: Colors.green, + secondColor: Colors.white, + headerIcon: Lottie.asset(Assets.lottieConnected, + decoder: customDecoder, repeat: false,width: 200.0,height: 200.0)) + : CustomDialog( + content: Text("Api services unavailable"), + title: Text("API KEY INVALID",style: TextStyle(color: Colors.red.shade500,fontSize: 25.0,fontWeight: FontWeight.bold),), + firstColor: Colors.red.shade400, + secondColor: Colors.white, + headerIcon: Lottie.asset(Assets.lottieConnectionfailed, + decoder: customDecoder, repeat: false,width: 200.0,height: 200.0)); + showDialog( + context: context, + builder: (BuildContext context) { + return dialog; + }, + ); + + } + + Future customDecoder(List bytes) { + return LottieComposition.decodeZip(bytes, filePicker: (files) { + return files.firstWhere( + (f) => f.name.startsWith('animations/') && f.name.endsWith('.json'), + ); + }); } } diff --git a/lib/components/navisland.dart b/lib/components/navisland.dart index 2c5ebb7..ef8e06f 100644 --- a/lib/components/navisland.dart +++ b/lib/components/navisland.dart @@ -144,9 +144,9 @@ class NavIsland extends StatelessWidget { color: Colors.transparent, child: InkWell( onTap: () async { - var manager = ApiManager.instance; + /*var manager = ApiManager.instance; var response = await manager.getAutoCompleteResponse("How"); - print(response.data); + print(response.data);*/ }, borderRadius: BorderRadius.circular(15), child: Container( diff --git a/lib/main.dart b/lib/main.dart index e647c75..0bbf04a 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,6 +3,7 @@ import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:google_maps_flutter_android/google_maps_flutter_android.dart'; import 'package:super_liquid_galaxy_controller/screens/splashscreen.dart'; +import 'package:super_liquid_galaxy_controller/utils/api_manager.dart'; import 'package:super_liquid_galaxy_controller/utils/lg_connection.dart'; AndroidMapRenderer mapRenderer = AndroidMapRenderer.platformDefault; @@ -17,7 +18,8 @@ void main() { mapRenderer = await mapsImplementation .initializeWithRenderer(AndroidMapRenderer.latest); }*/ - Get.put(LGConnection()); + Get.lazyPut(() => LGConnection(),fenix: true); + Get.lazyPut(() => ApiManager(),fenix: true); LGConnection client = Get.find(); client.connectToLG(); SystemChrome.setPreferredOrientations([ diff --git a/lib/screens/dashboard.dart b/lib/screens/dashboard.dart index 0c900a7..dac247d 100644 --- a/lib/screens/dashboard.dart +++ b/lib/screens/dashboard.dart @@ -7,6 +7,7 @@ import 'package:super_liquid_galaxy_controller/components/navisland.dart'; import 'package:super_liquid_galaxy_controller/components/planet_selector.dart'; import 'package:super_liquid_galaxy_controller/generated/assets.dart'; import 'package:super_liquid_galaxy_controller/screens/settings.dart'; +import 'package:super_liquid_galaxy_controller/utils/api_manager.dart'; import 'package:super_liquid_galaxy_controller/utils/lg_connection.dart'; import 'package:lottie/lottie.dart'; @@ -27,12 +28,14 @@ class _DashBoardState extends State { late double screenHeight; late double screenWidth; late LGConnection connectionClient; + late ApiManager apiClient; @override void initState() { super.initState(); log("ui", "dashboard-built"); initializeLGClient(); + initializeApiClient(); } @@ -107,7 +110,6 @@ class _DashBoardState extends State { onTap: () async { log("gesture", "settings tapped"); await Get.to(() => Settings()); - _reload(); }, ), ) @@ -145,7 +147,7 @@ class _DashBoardState extends State { children: [ Obx(() { return ConnectionFlag( - status: connectionClient.isConnected.value, + status: apiClient.isConnected.value, backgroundColor: Colors.white.withOpacity( 0.0), selectedText: 'API CONNECTED', @@ -195,9 +197,10 @@ class _DashBoardState extends State { void initializeLGClient() async { connectionClient = Get.find(); await connectionClient.connectToLG(); - setState(() { - - }); + } + void initializeApiClient() async { + apiClient = Get.find(); + await apiClient.testApiKey(); } void _reload() { @@ -207,7 +210,7 @@ class _DashBoardState extends State { //test - Future testDialog() async { + /*Future testDialog() async { var customDialog = CustomDialog( content: Text("SSH operations are now possible."), title: Text("Connection established",style: TextStyle(color: Colors.green.shade500,fontSize: 25.0,fontWeight: FontWeight.bold),), @@ -229,7 +232,7 @@ class _DashBoardState extends State { return customDialog1; }, ); - } + }*/ Future customDecoder(List bytes) { return LottieComposition.decodeZip(bytes, filePicker: (files) { @@ -239,4 +242,6 @@ class _DashBoardState extends State { }); } + + } diff --git a/lib/screens/kml_builder.dart b/lib/screens/kml_builder.dart index de0814e..ff22797 100644 --- a/lib/screens/kml_builder.dart +++ b/lib/screens/kml_builder.dart @@ -1,15 +1,20 @@ +import 'dart:io'; import 'dart:ui'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_map/flutter_map.dart'; import 'package:latlong2/latlong.dart'; +import 'package:get/get.dart'; import 'package:super_liquid_galaxy_controller/components/galaxy_button.dart'; import 'package:super_liquid_galaxy_controller/components/glassbox.dart'; import 'package:super_liquid_galaxy_controller/components/kml_elements/linestring.dart'; import 'package:super_liquid_galaxy_controller/components/kml_elements/placemark.dart'; import 'package:super_liquid_galaxy_controller/components/kml_elements/polygon.dart'; +import 'package:super_liquid_galaxy_controller/screens/test.dart'; import 'package:super_liquid_galaxy_controller/utils/galaxy_colors.dart'; +import 'package:super_liquid_galaxy_controller/utils/kmlgenerator.dart'; +import 'package:super_liquid_galaxy_controller/utils/lg_connection.dart'; import '../data_class/kml_element.dart'; import '../generated/assets.dart'; @@ -25,6 +30,7 @@ class _KmlUploaderState extends State { late double screenHeight; late double screenWidth; late DataRetrieverHandler dataController; + late LGConnection sshClient; int elementIndex = 0; List labels = ['Placemark', 'Polyline', 'Polygon']; @@ -35,6 +41,12 @@ class _KmlUploaderState extends State { ]; List kmlList = []; + @override + void initState() { + sshClient = Get.find(); + super.initState(); + } + @override Widget build(BuildContext context) { screenHeight = MediaQuery.of(context).size.height; @@ -219,7 +231,16 @@ class _KmlUploaderState extends State { actionText: "VISUALIZE IN LG", icon: Icons.smart_screen, isLeading: true, - onTap: () {}, + onTap: () async { + print("tapped"); + await sshClient.connectToLG(); + await sshClient.clearKml(); + File? file = await sshClient.makeFile('custom_kml', KMLGenerator.generateKml('slave_1', kmlList)); + print("made successfully"); + await sshClient.kmlFileUpload(context, file!, 'custom_kml'); + print("uploaded successfully"); + await sshClient.runKml('custom_kml'); + }, backgroundColor: GalaxyColors.blue.withOpacity(0.4), ), GalaxyButton( @@ -228,7 +249,8 @@ class _KmlUploaderState extends State { actionText: "VISUALIZE IN MAP", icon: Icons.map, isLeading: true, - onTap: () {}, + onTap: () { + }, backgroundColor: GalaxyColors.blue.withOpacity(0.4), ) ], diff --git a/lib/screens/settings.dart b/lib/screens/settings.dart index ce882d6..aebec61 100644 --- a/lib/screens/settings.dart +++ b/lib/screens/settings.dart @@ -7,6 +7,7 @@ import 'package:super_liquid_galaxy_controller/generated/assets.dart'; import 'package:super_liquid_galaxy_controller/tabs/apikey_tab.dart'; import 'package:super_liquid_galaxy_controller/tabs/connection_tab.dart'; import 'package:super_liquid_galaxy_controller/tabs/sshcommands_tab.dart'; +import 'package:super_liquid_galaxy_controller/utils/api_manager.dart'; import 'package:super_liquid_galaxy_controller/utils/lg_connection.dart'; //ignore_for_file: prefer_const_constructors @@ -22,11 +23,13 @@ class Settings extends StatefulWidget { class _SettingsState extends State { late double screenHeight; late double screenWidth; - late LGConnection client; + late LGConnection connectionClient; + late ApiManager apiClient; @override void initState() { - client = Get.find(); + connectionClient = Get.find(); + apiClient = Get.find(); super.initState(); } @@ -89,7 +92,7 @@ class _SettingsState extends State { ]), child: ImageIcon( AssetImage(Assets.iconsSshIndicator), - color: client.isConnected.value ? Colors.green : Colors.red, + color: connectionClient.isConnected.value ? Colors.green : Colors.red, size: screenHeight *0.06, )), );} @@ -107,7 +110,7 @@ class _SettingsState extends State { ]), child: ImageIcon( AssetImage(Assets.iconsApiIndicator), - color: client.isConnected.value ? Colors.green : Colors.red, + color: apiClient.isConnected.value ? Colors.green : Colors.red, size: screenHeight *0.07, )), );} diff --git a/lib/screens/test.dart b/lib/screens/test.dart new file mode 100644 index 0000000..088def1 --- /dev/null +++ b/lib/screens/test.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; + +class TestScreen extends StatefulWidget { + const TestScreen({super.key, required this.kml}); + + final String kml; + + @override + State createState() => _TestScreenState(); +} + +class _TestScreenState extends State { + TextEditingController controller = TextEditingController(); + + @override + void initState() { + controller.text = widget.kml; + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Center( + child: TextField( + controller: controller, + ), + ), + ); + } +} diff --git a/lib/tabs/sshcommands_tab.dart b/lib/tabs/sshcommands_tab.dart index 609c250..be373ff 100644 --- a/lib/tabs/sshcommands_tab.dart +++ b/lib/tabs/sshcommands_tab.dart @@ -180,7 +180,10 @@ class _SSHCommandsTabState extends State with AutomaticKeepAlive ), TextButton( child: const Text("CONTINUE", style: TextStyle(color: Colors.red),), - onPressed: () => action(), + onPressed: () async { + await action(); + Get.back(); + }, ), ], ), diff --git a/lib/utils/api_manager.dart b/lib/utils/api_manager.dart index 1c94978..bfeba9c 100644 --- a/lib/utils/api_manager.dart +++ b/lib/utils/api_manager.dart @@ -1,13 +1,14 @@ -import 'package:flutter/material.dart'; -import 'package:shared_preferences/shared_preferences.dart'; import 'package:dio/dio.dart'; +import 'package:flutter/material.dart'; import 'package:get/get.dart' as getx; +import 'package:shared_preferences/shared_preferences.dart'; import 'package:super_liquid_galaxy_controller/data_class/api_error.dart'; -class ApiManager { +class ApiManager extends getx.GetxController { late String _placesApiKey; late Dio _apiClient; + var isConnected = false.obs; //base-urls static const geoApifyV2BaseUrl = "https://api.geoapify.com/v2"; @@ -17,56 +18,49 @@ class ApiManager { static const autocompleteEndPoint = "/geocode/autocomplete"; static const placesEndPoint = "/places"; - - ApiManager._privateConstructor() { + /*ApiManager._privateConstructor() { print("instance created"); _apiClient = Dio(); } static final ApiManager _instance = ApiManager._privateConstructor(); - static ApiManager get instance => _instance; + static ApiManager get instance => _instance;*/ //parameter decides base-url version - connectApi(int version) async { + _connectApi(int version) async { SharedPreferences preferences = await SharedPreferences.getInstance(); _placesApiKey = preferences.getString("places_apikey") ?? ""; _placesApiKey = _placesApiKey.trim(); print({"places_api": _placesApiKey}); final options = BaseOptions( - baseUrl:(version == 1)?geoApifyV1BaseUrl:geoApifyV2BaseUrl, - connectTimeout: const Duration(seconds: 5), - receiveTimeout: const Duration(seconds: 10), - validateStatus: (status){ - if(status!=null) - return status < 500; - else - return false; - } - ); + baseUrl: (version == 1) ? geoApifyV1BaseUrl : geoApifyV2BaseUrl, + connectTimeout: const Duration(seconds: 5), + receiveTimeout: const Duration(seconds: 7), + validateStatus: (status) { + if (status != null) { + return status < 500; + } else { + return false; + } + }); _apiClient = Dio(options); - return {"places_api": _placesApiKey}; + print({"places_api": _placesApiKey}); + // await _testApiKey(); + return isConnected.value; } - Future getAutoCompleteResponse(String text) async - { - await connectApi(1); - var response = await _apiClient.get( - autocompleteEndPoint, - queryParameters: { - 'text':text, - 'apiKey':_placesApiKey.trim() - } - - ); - if(response.statusCode != 200) { + Future getAutoCompleteResponse(String text) async { + await _connectApi(1); + var response = await _apiClient.get(autocompleteEndPoint, + queryParameters: {'text': text, 'apiKey': _placesApiKey.trim()}); + if (response.statusCode != 200) { handleError(response); } return response; } void handleError(Response response) { - var errorResponse = ApiErrorResponse.fromJson(response.data); print('error : ${response.statusMessage}'); if (!getx.Get.isSnackbarOpen) { @@ -80,4 +74,28 @@ class ApiManager { } } + testApiKey() async { + await _connectApi(1); + try { + var response = await _apiClient.get( + autocompleteEndPoint, + queryParameters: {'text': "test", + 'apiKey': _placesApiKey.trim() + }); + + if (response.statusCode == 200) { + isConnected.value = true; + } + else { + var error = ApiErrorResponse.fromJson(response.data); + isConnected.value = false; + print('${error.error} - ${error.message}'); + } + } + catch(e) + { + print(e); + isConnected.value = false; + } + } } diff --git a/lib/utils/autocomplete_controller.dart b/lib/utils/autocomplete_controller.dart index 7cb3a0c..f7cd919 100644 --- a/lib/utils/autocomplete_controller.dart +++ b/lib/utils/autocomplete_controller.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:super_liquid_galaxy_controller/data_class/PlaceSuggestionResponse.dart'; import 'package:super_liquid_galaxy_controller/utils/api_manager.dart'; +import 'package:get/get.dart'; const Duration debounceDuration = Duration(milliseconds: 250); @@ -30,7 +31,7 @@ class AutocompleteController { late final Iterable options; try { - var apiClient = ApiManager.instance; + ApiManager apiClient = Get.find(); var response = await apiClient.getAutoCompleteResponse(query); if(response.statusCode != 200) { return null; diff --git a/lib/utils/kmlgenerator.dart b/lib/utils/kmlgenerator.dart index 857c69a..de01437 100644 --- a/lib/utils/kmlgenerator.dart +++ b/lib/utils/kmlgenerator.dart @@ -1,5 +1,7 @@ -class KMLGenerator{ +import 'package:super_liquid_galaxy_controller/data_class/coordinate.dart'; +import 'package:super_liquid_galaxy_controller/data_class/kml_element.dart'; +class KMLGenerator { static String generateBlank(String id) { return ''' @@ -9,4 +11,110 @@ class KMLGenerator{ '''; } -} \ No newline at end of file + + static getPlacemark(Placemark placeMark) => ''' + ${placeMark.label} + + ${placeMark.description} + + + ${placeMark.coordinate.latitude},${placeMark.coordinate.longitude} + + '''; + + + static getCoordinateList(List list) + { + var coordinates = ''; + for(final coordinate in list) + { + coordinates += '${coordinate.latitude},${coordinate.longitude},0 '; + } + return '''${coordinates}'''; + } + + static getLineString(LineString placeMark) => ''' + ${placeMark.label} + + ${placeMark.description} + + #lineStyle + + 1 + absolute + ${getCoordinateList(placeMark.coordinates)} + + '''; + + static getLinearRing(Polygon placeMark) => ''' + ${placeMark.label} + + ${placeMark.description} + + #polyStyle + + 1 + relativeToGround + + + ${getCoordinateList(placeMark.coordinates)} + + + + '''; + + static String generateKml(String id, List list) { + var visList = ''; + for (final item in list) { + switch (item.index) { + case 0: + { + Placemark element = item.elementData; + visList+=getPlacemark(element); + } + case 1: + { + LineString element = item.elementData; + visList+=getLineString(element); + } + case 2: + { + Polygon element = item.elementData; + visList+=getLinearRing(element); + } + default: + { + Placemark element = item.elementData; + visList+=getPlacemark(element); + } + } + } + + return ''' + + + + + + ${visList} + + + '''; + } +} diff --git a/lib/utils/lg_connection.dart b/lib/utils/lg_connection.dart index 1486608..3067e0a 100644 --- a/lib/utils/lg_connection.dart +++ b/lib/utils/lg_connection.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:dartssh2/dartssh2.dart'; import 'package:get/get.dart'; +import 'package:path_provider/path_provider.dart'; import 'package:super_liquid_galaxy_controller/data_class/map_position.dart'; import 'package:super_liquid_galaxy_controller/utils/kmlgenerator.dart'; @@ -120,7 +121,7 @@ class LGConnection extends GetxController { try { if(_client==null) return; - for (var i = 2; i <= int.parse(_numberOfRigs); i++) { + for (var i = 1; i <= int.parse(_numberOfRigs); i++) { String search = '##LG_PHPIFACE##kml\\/slave_$i.kml<\\/href>onInterval<\\/refreshMode>2<\\/refreshInterval>'; String replace = '##LG_PHPIFACE##kml\\/slave_$i.kml<\\/href>'; @@ -154,7 +155,7 @@ class LGConnection extends GetxController { setRefresh() async { try { - for (var i = 2; i <= int.parse(_numberOfRigs); i++) { + for (var i = 1; i <= int.parse(_numberOfRigs); i++) { if(_client==null) { return; } @@ -196,19 +197,19 @@ class LGConnection extends GetxController { runKml(String kmlName) async { try { if(_client==null) - return; + return false; print('running kml'); await _client!.run('echo "\nhttp://lg1:81/$kmlName.kml" > /var/www/html/kmls.txt'); + print('kml ran'); + return true; } catch (error) { - - print('Error occurred while running'); + print('Error occurred while running:$error'); + return false; + // await connectToLG(); + // await runKml(kmlName); } } - void test() { - print("ran"); - } - Future shutdown() async { @@ -278,6 +279,34 @@ fi } } + kmlFileUpload(context, File inputFile, String kmlName) async { + try { + if(_client == null) + return; + + //bool uploading = true; + final sftp = await _client?.sftp(); + final file = await sftp?.open('/var/www/html/$kmlName.kml', + mode: SftpFileOpenMode.create | + SftpFileOpenMode.truncate | + SftpFileOpenMode.write); + var fileSize = await inputFile.length(); + file?.write(inputFile.openRead().cast(), + onProgress: (progress) { + print(progress / fileSize); + if (fileSize == progress) { + //uploading = false; + } + }); + if (file == null) { + return; + } + } catch (error) { + print(error); + } + } + + Future moveTo(MapPosition position) async { if(_client==null) { @@ -297,4 +326,19 @@ fi } } + makeFile(String filename, String content) async { + try{ + var localPath = await getApplicationDocumentsDirectory(); + print(localPath.path.toString()); + File localFile = File('${localPath.path}/$filename.kml'); + await localFile.writeAsString(content); + print("file created"); + return localFile; + } + catch(e) + { + print("error : $e"); + } + } + } \ No newline at end of file diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 1af9462..fa9e851 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -6,11 +6,13 @@ import FlutterMacOS import Foundation import geolocator_apple +import path_provider_foundation import shared_preferences_foundation import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin")) + PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) } diff --git a/pubspec.lock b/pubspec.lock index 5327e9b..ba89391 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -480,6 +480,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.1" + path_provider: + dependency: "direct dev" + description: + name: path_provider + sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161 + url: "https://pub.dev" + source: hosted + version: "2.1.3" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: bca87b0165ffd7cdb9cad8edd22d18d2201e886d9a9f19b4fb3452ea7df3a72a + url: "https://pub.dev" + source: hosted + version: "2.2.6" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16 + url: "https://pub.dev" + source: hosted + version: "2.4.0" path_provider_linux: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index f1a2ab4..de8d915 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -59,6 +59,7 @@ dev_dependencies: url_launcher: ^6.3.0 flutter_map: ^7.0.1 dio: ^5.4.3+1 + path_provider: ^2.1.3 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec