Important notice:Moor has been renamed to Drift. Learn more here.

Type converters

Store more complex data in columns with type converters

Drift supports a variety of types out of the box, but sometimes you need to store more complex data. You can achieve this by using TypeConverters. In this example, we'll use the the json_serializable package to store a custom object in a text column. Drift supports any Dart type for which you provide a TypeConverter, we're using that package here to make the example simpler.

Using converters in Dart

import 'dart:convert';

import 'package:json_annotation/json_annotation.dart' as j;
import 'package:drift/drift.dart';

part 'database.g.dart';

@j.JsonSerializable()
class Preferences {
  bool receiveEmails;
  String selectedTheme;

  Preferences(this.receiveEmails, this.selectedTheme);

  factory Preferences.fromJson(Map<String, dynamic> json) =>
      _$PreferencesFromJson(json);

  Map<String, dynamic> toJson() => _$PreferencesToJson(this);
}

Next, we have to tell drift how to store a Preferences object in the database. We write a TypeConverter for that:

// stores preferences as strings
class PreferenceConverter extends TypeConverter<Preferences, String> {
  const PreferenceConverter();
  @override
  Preferences? mapToDart(String? fromDb) {
    if (fromDb == null) {
      return null;
    }
    return Preferences.fromJson(json.decode(fromDb) as Map<String, dynamic>);
  }

  @override
  String? mapToSql(Preferences? value) {
    if (value == null) {
      return null;
    }

    return json.encode(value.toJson());
  }
}

Finally, we can use that converter in a table declaration:

class Users extends Table {
  IntColumn get id => integer().autoIncrement()();
  TextColumn get name => text()();

  TextColumn get preferences =>
      text().map(const PreferenceConverter()).nullable()();
}

The generated User class will then have a preferences column of type Preferences. Drift will automatically take care of storing and loading the object in select, update and insert statements. This feature also works with compiled custom queries.

Implicit enum converters

A common scenario for type converters is to map between enums and integers by representing enums as their index. Since this is so common, drift has the integrated intEnum column type to make this easier.

enum Status { 
   none, 
   running, 
   stopped, 
   paused 
}

class Tasks extends Table {
  IntColumn get id => integer().autoIncrement()();
  IntColumn get status => intEnum<Status>()();
}

Also note that you can't apply another type converter on a column declared with an enum converter.

Using converters in drift

Type converters can also be used inside drift files. Assuming that the Preferences and PreferenceConverter are contained in preferences.dart, that file can imported into drift for the type converter to be available.

import 'preferences.dart';

CREATE TABLE users (
  id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
  name TEXT,
  preferences TEXT MAPPED BY `const PreferenceConverter()`
);

When using type converters in drift files, we recommend the apply_converters_on_variables build option. This will also apply the converter from Dart to SQL, for instance if used on variables: SELECT * FROM users WHERE preferences = ?. With that option, the variable will be inferred to Preferences instead of String.

Drift files also have special support for implicit enum converters:

import 'status.dart';

CREATE TABLE tasks (
  id INTEGER NOT NULL PRIMARY KEY,
  status ENUM(Status)
);

Of course, the warning about automatic enum converters also applies to drift files.