Floor — cos'è, ORM su SQLite in Flutter

Autore: IT Sectr Pubblicato: 2026-03-13 Tempo di lettura: 9 min

Floor — ORM (Object-Relational Mapping) per Flutter, che fornisce un livello tipizzato su SQLite. A differenza delle query SQLite grezze, Floor genera classi DAO da modelli Dart annotati. Secondo Pub.dev, 2024, Floor è utilizzato in oltre 3500 progetti Flutter ed è tra le tre soluzioni ORM più popolari per l'archiviazione locale dei dati insieme a drift e hive.

Punti Chiave

  • Floor — ORM su SQLite con generazione di codice DAO e Entity tramite annotazioni
  • Sicurezza dei tipi — le query vengono verificate in fase di compilazione, eliminando errori SQL a runtime
  • Pattern DAO — Data Access Object incapsula le query SQL in metodi Dart
  • Migrazioni — supporto integrato per il versionamento dello schema SQLite
  • Reattività — query Flow tramite Stream per l'aggiornamento automatico dell'UI

Cos'è Floor?

Floor è una libreria ORM per Flutter e Dart costruita su SQLite. Utilizza annotazioni per descrivere entità (Entity), Data Access Objects (DAO) e il database (Database). La generazione del codice viene eseguita tramite build_runner e floor_generator — il compilatore crea implementazioni DAO e una classe di gestione del database. A differenza di sqflite grezzo, Floor elimina completamente la necessità di conversione manuale da ResultSet a oggetti Dart mappando automaticamente le colonne ai campi Entity tramite reflection dei tipi.

Floor segue il pattern Repository + DAO familiare agli sviluppatori Android grazie a Room. Ogni tabella è rappresentata da una classe Dart con l'annotazione @Entity, le query SQL sono raggruppate in interfacce con l'annotazione @dao e il database viene assemblato in una classe astratta con @Database. Questo approccio separa rigorosamente il modello dei dati dalla logica delle query.

Secondo Flutter Pulse (2023), Floor viene scelto nel 28% dei progetti Flutter che richiedono un database locale. I motivi principali della scelta sono la familiarità con SQL (nessuna necessità di imparare un nuovo linguaggio di query) e la verifica delle query in fase di compilazione. Inoltre, Floor genera codice leggibile che è facile da debuggare rispetto a ORM più astratti con DSL personalizzati, abbassando la barriera d'ingresso per i nuovi sviluppatori del team.

Architettura di Floor

