drift_dev package supports a range of options that control how code is generated. In most cases, the default settings should be sufficient. But if you want to try out new features faster or configure how drift-generated code looks like, you can use the available options listed below. You can also see the section on recommended options for advice on which options to use.
To use the options, create a
build.yaml file in the root of your project (e.g. next to your
# build.yaml. This file is quite powerful, see https://pub.dev/packages/build_config targets: $default: builders: drift_dev: options: store_date_time_values_as_text: true
At the moment, drift supports these options:
write_from_json_string_constructor: boolean. Adds a
.fromJsonStringfactory constructor to generated data classes. By default, we only write a
.fromJsonconstructor that takes a
override_hash_and_equals_in_result_sets: boolean. When drift generates another class to hold the result of generated select queries, this flag controls whether drift should override
hashCodein those classes. In recent versions, it will also override
toStringif this option is enabled.
skip_verification_code: Generated tables contain a significant chunk of code to verify integrity of inserted data and report detailed errors when the integrity is violated. If you're only using inserts with SQL, or don't need this functionality, enabling this flag can help to reduce the amount generated code.
use_data_class_name_for_companions: By default, the name for companion classes is based on the table name (e.g. a
@DataClassName('Users') class UsersTable extends Tablewould generate a
UsersTableCompanion). With this option, the name is based on the data class (so
UsersCompanionin this case).
true): When serializing columns declared inside a
.drift) file from and to json, use their sql name instead of the generated Dart getter name (so a column named
user_namewould also use
user_nameas a json key instead of
userName). You can always override the json key by using a
JSON KEYcolumn constraint (e.g.
user_name VARCHAR NOT NULL JSON KEY userName).
generate_connect_constructor(deprecated): Generates a named
connect()constructor on database classes that takes a
DatabaseConnectioninstead of a
QueryExecutor. This option was deprecated in drift 2.5 because
true): Controls whether drift will write the
toCompanionmethod in generated data classes.
false): The fields generated in generated data, companion and result set classes are final by default. You can make them mutable by setting
raw_result_set_data: The generator will expose the underlying
QueryRowfor generated result set classes
true): Applies type converters to variables in compiled statements.
true): Generates a
T?for nullable columns in
copyWith. This allows to set columns back to null (by using
nullwas ignored before, making it impossible to set columns to
named_parameters: Generates named parameters for named variables in SQL queries.
named_parameters_always_required: All named parameters (generated if
true) will be required in Dart.
true): Generates a function parameter for Dart placeholders in SQL. The function has a parameter for each table that is available in the query, making it easier to get aliases right when using Dart placeholders.
store_date_time_values_as_text: Whether date-time columns should be stored as ISO 8601 string instead of a unix timestamp. For more information on these modes, see datetime options.
snake_case): Controls how the table and column names are re-cased from the Dart identifiers. The possible values are
write_to_columns_mixins: Whether the
toColumnsmethod should be written as a mixin instead of being added directly to the data class. This is useful when using existing row classes, as the mixin is generated for those as well.
Assumed SQL environment
You can configure the SQL dialect you want to target with the
sql build option. When using sqlite, you can further configure the assumed sqlite3 version and enabled extensions for more accurate analysis.
Note that these options are used for static analysis only and don't have an impact on the actual sqlite version at runtime.
To define the sqlite version to use, set
sqlite.version to the
targets: $default: builders: drift_dev: options: sql: dialect: sqlite options: version: "3.34"
With that option, the generator will emit warnings when using features introduced in more recent sqlite versions. For instance, using more than one upsert clause is not supported in 3.34, so an error would be reported. Currently, the generator can't provide compatibility checks for versions below 3.34, which is the minimum version needed in options.
Note: This enables extensions in the analyzer for custom queries only. For instance, when the
json1 extension is enabled, the
json functions can be used in drift files. This doesn't necessarily mean that those functions are supported at runtime! Both extensions are available on iOS 11 or later. On Android, they're only available when using a
targets: $default: builders: drift_dev: options: sql: dialect: sqlite options: modules: - json1 - fts5 - math
We currently support the following extensions:
- json1: Support static analysis for
json_functions in moor files
- fts5: Support
CREATE VIRTUAL TABLEstatements for
fts5tables and the
MATCHoperator. Functions like
bm25are available as well.
rtree: Static analysis support for the R*Tree extension. Enabling this option is safe when using a
sqlite3_flutter_libs, which compiles sqlite3 with the R*Tree extension enabled.
moor_ffi: Enables support for functions that are only available when using a
NativeDatabase. This contains
sqrtand a variety of trigonometric functions. Details on those functions are available here.
math: Assumes that sqlite3 was compiled with math functions. This module is largely incompatible with the
spellfix1: Assumes that the spellfix1 module is available. Note that this is not the case for most sqlite3 builds, including the ones shipping with
Known custom functions
modules options can be used to tell drift's analyzer that a well-known sqlite3 extension is available at runtime. In some backends (like a
NativeDatabase), it is also possible to specify entirely custom functions.
To be able to use these functions in
.drift files, you can tell drift's analyzer about them. To do so, add a
known_functions block to the options:
targets: $default: builders: drift_dev: options: sql: dialect: sqlite options: known_functions: my_function: "boolean (text, int null)"
With these options, drift will analyze queries under the assumption that a SQL function called
my_function taking a non-nullable textual value an a nullable integer will return a non-null value that drift can interpret as a boolean.
The syntax for a function type is defined as
<return type> (<argument types>). Each type consists of an arbitrary word used to determine column affinity, with drift also supporting
BOOLEAN as type hints. Then, the optional
NULL keyword can be used to indicate whether the type is nullable.
In general, we recommend using the default options. However, you can disable some default drift features and reduce the amount of generated code with the following options:
skip_verification_code: true: You can remove a significant portion of generated code with this option. The downside is that error messages when inserting invalid data will be less specific.
data_class_to_companions: false: Don't generate the
toCompanionmethod on data classes. If you don't need that method, you can disable this option.
Using drift classes in other builders
It is possible to use classes generated by drift in other builders. Due to technicalities related to Dart's build system and
source_gen, this approach requires a custom configuration and minor code changes. Put this content in a file called
build.yaml next to your
targets: $default: # disable the default generators, we'll only use the non-shared drift generator here auto_apply_builders: false builders: drift_dev|not_shared: enabled: true # If needed, you can configure the builder like this: # options: # skip_verification_code: true # use_experimental_inference: true # This builder is necessary for drift-file preprocessing. You can disable it if you're not # using .drift files with type converters. drift_dev|preparing_builder: enabled: true run_built_value: dependencies: ['your_package_name'] builders: # Disable drift builders. By default, those would run on each target drift_dev: enabled: false drift_dev|preparing_builder: enabled: false # we don't need to disable drift|not_shared, because it's disabled by default
In all files that use generated drift code, you'll have to replace
part 'filename.g.dart' with
part 'filename.drift.dart'. If you use drift and another builder in the same file, you'll need both
.drift.dart as part-files.
A full example is available as part of the drift repo.
If you run into any problems with this approach, feel free to open an issue on drift.
The technicalities, explained
Almost all code generation packages use a so called "shared part file" approach provided by
source_gen. It's a common protocol that allows unrelated builders to write into the same
.g.dart file. For this to work, each builder first writes a
.part file with its name. For instance, if you used
built_value in the same project, those part files could be called
.built_value.part. Later, the common
source_gen package would merge the part files into a single
This works great for most use cases, but a downside is that each builder can't see the final
.g.dart file, or use any classes or methods defined in it. To fix that, drift offers an optional builder -
drift_dev|not_shared - that will generate a separate part file only containing code generated by drift. So most of the work resolves around disabling the default generator of drift and use the non-shared generator instead.
Finally, we need to the build system to run drift first, and all the other builders otherwise. This is why we split the builders up into multiple targets. The first target will only run drift, the second target has a dependency on the first one and will run all the other builders.
Modular code generation
By default, drift generates code from a single entrypoint - all tables, views and queries for a database are generated into a single part file. For larger projects, this file can become quite large, slowing down builds and the analyzer when it is re-generated. Drift supports an alternative and modular code-generation mode intended as an alternative for larger projects. With this setup, drift generates multiple files and automatically manages imports between them.
As a motivating example, consider a large drift project with many tables or views being split across different files:
lib/src/database/ ├── database.dart ├── tables/ │ ├── users.drift │ ├── settings.drift │ ├── groups.drift │ └── search.drift └── views/ ├── friends.drift └── categories.dart
While a modular structure (with
imports in drift files) is helpful to structure sources, drift still generates everything into a single
database.g.dart file. With a growing number of tables and queries, drift may need to generate tens of thousands of lines of code for data classes, companions and query results.
With its modular generation mode, drift instead generates sources for each input file, like this:
lib/src/database/ ├── database.dart ├── database.drift.dart ├── tables/ │ ├── users.drift │ ├── users.drift.dart │ ├── settings.drift │ ├── settings.drift.dart │ └── ... └── views/ ├── friends.drift ├── friends.drift.dart ├── categories.dart └── categories.drift.dart
Enabling modular code generation
Note: A small example using modular code generation is also part of drift's repository.
As drift's modular code generation mode generates different file patterns than the default builder, it needs to be enabled explicitly. For this, create a
build.yaml file in which you disable the default
drift_dev build and enable the two builders for modular generation:
drift_dev:modular. They should both get the same options:
targets: $default: builders: drift_dev: # disable drift's default builder, we're using the modular setup # instead. enabled: false # Instead, enable drift_dev:analyzer and drift_dev:modular manually: drift_dev:analyzer: enabled: true options: # Drift build options, as per https://drift.simonbinder.eu/docs/advanced-features/builder_options/ store_date_time_values_as_text: true named_parameters: true sql: dialect: sqlite options: version: "3.39" modules: [fts5] drift_dev:modular: enabled: true # We use yaml anchors to give the two builders the same options options:
What gets generated
With modular generation, drift generates standalone Dart libraries (Dart files without a
part of statement). This also means that you no longer need
part statements in your sources. Instead, you import the generated
When it comes to using the generated code, not much is different: The API for the database and DAOs stays mostly the same. A big exception are how
.drift files are handled in the modular generation mode. In the default builder, all queries in all drift files are generated as methods on the database. With modular code generation, drift generates an implicit database accessor reachable through getters from the database class. Consider a file
user.drift like this:
CREATE TABLE users ( id INTEGER NOT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, name TEXT NOT NULL, is_admin BOOLEAN NOT NULL DEFAULT FALSE ); findUsers($predicate = TRUE): SELECT * FROM users WHERE $predicate;
If such a
users.drift file is included from a database, we no longer generate a
findUsers method for the database itself. Instead, a
users.drift.dart file contains a database accessor called
UsersDrift which is implicitly added to the database. To call
findUsers, you'd now call