Migración de Isar v3 a Isar Plus v4
Actualizar los paquetes heredados isar 3.x a isar_plus (v4) es un cambio incompatible en el formato de archivos. El núcleo de v4 escribe metadatos distintos y no puede abrir una base escrita por v3, por lo que verás errores como:
VersionError: The database version is not compatible with this version of Isar.
La solución es exportar tus datos con el runtime antiguo e importarlos en una base nueva de Isar Plus. Los pasos siguientes describen el proceso.
Resumen de la migración
- Publica (o conserva) una build que dependa de
isar:^3.1.0+1para poder leer los archivos heredados. - Agrega
isar_pluseisar_plus_flutter_libsjunto a los paquetes antiguos mientras migras. - Ejecuta de nuevo el generador para que tus esquemas compilen contra las API de v4.
- Copia cada registro de la instancia v3 a una instancia nueva de Isar Plus.
- Elimina los archivos heredados y quita las dependencias antiguas cuando la copia termine.
Si no necesitas los datos viejos, basta con borrar el directorio de v3 y comenzar con una base vacía. El resto de esta guía se enfoca en preservar los registros existentes.
Actualiza las dependencias en paralelo
Mantén el runtime legado hasta que la copia finalice y luego añade el nuevo:
dependencies:
isar: ^3.1.0+1
isar_flutter_libs: ^3.1.0+1
isar_generator: ^3.1.0+1
isar_plus: ^1.1.5
isar_plus_flutter_libs: ^1.1.5
dev_dependencies:
build_runner: ^2.4.10
Ambos paquetes exponen los mismos símbolos de Dart, así que impórtalos con alias durante la migración:
import 'package:isar/isar.dart' as legacy;
import 'package:isar_plus/isar_plus.dart' as plus;
Regenera tus esquemas para v4
Isar Plus incluye su generador dentro del paquete principal. Vuelve a ejecutar el builder para que emita los nuevos helpers y adaptadores:
dart run build_runner build --delete-conflicting-outputs
Haz una pausa y corrige cualquier error de compilación (por ejemplo, los campos Id? deben volverse int id o usar Isar.autoIncrement). La guía de migración de la API resume los cambios clave:
writeTxn()->writeAsync()ywriteTxnSync()->write()txn()->readAsync()ytxnSync()->read()- Los ID deben llamarse
ido llevar@id; el auto-incremento ahora usaIsar.autoIncrement @enumeratedpasó a@enumValue- Los objetos embebidos sustituyen la mayoría de los enlaces heredados
Copia los datos reales
Crea una rutina de migración puntual (por ejemplo en main() antes de iniciar tu app o en un bin/migrate.dart). El patrón es:
- Abre el almacén heredado con el runtime de v3.
- Abre una instancia v4 nueva en otro directorio o con otro nombre.
- Recorre cada colección por páginas, ajusta los modelos al nuevo esquema y haz
puten la base nueva. - Marca la migración como finalizada (SharedPreferences, archivo local o feature flag) para no ejecutarla dos veces.
Future<void> migrateLegacyDb(String directoryPath) async {
final legacyDb = await legacy.Isar.open(
[LegacyUserSchema, LegacyTodoSchema],
directory: directoryPath,
inspector: false,
name: 'legacy',
);
final plusDb = await plus.Isar.open(
[UserSchema, TodoSchema],
directory: directoryPath,
name: 'app_v4',
engine: plus.IsarEngine.sqlite, // o IsarEngine.isar para el core nativo
inspector: false,
);
await _copyUsers(legacyDb, plusDb);
await _copyTodos(legacyDb, plusDb);
await legacyDb.close();
await plusDb.close();
}
Future<void> _copyUsers(legacy.Isar legacyDb, plus.Isar plusDb) async {
const pageSize = 200;
final total = await legacyDb.legacyUsers.count();
for (var offset = 0; offset < total; offset += pageSize) {
final batch = await legacyDb.legacyUsers.where().offset(offset).limit(pageSize).findAll();
await plusDb.writeAsync((isar) async {
await isar.users.putAll(
batch.map((user) => User(
id: user.id ?? plus.Isar.autoIncrement,
email: user.email,
status: _mapStatus(user.status),
)),
);
});
}
}
Consejo: Mantén las funciones de mapeo (como
_mapStatus) junto a la rutina para manejar renombrados de enums, campos removidos o limpieza de datos en un solo lugar.
Si tienes colecciones enormes, ejecuta el bucle en un isolate o servicio en segundo plano para no bloquear la UI. El mismo patrón aplica para objetos embebidos y enlaces: cárgalos con la API heredada y persístelos con el nuevo esquema.
Asegura que solo se ejecute una vez en producción
Cuando distribuyes ambos runtimes, cada inicio en frío podría intentar migrar de nuevo a menos que lo controles con una bandera. Persiste una versión o estado para que la copia corra solo una vez por instalación:
class MigrationTracker {
static const key = 'isarPlusMigration';
static Future<bool> needsMigration() async {
final prefs = await SharedPreferences.getInstance();
return !prefs.getBool(key).toString().contains('true');
}
static Future<void> markDone() async {
final prefs = await SharedPreferences.getInstance();
await prefs.setBool(key, true);
}
}
Future<void> bootstrapIsar(String dir) async {
if (await MigrationTracker.needsMigration()) {
await migrateLegacyDb(dir);
await MigrationTracker.markDone();
}
final isar = await plus.Isar.open(
[UserSchema, TodoSchema],
directory: dir,
);
runApp(MyApp(isar: isar));
}
En lugar de un booleano puedes guardar una versión numérica del esquema (por ejemplo 3 para legacy y 4 para Isar Plus) si esperas migraciones futuras. En escritorio o servidor también puedes escribir un archivo .migrated junto al directorio de la base.
Limpieza
Después de copiar todas las colecciones:
- Guarda una bandera (por ejemplo
prefs.setBool('migratedToIsarPlus', true)) para que la migración no vuelva a ejecutarse. - Borra los archivos heredados (manualmente o con
plus.Isar.deleteDatabase(name: 'legacy', directory: directoryPath, engine: plus.IsarEngine.isar)). - Quita las dependencias
isareisar_flutter_libsdepubspec.yaml. - Renombra la base nueva a tu nombre/directorio original si lo necesitas.
Solo cuando estés seguro de que ningún usuario abre la build heredada deberías publicar una actualización que dependa únicamente de isar_plus.
Resolución de problemas
- Sigue apareciendo
VersionError: Asegúrate de borrar los archivos de v3 antes de abrir la instancia v4. Los archivos WAL/LCK antiguos pueden mantener el encabezado heredado. - Claves primarias duplicadas: Los ID en v4 deben ser enteros únicos y no nulos. Usa
Isar.autoIncremento genera tus propios identificadores al copiar. - El generador falla: Ejecuta
dart pub cleanantes debuild_runnery confirma que no falten directivaspart '...g.dart';. - Necesitas revertir: Como la migración escribe en una base separada, puedes descartar los archivos nuevos y conservar los heredados hasta que la copia termine.
Con estos pasos listos, tus usuarios pueden actualizar directamente de un build con isar 3.x a una versión con isar_plus sin perder datos.