gsd_utilities 0.0.42 copy "gsd_utilities: ^0.0.42" to clipboard
gsd_utilities: ^0.0.42 copied to clipboard

PlatformWindows

Flutter utilities for config management, local storage, and file uploads to DOCUframe.

GSD-Utilities #

Dieses Paket bietet umfassende Utility-Funktionen für Flutter-Anwendungen mit plattformübergreifender Konfigurationsverwaltung, LocalStorage mit Cross-Tab-Events, fortschrittlicher Datei-Upload-Funktionalität und DOCUframe-Integration. Es ermöglicht die strukturierte Verwaltung von Anwendungsdaten sowohl für Web- als auch für Mobile/Desktop-Plattformen.

👉 Online-Demo ansehen

Features #

  • Plattformübergreifende Konfigurationsverwaltung: Automatische Plattformauswahl zwischen Web und Mobile/Desktop
  • Sichere Datenpersistierung: Web mit verschlüsseltem LocalStorage, Mobile/Desktop mit FlutterSecureStorage
  • LocalStorage mit Events: Cross-Tab-Kommunikation über JavaScript Interop
  • URI-Management: Browser-URI-Manipulation
  • Web-Benachrichtigungen: Browser-Notification-API mit Berechtigungsmanagement und Browser-Erkennung
  • File Upload System: Real-time Fortschrittsverfolgung mit Byte-Level-Genauigkeit
  • Bildverarbeitung: Automatische Bilderkennung und Größenanpassung
  • Batch-Upload: Mehrere Dateien gleichzeitig hochladen
  • DOCUframe Integration: Enterprise-Funktionen für DOCUframe-Systeme
  • Event-basierte Architektur: Benachrichtigungen bei Datenänderungen
  • TypeScript-kompatibel: JavaScript Interop für Web-Plattformen
  • Memory-effizient: Automatisches Cleanup und Ressourcenverwaltung

Installation #

Fügen Sie das Paket in Ihrer pubspec.yaml hinzu:

dependencies:
  gsd_utilities: [version]

Führen Sie anschließend flutter pub get aus, um das Paket zu installieren.

Nutzung #

Konfigurationsmanagement #

Erstellen Sie eine Konfigurationsklasse und verwalten Sie sie automatisch:

import 'package:gsd_utilities/gsd_utilities.dart';

// Eigene Konfigurationsklasse erstellen
class AppConfig extends GSDBaseConfig {
  String? serverUrl;
  int? timeout;
  bool? debugMode;

  AppConfig({this.serverUrl, this.timeout, this.debugMode});

  @override
  String toJson() {
    return '{"serverUrl": ${serverUrl != null ? '"$serverUrl"' : 'null'}, '
           '"timeout": $timeout, "debugMode": $debugMode}';
  }

  @override
  void loadFromJson(String jsonString) {
    final data = jsonDecode(jsonString);
    serverUrl = data['serverUrl'];
    timeout = data['timeout'];
    debugMode = data['debugMode'];
  }

  @override
  GSDBaseConfig createInstance() => AppConfig();
}

// Konfiguration laden und verwalten
final configManager = GSDConfigManager(key: "app_config");
final configResult = await configManager.loadConfig(AppConfig());

if (configResult.isSuccess && configResult.config != null) {
  print('Konfiguration geladen: ${configResult.config!.serverUrl}');
} else {
  print('Konfiguration konnte nicht geladen werden: ${configResult.log}');
}

ConfigStore (feldbasiert, prozesssicher) #

Der GSDConfigStore ist der Nachfolger des GSDConfigManager für Apps, die mehrfach parallel auf einem Gerät laufen (mehrere Fenster, Browser-Tabs, Notification-Prozess) und sich eine Konfiguration teilen. Statt eines JSON-Blocks ist jedes Feld ein eigener Key; zwei Prozesse, die verschiedene Felder ändern, überschreiben sich nicht mehr. Jedes Feld deklariert, ob es im sicheren Speicher (secure: true) oder im öffentlichen Speicher liegt. Der alte GSDConfigManager bleibt unverändert erhalten.

