Drift (Moor) — o que é, ORM reativo e trabalho com bancos de dados

Autor: IT Sectr Publicado: 2026-03-13 Tempo de leitura: 9 min

Drift (anteriormente Moor) é um ORM reativo para Flutter e Dart, construído sobre SQLite com seu próprio DSL para consultas. Ao contrário dos ORMs tradicionais, o Drift compila consultas Dart em SQL em tempo de compilação, eliminando erros de tempo de execução. De acordo com Drift Docs, 2024, o Drift gera até 40% mais código do que consultas SQL manuais, mas elimina completamente a escrita manual de SQL, substituindo-a por sintaxe Dart com segurança de tipos.

Principais pontos

  • Drift — ORM reativo com compilação de consultas em SQL em tempo de compilação
  • Consultas DSL — interface fluente em Dart sem escrever strings SQL
  • Reatividade — Stream e consultas de atualização automática para UI em tempo real
  • Multiplataforma — Android, iOS, Web, macOS, Linux, Windows
  • Migrações — versionamento automático e migrações manuais via SQL

O que é Drift?

Drift é um ORM para Dart e Flutter, anteriormente conhecido como Moor. Foi desenvolvido por Simon Binder em 2019 e desde então passou por várias versões principais. O Drift compila consultas Dart em SQL em tempo de compilação usando drift_dev e build_runner, fornecendo segurança total de tipos e eliminando erros de sintaxe SQL em tempo de execução.

Ao contrário do Floor, o Drift usa seu próprio DSL (Domínio-Specific Language) para construir consultas — o desenvolvedor escreve em Dart, e o gerador traduz para SQL. Isso permite que a IDE verifique a sintaxe, autocomplete campos de tabela e refatore o modelo de dados sem medo de quebrar consultas.

De acordo com Drift (2024), a biblioteca é usada em mais de 8000 projetos Flutter. Ela suporta todas as plataformas populares: Android via sqflite, iOS via sqflite, web via sqlite3 WASM, desktop via driver nativo sqlite3.

Histórico de renomeação: Moor → Drift

Moor foi renomeado para Drift na versão 2.0 (2022). O motivo foi um conflito de nomes com outros projetos e o desejo de se distanciar do código antigo. A API permaneceu compatível: para migrar, basta substituir o import de moor para drift e atualizar as dependências.

Recursos principais

Drift fornece: consultas Stream integradas com atualização automática quando os dados mudam, suporte a transações com reversão, consultas SQL personalizadas via rawQuery, padrão DAO para encapsulamento de lógica, migrações multiplataforma e integração com Riverpod e BLoC através dos pacotes drift_riverpod e drift_bloc.

Como o Drift funciona?

Drift usa geração de código em tempo de compilação. O desenvolvedor descreve as tabelas através de anotações @DataClass ou classes Dart que estendem Table. O gerador cria classes auxiliares: Companion (para campos anuláveis durante inserção/atualização), DriftDatabase (ponto de entrada) e implementações DAO.

O Drift não executa consultas SQLite diretamente. Em vez disso, o desenvolvedor escreve em Dart: select(tasks).where(tasks.priority.greaterThan(3)).build(). O gerador traduz isso para SQL, e em tempo de execução o Drift simplesmente envia a consulta SQL pronta para o SQLite. Isso combina a conveniência da sintaxe Dart com o desempenho do SQL nativo.

Arquitetura de consultas

Drift suporta dois modos de consulta: DSL (recomendado) e SQL bruto. As consultas DSL são mais seguras de escrever — o compilador verifica nomes de campos, tipos e compatibilidade. O SQL bruto é necessário para consultas complexas não cobertas pelo DSL: funções de janela, CTEs recursivas, extensões SQLite específicas.

Drift DSL vs SQL: comparação de abordagens

Drift oferece duas maneiras de escrever consultas: Dart DSL (nativo) e SQL bruto (para casos complexos). O DSL é preferível para 90% dos cenários: é mais seguro, mais legível e suporta refatoração. O SQL bruto é usado apenas quando o DSL não cobre a construção necessária.

