You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在无数据丢失的情况下将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指向原路径:
    final db = NativeDatabase(File('/data/data/com.yourcompany.yourapp/databases/main_db'));
    
    无需额外申请权限,App默认拥有访问自身内部存储的权限。

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 02:23:24