Floor è composto da tre livelli: Entity (modello di tabella), DAO (interfaccia query) e Database (punto d'ingresso). Il generatore crea implementazioni _$_Entity per il mapping dei campi e _$_Dao per l'esecuzione SQL. Quando Entity o DAO cambiano, è sufficiente riavviare build_runner — il codice viene aggiornato automaticamente. Per la migrazione tra versioni dello schema, Floor utilizza numeri di versione sequenziali, garantendo l'integrità dei dati durante l'aggiornamento dell'app sui dispositivi degli utenti.

Come funziona Floor in Flutter?

Floor utilizza SQLite tramite il pacchetto sqflite per le build native e sqlite3 per desktop e web. All'avvio dell'app, Floor crea o apre il file SQLite, applica le migrazioni e prepara i metodi DAO per eseguire le query. Tutte le operazioni vengono eseguite in modo asincrono tramite Future e Stream.

La generazione del codice in Floor funziona come segue: il parser legge le annotazioni dai file sorgente, crea un AST (Albero Sintattico Astratto) di modelli e query, quindi genera file Dart con il prefisso _$. Il codice generato include mapper ResultSet → Entity e viceversa.

Sicurezza dei thread

Floor lavora con SQLite in un unico isolate. Tutte le query vengono eseguite in modo asincrono, ma le scritture concorrenti vengono bloccate a livello SQLite. Per le transazioni, viene utilizzata l'annotazione @transaction, che garantisce l'atomicità di un gruppo di query e il rollback in caso di errore.

Floor vs Drift: confronto ORM per Flutter

Sia Floor che Drift sono ORM su SQLite, ma differiscono per filosofia. Floor è più vicino a Room di Android, Drift è più reattivo con API Stream integrata e compilazione delle query tramite file SQL. La scelta tra di essi dipende dall'esperienza del team e dalla reattività richiesta.

CaratteristicaFloorDrift
Tipo di queryStringhe SQL in @QueryMetodi Dart + file sql
Generazione codicefloor_generator (build_runner)drift_dev (build_runner)
ReattivitàStream dal DAOAPI Stream integrata + auto-aggiornamento
ComplessitàBassa (SQL familiare)Media (DSL proprio)
MigrazioniScript SQL manualiAutomatiche + manuali
CompatibilitàAndroid, iOS, macOSAndroid, iOS, Web, macOS, Linux

Quando scegliere Floor

Floor è la scelta per i team già familiari con SQL e Android Room. Se gli sviluppatori sono abituati a scrivere query SQL manualmente e desiderano un wrapper minimo su SQLite — Floor offre la tipizzazione senza imparare un nuovo DSL. È anche più facile da debuggare poiché il codice generato è leggibile e prevedibile.

Quando Drift è migliore

Drift offre una reattività più potente e supporta più piattaforme. Se l'app utilizza attivamente Stream per aggiornare l'UI, richiede query complesse con JOIN e sottodomande o ha come target il web — Drift è preferibile. Tuttavia, la sua barriera d'ingresso è più alta a causa della necessità di imparare il proprio DSL.

Esempi di codice con Floor

Floor è costruito attorno alle annotazioni. Di seguito è riportato un esempio completo di Entity, DAO e Database per un'applicazione di elenco attività. Dopo aver eseguito build_runner, le classi generate sono pronte all'uso.

Definizione di Entity e DAO

La classe TaskEntity con annotazione @Entity viene mappata sulla tabella task. Un campo con @primaryKey diventa la chiave primaria. L'interfaccia TaskDao contiene metodi per le operazioni sulla tabella — ogni metodo è annotato con @Query, @Insert, @Update o @Delete.

dart
@entity
class TaskEntity {
    @PrimaryKey(autoGenerate: true)
    final int id;
    final String title;
    final bool isCompleted;
    final int priority;

    TaskEntity({this.id, required this.title,
        this.isCompleted = false, this.priority = 0});
}

@dao
abstract class TaskDao {
    @Query('SELECT * FROM TaskEntity ORDER BY priority DESC')
    Future<List<TaskEntity>> getAllTasks();

    @Insert
    Future<int> insertTask(TaskEntity task);

    @Update
    Future<void> updateTask(TaskEntity task);

    @Query('SELECT * FROM TaskEntity WHERE isCompleted = :status')
    Stream<List<TaskEntity>> watchTasks(bool status);
}

Inizializzazione del database

Una classe astratta con annotazione @Database collega Entity e DAO. Il metodo databaseBuilder crea un'istanza del database. Dopo aver chiamato build, il database è pronto: Floor apre il file SQLite, applica le migrazioni e restituisce il DAO per lavorare.

dart
@Database(version: 1, entities: [TaskEntity])
abstract class AppDatabase extends FloorDatabase {
    TaskDao get taskDao;
}

// Utilizzo
final database = await $FloorAppDatabase.databaseBuilder('app.db').build();
final taskDao = database.taskDao;
final tasks = await taskDao.getAllTasks();

Query reattive tramite Stream

Floor supporta la restituzione di Stream dai metodi DAO. A qualsiasi modifica nella tabella, lo Stream emette una nuova lista. Questo si integra con StreamBuilder in Flutter — l'UI si aggiorna automaticamente quando i record vengono aggiunti, modificati o eliminati.

dart
@Query('SELECT * FROM TaskEntity ORDER BY priority DESC')
Stream<List<TaskEntity>> watchAllTasks();

// Nel widget Flutter
StreamBuilder<List<TaskEntity>>(
    stream: taskDao.watchAllTasks(),
    builder: (context, snapshot) {
        final tasks = snapshot.data ?? [];
        return ListView.builder(
            itemCount: tasks.length,
            itemBuilder: (_, i) => TaskTile(tasks[i]),
        );
    },
)

Migrazioni e versionamento in Floor

Floor supporta il versionamento del database tramite il parametro version nell'annotazione @Database. Quando Entity cambia (aggiunta o rimozione di campi), è necessario incrementare la versione e aggiungere una migrazione. Una migrazione è una funzione Dart che riceve una transazione ed esegue query SQL ALTER TABLE.

Esempio di migrazione

Supponiamo che nella versione 2 abbiamo aggiunto un campo dueDate a TaskEntity. La migrazione viene eseguita tramite una query SQL ALTER TABLE. Se non viene specificata una migrazione, Floor chiama MigrationStrategy dove è possibile impostare un fallback (ad esempio, ricreare la tabella con perdita di dati).

Test delle query Floor

Floor non fornisce un framework mock integrato, ma il database può essere facilmente sostituito nei test. Creare un inMemoryDatabaseBuilder — crea un database SQLite in memoria identico nello schema alla produzione. Dopo ogni test, pulire i dati tramite deleteDatabase per isolare gli scenari di test.

Transazioni e operazioni batch in Floor

Floor supporta le transazioni tramite l'annotazione @transaction sui metodi DAO. All'interno di una transazione, più query vengono eseguite sequenzialmente con garanzia di rollback in caso di errore. L'inserimento batch tramite @Insert con un parametro List ottimizza l'inserimento di più record in una singola chiamata — questo è diverse volte più veloce rispetto all'inserimento di un record alla volta in un ciclo. Per le operazioni di massa, utilizzare inserimenti batch di 100–200 record: questo è l'equilibrio ottimale tra velocità di esecuzione e consumo di RAM sui dispositivi mobili con risorse limitate.

dart
final migration1to2 = Migration(1, 2, (database) async {
    await database.execute(
        'ALTER TABLE TaskEntity ADD COLUMN dueDate TEXT'
    );
});

final database = await $FloorAppDatabase.databaseBuilder('app.db')
    .addMigrations([migration1to2])
    .build();

Domande Frequenti

In cosa Floor è diverso da sqflite grezzo?

sqflite richiede la scrittura manuale di query SQL e il mapping di ResultSet agli oggetti. Floor genera questo codice automaticamente: si descrivono Entity e DAO, e i metodi tipizzati restituiscono oggetti Dart pronti all'uso. Floor verifica anche le query SQL in fase di compilazione tramite annotazioni.

Floor supporta le relazioni tra tabelle?

Floor non ha annotazioni integrate per le relazioni (ForeignKey, @Relation) come Room. Le relazioni vengono implementate tramite query SQL JOIN manuali in @Query. Per schemi relazionali complessi, considerare Drift con il suo supporto integrato alle relazioni.

Come debuggare le query SQL di Floor?

Floor consente di abilitare un callback durante la creazione di DatabaseBuilder — riceve un'istanza di sqflite.Database a cui è possibile collegare un logger. In alternativa, utilizzare floor_doctor per visualizzare schema e dati in modalità dev.

Floor può essere utilizzato per build web?

Floor utilizza sqflite, che non funziona in ambiente web. Per il web, è necessaria una build separata con sqlite3 tramite WASM. Nella versione attuale, Floor supporta ufficialmente Android, iOS e macOS. Per il web, utilizzare Drift con l'adattatore sqlite3.

Come funziona la cache delle query in Floor?

Floor non ha una cache integrata — ogni query viene eseguita su SQLite. Per memorizzare nella cache le query ripetute, utilizzare un livello Repository con cache in memoria (ad esempio, dart_cache). Floor genera solo codice per lavorare con SQLite senza aggiungere overhead su di esso.

Riepilogo

  • Floor — ORM su SQLite con generazione di codice Entity, DAO e Database tramite annotazioni
  • Sicurezza dei tipi — le query SQL vengono verificate in fase di compilazione tramite l'annotazione @Query
  • Pattern DAO — le query SQL sono incapsulate in metodi Dart, separando il modello dalla logica
  • Migrazioni — versionamento dello schema tramite Migration con ALTER TABLE manuale
  • Reattività — Stream dal DAO per aggiornamento automatico dell'UI alle modifiche
  • Limitazioni — nessuna relazione integrata, non supporta build web
  • Raccomandazione — scegliere Floor per progetti Flutter dove il team ha familiarità con SQL e l'approccio Room

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche