Why Drift is the best Flutter ORM

Omasuaku
5 min readAug 5, 2024

In Flutter development, choosing the right ORM (Object-Relational Mapping) solution can be a game-changer. An ORM simplifies database interactions by mapping database tables to Dart classes, reducing boilerplate code and improving productivity. Among the various options available, sqfentity, floor and others. Drift stands out as a robust choice for managing local databases in Flutter apps. In this article, we’ll explore why Drift is considered the best ORM for Flutter and how it can enhance your development experience.

I won’t compare Drift to Isar Database or ObjectBox, I stay on relational databases.

Drift

1. Ease of Use and Setup

Drift offers a straightforward setup process that integrates seamlessly into existing Flutter projects. It supports features like safe SQL queries and database verification. To do this, it uses tools that work during compile-time, including a powerful builder and command-line utilities.

2. Isolates for SQL Statements

SQLite3 is a synchronous C library, meaning database access can block the main thread and slow down your app. To avoid this, Drift can run SQL operations in a separate isolate, preventing the main thread from being blocked and keeping your app responsive. When you use NativeDatabase.createInBackground, Drift automatically handles this for you, so you don't need any extra setup.

Why Use Isolates?
Isolates are essential because they prevent the main thread from being blocked by database operations, ensuring your app stays smooth and responsive. You can also use isolates to run multiple independent databases, even on the same database file. However, if you need to share a single database across multiple isolates, Drift’s isolate APIs help set this up efficiently.

3. Dart API

In other ORMs, like Floor, you often encounter a lot of raw SQL queries, which can be error-prone and redundant. This is something an ORM should help developers avoid. Drift stands out by offering extensive Dart APIs that support SQL queries directly, allowing you to work with data as Dart objects. This not only minimizes the need for raw SQL but also ensures type safety and compile-time checks.

Drift’s Dart APIs make advanced SQL concepts easily accessible. You can manage transactions to ensure data integrity, create and query views for complex data representations, and use DAOs (Data Access Objects) for organizing your database operations. The APIs also support various SQL operations like select, update, where clauses, limit, and much more. For instance, you can effortlessly filter data using where, paginate results with limit, and handle multiple related operations in a single transaction.

This rich set of features makes Drift not just a database tool but a comprehensive solution for building efficient and maintainable data-driven Flutter apps.

4. Supported Types

It is such impressive the number of types Drift supports natively, you really don’t need a converter for most of your columns.

Drift Supported column types

5. Schema tools

Drift offers powerful schema tools that enhance the development and maintenance of your database. One standout feature is the ability to export the database schema according to the database version. This means you can easily generate a snapshot of your database structure at any given point in time.

6. Complex SQL

Drift excels in handling complex SQL operations, including inner joins, and offers powerful features for query separation. These capabilities make it easier to write clean, modular, and maintainable database code.

Inner Joins

With Drift, performing inner joins is straightforward. You can easily combine rows from two or more tables based on a related column. For example, if you have an orders table and a customers table, you can join them to fetch orders along with customer details:

final query = select(orders).join(
[
innerJoin(customers, customers.id.equalsExp(orders.customerId)),
],
)..where(orders.status.equals('completed'));

final result = await query.get();

Query Separation

Drift’s Dart APIs also support query separation, allowing you to construct parts of a query conditionally or reuse them across different queries. This is particularly useful when building complex queries that depend on various conditions.

For example, you might want to filter a list of products based on availability and price range:

// Base query
final query = select(products);

// Optional conditions
Expression<bool> condition = const Constant(true);

if (includeOutOfStock) {
condition = condition & products.stock.isBiggerThanValue(0);
}

if (minPrice != null) {
condition = condition & products.price.isBiggerOrEqualValue(minPrice);
}

if (maxPrice != null) {
condition = condition & products.price.isSmallerOrEqualValue(maxPrice);
}

// Apply conditions
query.where(condition);

// Execute the query
final result = await query.get();

7. Custom Query

Drift provides a powerful feature that allows you to write custom queries using raw SQL while still benefiting from the framework’s type safety and generated APIs. This flexibility is particularly useful when you need to perform complex operations that aren’t easily expressed using the standard API.

Custom select statements

With custom queries, you can write SQL statements directly in your Dart code. This is ideal for scenarios where you need more control over the query logic, such as performing complex joins, using advanced SQL functions, or optimizing specific database operations. Despite being raw SQL, Drift ensures that these queries are type-safe and that their results are correctly mapped to Dart classes.

Here’s an example of a custom query:

final customQuery = customSelect(
'SELECT * FROM products WHERE price > ? AND stock > ?',
variables: [Variable.withInt(minPrice), Variable.withInt(minStock)],
).watch();

In this example, the custom query selects all products with a price greater than minPrice and stock greater than minStock. The customSelect method allows you to pass SQL strings and bind parameters, ensuring that the query is both secure and efficient.

Impressive Statements with Generated API

You can see below a custom SQL query wrote on a Map named queries on DriftDatabase annotation.

@DriftDatabase(
tables: [TodoItems, Categories],
queries: {
'categoriesWithCount': 'SELECT *, '
'(SELECT COUNT(*) FROM todo_items WHERE category = c.id) AS "amount" '
'FROM categories c;'
},
)
class MyDatabase extends $MyDatabase {
// rest of class stays the same
}

Drift with generate a function categoriesWithCount as Selectable<CategoriesWithCountResult>. Why Selectable ? Because with that, we can make watch to stream data or get to get data as Future.

Understanding Table Naming Conventions in Drift

Drift automatically converts Dart class names to SQL table names using snake_case. For example, a Dart class named Categories will be mapped to a SQL table named categories, and UserAddressInformation will be user_address_information.

Column names follow the same rule. If a Dart getter is named userAddress, the SQL column will be user_address.

You can override these defaults by specifying custom names in Drift files. This flexibility ensures that your database schema can match your preferred naming conventions.

Conclusion

While this overview covers some of Drift’s key features, there is much more to explore beyond what we’ve discussed here. Drift offers a wide range of functionalities and customization options that can greatly enhance your database management experience in Flutter. For a deeper dive into all that Drift has to offer, I highly recommend checking out the official documentation.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

Omasuaku
Omasuaku

Written by Omasuaku

Programmer from Africa. I’m here just to say a hello to the world.

No responses yet

Write a response