Skip to content

Instantly share code, notes, and snippets.

@BrandowBuenos
Created July 31, 2024 14:04
Show Gist options
  • Save BrandowBuenos/934db9e4be683561d9285af7803c4863 to your computer and use it in GitHub Desktop.
Save BrandowBuenos/934db9e4be683561d9285af7803c4863 to your computer and use it in GitHub Desktop.
App data handler
// ignore_for_file: unnecessary_lambdas, use_build_context_synchronously
import 'dart:async';
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:idb_shim/idb.dart';
import 'package:idb_shim/idb_browser.dart';
// Enum representing the different models in your app. TODO: Add your specific models here.
enum AppModel {
// TODO: Replace with your models
model1,
model2,
model3,
// ...
}
class AppData extends ChangeNotifier {
List<AppModel> hasError = [];
bool finishedSync = false;
Database? _db;
// Function to get the IndexedDB database instance.
Future<Database> _getDatabase() async {
if (_db == null) {
IdbFactory idbFactory = getIdbFactory()!;
_db = await idbFactory.open('appDatabase', version: 1,
onUpgradeNeeded: (VersionChangeEvent e) {
Database db = e.database;
if (!db.objectStoreNames.contains('dataStore')) {
db.createObjectStore('dataStore');
}
});
}
return _db!;
}
void setFinishedSync(bool value) {
finishedSync = value;
notifyListeners();
}
// Function to get information about a specific model. TODO: Customize with your model details.
dynamic getModelInfo(AppModel model) {
switch (model) {
case AppModel.model1:
return {
'key': 'model1_key',
'model': Model1,
'list': model1Controller.model1List,
'stater': (items) => model1Controller.setModel1(items),
};
case AppModel.model2:
return {
'key': 'model2_key',
'model': Model2,
'list': model2Controller.model2List,
'stater': (items) => model2Controller.setModel2(items),
};
case AppModel.model3:
return {
'key': 'model3_key',
'model': Model3,
'list': model3Controller.model3List,
'stater': (items) => model3Controller.setModel3(items),
};
// TODO: Add cases for other models
default:
return '';
}
}
// Function to save current data for specified models.
Future<void> saveCurrentData(List<AppModel> models) async {
for (final AppModel model in models) {
getModelInfo(model)['stater'](getModelInfo(model)['list']);
}
for (final AppModel model in models) {
List<dynamic> list = getModelInfo(model)['list'];
await saveList(model, list);
}
}
// Function to save a list of data for a specific model.
Future<void> saveList(AppModel model, List<dynamic> list) async {
final String key = getModelInfo(model)['key'];
String encodedList = jsonEncode(list);
if (kIsWeb) {
await saveListOnIndexedDB(key, encodedList);
} else {
FlutterSecureStorage flutterSecureStorage = const FlutterSecureStorage();
await flutterSecureStorage.write(key: key, value: encodedList);
}
}
// Function to save data to IndexedDB.
Future<void> saveListOnIndexedDB(String key, String value) async {
Database db = await _getDatabase();
Transaction txn = db.transaction('dataStore', 'readwrite');
ObjectStore store = txn.objectStore('dataStore');
await store.put(value, key);
await txn.completed;
}
// Function to retrieve data from IndexedDB.
Future<String?> getListFromIndexedDB(String key) async {
Database db = await _getDatabase();
Transaction txn = db.transaction('dataStore', 'readonly');
ObjectStore store = txn.objectStore('dataStore');
String? value = await store.getObject(key) as String?;
await txn.completed;
return value;
}
// Function to retrieve a list of data for a specific model.
Future<dynamic> getList(AppModel model) async {
final String key = getModelInfo(model)['key'];
String? encodedList;
if (kIsWeb) {
encodedList = await getListFromIndexedDB(key);
} else {
FlutterSecureStorage flutterSecureStorage = const FlutterSecureStorage();
encodedList = await flutterSecureStorage.read(key: key);
}
if (encodedList != null) {
List<dynamic> jsonList = jsonDecode(encodedList);
// TODO: Customize the deserialization for your models
if (model == AppModel.model1) {
List<Model1> model1List = [];
for (final json in jsonList) {
model1List.add(Model1.fromJson(json));
}
return model1List;
} else if (model == AppModel.model2) {
List<Model2> model2List = [];
for (final json in jsonList) {
model2List.add(Model2.fromJson(json));
}
return model2List;
} else if (model == AppModel.model3) {
List<Model3> model3List = [];
for (final json in jsonList) {
model3List.add(Model3.fromJson(json));
}
return model3List;
}
} else {
// TODO: Implement API call or other fallback logic
return api.get(model);
}
}
// Function to inject data from secure storage into the app state.
Future<void> getListFromSecureStorageAndInject(AppModel model) async {
dynamic list = await getList(model);
getModelInfo(model)['stater'](list);
}
// Function to create a new item for a specific model.
Future<void> createItem(AppModel model, dynamic itemToCreate,
{bool? ignoreRefresh}) async {
List<dynamic> list = getModelInfo(model)['list'];
list.add(itemToCreate);
if (ignoreRefresh != true) {
await saveList(model, list);
getModelInfo(model)['stater'](list);
}
unawaited(api.create(model, itemToCreate));
}
// Function to update an existing item for a specific model.
Future<void> updateItem(AppModel model, dynamic itemToUpdate,
{bool? ignoreRefresh}) async {
List<dynamic> list = getModelInfo(model)['list'];
int index = list.indexWhere((element) => element.id == itemToUpdate.id);
if (index != -1) {
list[index] = itemToUpdate;
if (ignoreRefresh != true) {
await saveList(model, list);
getModelInfo(model)['stater'](list);
}
}
unawaited(api.update(model, itemToUpdate));
}
// Function to delete an item for a specific model.
Future<void> deleteItem(AppModel model, dynamic itemToDelete,
{bool? ignoreRefresh}) async {
List<dynamic> list = getModelInfo(model)['list'];
list.removeWhere((element) => element.id == itemToDelete.id);
if (ignoreRefresh != true) {
await saveList(model, list);
getModelInfo(model)['stater'](list);
}
unawaited(api.delete(model, itemToDelete));
}
// Function to clear a specific key from storage.
Future<void> clearKey(String key) async {
if (kIsWeb) {
await clearKeyFromIndexedDB(key);
} else {
FlutterSecureStorage flutterSecureStorage = const FlutterSecureStorage();
if (await flutterSecureStorage.containsKey(key: key)) {
await flutterSecureStorage.delete(key: key);
}
}
}
// Function to clear a specific key from IndexedDB.
Future<void> clearKeyFromIndexedDB(String key) async {
Database db = await _getDatabase();
Transaction txn = db.transaction('dataStore', 'readwrite');
ObjectStore store = txn.objectStore('dataStore');
await store.delete(key);
await txn.completed;
}
// Function to clear all keys from storage.
Future<void> clearAllStoreKeys() async {
for (final AppModel model in AppModel.values) {
await clearKey(getModelInfo(model)['key']);
}
}
bool isFinishedSync() => finishedSync && hasError.isEmpty;
// Function to refresh data by clearing and re-fetching it.
Future<void> refreshData() async {
await clearAllStoreKeys();
api.itemsWithRelationErrors.clear();
hasError.clear();
setFinishedSync(false);
await Future.wait([
getListFromSecureStorageAndInject(AppModel.model1).catchError((e) {
hasError.add(AppModel.model1);
}),
getListFromSecureStorageAndInject(AppModel.model2).catchError((e) {
hasError.add(AppModel.model2);
}),
getListFromSecureStorageAndInject(AppModel.model3).catchError((e) {
hasError.add(AppModel.model3);
}),
// TODO: Add more models as needed
]);
setFinishedSync(true);
if (hasError.isNotEmpty) {
CustomSnackbar.confirm(
navigatorKey.currentState!.context,
'Error syncing data. Try again?',
'Yes',
() {
refreshData();
},
);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment