frideos_kvprx 0.4.0

  • Readme
  • Changelog
  • Example
  • Installing
  • 50

frideos_kvprx pub package

frideos_kvprx is a library that offers persistent and reactive classes, and helpers to easily store key/value pairs in the database, using the sqflite plugin.

  • DbProvider : class used to inizialize the database.
  • PersistentValue and derived classes : classes derived from the StreamedValue class of the frideos_core package to make a key/value pair persistent and reactive using streams. Used along with the StreamBuilder or the ValueBuilder widget of the frideos package, make it possible to update the UI with the last value on the stream and save the value to the database every time a new value is set.
  • KeyValue : class to handle a key/value pair.
  • KeyValueProvider : class with methods to get, insert, update, delete the key/value pairs stored in the db.
  • KeyValueMetaProvider : similar to the KeyValueProvider, but it is possible add a tag to every key/value pair.

DbProvider #

Initialization #

// Initialize a database.
// By default the database name is set to 'appdb.db'.
final dbProvider = DbProvider(databaseName: 'test.db');

// Init the dabase
await dbProvider.init();  

Close the connection #

await dbProvider.close();  

Delete the current database #

await dbProvider.deleteDatabase();

Truncate a table of the database #

await dbProvider.truncate('tableName');

PersistentValues #

These classes derive from the PersistentValue class that extends the StreamedValue class of the frideos package in order to take advantages of the streams, so that, every time a new value is set, this is both stored in the database (if the flag continuousSave is set to true ) and sent to the stream to drive a StreamBuilder or a ValueBuilder to update the UI.

- PersistentString #

- PersistentBoolean #

- PersistentInt #

- PersistentDouble #

Usage: #

1 - Declare an instance of a PersistentValue derived object

  • table is set by default to 'kvp', so that if not specified, all the instances of a PersistentValue derived class, will store their values to the same table.

  • persistentKey is the key associated to the PersistentValue derived entity.

  • initialData is used when no record is found in the database to make an insert with the first key/value pair having as key the argument passed to the persistentKey parameter and for value the argument passed to the initialData parameter.

  • continuousSave is set by default to true, so that every time to the persistentString is given a new value, the record in the db will be updated. If set to false, to update the record in the db, it is necessary to call the save method.

  final persistentString = PersistentString(
      table: 'kvprx',      
      persistentKey: 'persistentValueString',
      initialData: 'ABCDEFGHIJKLMNOPRQSTUVWXY',
      continuousSave: true);

2 - Initialization #

// Initialize the dbProvider
await dbProvider.init();

// Call the init method of the `PersistentString` class to initialize
// the KeyValueProvider, check if a record is already present in the db
// and set it as the current value. If no record is found, the argument passed
// to the `initialData` parameters is used to insert a new record in the db.
await persistentString.init(dbProvider: dbProvider);

3 - Change the value #

// Every time a new value is set, this will be sent to stream and stored 
// in the database.
persistentString.value = 'New String';

// Only if `continuousSave` is set to `false` it is necessary to call
// the `save` method in order to update the record in the db.
// await persistentString.save();

KeyValueProvider #

By default the table name is set to 'kvp'. It is important to notice that if more KeyValueProvider are created with the default table name, both the getAll and truncate method will affects all the records. To avoid this behavior, use the table paramater to give to each provider a different table name.

Initialization #

    final dbProvider = DbProvider(databaseName: 'test.db');
    await dbProvider.init();

    final kvpDb = KeyValueProvider(dbProvider: dbProvider);
    var init = await kvpDb.init();

INSERT #

- Insert by insert method #

    final kvpTest1 = KeyValue(key: 'testKeyValue', value: 'test value');
    final kvpTest2 = KeyValue(key: 'testKeyValue2', value: 'test value2');

    // INSERT
    await kvpDb.insert(kvpTest1);
    await kvpDb.insert(kvpTest2);

- Insert by insertKeyValue method #

    await kvpDb.insertKeyValue('key', 'value')

- Bulk insert #

 final kvps = [
      KeyValue(key: 'testKeyValue1', value: 'test value1'),
      KeyValue(key: 'testKeyValue2', value: 'test value2'),
      KeyValue(key: 'testKeyValue3', value: 'test value3'),
      KeyValue(key: 'testKeyValue4', value: 'test value4'),
      KeyValue(key: 'testKeyValue5', value: 'test value5'),
      KeyValue(key: 'testKeyValue6', value: 'test value6'),
    ];

  await kvpDb.bulkInsert(kvps);  

