flutterdartobjectboxflutter-objectboxobjectbuilder

How setup dart objectbox with a local database pre-populated?


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();
    });
  }
}

Solution

  • 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());
      }
    }