From debf960d70a5b569a06060313b1ca038f94964b4 Mon Sep 17 00:00:00 2001 From: marco Date: Thu, 22 Jan 2026 17:51:50 +0100 Subject: [PATCH] simple working json import and export --- lib/pages/settings_page.dart | 49 ++++++++++++++++++++- lib/service/json_file_service.dart | 69 ++++++++++++++++++++++++++++++ lib/service/storage.dart | 17 ++++++++ 3 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 lib/service/json_file_service.dart diff --git a/lib/pages/settings_page.dart b/lib/pages/settings_page.dart index 98cfc8d..606c56c 100644 --- a/lib/pages/settings_page.dart +++ b/lib/pages/settings_page.dart @@ -1,15 +1,62 @@ import 'package:flutter/material.dart'; +import 'package:permission_handler/permission_handler.dart'; import '../l10n/app_localizations.dart'; +import '../service/notifying.dart'; +import '../service/permission_service.dart'; +import '../service/storage.dart'; -class SettingsPage extends StatelessWidget { +class SettingsPage extends StatefulWidget { static const routeName = '/settings'; const SettingsPage({super.key}); + @override + State createState() => _SettingsPageState(); +} + +class _SettingsPageState extends State { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(AppLocalizations.of(context)!.settings)), + body: SizedBox( + width: MediaQuery.of(context).size.width * 0.9, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + AppLocalizations.of(context)!.appData, + style: Theme.of(context).textTheme.titleLarge, + ), + ElevatedButton( + onPressed: () => onActivateJsonImportPressed(), + child: Text(AppLocalizations.of(context)!.import), + ), + ElevatedButton( + onPressed: () => onActivateJsonExportPressed(), + child: Text(AppLocalizations.of(context)!.export), + ), + ], + ), + ), ); } + + void onActivateJsonExportPressed() async { + if (await checkStoragePermission) Storage.exportToJsonFile(); + } + + void onActivateJsonImportPressed() async { + if (await checkStoragePermission) Storage.importFromJsonFile(); + } + + Future get checkStoragePermission async { + if (!(await PermissionService.requestStoragePermission).isGranted) { + if (mounted) { + Notifying.showStoragePermissionErrorSnackbar(context); + return false; + } + } + return true; + } } diff --git a/lib/service/json_file_service.dart b/lib/service/json_file_service.dart new file mode 100644 index 0000000..56c2159 --- /dev/null +++ b/lib/service/json_file_service.dart @@ -0,0 +1,69 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:file_selector/file_selector.dart'; +import 'package:flutter/services.dart'; + +import '../model/bookmark.dart'; +import '../model/collection.dart'; +import '../assets/constants.dart' as constants; + +class JsonFileService { + static Future exportToJson({ + required List collections, + required List bookmarks, + }) async { + final dir = await _directoryPath; + + final data = { + 'collections': collections.map((c) => c.toJson()).toList(), + 'bookmarks': bookmarks.map((b) => b.toJson()).toList(), + }; + final json = jsonEncode(data).codeUnits; + final file = XFile.fromData( + Uint8List.fromList(json), + mimeType: 'application/json', + name: constants.jsonFileName, + ); + + file.saveTo('$dir/${constants.jsonFileName}'); + + return false; + } + + static Future<({List collections, List bookmarks})> + importFromJson() async { + const typeGroup = XTypeGroup(label: 'json', extensions: ['json']); + final XFile? file = await openFile( + acceptedTypeGroups: [typeGroup], + ); + + if (file == null) { + return (collections: [], bookmarks: []); + } + + final jsonString = await file.readAsString(); + + final data = jsonDecode(jsonString) as Map; + + final collections = (data['collections'] as List? ?? []) + .map((json) => Collection.fromJson(json as Map)) + .toList(); + + final bookmarks = (data['bookmarks'] as List? ?? []) + .map((json) => Bookmark.fromJson(json as Map)) + .toList(); + + return (collections: collections, bookmarks: bookmarks); + } + + static Future get _directoryPath async { + if (Platform.isAndroid) { + return await getDirectoryPath( + initialDirectory: constants.defaultAndroidExportDirectory, + ) ?? + ''; + } + return await getDirectoryPath() ?? ''; + } +} diff --git a/lib/service/storage.dart b/lib/service/storage.dart index ef91ac2..8ae8bac 100644 --- a/lib/service/storage.dart +++ b/lib/service/storage.dart @@ -4,6 +4,7 @@ import 'package:shared_preferences/shared_preferences.dart'; import '../model/bookmark.dart'; import '../model/collection.dart'; +import 'json_file_service.dart'; class Storage { static const String _bookmarksKey = 'bookmarks'; @@ -163,4 +164,20 @@ class Storage { } return _prefsWithCache!; } + + static Future exportToJsonFile() => JsonFileService.exportToJson( + collections: loadCollections(), + bookmarks: loadBookmarks(), + ); + + static Future importFromJsonFile() async { + final import = await JsonFileService.importFromJson(); + + if (import.bookmarks.isNotEmpty || import.collections.isNotEmpty) { + saveBookmarks(import.bookmarks); + saveCollections(import.collections); + return true; + } + return false; + } }