import 'package:gsd_utilities/gsd_utilities.dart';

// Ein Eintrag einer Collection, z.B. ein Account
class AccountConfig extends GSDConfigItem {
  AccountConfig(super.id) {
    declareAll([name, username, password, sessionId]);
  }

  final name = GSDConfigValue<String>('name', defaultValue: '');
  final username = GSDConfigValue<String>('username', defaultValue: '');
  final password = GSDConfigOptionalValue<String>('password', secure: true);
  final sessionId = GSDConfigOptionalValue<String>('sessionId', secure: true);
}

// Die Konfiguration: Felder und Collections im Konstruktor registrieren
class AppConfig extends GSDConfig {
  AppConfig() {
    declareAll([autoLogin, selectedTheme, lastAccount]);
    collection(accounts);
  }

  final autoLogin = GSDConfigValue<bool>('autoLogin', defaultValue: false);
  final selectedTheme = GSDConfigValue<String>('selectedTheme', defaultValue: 'system');
  final lastAccount = GSDConfigOptionalValue<String>('lastAccount');
  final accounts = GSDConfigCollection<AccountConfig>('accounts', AccountConfig.new);
}

// Auf dem Desktop das SQLite-Backend für SharedPreferencesAsync registrieren,
// unter Windows auch das für FlutterSecureStorage, bevor die App ihr erstes
// SharedPreferencesAsync() erzeugt oder ein Secret liest (am Anfang von
// main). forPlatform macht das auch, erreicht aber nur spätere Zugriffe.
if (!kIsWeb) {
  switch (defaultTargetPlatform) {
    case TargetPlatform.windows:
      await GSDSqliteSharedPreferencesPlatform.register();
      await GSDSqliteSecureStoragePlatform.register();
    case TargetPlatform.macOS:
    case TargetPlatform.linux:
      await GSDSqliteSharedPreferencesPlatform.register();
    default:
      break;
  }
}

// Store für die Plattform bauen (SharedPreferences + Secure Storage; auf dem
// Desktop SQLite-Backend und Dateilock im App-Support-Verzeichnis).
final store = await GSDConfigStoreFactory.forPlatform();

// Laden. Beim ersten Mal werden die alten Blöcke des GSDConfigManager
// (Key "config" / "config_secure"; bis 0.0.34 lag der öffentliche Block im
// sicheren Speicher unter "config") über den Hook übernommen. Ist ein alter
// Block gerade nicht lesbar (iOS vor dem ersten Entsperren), wird die
// Migration verschoben (result.migrationDeferred) und beim nächsten load
// nachgeholt; bis dahin angelegte Collection-Einträge bleiben erhalten.
final config = AppConfig();
final result = await store.load(config, migrate: (config, legacy) async {
  final json = legacy.publicJson ?? {};
  await config.autoLogin.set(json['autoLogin'] ?? false);
  for (final entry in (json['accounts'] as List? ?? [])) {
    final account = AccountConfig(entry['uuid']);
    await account.name.set(entry['name'] ?? '');
    await account.password.set(legacy.secureJson?[entry['uuid']]?['password']);
    await config.accounts.add(account);
  }
});
print(result.log);

// Schreiben: jeder set() schreibt genau einen Key unter dem Lock
await config.selectedTheme.set('dark');
await config.accounts.byId('a1')?.sessionId.set('abc');

// Mehrere Felder unter einem Lock
await store.batch(config, () async {
  await config.autoLogin.set(true);
  await config.lastAccount.set('a1');
});

// Anderer Prozess hat geschrieben? Frisch lesen:
await store.reload(config);

