Using Drift ORM for Type-Safe SQL Queries in Flutter
Jul 24, 2025



Summary
Summary
Summary
Summary
This tutorial covers integrating Drift ORM into Flutter mobile development for type-safe SQL. Learn to set up Drift, define tables and DAOs, execute queries, handle migrations, and implement advanced techniques like transactions. Drift’s generated Dart classes guarantee compile-time safety, while its migration strategy preserves data across schema changes, making database management in Flutter apps more maintainable and error-free.
This tutorial covers integrating Drift ORM into Flutter mobile development for type-safe SQL. Learn to set up Drift, define tables and DAOs, execute queries, handle migrations, and implement advanced techniques like transactions. Drift’s generated Dart classes guarantee compile-time safety, while its migration strategy preserves data across schema changes, making database management in Flutter apps more maintainable and error-free.
This tutorial covers integrating Drift ORM into Flutter mobile development for type-safe SQL. Learn to set up Drift, define tables and DAOs, execute queries, handle migrations, and implement advanced techniques like transactions. Drift’s generated Dart classes guarantee compile-time safety, while its migration strategy preserves data across schema changes, making database management in Flutter apps more maintainable and error-free.
This tutorial covers integrating Drift ORM into Flutter mobile development for type-safe SQL. Learn to set up Drift, define tables and DAOs, execute queries, handle migrations, and implement advanced techniques like transactions. Drift’s generated Dart classes guarantee compile-time safety, while its migration strategy preserves data across schema changes, making database management in Flutter apps more maintainable and error-free.
Key insights:
Key insights:
Key insights:
Key insights:
Setting Up Drift in Flutter: Configure dependencies and initialize a LazyDatabase for SQLite access.
Defining Tables and DAOs: Use Dart classes to declare tables and DAOs for clean, reusable queries.
Executing Type-Safe Queries: Write queries with fluent APIs that catch SQL errors at compile time.
Handling Migrations: Leverage Drift’s MigrationStrategy to evolve your schema without data loss.
Advanced Query Techniques: Perform transactions and custom updates while preserving type safety.
Introduction
Drift is a powerful Dart ORM that brings type-safe SQL to Flutter mobile development. By generating Dart classes from your database schema, Drift eliminates runtime errors caused by malformed SQL. In this tutorial, you’ll learn how to set up Drift in your Flutter project, define tables and DAOs, perform type-safe queries, manage migrations, and leverage advanced techniques for complex data access.
Setting Up Drift in Flutter
First, add the following dependencies in your pubspec.yaml:
dependencies:
flutter:
sdk: flutter
drift: ^2.0.0
drift_flutter: ^2.0.0
dev_dependencies:
drift_dev: ^2.0.0
build_runner
Then create a database file under lib/src/database.dart
. Import Drift and initialize a LazyDatabase
that uses a platform-specific implementation:
import 'dart:io';
import 'package:drift/drift.dart';
import 'package:drift/native.dart';
LazyDatabase openConnection() {
return LazyDatabase(() async {
final file = File('app.sqlite');
return NativeDatabase(file);
});
}
Annotate your database class with @DriftDatabase
, list your tables, and run flutter pub run build_runner build
to generate code.
Defining Tables and DAOs
Drift uses Dart classes to describe your schema. Each table extends Table
. Drift generates a companion class and a data class for each table.
import 'package:drift/drift.dart';
class Users extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get name => text().withLength(min: 1, max: 50)();
DateTimeColumn get joinedAt => dateTime().withDefault(Constant(DateTime.now()))();
}
You can also define DAOs to encapsulate common queries:
@DriftAccessor(tables: [Users])
class UserDao extends DatabaseAccessor<MyDatabase> with _$UserDaoMixin {
UserDao(MyDatabase db) : super(db);
Future<int> insertUser(UsersCompanion user) => into(users).insert(user);
Stream<List<User>> watchAllUsers() => select(users).watch();
}
Executing Type-Safe Queries
Drift’s fluent API ensures your queries are validated at compile time. For example, to fetch users joined after a certain date:
Future<List<User>> getRecentUsers(DateTime since) {
return (select(users)..where((u) => u.joinedAt.isBiggerThanValue(since)))
.get();
}
Complex filters and joins are equally straightforward. Use .join()
and innerJoin()
to combine tables while keeping type safety.
Handling Migrations
As your schema evolves, Drift can apply migrations automatically. Specify a migration
strategy in your database class:
@DriftDatabase(tables: [Users])
class MyDatabase extends _$MyDatabase {
MyDatabase() : super(openConnection());
@override
int get schemaVersion => 2;
@override
MigrationStrategy get migration => MigrationStrategy(
onUpgrade: (m, from, to) async {
if (from == 1) {
await m.addColumn(users, users.joinedAt);
}
},
);
}
This code ensures existing data is preserved and new columns are applied without manual SQL scripts.
Advanced Query Techniques
For transactions and custom statements, use Drift’s API directly:
Future<void> transferCredits(int fromId, int toId, int amount) {
return transaction(() async {
await customUpdate(
'UPDATE accounts SET balance = balance - ? WHERE id = ?',
variables: [Variable(amount), Variable(fromId)],
);
await customUpdate(
'UPDATE accounts SET balance = balance + ? WHERE id = ?',
variables: [Variable(amount), Variable(toId)],
);
});
}
For batch inserts or complex joins, Drift maintains compile-time checks and maps results to Dart objects, simplifying error handling in your Flutter app.
Vibe Studio

