Using drift classes in other builders

Configure your build to allow drift dataclasses to be seen by 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 build configuration. For complex builds like this, we recommend running drift in it's modular mode. This more is more efficient for larger builds and can be enabled by putting this content in a file called build.yaml next to your pubspec.yaml:

targets:
  drift:
    auto_apply_builders: false
    builders:
      drift_dev:analyzer:
        enabled: true
        options: &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: *options

  $default:
    dependencies:
      # run drift's builder first
      - ":drift"
    builders:
      # This builder is enabled by default, but we're using the modular builder in
      # its own target instead.
      drift_dev:
        enabled: false

With modular generation, you'll have to replace the part statement in the database file with an import to filename.drift.dart. Also, your database class now extends from $DatabaseName, without a leading underscore.

By generating independent libraries, drift can manage imports on its own. By declaring a dependency in build.yaml, the build system also ensures that drift-generated files are ready before built_value or other builders that need to see them are running.

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 drift and built_value in the same project, those part files could be called .drift.part and .built_value.part. Later, the common source_gen package would merge the part files into a single .g.dart file.

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 other builders - drift_dev|not_shared and drift_dev|modular - those will generate a separate 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.

Using drift_dev:not_shared

For complex build setups like those requiring other builders to see drift code, the drift_dev:modular builder is recommended. However, enabling the modular builder requires other code modifications like replacing part statements with imports. A simpler change may be the not_shared builder offered by drift_dev. It works like the default setup, except that it emits a .drift.dart part file instead of a shared .g.dart file - so you only have to change a single part statement to migrate.

To enable this builder, also enable the drift_dev:analyzer builder and the has_separate_analyzer option:

targets:
  drift:
    auto_apply_builders: false
    builders:
      drift_dev:analyzer:
        enabled: true
        options: &options
          has_separate_analyzer: true # always enable this option when using `not_shared`
          # remaining options...
      drift_dev:not_shared:
        enabled: true
        # We use yaml anchors to give the two builders the same options
        options: *options

  $default:
    dependencies:
      # run drift's builder first
      - ":drift"
    builders:
      # This builder is enabled by default, but we're using the modular builder in
      # its own target instead.
      drift_dev:
        enabled: false