Wichtige Regeln:

  • Felder registrieren: declare/declareAll und collection müssen im Konstruktor aufgerufen werden, sonst werden die Felder weder geladen noch geschrieben.
  • GSDConfigValue<T> hat immer einen Default, GSDConfigOptionalValue<T> liefert null, solange nichts gespeichert ist. set(null) löscht den Key.
  • Secrets werden nur geschrieben, wenn die App sie explizit setzt. Ein leeres Secret löscht den Key. Kann der sichere Speicher nicht gelesen werden (z.B. iOS vor dem ersten Entsperren), steht der Fehler in result.secureError und feld.loadError; es wird nie ein leeres Secret zurückgeschrieben.
  • Ids von Items dürfen nur A-Z a-z 0-9 _ - enthalten (UUIDs passen); Feldnamen nur A-Z a-z 0-9 _.
  • Keys: <namespace>.<feld>, <namespace>.<collection> (Index der Ids) und <namespace>.<collection>.<id>.<feld>.
  • Testen ohne Plugins: GSDConfigStore(values: GSDConfigMemoryValueStore(), secrets: GSDConfigMemorySecretStore(), lock: GSDConfigInProcessLock()).

LocalStorage mit Cross-Tab-Events #

Überwachen Sie LocalStorage-Änderungen zwischen Browser-Tabs:

// LocalStorage Manager erstellen
final localStorageManager = GSDLocalStorageManager();

// Storage-Events überwachen (nur Web)
localStorageManager.storageChanged.subscribe((GSDWebLocalStorageEventArgs args) async {
  print('Storage geändert von anderem Tab:');
  print('Key: ${args.key}, Neuer Wert: ${args.value}');
});

// Daten schreiben und lesen
localStorageManager.writeData('user_settings', '{"theme": "dark"}');
String settings = localStorageManager.readData('user_settings');
print('Geladene Einstellungen: $settings');

URI-Management mit Browser History API #

Verwalten Sie Browser-URIs und Query-Parameter ohne Seitenreload:

// URI Manager erstellen
final uriManager = GSDUriManager();

// Aktuelle Browser-URI abrufen
Uri currentUri = uriManager.getCurrentUri();
print('Aktuelle URI: $currentUri');

// Query-Parameter verwalten
uriManager.setQueryParameter('page', '1');
uriManager.setQueryParameter('filter', 'active');

// Mehrere Parameter gleichzeitig setzen
uriManager.setQueryParameters({
  'category': 'flutter',
  'sort': 'date',
  'view': 'grid'
});

// Parameter lesen
String? currentPage = uriManager.getQueryParameter('page');
Map<String, String> allParams = uriManager.getAllQueryParameters();

// Parameter entfernen
uriManager.removeQueryParameter('filter');

// Alle Parameter zurücksetzen
uriManager.resetUri();

// Browser-Titel ändern
uriManager.updateTitle('Meine Flutter App - Seite 1');

// Zu neuer URI navigieren
uriManager.navigateToUri(Uri.parse('https://example.com/newpage?tab=settings'));

URI-Manager Features #

// State-Management über URI
class SearchPage extends StatefulWidget {
  @override
  _SearchPageState createState() => _SearchPageState();
}

class _SearchPageState extends State<SearchPage> {
  final GSDUriManager _uriManager = GSDUriManager();
  
  @override
  void initState() {
    super.initState();
    // State aus URI laden
    _loadStateFromUri();
  }
  
  void _loadStateFromUri() {
    final params = _uriManager.getAllQueryParameters();
    setState(() {
      searchQuery = params['q'] ?? '';
      selectedCategory = params['category'] ?? 'all';
      currentPage = int.tryParse(params['page'] ?? '1') ?? 1;
    });
  }
  
  void _updateSearch(String query) {
    // State in URI speichern
    _uriManager.setQueryParameter('q', query);
    _uriManager.setQueryParameter('page', '1'); // Reset zur ersten Seite
    _uriManager.updateTitle('Suche: $query');
    
    setState(() {
      searchQuery = query;
      currentPage = 1;
    });
  }
  
  void _changeCategory(String category) {
    _uriManager.setQueryParameter('category', category);
    _uriManager.setQueryParameter('page', '1');
    
    setState(() {
      selectedCategory = category;
      currentPage = 1;
    });
  }
}

