如何在无数据丢失的情况下将Android Room数据库迁移至Flutter数据库(Floor/Drift)
从Android Room迁移到Flutter Floor/Drift:零数据丢失的生产级方案
核心逻辑:SQLite是通用底层
Room、Floor、Drift都是SQLite的上层封装,底层数据库文件完全兼容——这意味着你根本不需要转换数据格式,只需要让Flutter端能正确找到并读取原Room的数据库,同时对齐表结构和访问逻辑即可。
分步实现(以Floor为例,Drift逻辑一致)
1. 定位Room数据库的存储路径
Room默认将数据库存在App的内部存储中,路径格式固定:/data/data/<你的Android包名>/databases/<Room数据库名>
比如你的包名是com.yourcompany.yourapp,数据库名为main_db,完整路径就是/data/data/com.yourcompany.yourapp/databases/main_db
2. 让Flutter直接复用原数据库文件
- Floor配置:初始化时直接指定Room的数据库路径,无需创建新文件:
final database = await $FloorMainDatabase.databaseBuilder( '/data/data/com.yourcompany.yourapp/databases/main_db' ).build(); - Drift配置:通过
NativeDatabase指向原路径:
无需额外申请权限,App默认拥有访问自身内部存储的权限。final db = NativeDatabase(File('/data/data/com.yourcompany.yourapp/databases/main_db'));
3. 1:1对齐数据模型
把Room中的实体类原封不动迁移到Floor/Drift中——表名、列名、主键、可空性、约束必须完全一致,任何差异都会导致数据读取失败。
比如Room的User实体:
@Entity(tableName = "users") data class User( @PrimaryKey val id: Int, @ColumnInfo(name = "user_name") val userName: String, val email: String? )
对应的Floor实体必须完全匹配:
@Entity(tableName: 'users') class User { @PrimaryKey() final int id; @ColumnInfo(name: 'user_name') final String userName; final String? email; User(this.id, this.userName, this.email); }
4. 同步数据库版本与迁移脚本
如果你的Room数据库已经迭代过多个版本,Flutter端的Floor/Drift必须同步所有历史迁移操作:
- Floor迁移:在
@Database注解中添加migrations参数,SQL语句和Room中的逻辑完全一致:@Database(version: 3, entities: [User], migrations: [Migration1to2, Migration2to3]) abstract class MainDatabase extends FloorDatabase { UserDao get userDao; } class Migration1to2 extends Migration { Migration1to2() : super(1, 2); @override void migrate(Database database) { // 和Room迁移逻辑完全对齐,比如新增phone列 database.execute('ALTER TABLE users ADD COLUMN phone TEXT'); } } - Drift迁移:使用
@Migration注解或手动编写迁移逻辑,确保和Room的历史变更完全同步,禁止跳步执行。
5. 版本发布的过渡注意事项
- 发布Flutter版本时,不要删除原Room的数据库文件——Flutter端直接复用该文件,用户更新应用后数据不会被清空。
- 无需保留Room的数据库操作逻辑,Flutter接管后可直接移除Room相关代码,但要确保新版本不会初始化新的数据库文件,避免数据分裂。
特殊场景处理
场景1:Room使用SQLCipher加密
如果你的Room数据库已加密,Floor/Drift需要配置相同的加密密钥:
- Floor:使用
floor_cipher插件,初始化时传入与Room一致的密钥。 - Drift:使用
drift_sqlcipher,初始化时指定密钥:final db = NativeDatabase(File(path), encryptionKey: Uint8List.fromList(yourEncryptionKeyBytes));
场景2:迁移到Flutter标准存储路径
如果想将数据库转移到Flutter常用的getApplicationDocumentsDirectory()路径下,可在Flutter应用首次启动时复制原Room数据库到新路径,后续使用新路径:
import 'package:path_provider/path_provider.dart'; import 'dart:io'; Future<String> getFlutterDbPath() async { final docDir = await getApplicationDocumentsDirectory(); return '${docDir.path}/main_db'; } Future<void> copyRoomDbToFlutterDir() async { final roomDbPath = '/data/data/com.yourcompany.yourapp/databases/main_db'; final flutterDbPath = await getFlutterDbPath(); final roomFile = File(roomDbPath); final flutterFile = File(flutterDbPath); // 仅在Flutter数据库不存在时复制,避免覆盖现有数据 if (!await flutterFile.exists()) { try { await roomFile.copy(flutterDbPath); } catch (e) { // 处理复制失败场景,如磁盘空间不足 print('数据库复制失败:$e'); } } }
之后初始化Floor/Drift时使用flutterDbPath即可。
必做验证测试
- 在真机安装旧版Room应用,插入测试数据后升级到Flutter版本,检查数据完整性。
- 测试数据增删改操作,确认数据可正常持久化。
- 测试数据库版本升级流程,验证迁移脚本执行后数据无丢失。
内容的提问来源于stack exchange,提问作者Divyesh Rudani
相关产品推荐
相关产品推荐

