Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,5 @@

## 3.0.0

- Some parameters have been replaced by different rules, which will definitely introduce breaking changes
- Some parameters have been replaced by different rules, which will definitely introduce breaking changes
- Full coverage test
38 changes: 24 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,42 +44,52 @@ typedef DB = LocalShared;

![LocalShared Collection.gif](https://github.com/Nialixus/local_shared/assets/45191605/6f5be9e9-892b-4381-9691-98c7cc92c92b)

This guide illustrates fundamental CRUD (Create, Read, Update, Delete) operations for collection management. Interacting with it can be achieved through the following methods: `Shared.col(id)` or `Shared.collection(id)`.
`SharedCollection` organizes documents in a JSON map. Use `Shared.col(id)` or `Shared.collection(id)`,
then operate with the following methods.

### Create

To initiate the creation of a new collection, utilize this method:

```dart
final result = await Shared.col('myCollection').create();
print(result); // SharedMany(success: true, message: '...', data: <JSON>[])
// SharedMany(success: true, message: ..., data: [])
```

### Read
- `replace: true` can overwrite existing collection.

To retrieve information pertaining to a collection, invoke this method:
### Read

```dart
final response = await Shared.col('myCollection').read();
print(response); // SharedMany(success: true, message: '...', data: <JSON>[])
// SharedMany(success: true, message: ..., data: [ ... ])
```

### Update

To migrate or change collection id implement this method:
```dart
final response = await Shared.col('myCollection').update({
'doc1': {'k': 'v'},
'doc2': {'k': 'v2'},
});
// SharedMany(success: true, message: ..., data: [ ... ])
```

- `force: true` creates missing collection if it does not exist.

### Migrate

```dart
final response = await Shared.col('myCollection').update('myNewCollection');
print(response); // SharedMany(success: true, message: '...', data: <JSON>[])
final response = await Shared.col('myCollection').migrate('targetCollection');
// SharedMany(success: true, message: ..., data: [ ... ])
```

### Delete
- `merge: true` merges existing target collection data.
- `force: true` allows migration from non-existing source.

To remove a collection, employ this method:
### Delete

```dart
final response = await Shared.col('myNewCollection').delete();
print(response): // SharedNone(success: true, message: '....')
final response = await Shared.col('myCollection').delete();
// SharedNone(success: true, message: ...)
```

## Document | [View Code](https://github.com/Nialixus/local_shared/blob/main/example/lib/src/document_crud.dart)
Expand Down
2 changes: 1 addition & 1 deletion example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ part 'src/many_document_crud.dart';

Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await LocalShared('MY_DB').initialize();
await const LocalShared('MY_DB').initialize();

// ignore: avoid_print
if (kDebugMode) LocalShared.stream.listen(print);
Expand Down
47 changes: 19 additions & 28 deletions lib/local_shared.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
/// A SharedPreferences-based local storage, designed as an alternative to the localstore package
/// Lightweight local storage that uses Flutter Secure Storage and SharedPreferences.
///
/// Provides a simplified API for managing collections and documents in a key/value store.
/// It is designed as an alternative to `localstore` with explicit, typed responses.
library local_shared;

import 'dart:async';
Expand All @@ -21,31 +24,21 @@ typedef JSON = Map<String, dynamic>;
/// A shorter term for [LocalShared].
typedef Shared = LocalShared;

/// Parent of [SharedCollection], [SharedDocument] and [SharedManyDocument]. Need to be initiated before used!
/// LocalShared is the core datastore object.
///
/// ```dart
/// void main() {
/// WidgetsFlutterBinding.ensureInitialized();
/// await LocalShared('DATABASE').initialize();
/// }
/// ```
///
/// and later in app, you can access it anywhere by calling this
/// Call [initialize] once at app startup, then use [Shared.col] or [Shared.collection]
/// to access collection and document CRUD operations.
///
/// ```dart
/// final response = await Shared.col(id).doc(id).read();
/// print(response); // SharedOne(success: true, message: '..., data: JSON) */
/// WidgetsFlutterBinding.ensureInitialized();
/// await LocalShared('appId').initialize();
/// final result = await Shared.col('users').doc('user1').read();
/// ```
class LocalShared {
/// Default constructor of [LocalShared] containing [id] to be used as prefix in [SharedPreferences].
///
/// Let [id] empty if you want to use default prefix of [SharedPreferences].
/// Constructs a LocalShared instance.
///
/// ```dart
/// LocalShared('app.inidia.example');
/// ```
LocalShared(this.id);

/// The [id] is used as a namespace prefix for all saved keys.
const LocalShared(this.id);
/// The unique identifier for this [LocalShared] database instance
/// which will be used as prefix for [SharedPreferences].
final String id;
Expand All @@ -63,15 +56,13 @@ class LocalShared {
static final StreamController<JSON> _controller =
StreamController<JSON>.broadcast();

/// Initializes the [LocalShared] instance.
/// Initializes internal storage instances.
///
/// Call this once inside main function after ensuring flutter initialized.
/// Must be called once before retrying any read/write operations.
///
/// ```dart
/// Future<void> main() async {
/// WidgetsFlutterBinding.ensureInitialized();
/// await LocalShared('app.inidia.example').initialize();
/// }
/// WidgetsFlutterBinding.ensureInitialized();
/// await LocalShared('app.example').initialize();
/// ```
Future<void> initialize() async {
_storage = FlutterSecureStorage(
Expand All @@ -82,9 +73,9 @@ class LocalShared {
_db = await SharedPreferences.getInstance();
}

/// Loaded [FlutterSecureStorage] instance from awaiting [initialize].
/// Returns the initialized secure storage instance.
///
/// This instance will be used in entire lifecycle of the app.
/// Throws [StateError] if [initialize] has not been called.
static FlutterSecureStorage get storage {
if (_storage != null) {
return _storage!;
Expand Down
34 changes: 8 additions & 26 deletions lib/src/shared_collection.dart
Original file line number Diff line number Diff line change
@@ -1,33 +1,15 @@
part of '../local_shared.dart';

/// Represents a collection of [SharedDocument] within the [LocalShared] storage.
/// A collection is a container of documents inside `LocalShared`.
///
/// Collections are used to organize and manage related pieces of data.
/// This class provides methods for creating, reading, updating, and deleting collections,
/// as well as shortcuts for interacting with documents and multiple documents.
/// ---
/// ```dart
/// // Create a new collection
/// final result = await Shared.col('myCollection').create();
/// print(response); // SharedMany(success: true, message: '...', data: <JSON>[])
/// ```
/// ---
/// ```dart
/// // Read the contents of a collection
/// final response = await Shared.col('myCollection').read();
/// print(response); // SharedMany(success: true, message: '...', data: <JSON>[])
/// ```
/// ---
/// ```dart
/// // Update a collection
/// final response = await Shared.col('myCollection').update();
/// print(response); // SharedMany(success: true, message: '...', data: <JSON>[])
/// ```
/// ---
/// Use this class to manage whole collections and to access document-level operations:
/// creation, reading, updating, deleting, and migration.
///
/// Recommended usage:
/// ```dart
/// // Delete a collection
/// final response = await Shared.col('myCollection').delete();
/// print(response): // SharedNone(success: true, message: '...')
/// final collection = Shared.col('myCollection');
/// await collection.create();
/// await collection.doc('item1').create({'value': 1});
/// ```
class SharedCollection {
/// Creates a new instance of [SharedCollection].
Expand Down
49 changes: 17 additions & 32 deletions lib/src/shared_document.dart
Original file line number Diff line number Diff line change
@@ -1,33 +1,14 @@
part of '../local_shared.dart';

/// Represents a document within a [SharedCollection] in [LocalShared] storage.
/// A single document stored inside a collection.
///
/// Documents are individual pieces of data stored within a collection.
/// This class provides methods for creating, reading, updating, and deleting documents
/// within the context of a specific collection.
/// ---
/// ```dart
/// // Create a new document within a collection
/// final result = await Shared.col('myCollection').doc('documentId').create({'key': 'value'});
/// print(response); // SharedOne(success: true, message: '...', data: JSON)
/// ```
/// ---
/// ```dart
/// // Read the contents of a document within a collection
/// final response = await Shared.col('myCollection').doc('documentId').read();
/// print(response); // SharedOne(success: true, message: '...', data: JSON)
/// ```
/// ---
/// ```dart
/// // Update the contents of a document within a collection
/// final response = await Shared.col('myCollection').doc('documentId').update({'newKey': 'newValue'});
/// print(response); // SharedOne(success: true, message: '...', data: JSON)
/// ```
/// ---
/// Use this class for document-level CRUD in a collection:
/// create, read, update, delete, and migrate.
///
/// Example:
/// ```dart
/// // Delete a document within a collection
/// final response = await Shared.col('myCollection').doc('documentId').delete();
/// print(response): // SharedNone(success: true, message: '...')
/// final doc = Shared.col('users').doc('userA');
/// await doc.create({'name': 'Alice'});
/// ```
class SharedDocument {
/// Creates a new instance of [SharedDocument].
Expand Down Expand Up @@ -219,16 +200,19 @@ class SharedDocument {
/// final response = await Shared.col('myCollection').doc('doc1').migrate('doc2');
/// print(response); // SharedOne(success: true, message: '...', data: JSON)
/// ```
Future<SharedResponse> migrate(String id,
{bool merge = false, bool force = false,}) async {
Future<SharedResponse> migrate(
String id, {
bool merge = false,
bool force = false,
}) async {
try {
// [1] Get collection 📂.
JSON? collection = await Shared._read(this.collection.id);

// [2] Check collection existence 🔍.
if (collection == null && !force) {
throw 'Unable to migrate the document. '
'The specified collection with ID `${this.collection.id}` does not exist.';
throw 'Unable to migrate the document. '
'The specified collection with ID `${this.collection.id}` does not exist.';
}

// [3] Source and destination cannot be identical.
Expand All @@ -244,7 +228,7 @@ class SharedDocument {
}

// [5] Target existence check.
if (collection?[id] != null && !merge) {
if (collection?[id] != null && !merge && this.id != id) {
throw 'Unable to migrate the document. '
'The target document with ID `$id` already exists. '
'To merge with existing target, set `merge` to true.';
Expand All @@ -267,7 +251,8 @@ class SharedDocument {
this.collection._controller.add({
'id': this.collection.id,
'documents': [
for (var item in ((await Shared._read(this.collection.id)) ?? {}).entries)
for (var item
in ((await Shared._read(this.collection.id)) ?? {}).entries)
{'id': item.key, 'data': item.value},
],
});
Expand Down
10 changes: 4 additions & 6 deletions lib/src/shared_extension.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
part of '../local_shared.dart';

/// Extension methods for enhancing [String] functionality.
/// Utility extensions used internally for JSON encoding/decoding and validation.
extension _StringExtension on String {
/// Decodes a JSON-formatted string into a [JSON] object.
JSON get decode => jsonDecode(this);
Expand Down Expand Up @@ -29,12 +29,10 @@ extension _ListExtension on List {
}

extension JSONExtension on JSON {
/// Merges the current [JSON] object with another [JSON] object.
/// Merge two JSON documents, giving precedence to existing target values.
///
/// The [other] parameter is the [JSON] object to merge into the current object.
/// The merge operation combines the key-value pairs from both objects.
/// If a key exists in both objects and the values are themselves [JSON] or [Map] objects,
/// the values are recursively merged.
/// - Nested maps are merged recursively.
/// - Scalar values in target replace values from source.
JSON merge(JSON other) {
// 1. Create a copy of the other (the target/base)
final result = JSON.from(other);
Expand Down
30 changes: 3 additions & 27 deletions lib/src/shared_many_document.dart
Original file line number Diff line number Diff line change
@@ -1,33 +1,9 @@
part of '../local_shared.dart';

/// Representing a group of document within a [SharedCollection] in [LocalShared] storage.
/// Handles bulk operations for many documents in a collection.
///
/// This class provides methods for creating, reading, updating, and deleting multiple documents
/// within the context of a specific collection.
/// ---
/// ```dart
/// // Create multiple documents within a collection
/// final result = await Shared.col('myCollection').docs(['id1', 'id2', 'id3']).create((index) => {'key': 'value for $id'});
/// print(result); // SharedMany(success: true, message: '...', data: <JSON>[])
/// ```
/// ---
/// ```dart
/// // Read the contents of multiple documents within a collection
/// final response = await Shared.col('myCollection').docs(['id1', 'id2', 'id3']).read();
/// print(response); // SharedMany(success: true, message: '...', data: <JSON>[])
/// ```
/// ---
/// ```dart
/// // Update the contents of multiple documents within a collection
/// final response = await Shared.col('myCollection').docs(['id1', 'id2', 'id3']).update((index) => {'newKey': 'newValue for $id'});
/// print(response); // SharedMany(success: true, message: '...', data: <JSON>[])
/// ```
/// ---
/// ```dart
/// // Delete multiple documents within a collection
/// final response = await Shared.col('myCollection').docs(['id1', 'id2', 'id3']).delete();
/// print(response); // SharedNone(success: true, message: '...')
/// ```
/// Use `SharedCollection.docs(ids)` to update or delete multiple documents, or
/// to merge them into a single target document via [migrate].
class SharedManyDocument {
/// Creates a new instance of [SharedManyDocument].
///
Expand Down
13 changes: 3 additions & 10 deletions lib/src/shared_response.dart
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
part of '../local_shared.dart';

/// Represents the response of an operation in [LocalShared].
/// Base class for structured results returned by LocalShared operations.
///
/// A [SharedResponse] contains information about the success or failure of an operation,
/// along with an optional data payload. This is an abstract class, and concrete implementations
/// include [SharedOne], [SharedMany], and [SharedNone].
///
/// Usage example:
/// ```dart
/// final response = SharedOne(success: true, message: 'Operation successful', data: {'key': 'value'});
/// print(response); // SharedOne(success: true, message: 'Operation successful', data: {'key': 'value'})
/// ```
/// Every result communicates whether the operation succeeded and carries
/// an informative message plus optional payload data.
abstract class SharedResponse<T extends Object> {
/// Creates a new instance of [SharedResponse].
///
Expand Down
13 changes: 8 additions & 5 deletions pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: local_shared
description: A secure wrapper for JSON file-based storage, providing an alternative to Localstore. Safely execute CRUD operations across all platforms with built in support for sensitive data handling.
description: Lightweight cross-platform secure local storage for Flutter. Native-safe JSON collections and documents with tested CRUD, batch operations, migration semantics, and stream updates.
version: 3.0.0
homepage: https://inidia.app
repository: https://github.com/Nialixus/local_shared.git
Expand All @@ -10,11 +10,14 @@ screenshots:
- description: 'Local Shared Logo'
path: logo.png
topics:
- utility
- storage
- flutter
- secure-storage
- shared-preferences
- local-storage
- data-sync
- database
- cache
- secure
- caching
- crud
platforms:
android:
ios:
Expand Down
Loading
Loading