Vibe Studio, powered by Steve’s advanced AI agents, is a revolutionary no-code, conversational platform that empowers users to quickly and efficiently create full-stack Flutter applications integrated seamlessly with Firebase backend services. Ideal for solo founders, startups, and agile engineering teams, Vibe Studio allows users to visually manage and deploy Flutter apps, greatly accelerating the development process. The intuitive conversational interface simplifies complex development tasks, making app creation accessible even for non-coders.
Conclusion
Drift ORM brings robust, type-safe SQL capabilities to Flutter mobile development. From schema definitions and generated DAOs to migrations and advanced queries, Drift streamlines data access and minimizes runtime errors. Integrate Drift in your next project to enjoy compile-time validation, maintainable migrations, and a fluent Dart API for working with SQLite.
Introduction
Drift is a powerful Dart ORM that brings type-safe SQL to Flutter mobile development. By generating Dart classes from your database schema, Drift eliminates runtime errors caused by malformed SQL. In this tutorial, you’ll learn how to set up Drift in your Flutter project, define tables and DAOs, perform type-safe queries, manage migrations, and leverage advanced techniques for complex data access.
Setting Up Drift in Flutter
First, add the following dependencies in your pubspec.yaml:
dependencies:
flutter:
sdk: flutter
drift: ^2.0.0
drift_flutter: ^2.0.0
dev_dependencies:
drift_dev: ^2.0.0
build_runner
Then create a database file under lib/src/database.dart
. Import Drift and initialize a LazyDatabase
that uses a platform-specific implementation:
import 'dart:io';
import 'package:drift/drift.dart';
import 'package:drift/native.dart';
LazyDatabase openConnection() {
return LazyDatabase(() async {
final file = File('app.sqlite');
return NativeDatabase(file);
});
}
Annotate your database class with @DriftDatabase
, list your tables, and run flutter pub run build_runner build
to generate code.
Defining Tables and DAOs
Drift uses Dart classes to describe your schema. Each table extends Table
. Drift generates a companion class and a data class for each table.
import 'package:drift/drift.dart';
class Users extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get name => text().withLength(min: 1, max: 50)();
DateTimeColumn get joinedAt => dateTime().withDefault(Constant(DateTime.now()))();
}
You can also define DAOs to encapsulate common queries:
@DriftAccessor(tables: [Users])
class UserDao extends DatabaseAccessor<MyDatabase> with _$UserDaoMixin {
UserDao(MyDatabase db) : super(db);
Future<int> insertUser(UsersCompanion user) => into(users).insert(user);
Stream<List<User>> watchAllUsers() => select(users).watch();
}
Executing Type-Safe Queries
Drift’s fluent API ensures your queries are validated at compile time. For example, to fetch users joined after a certain date:
Future<List<User>> getRecentUsers(DateTime since) {
return (select(users)..where((u) => u.joinedAt.isBiggerThanValue(since)))
.get();
}
Complex filters and joins are equally straightforward. Use .join()
and innerJoin()
to combine tables while keeping type safety.
Handling Migrations
As your schema evolves, Drift can apply migrations automatically. Specify a migration
strategy in your database class:
@DriftDatabase(tables: [Users])
class MyDatabase extends _$MyDatabase {
MyDatabase() : super(openConnection());
@override
int get schemaVersion => 2;
@override
MigrationStrategy get migration => MigrationStrategy(
onUpgrade: (m, from, to) async {
if (from == 1) {
await m.addColumn(users, users.joinedAt);
}
},
);
}
This code ensures existing data is preserved and new columns are applied without manual SQL scripts.
Advanced Query Techniques
For transactions and custom statements, use Drift’s API directly:
Future<void> transferCredits(int fromId, int toId, int amount) {
return transaction(() async {
await customUpdate(
'UPDATE accounts SET balance = balance - ? WHERE id = ?',
variables: [Variable(amount), Variable(fromId)],
);
await customUpdate(
'UPDATE accounts SET balance = balance + ? WHERE id = ?',
variables: [Variable(amount), Variable(toId)],
);
});
}
For batch inserts or complex joins, Drift maintains compile-time checks and maps results to Dart objects, simplifying error handling in your Flutter app.
Vibe Studio

Vibe Studio, powered by Steve’s advanced AI agents, is a revolutionary no-code, conversational platform that empowers users to quickly and efficiently create full-stack Flutter applications integrated seamlessly with Firebase backend services. Ideal for solo founders, startups, and agile engineering teams, Vibe Studio allows users to visually manage and deploy Flutter apps, greatly accelerating the development process. The intuitive conversational interface simplifies complex development tasks, making app creation accessible even for non-coders.
Conclusion
Drift ORM brings robust, type-safe SQL capabilities to Flutter mobile development. From schema definitions and generated DAOs to migrations and advanced queries, Drift streamlines data access and minimizes runtime errors. Integrate Drift in your next project to enjoy compile-time validation, maintainable migrations, and a fluent Dart API for working with SQLite.
Build Flutter Apps Faster with Vibe Studio
Build Flutter Apps Faster with Vibe Studio
Build Flutter Apps Faster with Vibe Studio
Build Flutter Apps Faster with Vibe Studio
Vibe Studio is your AI-powered Flutter development companion. Skip boilerplate, build in real-time, and deploy without hassle. Start creating apps at lightning speed with zero setup.
Vibe Studio is your AI-powered Flutter development companion. Skip boilerplate, build in real-time, and deploy without hassle. Start creating apps at lightning speed with zero setup.
Vibe Studio is your AI-powered Flutter development companion. Skip boilerplate, build in real-time, and deploy without hassle. Start creating apps at lightning speed with zero setup.
Vibe Studio is your AI-powered Flutter development companion. Skip boilerplate, build in real-time, and deploy without hassle. Start creating apps at lightning speed with zero setup.











