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

Getting started

Simple guide to get a drift project up and running

Note: If you prefer a tutorial video, Reso Coder has made a detailed video explaining how to get started. You can watch it here.

A complete cross-platform Flutter app using drift is also available here.

Adding the dependency

First, lets add drift to your project's pubspec.yaml. At the moment, the current version of drift is Drift version and the latest version of drift_dev is Generator version.

dependencies:
  drift: ^1.7.1
  sqlite3_flutter_libs: ^0.5.0
  path_provider: ^2.0.0
  path: ^1.8.2

dev_dependencies:
  drift_dev: ^1.7.0
  build_runner: ^2.1.11

If you're wondering why so many packages are necessary, here's a quick overview over what each package does:

  • drift: This is the core package defining most apis
  • sqlite3_flutter_libs: Ships the latest sqlite3 version with your Android or iOS app. This is not required when you're not using Flutter, but then you need to take care of including sqlite3 yourself. For an overview on other platforms, see platforms.
  • path_provider and path: Used to find a suitable location to store the database. Maintained by the Flutter and Dart team
  • drift_dev: This development-only dependency generates query code based on your tables. It will not be included in your final app.
  • build_runner: Common tool for code-generation, maintained by the Dart team

Some versions of the Flutter tool create a broken settings.gradle on Android, which can cause problems with drift/native.dart. If you get a "Failed to load dynamic library" exception, see this comment.

Declaring tables

Using drift, you can model the structure of your tables with simple dart code:

import 'package:drift/drift.dart';

// assuming that your file is called filename.dart. This will give an error at
// first, but it's needed for drift to know about the generated code
part 'filename.g.dart';

// this will generate a table called "todos" for us. The rows of that table will
// be represented by a class called "Todo".
class Todos extends Table {
  IntColumn get id => integer().autoIncrement()();
  TextColumn get title => text().withLength(min: 6, max: 32)();
  TextColumn get content => text().named('body')();
  IntColumn get category => integer().nullable()();
}

// This will make drift generate a class called "Category" to represent a row in
// this table. By default, "Categorie" would have been used because it only
//strips away the trailing "s" in the table name.
@DataClassName('Category')
class Categories extends Table {
  IntColumn get id => integer().autoIncrement()();
  TextColumn get description => text()();
}

// this annotation tells drift to prepare a database class that uses both of the
// tables we just defined. We'll see how to use that database class in a moment.
@DriftDatabase(tables: [Todos, Categories])
class MyDatabase extends _$MyDatabase {
}

⚠️ Note: The column definitions, the table name and the primary key must be known at compile time. For column definitions and the primary key, the function must use the => operator and can't contain anything more than what's included in the documentation and the examples. Otherwise, the generator won't be able to know what's going on.

Generating the code

Drift integrates with Dart's build system, so you can generate all the code needed with flutter pub run build_runner build. If you want to continuously rebuild the generated code where you change your code, run flutter pub run build_runner watch instead. After running either command once, the drift generator will have created a class for your database and data classes for your entities. To use it, change the MyDatabase class as follows:

// These imports are only needed to open the database
import 'dart:io';

import 'package:drift/native.dart';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' as p;

@DriftDatabase(tables: [Todos, Categories])
class MyDatabase extends _$MyDatabase {
  // we tell the database where to store the data with this constructor
  MyDatabase() : super(_openConnection());

  // you should bump this number whenever you change or add a table definition.
  // Migrations are covered later in the documentation.
  @override
  int get schemaVersion => 1;
}

LazyDatabase _openConnection() {
  // the LazyDatabase util lets us find the right location for the file async.
  return LazyDatabase(() async {
    // put the database file, called db.sqlite here, into the documents folder
    // for your app.
    final dbFolder = await getApplicationDocumentsDirectory();
    final file = File(p.join(dbFolder.path, 'db.sqlite'));
    return NativeDatabase(file);
  });
}

Next steps

Congratulations! You're now ready to use all of drift. See the articles below for further reading. The "Writing queries" article contains everything you need to know to write selects, updates and inserts in drift!

  • The articles on writing queries and Dart tables introduce important concepts of the Dart API used to write queries.
  • The setup shown here uses the sqlite3 package to run queries synchronously on the main isolate. With a bit of additional setup, drift can transparently run in a background isolate without you having to adapt your query code. See Isolates for more on that.
  • Drift has excellent support for custom SQL statements, including a static analyzer and code-generation tools. See Getting started with sql or Using SQL for everything there is to know about using drift's SQL-based APIs.
  • Something to keep in mind for later: When you change the schema of your database and write migrations, drift can help you make sure they're correct. Use runtime checks, which don't require additional setup, or more involved test utilities if you want to test migrations between any schema versions.