I want to set up an ObjectBox database with Flutter. I would like to pre-populate the database file with values. When installing the application, the database file will be copied and is used by the application. I want to be able to continue to provide schema migrations. Is it possible?
How to set up this type of architecture? Have you any example?
SOLUTION : With @vaind response, I have implemented a database Manager like :
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/services.dart';
import 'package:path_provider/path_provider.dart';
import '../objectbox.g.dart';
/// Database manager manage database initialisation and configuration.
class DatabaseManager {
static DatabaseManager? _instance;
DatabaseManager._internal();
/// Singleton Factory constructor
factory DatabaseManager() {
if (_instance == null) {
_instance = DatabaseManager._internal();
}
return _instance!;
}
/// Initialize configuration of database:
/// - Init database file by copying.
/// - Open instance of database and close it.
Future<void> init() async {
await copyDatabaseFileFromAssets();
await testInstanceAndCloseIt();
}
/// Copy packaged database from the assets folder to the app directory.
Future<void> copyDatabaseFileFromAssets() async {
// Get a pre-populated DB file.
ByteData data = await rootBundle.load("assets/databases/data.mdb");
List<int> bytes =
data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
// Search and create DB file destination folder if not exist
Directory documentsDirectory = await getApplicationDocumentsDirectory();
String path = documentsDirectory.path + "/objectbox/data.mdb";
if (!await File(path).exists()) {
var objectBoxDirectory = Directory(documentsDirectory.path + "/objectbox/");
if ((!await objectBoxDirectory.exists())) {
objectBoxDirectory.create();
}
// Copying source data into destination file.
await new File(path).writeAsBytes(bytes);
}
}
/// Open an instance of the database and close it.
Future<void> testInstanceAndCloseIt() async {
await openStore().then((Store store) {
store.close();
});
}
}
Yes, even if you want to avoid populating the database in-app (e.g. on the first run), you can bundle an existing database. Just create the database file (data.mdb
) locally, even on your machine (they're compatible across platforms) and than add it to the app as a resource.
On the first app run you can then just move the data.mdb
file to the application documents directory (into subdirectory objectbox
) - that's where ObjectBox stores the by default if you've used the generated openStore()
method. So the path to the database file should be (using package path_provider
):
(await getApplicationDocumentsDirectory()).path + '/objectbox/data.mdb'
. Make sure you open the store after you write the file, of course.
Update after the question has been updated. I think your copyDatabaseFileFromAssets
should look more like the following. Create the database file only if it doesn't exist.
Future<void> copyDatabaseFileFromAssets() async {
// Search and create db file destination folder if not exist
final documentsDirectory = await getApplicationDocumentsDirectory();
final objectBoxDirectory = Directory(documentsDirectory.path + '/objectbox/');
if (!objectBoxDirectory.existsSync()) {
await objectBoxDirectory.create(recursive: true);
}
final dbFile = File(objectBoxDirectory.path + '/data.mdb');
if (!dbFile.existsSync()) {
// Get pre-populated db file.
ByteData data = await rootBundle.load("assets/databases/data.mdb");
// Copying source data into destination file.
await dbFile.writeAsBytes(data.buffer.asUint8List());
}
}