flutter_pom

Flutter Persistent Object Mapper based on sqflite provides model based SQLite access including automatic database creation

Installing

Add dependency

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

dependencies:
  flutter_pom: ^0.0.3

Install the plugin

Now you can install the package (if you got it from pub.dev) via console.

$ flutter pub get

Import the plugin

Finally you have to import the plugin into your Dart source

import 'package:flutter_pom/flutter_pom.dart';

Example

To kickstart with the newly installed library you will have to create the necessary model and database classes.

Create the Table-model

The table model represents the configuration of your table. This includes all columns and the table name. In order to work this needs to extend from Table (flutter_pom).

class SampleTable extends Table {
  // The constructor has to call 'super' with the name of the table
  SampleTable() : super("sample_table");
  
  // These are the fields that we define for the table.
  // Each field corresponds to a table column
  
  final IdField id = IdField("id").autoIncrement();
  final StringField str = StringField("str");
  
  // You have to override the method 'getInstance' for the
  // deserializer to get a new instance of your type as dart
  // does not support reflection well by now.
  
  @override
  Table getInstance() {
    return SampleTable();
  }
  
  // initializeFields provides the TableBuilder in the background
  // with all defined fields. As dart does not support reflection 
  // this is our way to go.
  
  @override
  List<Field> initializeFields() {
    return [
      id,
      str
    ];
  }
}

Create the Database model

Next you have to create a database model. The model needs to be inherited from Database (flutter_pom). The database model contains all tables that you want to access inside the specified database.

Note: There can be more than one database model inside your app

class SampleDb extends Database {
  // The constructor has to call 'super' with the database name
  SampleDb() : super("sample.db");
  
  // initializeDatabase provides the DatabaseBuilder in the background
  // with all containing databases. As dart does not support reflection
  // this is our way to go.
  
  @override
  Map<Type, Table> initializeDatabase() {
    return <Type, Table>{
      SampleTable: SampleTable()
    };
  }
}

Use the database in your App Logic

Now its time to make use of the newly created database and tables.

void Do() async {
  
    // initialize the database
    var db = SampleDb();
    
    // open() the connection to the database. 
    // This method has to be called once before 
    // accessing the database
    await db.open();
    
    // Get the automatically created context of the 
    // table 'SampleTable'
    var context = await db.of<SampleTable>();
    
    // Create a new SampleTable item (think of it as a row)
    var sampleItem = SampleTable();
    
    // Access the str field
    sampleItem.str.value = "String value";
    
    // Put the item into the database
    await context.put(sampleItem);
    
    // Get all items 
    var itemsFilter = await context.select();
    
    // A complex filter scenario with comparison of 
    // field values and ordering
    // We also support limiting and offsets
    var itemsFilter2 = await c.select((q) {
          return q
                 .where(c.fields.idField.gte(3))
                 .and(c.fields.idField.lte(20))
                 .orderByAsc([c.fields.idField])
                 .limit(40)
                 .offset(2);
        });    
    
    // NOTICE: There is an alternative way of querying 
    // data using Dart included test methods.
    // The downside of this approach is that all data will 
    // be queried and cached before the
    // filtering begins. 
    // For datasets > 1k items you should use the 'select' 
    // method to do filtering on db level
    // instead.
    //
    // The following query filters the exact same data 
    // like 'itemsFilter2' without ordering 
    // and limiting. This can later be done with dart language 
    
    var itemsFilter3 = await c.where((i) => 
                                i.idField.value >= 3 &&
                                i.idField.value <= 20);
    
    // Count all items
    var count1 = await c.count();
    
    // Count all items where id >= 3 and id <= 20
    var count2 = await c.count((q) {
           return q
                  .where(c.fields.idField.gte(3))
                  .and(c.fields.idField.lte(20));
        });
    
    // Delete the item
    await context.delete(sampleItem);
    
    // Update the item. Only changed values will be updated.
    await context.update(sampleItem);
    
    // Register for the onCreate Stream that gets fired 
    // everytime somebody adds an item
    context.onCreate.listen((sampleItem) {
      // do something with the item
    });
}

Relating Tables

Starting with version 0.1.23 you can now bind tables to a field. This gives you the chance to create dependent tables you can query with a new list extension.

For now only 1:1 relations are supported.

The following code will give you an example of how to get started:

class User extends Table {
  /// We only show things that changed here. 
  /// The Table implementation is still the same 
  /// as described prior.
  StringField userName = StringField("user_name").notNull();
 
  /// Here we reference the job table. 
  /// The framework will internally save the id of the
  /// referenced table item only
  KeyField<Job> job = KeyField<Job>("job_id");
}

/// You may want to manage existing jobs in a seperate table
class Job extends Table {
  /// Same as above
  StringField jobName = StringField("job_name").notNull();
}


/// Example Method adding a new user with a *new* job
void addUser(User user, Job job) async {
  var users = await db.of<User>();  
  var jobs = await db.of<Job>();
  
  user.job.binding = job;
  
  await jobs.put(job);
  await users.put(user);
}

/// Example method reading all users and resolving 
/// all job items automatically
void getUsers() async {
  var users = await db.of<User>();
  var jobs = await db.of<Job>();
  
  /// Here we select all users and then call the method
  /// 'include' on the resulting list.
  /// 'include' expects the database context and the field
  /// you want to resolve.
  var userList = users.select().include<Job>(db, User().job);
}

Indexing fields

You can easily let POM create indexes for your fields by adding .withIndex() to the field definition:

StringField email = StringField("email").withIndex(unique: true);

You can also define whether the indexed values shall be unique or not.

Transaction Support (since 0.1.26)

You can easily manage your transactions by using the 'transaction()' method of your database and provide it to all supported methods inside your context.

Example:

var txn = db.transaction();

await db.of<User>().add(newUser, txn);
await db.of<User>().deleteById(5, txn);

await txn.commit();

Libraries

base_model_context
base_model_transaction
bool_field
database
datetime_field
double_field
duplicate_field_error
field
field_constraint_error
flutter_pom
flutter_pom
id_field
integer_field
key_field
list_extensions
migration_context
migration_info
missing_field_error
missing_primary_key_error
model_context
model_transaction
multiple_primary_key_error
object_field
pom_logger
query_builder
query_count_builder
query_delete_builder
query_distinct_builder
query_select_builder
secure_string_field
serializable
sql_condition
sql_selector
sql_types
sql_where_selector
string_field
table_configuration_error
update_builder