- Insert a map #

Given a map of type <String, String>, this method save all of its key/value pairs in the database.

final map = {'1': 'a', '2': 'b', '3': 'c', '4': 'd'};

// INSERT
await kvpDb.insertMap(map);

GET #

- Get all key/value pairs #

Get all the key/value pairs stored in the table. It is important to notice that if more KeyValueProvider share the same table (by default is set to 'kvp'), this method will get the ones created with other providers. To avoid this behavior, use the table parameter to specify a different table.

    // GET ALL
    final pairs = await kvpDb.getAll();

- Get single kvp by getKeyValue method #

final kvpTest1 = KeyValue(key: 'testKeyValue', value: 'test value');
var kvp1 = await kvpDb.getKeyValue(kvpTest1);

- Get single kvp by getById method #

var kvp1 = await kvpDb.getById(id);

- Get single kvp by getByKey method #

var kvp1 = await kvpDb.getByKey('key');

UPDATE #

- Update single kvp by update method #

var kvp1 = await kvpDb.getByKey('key');
await kvpDb.update(kvp1, 'newValue');

- Update single kvp by updateById method #

await kvpDb.updateById(id, 'newValue');

- Update single kvp by updateByKey method #

await kvpDb.updateByKey('key', 'newValue');

- Bulk update #


final kvps = [
      KeyValue(key: 'testKeyValue1', value: 'test value1'),
      KeyValue(key: 'testKeyValue2', value: 'test value2'),
      KeyValue(key: 'testKeyValue3', value: 'test value3'),
      KeyValue(key: 'testKeyValue4', value: 'test value4'),
      KeyValue(key: 'testKeyValue5', value: 'test value5'),
      KeyValue(key: 'testKeyValue6', value: 'test value6'),
    ];

    // INSERT
    await kvpDb.bulkInsert(kvps);

    // GET ALL
    var pairs = await kvpDb.getAll();
    
    // Change the value of each kvp
    var updatedKvps = List<KeyValue>();
    pairs.forEach((p) => updatedKvps
        .add(KeyValue(key: p.key, value: 'newValue ${pairs.indexOf(p)}')));

    // UPDATE
    await kvpDb.bulkUpdate(updatedKvps);

DELETE #

- Delete single kvp by delete method #

var kvp1 = await kvpDb.getByKey('key');
await kvpDb.delete(kvp1);

- Delete single kvp by deleteById method #

await kvpDb.deleteById(id);

- Delete single kvp by deleteByKey method #

await kvpDb.deleteByKey('key');

- Bulk delete keys starting with a given prefix #

final kvps = [
      KeyValue(key: 'prefix_Value1', value: 'test value1'),
      KeyValue(key: 'prefix_Value2', value: 'test value2'),
      KeyValue(key: 'Prefix2Value3', value: 'test value3'),
      KeyValue(key: 'Prefix2Value4', value: 'test value4'),
      KeyValue(key: 'Value5', value: 'test value5'),
    ];

// INSERT
await kvpDb.bulkInsert(kvps);

// Bulk delete (2 out of 5)
await kvpDb.bulkDeleteKeysStartWith('prefix');

- Bulk delete a list of kvp #

final kvps = [
      KeyValue(key: 'testKeyValue1', value: 'test value1'),
      KeyValue(key: 'testKeyValue2', value: 'test value2'),
      KeyValue(key: 'testKeyValue3', value: 'test value3'),
      KeyValue(key: 'testKeyValue4', value: 'test value4'),
      KeyValue(key: 'testKeyValue5', value: 'test value5'),
      KeyValue(key: 'testKeyValue6', value: 'test value6'),
    ];

// INSERT
await kvpDb.bulkInsert(kvps);

// Bulk delete (3 out of 6)
await kvpDb.bulkDeleteKeys(['testKeyValue3', 'testKeyValue4','testKeyValue5']);

TRUNCATE #

To delete all the records in the table. It is important to notice that if more provider used the same table (set by defalt to 'kvp') this method will delete even the key/value pairs created with the other providers. To avoid this behavior, initialize the KeyValueProvider giving to the table parameter a different value for each provider.

await kvpDb.truncate();

KeyValueMetaProvider #

By default the table name is set to 'kvpmeta'. It is important to notice that if more KeyValueMetaProvider are created with the default table name, both the getAll and truncate method will affects all the records. To avoid this behavior, use the table paramater to give to each provider a different table name.