Web-Benachrichtigungen mit Browser-Erkennung #

Senden Sie native Browser-Benachrichtigungen mit automatischer Browser-Erkennung:

import 'package:gsd_utilities/gsd_utilities.dart';

// Notification Helper erstellen
final notificationHelper = GSDNotificationsHelper();

// Browser-Information abrufen
print('Chrome Browser: ${notificationHelper.isChromeBrowser}');
print('Firefox Browser: ${notificationHelper.isFirefoxBrowser}');
print('Edge Browser: ${notificationHelper.isEdgeBrowser}');
print('Safari Browser: ${notificationHelper.isSafariBrowser}');

// Berechtigungsstatus prüfen
String permission = notificationHelper.notificationPermission;
print('Aktueller Status: $permission'); // 'default', 'granted', oder 'denied'

bool canSend = notificationHelper.isNotificationPermissionGranted;
bool isDenied = notificationHelper.isNotificationPermissionDenied;
bool isDefault = notificationHelper.isNotificationPermissionDefault;

// Berechtigung anfordern
if (notificationHelper.isNotificationPermissionDefault) {
  notificationHelper.requestNotificationPermission();
  
  // Nach kurzer Verzögerung Status prüfen
  await Future.delayed(Duration(milliseconds: 500));
  if (notificationHelper.isNotificationPermissionGranted) {
    print('Berechtigung erteilt!');
  }
}

// Benachrichtigung senden
if (notificationHelper.isNotificationPermissionGranted) {
  notificationHelper.showNotification(
    'GSD Utilities',
    'Benachrichtigung erfolgreich gesendet!'
  );
}

// Ressourcen aufräumen
notificationHelper.dispose();

Notification Features #

class NotificationService {
  final GSDNotificationsHelper _notificationHelper = GSDNotificationsHelper();
  
  // Berechtigung initialisieren
  Future<bool> initializeNotifications() async {
    if (_notificationHelper.isNotificationPermissionDefault) {
      _notificationHelper.requestNotificationPermission();
      
      // Warten auf Benutzer-Entscheidung
      await Future.delayed(Duration(seconds: 1));
    }
    
    return _notificationHelper.isNotificationPermissionGranted;
  }
  
  // Status-Updates senden
  void sendStatusUpdate(String title, String message) {
    if (_notificationHelper.isNotificationPermissionGranted) {
      _notificationHelper.showNotification(title, message);
    }
  }
  
  // Browser-spezifische Behandlung
  void handleBrowserSpecific() {
    if (_notificationHelper.isChromeBrowser) {
      // Chrome-spezifische Features
      print('Chrome Browser erkannt - alle Features verfügbar');
    } else if (_notificationHelper.isSafariBrowser) {
      // Safari hat einige Einschränkungen
      print('Safari Browser - eingeschränkte Notification-Features');
    }
  }
  
  void dispose() {
    _notificationHelper.dispose();
  }
}

File Upload mit Fortschrittsverfolgung #

Laden Sie Dateien mit Real-time-Fortschrittsanzeige hoch:

import 'package:gsd_restapi/docuframe_api.dart';

// DOCUframe-API erstellen
final DOCUframeApi api = DOCUframeApi(
  configuration: RestApiDOCUframeConfig(
    appKey: 'GSD-DFApp',
    userName: 'username',
    appNames: ['app1'],
    serverUrl: 'https://server.com',
    alias: 'db',
  ),
);

// Upload Manager mit der Documents-Gruppe erstellen
final uploadManager = DOCUframeUploadManager(api.v1.documents);

// Dateien für Upload vorbereiten
List<GSDUploadFile> files = [
  GSDUploadFile.fromPath('/path/to/document.pdf'),
  GSDUploadFile.fromPath('/path/to/image.jpg'),
];

