Floor — ORM (Object-Relational Mapping) для Flutter, предоставляющий типизированную прослойку поверх SQLite. В отличие от raw SQLite-запросов, Floor генерирует DAO-классы из аннотированных Dart-моделей. По данным Pub.dev, 2024, Floor используется более чем в 3500 Flutter-проектах и входит в тройку самых популярных ORM-решений для локального хранения данных наряду с drift и hive.
Главное
Floor — это ORM-библиотека для Flutter и Dart, построенная на основе SQLite. Она использует аннотации для описания сущностей (Entity), Data Access Object (DAO) и базы данных (Database). Генерация кода выполняется через build_runner и floor_generator — компилятор создаёт реализации DAO и управляющий класс базы данных. В отличие от raw sqflite, Floor полностью устраняет необходимость ручного преобразования ResultSet в Dart-объекты, автоматически маппя столбцы на поля Entity через отражение типов.
Floor следует шаблону Repository + DAO, знакомому разработчикам Android по Room. Каждая таблица представлена Dart-классом с аннотацией @Entity, SQL-запросы группируются в интерфейсах с аннотацией @dao, а база данных собирается в абстрактном классе с @Database. Такой подход строго разделяет модель данных и логику запросов.
По данным Flutter Pulse (2023), Floor выбирают в 28% Flutter-проектов, где требуется локальная БД. Основные причины выбора — знакомство с SQL (не нужно учить новый язык запросов) и проверка запросов на этапе компиляции. Кроме того, Floor генерирует читаемый код, который легко отлаживать в отличие от более абстрактных ORM с кастомным DSL, что снижает порог входа для новых разработчиков в команде.
Floor состоит из трёх слоёв: Entity (модель таблицы), DAO (интерфейс запросов) и Database (точка входа). Генератор создаёт реализации _$_Entity для маппинга полей и _$_Dao для выполнения SQL. При изменении Entity или DAO достаточно перезапустить build_runner — код обновится автоматически. Для миграции между версиями схемы Floor использует последовательные номера версий, что гарантирует целостность данных при обновлении приложения на устройствах пользователей.
Floor использует SQLite через пакет sqflite для платформенных сборок и sqlite3 для десктопа и web. При запуске приложения Floor создаёт или открывает SQLite-файл, применяет миграции и подготавливает DAO-методы для выполнения запросов. Все операции выполняются в асинхронном режиме через Future и Stream.
Генерация кода в Floor работает по следующему принципу: парсер считывает аннотации из исходников, создаёт AST (Abstract Syntax Tree) моделей и запросов, после чего генерирует Dart-файлы с префиксом _$. Сгенерированный код включает мапперы ResultSet → Entity и обратно.
Floor работает с SQLite в одном изоляте. Все запросы выполняются асинхронно, но конкурентные записи блокируются на уровне SQLite. Для транзакций используется @transaction аннотация, которая гарантирует атомарность группы запросов и откат при ошибке.
И Floor, и Drift — ORM поверх SQLite, но они различаются по философии. Floor ближе к Room из Android, Drift — более реактивный с встроенным Stream API и компиляцией запросов через SQL-файлы. Выбор между ними зависит от опыта команды и требуемой реактивности.
| Характеристика | Floor | Drift |
|---|---|---|
| Тип запросов | SQL-строки в @Query | Dart-методы + sql-файлы |
| Кодогенерация | floor_generator (build_runner) | drift_dev (build_runner) |
| Реактивность | Stream из DAO | Встроенный Stream API + auto-updating |
| Сложность | Низкая (знакомый SQL) | Средняя (свой DSL) |
| Миграции | Ручные SQL-скрипты | Автоматические + ручные |
| Совместимость | Android, iOS, macOS | Android, iOS, Web, macOS, Linux |
Floor — выбор команд, уже знакомых с SQL и Android Room. Если разработчики привыкли писать SQL-запросы вручную и хотят минимальную обёртку над SQLite — Floor даёт типизацию без изучения нового DSL. Он также проще в отладке, так как сгенерированный код читаем и предсказуем.
Drift даёт более мощную реактивность и поддерживает больше платформ. Если приложение активно использует Stream для обновления UI, требует сложных запросов с JOIN и подзапросами или собирается под web — Drift предпочтительнее. Однако его порог входа выше из-за необходимости изучения собственного DSL.
Floor строится вокруг аннотаций. Ниже приведён полный пример Entity, DAO и Database для приложения-списка задач. После запуска build_runner сгенерированные классы готовы к использованию.
Класс TaskEntity с аннотацией @Entity маппится на таблицу task. Поле с @primaryKey становится первичным ключом. Интерфейс TaskDao содержит методы для операций с таблицей — каждый метод аннотирован @Query, @Insert, @Update или @Delete.
@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);
}
Абстрактный класс с аннотацией @Database связывает Entity и DAO. Метод databaseBuilder создаёт экземпляр базы. После вызова build база готова: Floor открывает SQLite-файл, применяет миграции и возвращает DAO для работы.
@Database(version: 1, entities: [TaskEntity])
abstract class AppDatabase extends FloorDatabase {
TaskDao get taskDao;
}
// Использование
final database = await $FloorAppDatabase.databaseBuilder('app.db').build();
final taskDao = database.taskDao;
final tasks = await taskDao.getAllTasks();
Floor поддерживает возврат Stream из DAO-методов. При любых изменениях в таблице Stream испускает новый список. Это интегрируется с StreamBuilder во Flutter — UI автоматически обновляется при добавлении, изменении или удалении записей.
@Query('SELECT * FROM TaskEntity ORDER BY priority DESC')
Stream<List<TaskEntity>> watchAllTasks();
// Во 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]),
);
},
)
Floor поддерживает версионирование базы данных через параметр version в аннотации @Database. При изменении Entity (добавлении или удалении полей) нужно увеличить версию и добавить миграцию. Миграция — это Dart-функция, которая получает транзакцию и выполняет SQL-запросы ALTER TABLE.
Допустим, в версии 2 мы добавили поле dueDate в TaskEntity. Миграция выполняется SQL-запросом ALTER TABLE. Если миграция не указана, Floor вызывает MigrationStrategy, где можно задать fallback (например, пересоздание таблицы с потерей данных).
Floor не предоставляет встроенного мок-фреймворка, но базу данных можно легко заменить в тестах. Создайте inMemoryDatabaseBuilder — он создаёт SQLite-базу в памяти, идентичную по схеме продакшну. После каждого теста очищайте данные через deleteDatabase для изоляции тестовых сценариев.
Floor поддерживает транзакции через аннотацию @transaction на DAO-методе. Внутри транзакции выполняются несколько запросов последовательно с гарантией отката при ошибке. Пакетная вставка через @Insert с параметром List
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();
Часто задаваемые вопросы
sqflite требует ручного написания SQL-запросов и маппинга ResultSet в объекты. Floor генерирует этот код автоматически: вы описываете Entity и DAO, а типизированные методы возвращают готовые Dart-объекты. Floor также проверяет SQL-запросы на этапе компиляции через аннотации.
Floor не имеет встроенных аннотаций для связей (ForeignKey, @Relation), как Room. Связи реализуются через ручные SQL JOIN-запросы в @Query. Для сложных реляционных схем лучше рассмотреть Drift с его встроенной поддержкой отношений.
Floor позволяет включить callback callback при создании DatabaseBuilder — в него передаётся экземпляр sqflite.Database, на который можно повесить логгер. Альтернативно используйте floor_doctor для визуализации схемы и данных в dev-режиме.
Floor использует sqflite, который не работает в web-окружении. Для web потребуется отдельная сборка с sqlite3 через WASM. В текущей версии Floor официально поддерживает Android, iOS и macOS. Для web используйте Drift с sqlite3-адаптером.
Floor не имеет встроенного кэширования — каждый запрос выполняется к SQLite. Для кэширования повторяющихся запросов используйте слой Repository с in-memory кэшем (например, dart_cache). Floor лишь генерирует код для работы с SQLite, не добавляя надстроек над ним.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также