It has most of the methods of the KeyValueProvider class. When needed, it uses the KeyValueMeta model class instead.

E.g. insert two kvpmeta:

    final kvpTest1 = KeyValueMeta(key: 'testKeyValue', value: 'test value', meta: 'meta');
    final kvpTest2 = KeyValueMeta(key: 'testKeyValue2', value: 'test value2', meta: 'meta2');   

    // INSERT
    await kvpDb.insert(kvpTest1);
    await kvpDb.insert(kvpTest2);

Initialization #

    final dbProvider = DbProvider(databaseName: 'test.db');
    await dbProvider.init();

    final kvpDb = KeyValueMetaProvider(dbProvider: dbProvider);
    var init = await kvpDb.init();

- Get a list of kvp by getByMeta #

    await kvpDb.insertKeyValueMeta('key1', 'value1', 'meta1');
    await kvpDb.insertKeyValueMeta('key2', 'value2', 'meta1');
    await kvpDb.insertKeyValueMeta('key3', 'value3', 'meta1');
    await kvpDb.insertKeyValueMeta('key4', 'value1', 'meta2');
    await kvpDb.insertKeyValueMeta('key5', 'value2', 'meta2');
    await kvpDb.insertKeyValueMeta('key6', 'value3', 'meta2');

    // Get all the kvp with the `meta` parameter equal to 'meta2'
    final pairs = await kvpDb.getByMeta('meta2');

- Update the meta parameter of a kvp #

    await kvpDb.insertKeyValueMeta('key1', 'value1', 'meta1');

    // change the `meta` parameter from 'meta1' to 'meta2'
    await kvpDb.updateMeta('key1', 'meta2');

- Bulk update meta #

    await kvpDb.insertKeyValueMeta('key1', 'value1', 'meta1');
    await kvpDb.insertKeyValueMeta('key2', 'value2', 'meta1');
    await kvpDb.insertKeyValueMeta('key3', 'value3', 'meta1');
    await kvpDb.insertKeyValueMeta('key4', 'value1', 'meta2');
    await kvpDb.insertKeyValueMeta('key5', 'value2', 'meta2');
    await kvpDb.insertKeyValueMeta('key6', 'value3', 'meta2');

    // the parameter `meta` of all kvps with the meta equal to `meta2` will change to `meta3`
    await kvpDb.bulkUpdateMeta('meta2', 'meta3');

[0.4.0] - 21/06/2019

  • Updated to frideos_core 0.5.0

[0.3.0+1] - 20/05/2019

  • Added the new KeyValueMetaProvider.
  • Code refactoring

[0.2.0] - 11/05/2019

  • Added the new method bulkDeleteKeysStartWith to the KeyValueProvider to mass delete keys starting with a given prefix.
  • Updated to frideos_core 0.4.4.

[0.1.1] - 03/05/2019

  • Example updated
  • Readme.md updated
  • Documentation improved
  • Added some tests

[0.1.0] - 01/05/2019

  • Initial release.

example/main.dart

import 'dart:async';
import 'dart:math' as math;

import 'package:flutter/material.dart';

import 'package:frideos_core/frideos_core.dart';

import 'package:frideos_kvprx/frideos_kvprx.dart';

const style = TextStyle(fontWeight: FontWeight.w500);

// Key for the first key/value pair
const kvp1Key = 'key1';