// Upload mit Fortschrittsverfolgung starten
await for (GSDUploadProgress progress in uploadManager.uploadFiles(files)) {
  print('Gesamt-Fortschritt: ${progress.percentage}%');
  print('Abgeschlossene Dateien: ${progress.completedFiles.length}/${progress.totalFiles}');
  
  for (int i = 0; i < progress.uploadFileProgresses.length; i++) {
    final fileProgress = progress.uploadFileProgresses[i];
    print('Datei ${i + 1}: ${fileProgress.percentage}% - Status: ${fileProgress.status}');
  }
  
  if (progress.isFinished) {
    print('Alle Uploads abgeschlossen!');
    break;
  }
}

Event-Handling #

// Konfigurationsänderungen überwachen
config.configChangedEvent.subscribe((args) async {
  print('Konfiguration wurde automatisch gespeichert');
});

// Account-Änderungen überwachen
account.accountChanged.subscribe((args) async {
  print('Account-Daten wurden geändert');
});

Konfigurationsarchitektur #

Das Paket verwendet eine plattformspezifische Konfigurationsarchitektur:

Web-Plattform #

  • LocalStorage: Browser-basierte Datenpersistierung
  • AES-Verschlüsselung: Sichere Speicherung sensibler Daten
  • Storage-Events: Cross-Tab-Kommunikation über JavaScript Interop

Mobile/Desktop-Plattform #

  • FlutterSecureStorage: Systemspezifische sichere Speicherung
  • Platform-native: Verwendet OS-eigene Sicherheitsmechanismen

ConfigStore-Backends je Plattform #

Plattform Öffentliche Werte Secrets Lock
Windows SharedPreferences mit SQLite-Backend (GSDSqliteSharedPreferencesPlatform: gsd_shared_preferences.db im App-Support-Verzeichnis, WAL, kein Prozess-Cache) FlutterSecureStorage mit SQLite-Backend (GSDSqliteSecureStoragePlatform: gsd_secure_storage.db, jeder Eintrag einzeln DPAPI-verschlüsselt) Dateilock (RandomAccessFile.lock) neben der Datenbank
macOS, Linux SharedPreferences mit SQLite-Backend (wie Windows) FlutterSecureStorage (Keychain, libsecret) Dateilock neben der Datenbank
Android, iOS SharedPreferences FlutterSecureStorage In-Process
Web localStorage (Klartext) FlutterSecureStorage (WebCrypto AES-GCM, nur im sicheren Kontext) Web Locks API (im unsicheren Kontext In-Process)

Überall liegen die öffentlichen Werte in SharedPreferencesAsync. Auf dem Desktop (Windows, macOS, Linux) wird die Plattform-Implementierung des Plugins getauscht: die Plugins für Windows und Linux halten alle Werte im Prozess-Cache und schreiben bei jedem Set die ganze JSON-Datei neu, wodurch parallel laufende Prozesse (mehrere Fenster, Notification-Prozess) sich gegenseitig überschreiben. macOS läuft für ein einheitliches Verhalten ebenfalls über SQLite. GSDSqliteSharedPreferencesPlatform.register() ersetzt es durch eine SQLite-Datenbank ohne Cache; das gilt dann für alle SharedPreferencesAsync-Objekte der App, die danach erzeugt werden. Die bisherigen Werte des Plugins werden nicht übernommen; nur der alte Konfigurationsblock des GSDConfigManager wird für die Migration von dort gelesen. Secrets bleiben unter macOS und Linux im Plugin (Keychain, libsecret): dort liegt jeder Eintrag einzeln im System-Dienst und ist damit prozesssicher.

Für Secrets gilt unter Windows dasselbe: flutter_secure_storage_windows hält alle Secrets in einer DPAPI-Datei und schreibt sie bei jedem Zugriff komplett neu. Zwei Prozesse verlieren dabei gegenseitig Werte, und liest einer eine halb geschriebene Datei, löscht das Plugin sie samt aller Secrets. GSDSqliteSecureStoragePlatform.register() ersetzt es: jedes Secret ist eine eigene, einzeln per DPAPI (Benutzerkontext) verschlüsselte Zeile in gsd_secure_storage.db. Ein nicht entschlüsselbarer Eintrag wirft GSDSecretUnreadableException und betrifft nur sich selbst. Die Registrierung gilt für jeden Nutzer von FlutterSecureStorage in der App (auch gsd_encryption). Die bisherigen Einträge des Plugins werden nicht übernommen; nur der alte Secret-Block des GSDConfigManager wird für die Migration von dort gelesen. Die alte Datei bleibt unverändert.

