Flutter — Persisting data locally using sembast.

Shivmaurya
4 min readJun 4, 2022

--

Photo by Jan Antonin Kolar on Unsplash

Part 1: Opening a sembast database and adding a new document.

Sembast(Simple Embedded Application Store) is a document based database which is saved to a single file within the device, and all documents are saved in JSON format, the sembast package is written in dart and as of this writing it currently supports Android, IOS, Linux, Web, Mac OS and Windows.

Sembast supports several datatypes including String, num (int and double), Map (Map<String, dynamic>), List (List<dynamic>), bool, null (value), Blob (custom type), TimeStamp (custom type).

Supporting Blob means you can store files like images, audios and other multimedia objects in the database itself.

If we were talking about relational databases table’s are the container’s of data. In sembast you use stores instead you could see a store as a sort of folder within the database, The stores are set’s of Map’s that are saved on disk and the values of the Map are object of generic type.

The only requirement using it is adding the package to your pubspec.yaml file.

sembast: ^3.2.0
path_provider: ^2.0.10

In this case we will add two libraries sembast itself and path_provider which we need to find the path were we will save the database.

Let’s code

We will just be needing two files, one is record.dart which will be our model class and sembast_db.dart which will interact with the database.

So let’s add a model class called record.dart

class Record {
int? id;
late String record;

Record(
this.record,
);

Map<String, dynamic> toMap() {
return {
'id': id,
'record': record,
};
}

Record.fromMap(Map<String, dynamic> map) {
id = map['id'];
record = map['record'];
}
}

And lastly create a new class that will interact with the database, let’s create a new file called sembast_db.dart.

Let’s declare a DatabaseFactory and let’s set it to databaseFactoryIo.

Next we will declare the Database calling it _db.

import 'dart:async';
import 'dart:io';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart';
import 'package:sembast/sembast.dart';
import 'package:sembast/sembast_io.dart';

class SembastDb {
final DatabaseFactory _dbFactory = databaseFactoryIo;
late Database _db;
}

Now we will create the method that opens the database, it returns a future and we will call it _openDb() and mark it as async.

Future<Database> _openDb() async {
final Directory docsDir= await getApplicationDocumentsDirectory();
final String dbPath = join(docsDir.path, 'record.db');
final Database _db = await _dbFactory.openDatabase(dbPath);
return _db;
}

Now we also need to specify the location inside the database where the data will be saved. Let’s set the declaration which specifies that we want to save all our data in the records store. It is a constant called store which calls the store() method of intMapStoreFactory.store() passing records.

records is just the name of our store.

final store = intMapStoreFactory.store('records');

Since the sembastDb class will be called from multiple screens in our app but we only need one instance of it through the lifecycle of our app.

So we will use the singleton approach, which means instead of creating a new instance of sembastDb we always return the same static singleton.

static final SembastDb _singleton = SembastDb._internal();

SembastDb._internal() {}

factory SembastDb() {
return _singleton;
}

Now let’s create a method that will also make sure we use the same instance of the database.

Future<Database> init() async {
if (_db == null) {
_db = await _openDb();
}
return _db;
}

We are now ready to add a method that adds new document to the store. So the add method returns an integer that acts as an unique identifier for the data you have entered.

Future<int> addRecord({required Record record}) async{
int id = await store.add(_db, record.toMap());
return id;
}

Here’s the complete sembast_db.dart file.

import 'dart:async';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart';
import 'package:sembast/sembast.dart';
import 'package:sembast/sembast_io.dart';
import '../models/record.dart';

class SembastDb {
final DatabaseFactory _dbFactory = databaseFactoryIo;
late Database _db;
final store = intMapStoreFactory.store('records');
static final SembastDb _singleton = SembastDb._internal();

SembastDb._internal() {}

factory SembastDb() {
return _singleton;
}

Future<Database> init() async {
if (_db == null) {
_db = await _openDb();
}
return _db;
}

Future<Database> _openDb() async {
final Directory docsDir = await getApplicationDocumentsDirectory();
final String dbPath = join(docsDir.path, 'record.db');
final Database _db = await _dbFactory.openDatabase(dbPath);
return _db;
}

Future<int> addRecord({required Record record}) async {
int id = await store.add(_db, record.toMap());
return id;
}
}

After adding data we also need to Read, Update and Delete them so let’s see how to do those actions next.

If you found it useful, or have some suggestions or feedback do let me know in the comments below.

--

--

Shivmaurya
Shivmaurya

No responses yet