NOTE: SUPER EARLY ALPHA. Most of the implementation is complete. I need to do other homework for today so I can't finish the tests / docs, but this version should be useable.

Features

helps save / load / delete singletons to local storage.

you can specify if the document is a application document, temporary document, support document, or shared prefrence. The distinction can be found here.

In short:

  • DocumentType.document: data that is user generated and cannot be recreated. (ex. my high score in a local-only game)
  • DocumentType.support: data that is NOT user generated and cannot be recreated. (ex. automatically set config files)
  • DocumentType.temporary: data that can be cleared at any time. (ex. caching a downloaded json)
  • DocumentType.prefs: user prefrences (ex. should the app open in light mode)

NOTE: uses shared_preferences on the backend instead of real files. this is because on web, there is no notion of files.

Use cases

Saving the logged in user:

class CurrentUser {
    final int id;
    final String name;
    final Color favoriteColor;

    // insert toJson here
    // insert fromJson here
}

final locallyPersistedUser = localSingleton<CurrentUser>(
      documentType: DocumentType.document,
      fileName: 'current_user',
      fromJson: CurrentUser.fromJson,
      toJson: (currentUser) => currentUser.toJson());


//save and load the user to memory like this:
CurrentUser? myCurrentUser = await locallyPersistedUser.read();
await locallyPersistedUser.write(someUser);

Caching many things to memory to avoid redundant network calls:

class CurrentUser {
    final int id;
    final String name;
    final Color favoriteColor;

    // insert toJson here
    // insert fromJson here
}

final userCache = LocalSingleton<UserCache>(
  fileName: 'current_user'
);

await UserCache(userId: 1).save(maybeUserOne);

Usage


//preface: import it!
import 'package:local_value/local_value.dart';

//first, create some sort of data model you want to save to local storage.
//can be a primative (just use LocalJsonFile<int> or similar)
class CounterObj {
    int value;

    CounterObj(this.value);
}


//then, call localJsonFile to build a localJsonFile:

//localJson File
final counterFile = localJsonFile<CounterObj>(
      documentType: DocumentType.document,
      fileName: 'counter',
      fromJson: (counterJson) => CounterObj(counterJson['value'] as int),
      toJson: (counterObj) => {'value': counterObj.value});

//or LocalTxtFile
localTxtFile(documentType: DocumentType.document, fileName: 'counter')


//read from it
CounterObj? myCurrentCounter = await counterStorage.read();

//write to it
await counterStorage.write(myCurrentCounter ?? CounterObj(8));

//clear it
await counterStorage.clear();

Integration with Dependency Injection

If you're looking to integrate with dependency injection for testing, look no further. The underlying classes (LocalTxtFile and LocalJsonFile) are exposed, so that you can subclass them. Something like this wouldn't work:

//DON'T DO THIS! THIS WILL NOT WORK.
getIt.registerSingleton(localTxtFile(fileName: 'blah1', documentType: ...));
getIt.registerSingleton(localTxtFile(fileName: 'blah2', documentType: ...)); //this will fail, since LocalTxtFile already registered

instead, we can subclass TxtFile:

class BlahOne extends LocalTxtFile {
  BlahOne() : super(
    documentType: DocumentType.document,
    fileName: 'blah1'
  );
}

class BlahTwo extends LocalTxtFile {
  BlahTwo() : super(
    documentType: DocumentType.document,
    fileName: 'blah2'
  );
}

//now we can register them inside get_it:
getIt.registerSingleton(BlahOne());
getIt.registerSingleton(BlahTwo());

Additional information

If you find an issue, please post it on the github.

I will be back if it proves useful to flesh it out! PR's welcome!