Grenzen des Locks: Der Lock sichert getrennte Prozesse gegeneinander ab (mehrere Fenster, zweite App-Instanz, Kindprozesse). Mehrere Isolates desselben Prozesses, etwa ein Notification-Handler im Hintergrund-Isolate, sind nur unter Windows gegeneinander gesichert: unter macOS und Linux gilt der fcntl-Dateilock pro Prozess, auf Android und iOS gibt es nur den In-Process-Lock pro Isolate. Einzelne set-Aufrufe bleiben trotzdem sicher (ein Key, atomar im Plugin); nur add/remove an Collections (Lesen-Ändern-Schreiben des Index) und die einmalige Migration können sich zwischen Isolates überholen. Auf Android nutzt SharedPreferencesAsync standardmäßig Jetpack DataStore, das selbst nicht für mehrere Prozesse ausgelegt ist; getrennte Android-Prozesse (android:process) sind daher nicht abgesichert.

Der Self-Test im Example (example/integration_test/config_store_test.dart, in der App unter „ConfigStore Self-Test“) prüft mehrere Store-Instanzen auf jeder Plattform und unter Windows zusätzlich echte Kindprozesse (dart run-Kinder im integration_test, die Example-EXE als zweite App-Instanz im Screen). Der Screen startet Kopien der EXE auf der Platte: nach flutter test integration_test ist das ein Test-Build, die App also vor dem Screen-Test per flutter run neu starten (kein Hot Restart).

File Upload Features #

Unterstützte Dateitypen #

  • Dokumente: PDF, DOC, DOCX, TXT, etc.
  • Bilder: PNG, JPEG, GIF, WebP (mit automatischer Komprimierung)
  • Archive: ZIP, RAR, etc.
  • Web-Dateien: Direkte Browser-Upload-Unterstützung

Upload-Funktionen #

// Bildkomprimierung konfigurieren
final uploadFile = GSDUploadFile.fromPath('/path/to/large_image.jpg');
uploadFile.setResolution(GSDUploadImageResolution(percentage: 75)); // 75% der Originalgröße

// Upload-Status überwachen
if (fileProgress.status == GSDUploadFileStatus.completed) {
  print('Upload erfolgreich! Server-ID: ${fileProgress.result?.oid}');
} else if (fileProgress.status == GSDUploadFileStatus.failed) {
  print('Upload fehlgeschlagen: ${fileProgress.result?.error}');
}

DOCUframe Integration #

Spezielle Enterprise-Funktionen für DOCUframe-Systeme:

// Account-Konfiguration
final account = DOCUframeAccount(
  'Mein Account',
  'https://docuframe.server.com',
  'account_alias',
  'username'
);

account.setDatabaseName('production_db');

// Account-Serialisierung
Map<String, dynamic> accountData = account.toJson();
DOCUframeAccount restoredAccount = DOCUframeAccount.fromJson(accountData);

Error-Handling #

Das Paket bietet umfassende Fehlerbehandlung:

try {
  final result = await configManager.loadConfig(AppConfig());
  if (!result.isSuccess) {
    print('Fehler beim Laden: ${result.error}');
    print('Debug-Log: ${result.log}');
  }
} catch (e) {
  print('Unerwarteter Fehler: $e');
}

Performance und Memory Management #

  • Automatisches Cleanup: Event-Listener werden automatisch entfernt
  • Memory-effizient: Große Dateien werden in Chunks verarbeitet
  • Background-Processing: Uploads laufen im Hintergrund
  • Platform-optimiert: Plattformspezifische Optimierungen

