drift

repository·develop·Indexed 25 days ago

https://github.com/simolus3/drift

A reactive, type-safe persistence library for Flutter and Dart built on SQLite. It supports writing queries in both SQL and Dart, automatic stream updates, cross-platform support, and built-in threading. The ecosystem includes drift_dev for code generation, drift_flutter for simplified database opening, and drift_sqflite for sqflite integration.

Tokens
53.7K
Snippets
123
Records
372
Agent score
85%

What's inside drift

  1. Overview of Drift features

    develop

    Drift is a reactive persistence library for Dart and Flutter applications, built on top of database libraries like sqlite3 and sqflite. Key features include:

    • Type safety: Automatically turns database rows into Dart objects.
    • Stream queries: Allows you to "watch" queries as auto-updating streams that emit new items when underlying data changes.
    • Fluent queries: Provides generated Dart methods and classes for writing queries without manual SQL.
    • Type-safe SQL: Includes an SQL parser and analyzer that validates queries at compile time and generates corresponding Dart code.
    • Migration utilities: Provides helpers like .createAllTables() to simplify schema migrations.
    • Data validation: Validates data before insertion to provide descriptive error messages.
    • Advanced features: Supports transactions, DAOs (Data Access Objects), and efficient batched inserts.
  2. Overview of Drift

    develop

    Drift is a reactive persistence library for Flutter and Dart built on top of SQLite. It provides a type-safe way to interact with databases using either SQL or a fluent Dart API.

    Key features include:

    • Flexible Querying: Supports both SQL and Dart APIs, including complex features like WITH and WINDOW clauses.
    • Reactive: Automatically turns SQL queries into auto-updating streams.
    • Type-Safe: Generates code based on your tables and queries to catch errors at compile time.
    • Cross-Platform: Works on Android, iOS, macOS, Windows, Linux, and the web.
    • Built-in Threading: Supports running database code across isolates with zero additional effort.
    • Modular: Supports DAOs and SQL file imports to keep database code organized.
  3. Explore Drift advanced usage examples

    develop

    The drift repository contains several example projects demonstrating advanced features and integration patterns. Use these examples to learn how to implement specific architectures or features in your own projects:

    • app: A cross-platform Flutter application implementing recommended drift configuration and options.
    • encryption: A Flutter application demonstrating how to run an encrypted drift database.
    • migrations_example: Demonstrates how to generate test utilities to verify database schema migrations.
    • modular: Shows how to use drift's upcoming modular generation mode.
    • with_built_value: Provides configuration for build_runner to ensure drift-generated classes are compatible with build_runner workflows.
    • multi_package: Demonstrates how to share drift database definitions across multiple Dart packages.

    Note on Web Workers: While the flutter_web_worker_example and web_worker_example are available, the pattern of running a database through a web worker is now a core feature accessible via WasmDatabase.open and does not require the specific manual setup shown in those legacy examples.

  4. Understand generated row and companion classes

    develop

    For every table you define in Drift, two associated classes are automatically generated:

    1. Row Class: Represents a full row of the table. These are used for reading data from the database with type safety. They include built-in equality, hashing, and a copyWith method.
    2. Companion Class: Represents a partial row. These are primarily used for inserts and updates where some columns might be absent (e.g., auto-incrementing primary keys) or where you need to distinguish between setting a column to NULL versus not changing it at all.

    In companion classes, all fields are wrapped in a Value class to track presence.

  5. Understand DateTime storage modes in Drift

    develop

    Drift supports two modes for storing DateTime values in SQL. You can toggle between them using the store_date_time_values_as_text build option.

    1. As unix timestamp (Default): Stores values as an SQL INTEGER containing the unix timestamp in seconds. Note that when retrieving rows, Drift returns a non-UTC value, meaning the distinction between UTC and local time is lost in the database.
    2. As ISO 8601 string: Stores values as text using DateTime.toIso8601String().
      • UTC values are stored with a Z suffix (e.g., 2022-07-25 09:28:42.015Z).
      • Local values include a UTC offset (e.g., 2022-07-25T11:28:42.015 +02:00).
      • If no suffix or offset is present, Drift parses it as UTC.

    Drift's built-in date and time expressions work with both modes by internally applying unixepoch for timestamps or julianday for text comparisons.

  6. Use the Drift Manager interface for common queries

    develop
    Drift generates a manager for each table (accessible via the managers getter on your database class) to simplify common queries. Managers provide an easier API than the standard query builder, making it simpler to read, watch, update, and delete rows without deep SQL knowledge. Note: Managers are not generated for tables using a custom row class.
  7. Choose a drift implementation based on platform

    develop

    Drift separates its core APIs from platform-specific database implementations. To achieve platform independence, use the APIs in package:drift/drift.dart for your application logic and only swap the QueryExecutor implementation when opening the database.

    ImplementationSupported PlatformsNotes
    SqfliteQueryExecutor (package:drift_sqflite)Android, iOSUses platform channels; Flutter only; no isolate support; does not support flutter test.
    NativeDatabase (package:drift/native.dart)Android, iOS, Windows, Linux, macOSUses dart:ffi; no further setup required; usage in an isolate is recommended.
    WasmDatabase (package:drift/wasm.dart)WebRequires additional setup (see web.md).
    WebDatabase (package:drift/web.dart)WebDeprecated in favor of WasmDatabase.
  8. Add Drift dependencies for Dart (postgres)

    develop

    To use Drift with a PostgreSQL database in a pure Dart application, add the following dependencies to your pubspec.yaml. You must also include drift_dev and build_runner in your dev_dependencies to enable code generation.

    dependencies:
      drift: ^{{ versions.drift }}
      postgres: ^{{ versions.postgres }}
      drift_postgres: ^{{ versions.drift_postgres }}
    
    dev_dependencies:
      drift_dev: ^{{ versions.drift_dev }}
      build_runner: ^{{ versions.build_runner }}
  9. Use drift_libsql for libSQL synchronization

    develop

    Use the drift_libsql package to connect to libSQL servers while maintaining a local copy of the database that stays in-sync with the server. This is ideal for applications requiring offline capabilities or synchronization mechanisms provided by libSQL.

    To implement this, pass a DriftLibsqlDatabase instance to your @DriftDatabase class constructor.

    import 'package:drift/drift.dart';
    import 'package:drift_libsql/drift_libsql.dart';
    
    @DriftDatabase(...)
    class AppDatabase extends _$AppDatabase {
      AppDatabase(super.e);
    
      @override
      int get schemaVersion => 1;
    }
    
    void main() async {
      final database = AppDatabase(DriftLibsqlDatabase(
        "${dir.path}/replica.db",
        syncUrl: 'hrana url',
        authToken: 'your-token',
        readYourWrites: true,
        syncIntervalSeconds: 3,
      ));
    }
  10. Model many-to-many relationships using JSON arrays

    develop

    An alternative to a join table is to store related entity IDs directly within the primary table as a JSON array. This is useful when the order of items is important, as SQL table rows are unordered.

    To implement this:

    1. Define the primary table with a column intended to hold a JSON string (e.g., itemIds).
    2. Use a package like json_serializable to handle the encoding/decoding of the list of IDs.

    This approach simplifies updates because a single row update can replace the entire collection of related items without needing a transaction across multiple tables.

  11. Manually trigger stream updates

    develop

    Drift's stream updates are based on a heuristic: drift tracks which tables are involved in a query and triggers a re-run when it detects an insert, update, or delete through its own APIs.

    If you are using an external tool (like a native SQLite client) to modify the database, drift will not automatically detect these changes. To fix this, you can manually mark a table as updated to force all active streams watching that table to re-run.