AspectoDrift DSLSQL bruto no Drift
Segurança de tiposCompleta (compilação)Não (execução)
AutocompletarSim (IDE)Apenas em arquivos sql
RefatoraçãoAutomáticaPesquisa manual em strings
JOINs complexosSuportadosLiberdade total
Funções de janelaLimitadoSuporte completo
ReatividadeIntegrada (Stream)Através de .watch()

Quando usar DSL

Drift DSL é a forma principal de trabalho. Ele cobre SELECT, INSERT, UPDATE, DELETE, WHERE, ORDER BY, LIMIT, JOIN e agrupamento. Para todas as consultas CRUD típicas, use DSL: é mais curto, mais seguro e atualiza automaticamente o Stream nas alterações.

Quando usar SQL bruto

SQL bruto no Drift é necessário para: funções SQLite personalizadas (FTS5, JSON1), subconsultas complexas com EXISTS, INSERT OR REPLACE, UPDATE em massa com CASE, bem como consultas onde o desempenho é crítico e o DSL não gera o plano de execução ótimo. O SQL bruto pode ser escrito em arquivos .sql com suporte a tipagem via drift_dev.

Exemplos de código com Drift

Drift usa classes que estendem Table ou a anotação @DataClass. Abaixo está um exemplo completo do modelo Task com consultas via DSL, SQL bruto e atualização reativa. Após executar o build_runner, todas as classes geradas estão prontas.

Definição de tabela e banco de dados

A classe Tasks estende Table e define colunas. Cada coluna é uma expressão do tipo Column<T>. Parâmetros: withDefault() define um valor padrão, autoIncrement() define auto-incremento. O banco de dados é uma classe abstrata que estende $DriftDatabase.

dart
class Tasks extends Table {
    IntColumn get id => integer().autoIncrement();
    TextColumn get title => text().withDefault(const Constant(''))();
    BoolColumn get isCompleted => boolean().withDefault(const Constant(false))();
    IntColumn get priority => integer().withDefault(const Constant(0))();
}

@DriftDatabase(tables: [Tasks])
class AppDatabase extends $AppDatabase {
    AppDatabase(QueryExecutor e) : super(e);
}

Operações CRUD via DSL

Drift gera os métodos into(tasks).insert(), select(tasks), update(tasks) e delete(tasks) para as tabelas. Todas as operações retornam Future — trabalhar com SQLite é assíncrono. Para rastrear alterações, use .watch() em vez de .get().

dart
// Inserir
await into(tasks).insert(TasksCompanion.insert(
    title: Value('Comprar mantimentos'),
    priority: Value(3),
));

// Leitura com filtro
final highPriority = await (select(tasks)
    ..where((t) => t.priority.greaterThan(2))
    ..orderBy([(t) => OrderingTerm(expression: t.priority, mode: OrderingMode.desc)]))
    .get();

// Observação reativa
select(tasks).watch().listen((tasksList) {
    // tasksList — List, atualiza a cada alteração da tabela
    updateUi(tasksList);
});

SQL bruto com resultado tipado

Para consultas complexas, o Drift permite escrever SQL bruto mantendo a tipagem. O método customSelect recebe uma string de consulta e retorna um resultado tipado via gerador de código. Essa abordagem combina a flexibilidade do SQL com a segurança de tipos do Drift.

dart
final result = await customSelect(
    'SELECT title, COUNT(*) as cnt FROM tasks GROUP BY title',
    readsFrom: { tasks },
).get();

for (final row in result) {
    print('${row.readString("title")}: ${row.readInt("cnt")}');
}

Migrações e versionamento no Drift

Drift suporta tanto migrações automáticas (para alterações simples) quanto manuais (para transformações complexas). A versão do banco de dados é definida no construtor do AppDatabase. Se a versão não corresponder, o Drift aplica todas as migrações pendentes sequencialmente.

Migrações automáticas