API-Design #

Das Paket folgt dem Prinzip der sauberen Architektur:

  • ✅ GSDConfigManager - Hauptklasse für Konfigurationsverwaltung (JSON-Block)
  • ✅ GSDConfigStore, GSDConfigStoreFactory - Feldbasierter, prozesssicherer Konfigurationsspeicher
  • ✅ GSDConfig, GSDConfigItem, GSDConfigValue, GSDConfigOptionalValue, GSDConfigCollection - Deklaration der Felder für den ConfigStore
  • ✅ GSDConfigMemoryValueStore, GSDConfigMemorySecretStore, GSDConfigInProcessLock - Backends für Tests
  • ✅ GSDLocalStorageManager - Plattformübergreifender Storage-Manager
  • ✅ GSDUriManager - Browser-URI-Manipulation
  • ✅ DOCUframeUploadManager - Upload-Funktionalität mit Fortschrittsverfolgung
  • ✅ GSDBaseConfig - Abstrakte Basis für eigene Konfigurationsklassen
  • ✅ GSDUploadFile - Datei-Repräsentation mit Metadaten
  • ❌ _GSDWebConfigManager - Interne Web-Implementierung (nicht zugänglich)
  • ❌ _GSDAppConfigManager - Interne Mobile-Implementierung (nicht zugänglich)
  • ❌ _GSDWebUriManager - Interne Web-URI-Implementierung (nicht zugänglich)

Plattform-Kompatibilität #

Feature Web Mobile Desktop
Konfigurationsverwaltung ✅ ✅ ✅
LocalStorage ✅ ❌ ❌
Storage-Events ✅ ❌ ❌
URI-Management ✅ ❌ ❌
Web-Benachrichtigungen ✅ ❌* ❌*
File-Upload ✅ ✅ ✅
Bildverarbeitung ✅ ✅ ✅
Secure Storage ❌ ✅ ✅

⚠️ Fallback-Implementierung verfügbar (eingeschränkte Funktionalität)

Hinweise #

  • Automatische Plattformauswahl: Das richtige Storage-System wird automatisch gewählt
  • Event-basiert: Alle Änderungen werden über Events kommuniziert
  • Type-safe: Vollständige TypeScript-Integration für Web-Plattformen
  • Memory-safe: Automatisches Cleanup verhindert Memory-Leaks
  • Production-ready: Umfassive Fehlerbehandlung und Logging
  • Browser-URI-Manipulation: URI-Änderungen ohne Seitenreload (nur Web)
  • Native Browser-Notifications: Verwendung der Browser Notification API (nur Web)
  • Browser-Erkennung: Automatische Erkennung von Chrome, Firefox, Edge, Safari
  • Dokumentiert: Alle Klassen und Methoden vollständig dokumentiert

URI-Manager Spezifika #

Web-Plattform:

  • ✅ Browser History API (history.replaceState())
  • ✅ Document Title API (document.title)
  • ✅ JavaScript Interop für native Browser-Integration
  • ✅ Live-URL-Updates ohne Seitenreload

Mobile/Desktop-Plattform:

  • ❌ Keine Funktionalität

Notification-Helper Spezifika #

Web-Plattform:

  • ✅ Native Browser Notification API (new Notification())
  • ✅ Permission Management (Notification.requestPermission())
  • ✅ Browser-Erkennung (Chrome, Firefox, Edge, Safari)
  • ✅ Automatisches Schließen nach 5 Sekunden
  • ✅ Resource Management und Cleanup

Mobile/Desktop-Plattform:

  • ❌ Dummy-Implementierung (UnimplementedError)
  • ❌ Für native Platforms sollten plattformspezifische Notification-Plugins verwendet werden

Dieses Paket ermöglicht die professionelle Utility-Verwaltung in Ihrer Flutter-Anwendung und bietet umfassende Funktionen für Konfiguration, Storage, File-Upload und Enterprise-Integration bei gleichzeitig sauberer und plattformübergreifender API.