diff --git a/lib/controllers/home_controller.dart b/lib/controllers/home_controller.dart index 9923726..0f90e7e 100644 --- a/lib/controllers/home_controller.dart +++ b/lib/controllers/home_controller.dart @@ -1,17 +1,126 @@ +import 'dart:io'; +import 'package:flutter/foundation.dart' show kIsWeb; import 'package:get/get.dart'; - +import 'package:file_picker/file_picker.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:share_plus/share_plus.dart'; +import 'dart:convert'; +import '../helpers/filament_repository.dart'; import '../pages/list_view.dart'; +import '../helpers/web_download_helper_stub.dart' + if (dart.library.html) '../helpers/web_download_helper.dart'; class HomeController extends GetxController { void loadFilaments() { // Navigiere zur Listenseite Get.toNamed(ListPage.namedRoute); } - void downloadFilaments() { - // JSON Backup file herunterladen + + Future downloadFilaments() async { + try { + // JSON-Daten vom Repository holen + final jsonData = FilamentRepository.to.exportToJson(); + + if (kIsWeb) { + // Web: Download direkt im Browser + final fileName = + 'filament_backup_${DateTime.now().millisecondsSinceEpoch}.json'; + downloadJsonFileWeb(jsonData, fileName); + } else { + // Mobile/Desktop: Datei speichern und teilen + await _downloadForMobile(jsonData); + } + + Get.snackbar( + 'Erfolg', + 'Backup wurde erstellt', + snackPosition: SnackPosition.BOTTOM, + backgroundColor: Get.theme.colorScheme.primary.withAlpha(26), + colorText: Get.theme.colorScheme.primary, + duration: Duration(seconds: 3), + ); + } catch (e) { + Get.snackbar( + 'Fehler', + 'Backup konnte nicht erstellt werden: $e', + snackPosition: SnackPosition.BOTTOM, + backgroundColor: Get.theme.colorScheme.error.withAlpha(26), + colorText: Get.theme.colorScheme.error, + ); + } } - void restoreFilaments() { - // JSON Backup file wiederherstellen + Future _downloadForMobile(String jsonData) async { + // Temporäre Datei erstellen + final directory = await getTemporaryDirectory(); + final fileName = + 'filament_backup_${DateTime.now().millisecondsSinceEpoch}.json'; + final file = File('${directory.path}/$fileName'); + + await file.writeAsString(jsonData); + + // Datei über Share-Dialog teilen + await Share.shareXFiles([XFile(file.path)], text: 'Filament Backup'); + } + + Future restoreFilaments() async { + try { + // Datei auswählen + FilePickerResult? result = await FilePicker.platform.pickFiles( + type: FileType.custom, + allowedExtensions: ['json'], + ); + + if (result != null) { + String? jsonData; + + if (kIsWeb) { + // Web: Bytes direkt lesen + final bytes = result.files.first.bytes; + if (bytes != null) { + jsonData = utf8.decode(bytes); + } + } else { + // Mobile/Desktop: Datei von Pfad lesen + final path = result.files.first.path; + if (path != null) { + final file = File(path); + jsonData = await file.readAsString(); + } + } + + if (jsonData != null) { + // Daten importieren + final success = await FilamentRepository.to.importFromJson(jsonData); + + if (success) { + Get.snackbar( + 'Erfolg', + 'Backup wurde erfolgreich wiederhergestellt', + snackPosition: SnackPosition.BOTTOM, + backgroundColor: Get.theme.colorScheme.primary.withAlpha(26), + colorText: Get.theme.colorScheme.primary, + duration: Duration(seconds: 3), + ); + } else { + Get.snackbar( + 'Fehler', + 'Ungültige Backup-Datei', + snackPosition: SnackPosition.BOTTOM, + backgroundColor: Get.theme.colorScheme.error.withAlpha(26), + colorText: Get.theme.colorScheme.error, + ); + } + } + } + } catch (e) { + Get.snackbar( + 'Fehler', + 'Wiederherstellung fehlgeschlagen: $e', + snackPosition: SnackPosition.BOTTOM, + backgroundColor: Get.theme.colorScheme.error.withAlpha(26), + colorText: Get.theme.colorScheme.error, + ); + } } } diff --git a/lib/helpers/web_download_helper.dart b/lib/helpers/web_download_helper.dart new file mode 100644 index 0000000..9049435 --- /dev/null +++ b/lib/helpers/web_download_helper.dart @@ -0,0 +1,15 @@ +import 'dart:convert'; +// ignore: avoid_web_libraries_in_flutter +import 'dart:html' as html; + +void downloadJsonFileWeb(String jsonData, String fileName) { + final bytes = utf8.encode(jsonData); + final blob = html.Blob([bytes]); + final url = html.Url.createObjectUrlFromBlob(blob); + + final anchor = html.AnchorElement(href: url) + ..setAttribute('download', fileName) + ..click(); + + html.Url.revokeObjectUrl(url); +} diff --git a/lib/helpers/web_download_helper_stub.dart b/lib/helpers/web_download_helper_stub.dart new file mode 100644 index 0000000..57319c0 --- /dev/null +++ b/lib/helpers/web_download_helper_stub.dart @@ -0,0 +1,3 @@ +void downloadJsonFileWeb(String jsonData, String fileName) { + throw UnsupportedError('Web download is only supported on web platform'); +} diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index e71a16d..f6f23bf 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -6,6 +6,10 @@ #include "generated_plugin_registrant.h" +#include void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); + url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); } diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 2e1de87..f16b4c3 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + url_launcher_linux ) list(APPEND FLUTTER_FFI_PLUGIN_LIST diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index e777c67..b82a786 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,8 +5,12 @@ import FlutterMacOS import Foundation +import file_picker import path_provider_foundation +import share_plus func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) + SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) } diff --git a/pubspec.lock b/pubspec.lock index 55e1acb..aca1b51 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -73,6 +73,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.19.1" + cross_file: + dependency: transitive + description: + name: cross_file + sha256: "701dcfc06da0882883a2657c445103380e53e647060ad8d9dfb710c100996608" + url: "https://pub.dev" + source: hosted + version: "0.3.5+1" crypto: dependency: transitive description: @@ -105,6 +113,30 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.5" + file: + dependency: transitive + description: + name: file + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + url: "https://pub.dev" + source: hosted + version: "7.0.1" + file_picker: + dependency: "direct main" + description: + name: file_picker + sha256: ab13ae8ef5580a411c458d6207b6774a6c237d77ac37011b13994879f68a8810 + url: "https://pub.dev" + source: hosted + version: "8.3.7" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + url: "https://pub.dev" + source: hosted + version: "1.1.1" flutter: dependency: "direct main" description: flutter @@ -126,11 +158,24 @@ packages: url: "https://pub.dev" source: hosted version: "6.0.0" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + sha256: ee8068e0e1cd16c4a82714119918efdeed33b3ba7772c54b5d094ab53f9b7fd1 + url: "https://pub.dev" + source: hosted + version: "2.0.33" flutter_test: dependency: "direct dev" description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" get: dependency: "direct main" description: @@ -227,6 +272,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.17.0" + mime: + dependency: transitive + description: + name: mime + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" + url: "https://pub.dev" + source: hosted + version: "2.0.0" path: dependency: transitive description: @@ -236,7 +289,7 @@ packages: source: hosted version: "1.9.1" path_provider: - dependency: transitive + dependency: "direct main" description: name: path_provider sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" @@ -315,6 +368,22 @@ packages: url: "https://pub.dev" source: hosted version: "6.0.3" + share_plus: + dependency: "direct main" + description: + name: share_plus + sha256: fce43200aa03ea87b91ce4c3ac79f0cecd52e2a7a56c7a4185023c271fbfa6da + url: "https://pub.dev" + source: hosted + version: "10.1.4" + share_plus_platform_interface: + dependency: transitive + description: + name: share_plus_platform_interface + sha256: cc012a23fc2d479854e6c80150696c4a5f5bb62cb89af4de1c505cf78d0a5d0b + url: "https://pub.dev" + source: hosted + version: "5.0.2" sky_engine: dependency: transitive description: flutter @@ -376,6 +445,46 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.0" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + sha256: d5e14138b3bc193a0f63c10a53c94b91d399df0512b1f29b94a043db7482384a + url: "https://pub.dev" + source: hosted + version: "3.2.2" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + sha256: d0412fcf4c6b31ecfdb7762359b7206ffba3bbffd396c6d9f9c4616ece476c1f + url: "https://pub.dev" + source: hosted + version: "2.4.2" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + sha256: "712c70ab1b99744ff066053cbe3e80c73332b38d46e5e945c98689b2e66fc15f" + url: "https://pub.dev" + source: hosted + version: "3.1.5" + uuid: + dependency: transitive + description: + name: uuid + sha256: a11b666489b1954e01d992f3d601b1804a33937b5a8fe677bd26b8a9f96f96e8 + url: "https://pub.dev" + source: hosted + version: "4.5.2" vector_math: dependency: transitive description: @@ -400,6 +509,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" + win32: + dependency: transitive + description: + name: win32 + sha256: d7cb55e04cd34096cd3a79b3330245f54cb96a370a1c27adb3c84b917de8b08e + url: "https://pub.dev" + source: hosted + version: "5.15.0" xdg_directories: dependency: transitive description: @@ -426,4 +543,4 @@ packages: version: "3.1.3" sdks: dart: ">=3.10.4 <4.0.0" - flutter: ">=3.35.0" + flutter: ">=3.38.0" diff --git a/pubspec.yaml b/pubspec.yaml index 11b4d0c..937bf09 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,6 +16,9 @@ dependencies: get: ^4.7.3 get_storage: ^2.1.1 intl: ^0.20.2 + file_picker: ^8.1.6 + path_provider: ^2.1.5 + share_plus: ^10.1.4 dev_dependencies: flutter_lints: ^6.0.0 diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 8b6d468..c3384ec 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,6 +6,12 @@ #include "generated_plugin_registrant.h" +#include +#include void RegisterPlugins(flutter::PluginRegistry* registry) { + SharePlusWindowsPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi")); + UrlLauncherWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("UrlLauncherWindows")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index b93c4c3..01d3836 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,6 +3,8 @@ # list(APPEND FLUTTER_PLUGIN_LIST + share_plus + url_launcher_windows ) list(APPEND FLUTTER_FFI_PLUGIN_LIST