// Key for the second key/value pair
const kvp2Key = 'key2';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Frideos KvpRx demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  // Initialize the DbProvider. This will create a connection to a
  // database stored in a file named 'example.db'.
  final dbProvider = DbProvider(databaseName: 'example.db');

  // Declare a KeyValueProvider to handle all the key/value
  // pairs stored in the database.
  KeyValueProvider kvp1;

  // Another KeyValueProvider can be used to store key/value pairs
  // in another table of the same database (e.g. one table 'appstate'
  // to store info about the appstate and another one name 'users' or
  // 'products' for other purposes).
  //
  // It is important to notice that by default the table is set to 'kvp'.
  // So, it is necessary to use the parameter `table` on the second provider
  // in order to not share the same table.
  KeyValueProvider kvp2;

  // These two StreamedValue are used to update the UI with the
  // value of the key/value pairs stored in the database.
  final keyValue1 = StreamedValue<KeyValue>();
  final keyValue2 = StreamedValue<KeyValue>();

  // A persistentString is used to store a kvp with a value of type String.
  // If provided, the `initialData` parameter is used to make the first insert
  // in the db if no value associated with the given `persistentKey` is found.
  //
  // It is important to notice the in this example all the persistentValues
  // share the same table 'kvprx'. It could be possible create a
  // KeyValueProvider and initialize it with the `table` parameters set to
  // 'kvprx' in order to handle these key/value/pairs (e.g. get all the
  // key/value pairs stored).
  final persistentString = PersistentString(
      table: 'kvprx',
      persistentKey: 'persistentValueString',
      initialData: 'ABCDEFGHIJKLMNOPRQSTUVWXY',
      continuousSave: true);

  final persistentBoolean = PersistentBoolean(
      table: 'kvprx', persistentKey: 'persistentBoolean', initialData: true);

  final persistentInteger = PersistentInteger(
      table: 'kvprx', persistentKey: 'persistentInteger', initialData: 10);

  final persistentDouble = PersistentDouble(
      table: 'kvprx',
      persistentKey: 'persistentValueDouble',
      initialData: 12.44);

  Future<void> init() async {
    // Uncomment to delete the database.
    // await dbProvider.deleteDatabase();

    // Step 1: DbProvider instance initialization.
    await dbProvider.init();

    // Step 2: PersistentValue / KeyValueProvider initialization.
    await persistentString.init(dbProvider: dbProvider);
    await persistentBoolean.init(dbProvider: dbProvider);
    await persistentInteger.init(dbProvider: dbProvider);
    await persistentDouble.init(dbProvider: dbProvider);

    // If a second table is needed to the second KeyValueProvider
    // the `table` parameter will be different.
    kvp1 = KeyValueProvider(dbProvider: dbProvider, table: 'kvp');
    kvp2 = KeyValueProvider(dbProvider: dbProvider, table: 'kvp2');

    await kvp1.init();
    await kvp2.init();

    // Cheking if already exists
    var result = await kvp1.getByKey(kvp1Key);
    if (result == null) {
      // Insert the first kvp
      var toAdd = KeyValue(key: kvp1Key, value: 'ABCDEFGHIJKLMNOPRQSTUVWXY');
      await kvp1.insert(toAdd);

      // get the kvp inserted by using its key (in this case this is step
      // is redundant because we could just use the `toAdd.value`)
      keyValue1.value = await kvp1.getByKey(kvp1Key);
    } else {
      // A record is already present in the database, then send to stream its value
      // and update the UI.
      keyValue1.value = result;
    }

    // INSERT by insertKeyValue
    result = await kvp2.getByKey(kvp2Key);
    if (result == null) {
      await kvp2.insertKeyValue(kvp2Key, 'ABCDEFGHIJKLMNOPRQSTUVWXY');
      keyValue2.value = await kvp2.getByKey(kvp2Key);
    } else {
      keyValue2.value = result;
    }
  }

  Future<void> updateRx() async {
    var rand = math.Random();
    var nextChar = persistentString.value[rand.nextInt(25)];

    // Every time a new value is set, the record in the database
    // is updated. To avoid this behavior set to `false` the
    // `continuousSave` in PersistentValue derived objects initialization
    // and call the `save` method to update the record in the database
    // whenever you need.
    persistentString.value += nextChar;
    persistentBoolean.value = !persistentBoolean.value;
    persistentDouble.value += 0.51;
    persistentInteger.value += 2;
  }

  Future<void> updateValues() async {
    var rand = math.Random();

    // Update KeyValue
    var oldValue = keyValue1.value.value;
    var nextChar = oldValue[rand.nextInt(25)];
    await kvp1.update(keyValue1.value, oldValue + nextChar);

    // Redundant step, only to show how to get kvp by id. In a normal case
    // you would assign to keyValue1.value the same value used to update
    // the record ('oldValue + nextChar') to avoid an unnecessary query.
    keyValue1.value = await kvp1.getById(keyValue1.value.id);

    // Update by key
    var oldValue2 = keyValue1.value.value;
    nextChar = oldValue2[rand.nextInt(25)];
    await kvp2.updateByKey(kvp2Key, oldValue2 + nextChar);
    keyValue2.value = await kvp2.getByKey(keyValue2
        .value.key); // Redundant step, only to show how to get a kvp by key.
  }

  @override
  void initState() {
    super.initState();
    init();
  }

  @override
  void dispose() {
    dbProvider.close();
    keyValue1.dispose();
    keyValue2.dispose();
    persistentString.dispose();
    persistentBoolean.dispose();
    persistentDouble.dispose();
    persistentInteger.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Frideos KvpRx demo'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: <Widget>[
            // As an alternative, you could use the ValueBuilder widget
            // of the frideos package:
            //
            // ValueBuilder<bool>(
            //    streamed: persistentBoolean
            //
            StreamBuilder<bool>(
                stream: persistentBoolean.outStream,
                builder: (context, snapshot) {
                  return !snapshot.hasData
                      ? Container()
                      : Column(
                          children: <Widget>[
                            const Text('PersistentBoolean:', style: style),
                            Text(
                              '${snapshot.data}',
                            ),
                            Divider(),
                          ],
                        );
                }),
            // ValueBuilder<int>(
            //    streamed: persistentInteger
            //
            StreamBuilder<int>(
                stream: persistentInteger.outStream,
                builder: (context, snapshot) {
                  return !snapshot.hasData
                      ? Container()
                      : Column(
                          children: <Widget>[
                            const Text('PersistentInteger:', style: style),
                            Text(
                              snapshot.data.toString(),
                            ),
                            Divider(),
                          ],
                        );
                }),
            // ValueBuilder<double>(
            //    streamed: persistentDouble
            //
            StreamBuilder<double>(
                stream: persistentDouble.outStream,
                builder: (context, snapshot) {
                  return !snapshot.hasData
                      ? Container()
                      : Column(
                          children: <Widget>[
                            const Text('PersistentDouble:', style: style),
                            Text(
                              '${snapshot.data.toStringAsFixed(2)}',
                            ),
                            Divider(),
                          ],
                        );
                }),
            // ValueBuilder<String>(
            //    streamed: persistentString
            //
            StreamBuilder<String>(
                stream: persistentString.outStream,
                builder: (context, snapshot) {
                  return !snapshot.hasData
                      ? Container()
                      : Column(
                          children: <Widget>[
                            const Text('PersistentString:', style: style),
                            Text(
                              '${snapshot.data}',
                            ),
                            Divider(),
                          ],
                        );
                }),
            // ValueBuilder<KeyValue>(
            //    streamed: keyValue1
            //
            StreamBuilder<KeyValue>(
                stream: keyValue1.outStream,
                builder: (context, snapshot) {
                  return !snapshot.hasData
                      ? Container()
                      : Column(
                          children: <Widget>[
                            const Text('KeyValue 1:', style: style),
                            Text(
                              snapshot.data.value,
                            ),
                            Divider(),
                          ],
                        );
                }),
            // ValueBuilder<KeyValue>(
            //    streamed: keyValue2
            //
            StreamBuilder<KeyValue>(
                stream: keyValue2.outStream,
                builder: (context, snapshot) {
                  return !snapshot.hasData
                      ? Container()
                      : Column(
                          children: <Widget>[
                            const Text('KeyValue 2:', style: style),
                            Text(
                              snapshot.data.value,
                            ),
                          ],
                        );
                }),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              children: <Widget>[
                RaisedButton(
                  onPressed: updateRx,
                  child: const Text('Update KvpRxs',
                      style: TextStyle(color: Colors.white)),
                  color: Colors.blue,
                ),
                RaisedButton(
                  onPressed: updateValues,
                  child: const Text('Update key values',
                      style: TextStyle(color: Colors.white)),
                  color: Colors.blue,
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  frideos_kvprx: ^0.4.0

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter pub get

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:frideos_kvprx/frideos_kvprx.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
0
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
100
Overall:
Weighted score of the above. [more]
50
Learn more about scoring.

We analyzed this package on Oct 15, 2019, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.5.1
  • pana: 0.12.21
  • Flutter: 1.9.1+hotfix.4

Platforms

Detected platforms: Flutter

References Flutter, and has no conflicting libraries.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.1.0 <3.0.0
flutter 0.0.0
frideos_core ^0.5.0 0.5.0+1
meta ^1.1.6 1.1.7
path ^1.6.2 1.6.4
sqflite ^1.1.5 1.1.7+1
Transitive dependencies
collection 1.14.11 1.14.12
rxdart 0.22.3
sky_engine 0.0.99
synchronized 2.1.0+1
typed_data 1.1.6
vector_math 2.0.8
Dev dependencies
flutter_test
test ^1.5.3