Para adicionar uma coluna com valor padrão, o Drift pode gerar uma migração automaticamente via MigrationStrategy. Se a alteração não quebrar os dados existentes (adicionar um campo anulável), você pode usar beforeOpen com verificação de versão e execução de ALTER TABLE.

Migrações manuais

Para alterações complexas (renomear uma tabela, mesclar dados, alterar o tipo de uma coluna), o Drift requer migração SQL manual. As migrações são especificadas através do parâmetro migrations na classe do banco de dados. Cada migração é um objeto com números from/to e consultas SQL.

Drift e testes

Drift suporta execução em modo de teste via NativeDatabase.memory(). O banco de dados em memória é criado do zero antes de cada teste e destruído depois. Para simulação, use o pacote mocktail com um QueryExecutor simulado. O Drift também fornece DatabaseTestHelper para testes de integração com verificação de migrações e consultas.

Padrão DAO no Drift

Drift suporta DAO (Data Access Object) através de classes abstratas com a anotação @DriftAccessor. O DAO encapsula consultas a uma ou mais tabelas e pode ser testado separadamente do banco de dados. Ao contrário de consultas diretas através do Database, o DAO permite reutilizar a lógica de consultas entre diferentes partes da aplicação e simplifica os testes unitários.

dart
@DriftDatabase(tables: [Tasks])
class AppDatabase extends $AppDatabase {
    AppDatabase(QueryExecutor e) : super(e) {
        migrations.add(Migration(1, 2, (m) async {
            await m.addColumn(tasks, tasks.dueDate);
            await m.createIndex(tasks.idxPriority);
        }));
    }
}

Perguntas frequentes

Como o Drift difere do Floor?

Drift usa seu próprio DSL em vez de strings SQL, fornecendo segurança total de tipos e autocompletar na IDE. O Floor usa strings SQL na anotação @Query. O Drift também suporta mais plataformas (incluindo web) e tem reatividade integrada via Stream, enquanto no Floor o Stream deve ser declarado manualmente.

O Drift suporta migrações sem perda de dados?

Sim, o Drift suporta migrações com preservação de dados. Para adicionar colunas, use addColumn em Migration. Para transformações complexas (renomear, mesclar), escreva SQL bruto dentro da migração. Se nenhuma migração for especificada, o Drift recria o banco de dados com perda de dados quando o esquema não corresponde.

Pode-se usar Drift sem build_runner?

Drift requer geração de código via build_runner e drift_dev. Sem geração, é impossível criar consultas tipadas. No entanto, para projetos pequenos, o Drift suporta sqlparser — escrita manual de arquivos SQL com tipagem automática, mas isso ainda requer uma etapa de geração.

Como integrar o Drift com Riverpod?

Para integração com Riverpod, use o pacote drift_riverpod. Ele fornece providers para Database, DAO e consultas Stream. Exemplo: final tasksProvider = databaseProvider.select((db) => db.select(db.tasks).watch()) — a UI é reconstruída automaticamente quando os dados mudam.

O Drift suporta criptografia SQLite?

Drift não tem criptografia integrada, mas suporta conectar bibliotecas sqlite3 personalizadas com SEE (SQLite Encryption Extension). Para plataformas móveis, use sqflite_sqlcipher como QueryExecutor — o Drift funciona com qualquer implementação SQLite através do QueryExecutor abstrato.

Resumo

  • Drift — um ORM reativo para Flutter e Dart com compilação de consultas em SQL em tempo de compilação
  • Sintaxe DSL — consultas Dart com segurança total de tipos e autocompletar na IDE
  • Reatividade — Stream e consultas de atualização automática para UI em tempo real
  • Multiplataforma — Android, iOS, Web (WASM), macOS, Linux, Windows
  • Migrações — automáticas para alterações simples e SQL manual para complexas
  • Ecossistema — integração com Riverpod (drift_riverpod) e BLoC (drift_bloc)
  • Recomendação — escolha Drift para projetos onde reatividade, segurança de tipos e suporte a todas as plataformas Flutter são importantes

Vamos desenvolver um aplicativo móvel chave na mão

A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.

Discutir o projeto

Leia também