added persistence using shared preferences

This commit is contained in:
2025-09-19 20:43:38 +02:00
parent 12459bb4cb
commit d0feca1ba8
9 changed files with 301 additions and 15 deletions

View File

@@ -1,7 +1,10 @@
import 'package:flutter/material.dart';
import 'pages/collections_page.dart';
import 'service/storage.dart';
void main() {
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Storage.initialize();
runApp(const MapsBookmarks());
}

View File

@@ -1,11 +1,35 @@
class Bookmark {
Bookmark({required this.name, required this.link});
Bookmark({
required this.collectionId,
required this.name,
required this.link,
required this.description,
int? createdAt,
}) : createdAt = createdAt ?? DateTime.now().millisecondsSinceEpoch;
factory Bookmark.fromJson(Map<String, dynamic> json) =>
Bookmark(name: json['name'] as String, link: json['link'] as String);
factory Bookmark.fromJson(Map<String, dynamic> json) => Bookmark(
collectionId: json['collectionId'] as int,
name: json['name'] as String,
link: json['link'] as String,
description: json['description'] as String,
createdAt: json['createdAt'] as int,
);
int collectionId;
String link;
String name;
String description;
int createdAt;
Map<String, dynamic> toJson() => {'name': name, 'link': link};
int get id => createdAt;
DateTime get createdDate => DateTime.fromMillisecondsSinceEpoch(createdAt);
Map<String, dynamic> toJson() => {
'collectionId': collectionId,
'name': name,
'link': link,
'description': description,
'createdAt': createdAt,
};
}

View File

@@ -1,10 +1,18 @@
class Collection {
Collection({required this.name});
Collection({required this.name, int? createdAt})
: createdAt = createdAt ?? DateTime.now().millisecondsSinceEpoch;
factory Collection.fromJson(Map<String, dynamic> json) =>
Collection(name: json['name'] as String);
factory Collection.fromJson(Map<String, dynamic> json) => Collection(
name: json['name'] as String,
createdAt: json['createdAt'] as int,
);
String name;
int createdAt; // used as Id with millisecondsSinceEpoch
Map<String, dynamic> toJson() => {'name': name};
int get id => createdAt;
DateTime get createdDate => DateTime.fromMillisecondsSinceEpoch(createdAt);
Map<String, dynamic> toJson() => {'name': name, 'createdAt': createdAt};
}

117
lib/service/storage.dart Normal file
View File

@@ -0,0 +1,117 @@
import 'dart:convert' show jsonDecode, jsonEncode;
import 'package:shared_preferences/shared_preferences.dart';
import '../model/bookmark.dart';
import '../model/collection.dart';
class Storage {
static const String _collectionsKey = 'collections';
static const String _bookmarksKey = 'bookmarks';
static const String _statsKey = 'stats';
static SharedPreferencesWithCache? _prefsWithCache;
static Future<void> initialize() async {
_prefsWithCache = await SharedPreferencesWithCache.create(
cacheOptions: const SharedPreferencesWithCacheOptions(
allowList: <String>{_collectionsKey, _bookmarksKey, _statsKey},
),
);
}
static SharedPreferencesWithCache get _prefs {
if (_prefsWithCache == null) {
throw StateError(
'BookmarkStorage not initialized. Call initialize() first.',
);
}
return _prefsWithCache!;
}
static List<Collection> loadCollections() {
final jsonString = _prefs.getString(_collectionsKey) ?? '[]';
final jsonList = jsonDecode(jsonString) as List;
return jsonList
.map((json) => Collection.fromJson(json as Map<String, dynamic>))
.toList();
}
static Future<void> saveCollections(List<Collection> collections) async {
final jsonList = collections.map((c) => c.toJson()).toList();
await _prefs.setString(_collectionsKey, jsonEncode(jsonList));
}
static List<Bookmark> loadAllBookmarks() {
final jsonString = _prefs.getString(_bookmarksKey) ?? '[]';
final jsonList = jsonDecode(jsonString) as List;
return jsonList
.map((json) => Bookmark.fromJson(json as Map<String, dynamic>))
.toList();
}
static Future<void> saveAllBookmarks(List<Bookmark> bookmarks) async {
final jsonList = bookmarks.map((b) => b.toJson()).toList();
await _prefs.setString(_bookmarksKey, jsonEncode(jsonList));
}
static List<Bookmark> loadBookmarksForCollection(int collectionId) {
final allBookmarks = loadAllBookmarks();
return allBookmarks.where((b) => b.collectionId == collectionId).toList();
}
static Future<void> addBookmark(Bookmark bookmark) async {
final bookmarks = loadAllBookmarks();
bookmarks.add(bookmark);
await saveAllBookmarks(bookmarks);
}
static Future<void> deleteBookmarkById(int bookmarkId) async {
final bookmarks = loadAllBookmarks();
bookmarks.removeWhere((b) => b.id == bookmarkId);
await saveAllBookmarks(bookmarks);
}
static Future<void> deleteBookmarksForCollection(int collectionId) async {
final bookmarks = loadAllBookmarks();
bookmarks.removeWhere((b) => b.collectionId == collectionId);
await saveAllBookmarks(bookmarks);
}
static Future<void> updateBookmarkById(
int bookmarkId, {
String? name,
String? description,
}) async {
final bookmarks = loadAllBookmarks();
final index = bookmarks.indexWhere((b) => b.id == bookmarkId);
if (index == -1) return;
if (name != null) bookmarks[index].name = name;
if (description != null) bookmarks[index].description = description;
await saveAllBookmarks(bookmarks);
}
static Map<String, int> getStats() {
final statsJson = _prefs.getString(_statsKey) ?? '{}';
final stats = jsonDecode(statsJson) as Map<String, dynamic>;
return {
'totalCollections': stats['totalCollections'] ?? 0,
'totalBookmarks': stats['totalBookmarks'] ?? 0,
};
}
static Future<void> updateStats() async {
final collections = loadCollections();
final bookmarks = loadAllBookmarks();
final stats = {
'totalCollections': collections.length,
'totalBookmarks': bookmarks.length,
'lastUpdated': DateTime.now().millisecondsSinceEpoch,
};
await _prefs.setString(_statsKey, jsonEncode(stats));
}
}