Flutter ObjectBox Provider
flutter_objectbox_provider es una biblioteca para gestionar la base de datos ObjectBox en aplicaciones Flutter. Proporciona funciones para inicializar, leer, guardar, actualizar y eliminar registros en ObjectBox, con soporte para claves alternativas, búsquedas vectoriales HNSW y sincronización offline.
Instalación
Para usar flutter_objectbox_provider, añade la dependencia en tu archivo pubspec.yaml:
dependencies:
flutter_objectbox_provider: ^1.0.0
flutter_models_provider: ^1.0.2
flutter_utils_providers: ^1.0.0
Luego ejecuta:
flutter pub get
Uso
Inicialización
Primero, inicializa el proveedor de ObjectBox:
final objectBox = FlutterObjectBoxProvider(debugMode: true);
await objectBox.initStore(
storeInMemory: false,
urlAdmin: 'http://127.0.0.1:8090',
maxDBSizeInKB: 1024 * 1024,
);
Para guardar registros en ObjectBox:
var datos = [
ColeccionObjectBox(
id: 0,
coleccion: 'AppCliente', //Nombre de la coleccion
idServer: '',
idMobile: '',
data: '{"nombre": "Juan", "apellido": "Perez", "localidad": "Buenos Aires"}',
dataOriginal: '{"nombre": "Juan", "apellido": "Perez", "localidad": "Buenos Aires"}',
estado: 'A',
creadoEl: '20230101234530001',
idAuth: 'auth1',
coleccionAuth: 'authColeccion',
)
];
final resultado = await objectBox.guardarEnObjectBox<ColeccionObjectBox>(
data: datos,
queryPropertiesWrapper: ColeccionObjBoxWrapper(),
keysAlternative: ['nombre', 'apellido', 'localidad'],
keysAlternativeHNSW: ['ubicacion'],
);
if (resultado.containsKey(EnvironmentApiRest.dataOk)) {
List<ColeccionObjectBox> registrosGuardados = resultado[EnvironmentApiRest.data];
// Procesar los registros guardados
} else {
String error = resultado['error'];
// Manejar el error
}
Leer Registros
Para leer un registro por su ID:
final resultado = await objectBox.leerByIdObjectBox<ColeccionObjectBox>(
coleccion: 'AppCliente',
id: '123',
tipoId: 'idServer',
queryPropertiesWrapper: ColeccionObjBoxWrapper(),
);
if (resultado.containsKey(EnvironmentApiRest.dataOk)) {
ColeccionObjectBox dato = resultado[EnvironmentApiRest.data];
// Procesar el dato obtenido
} else {
String error = resultado['error'];
// Manejar el error
}
Obtener Registros con Filtros
Para obtener registros aplicando filtros:
final resultado = await objectBox.obtenerObjectBox<ColeccionObjectBox>(
coleccion: 'AppCliente',
indexNames: ['estado', 'localidad'],
operador: ['=', '='],
values: ['A', 'Buenos Aires'],
queryPropertiesWrapper: ColeccionObjBoxWrapper(),
ordenar: 'creadoEl',
limit: '10',
skip: '0',
union: ['AND'],
);
if (resultado.containsKey(EnvironmentApiRest.dataOk)) {
List<ColeccionObjectBox> datos = resultado[EnvironmentApiRest.data];
int totalItems = resultado['totalItems'];
// Procesar los datos obtenidos
} else {
String error = resultado['error'];
// Manejar el error
}
Eliminar Registros
Para eliminar registros de una colección:
var resultado = await objectBox.eliminarObjectBox<ColeccionObjectBox>(
coleccion: 'AppCliente',
idsToErase: ['123', '456'],
tipoId: 'idServer',
queryPropertiesWrapper: ColeccionObjBoxWrapper(),
);
if (resultado.containsKey(EnvironmentApiRest.dataOk)) {
print('La eliminación fue exitosa');
} else {
String error = resultado['error'];
print('Error: $error');
}
Actualizar Registros
Para actualizar el estado de un registro:
final resultado = await objectBox.cambiaEstadoObjectBox<ColeccionObjectBox>(
coleccion: 'AppCliente',
id: '123',
tipoId: 'idServer',
estado: 'B',
queryPropertiesWrapper: ColeccionObjBoxWrapper(),
);
if (resultado.containsKey(EnvironmentApiRest.dataOk)) {
print('Registro actualizado exitosamente');
} else {
String error = resultado['error'];
print('Error: $error');
}
Leer por Claves Alternativas
Para buscar registros usando claves alternativas:
final resultado = await objectBox.leerByAlternativeKeyObjectBox<ColeccionObjectBox>(
coleccion: 'AppCliente',
alternativeKeyToRead: AlternativeKeyToRead.alternativeKey,
valueAlternativeKey: 'Juan-Perez-Buenos Aires',
queryPropertiesWrapper: ColeccionObjBoxWrapper(),
limit: '10',
);
if (resultado.containsKey(EnvironmentApiRest.dataOk)) {
List<ColeccionObjectBox> registros = resultado[EnvironmentApiRest.data];
int totalItems = resultado['totalItems'];
// Procesar los registros
} else {
String error = resultado['error'];
print('Error: $error');
}
Leer Registros Pendientes de Transferencia
Para obtener registros que necesitan sincronización con el servidor (eTransfer == 'D'):
final resultado = await objectBox.leerPendientesTransferirObjectBox<ColeccionObjectBox>(
coleccion: 'AppCliente',
queryPropertiesWrapper: ColeccionObjBoxWrapper(),
ordenar: 'creadoEl',
limit: '100',
);
if (resultado.containsKey(EnvironmentApiRest.dataOk)) {
List<ColeccionObjectBox> pendientes = resultado[EnvironmentApiRest.data];
int totalItems = resultado['totalItems'];
// Procesar registros pendientes de transferencia
} else {
String error = resultado['error'];
print('Error: $error');
}
Ejemplo de uso de keys HNSW (Obteniendo localidad cercana a Madrid)
final madrid = [40.416775, -3.703790];
/// Consultas de tipo *HNSW* en `alternativeHnswKey`, `alternativeHnswKey1`,`alternativeHnswKey2`.
/// Retorna `{'dataOk': 'OK', 'data': List<Map>, 'totalItems': int}` donde cada `Map` posee `{'item': T, 'score': double}`.
final resultado = await objectBox.leerByAlternativeHNSWKeyObjectBox<ColeccionObjectBox>(
coleccion: 'localidades',
alternativeHnswKeyToRead: AlternativeHNSWKeyToRead.alternativeHnswKey,
queryVector: madrid,
queryPropertiesWrapper: ColeccionObjBoxWrapper(),
maxResultCount: 2,
);
if (resultado.containsKey(EnvironmentApiRest.dataOk)) {
List<Map> registros = resultado[EnvironmentApiRest.data];
int totalItems = resultado['totalItems'];
for (final registro in registros) {
final ColeccionObjectBox item = registro['item'];
final double score = registro['score'];
print('Localidad: ${item.data}, Score: $score');
}
} else {
String error = resultado['error'];
print('Error: $error');
}
Obtener el Último creadoEl
Para obtener el valor del campo creadoEl más reciente:
final ultimoCreadoEl = await objectBox.ultimoCreadoElObjectBox<ColeccionObjectBox>(
coleccion: 'AppCliente',
queryPropertiesWrapper: ColeccionObjBoxWrapper(),
);
if (ultimoCreadoEl.isNotEmpty) {
print('Último registro creado: $ultimoCreadoEl');
}
Actualizar como Transferido
Para marcar registros como transferidos al servidor (limpia eTransfer):
final resultado = await objectBox.actualizoComoTransferidoObjectBox<ColeccionObjectBox>(
coleccion: 'AppCliente',
idsToAct: ['idMobile1', 'idMobile2'],
queryPropertiesWrapper: ColeccionObjBoxWrapper(),
);
if (resultado.containsKey(EnvironmentApiRest.dataOk)) {
print('Registros marcados como transferidos');
} else {
String error = resultado['error'];
print('Error: $error');
}
Gestión de Colecciones
El paquete soporta 6 tipos de colecciones que extienden de ColeccionBase:
ColeccionObjectBox(colección principal)ColeccionObjectBox1ColeccionObjectBox2ColeccionObjectBox3ColeccionObjectBox4ColeccionObjectBox5
Cada colección tiene su propia QueryPropertiesWrapper correspondiente:
ColeccionObjBoxWrapperparaColeccionObjectBoxColeccionObjBox1WrapperparaColeccionObjectBox1ColeccionObjBox2WrapperparaColeccionObjectBox2ColeccionObjBox3WrapperparaColeccionObjectBox3ColeccionObjBox4WrapperparaColeccionObjectBox4ColeccionObjBox5WrapperparaColeccionObjectBox5
En cada función que maneja una colección específica, debes especificar el tipo genérico <T> para indicar a qué colección te estás refiriendo y utilizar su respectiva QueryPropertiesWrapper.
Estructura de las Entidades
Todas las colecciones extienden de ColeccionBase y tienen la siguiente estructura:
Campos Principales
id(int): ID interno de ObjectBox (auto-generado)coleccion(String): Nombre de la colección en el servidor (ej: 'ciudadano', 'desarrollador', 'provincias')idServer(String): ID definitivo del registro en MongoDB ServeridMobile(String): ID provisional generado en el dispositivo cuando se trabaja offlinedata(String): Datos del registro en formato JSONdataOriginal(String): Datos originales para control de cambiosestado(String): Estado del registro ('A' = Activo, 'B' = Borrado, etc.)creadoEl(String): Fecha de creación en formato timestampidAuth(String): ID del usuario autenticadocoleccionAuth(String): Colección de autenticación
Claves Alternativas
Las claves alternativas se extraen del campo data para permitir búsquedas eficientes sin analizar JSON:
alternativeKey(String): Primera clave alternativaalternativeKey1(String): Segunda clave alternativaalternativeKey2(String): Tercera clave alternativa
Claves HNSW para Búsqueda Vectorial
Para búsquedas de vecinos más cercanos (ej: localidades cercanas por coordenadas):
alternativeHnswKey(ListalternativeHnswKey1(ListalternativeHnswKey2(List
Estado de Sincronización
eTransfer(String): Indica el estado de transferencia al servidor''(vacío): Registro sincronizado con el servidor'D': Registro modificado en el dispositivo, pendiente de transferencia
Ejemplo de Definición de Colección
@Entity()
class ColeccionObjectBox extends ColeccionBase {
final int id;
final String coleccion;
final String idServer;
final String idMobile;
final String data;
final String dataOriginal;
final String estado;
final String creadoEl;
final String idAuth;
final String coleccionAuth;
final String alternativeKey;
final String alternativeKey1;
final String alternativeKey2;
final List<double> alternativeHnswKey;
final List<double> alternativeHnswKey1;
final List<double> alternativeHnswKey2;
final String eTransfer;
ColeccionObjectBox({
this.id = 0,
this.coleccion = '',
this.idServer = '',
this.idMobile = '',
this.data = '',
this.dataOriginal = '',
this.estado = '',
this.creadoEl = '',
this.idAuth = '',
this.coleccionAuth = '',
this.alternativeKey = '',
this.alternativeKey1 = '',
this.alternativeKey2 = '',
this.alternativeHnswKey = const [],
this.alternativeHnswKey1 = const [],
this.alternativeHnswKey2 = const [],
this.eTransfer = '',
});
// Métodos copyWith, toJson, fromJson, etc.
// ...
}
Requisitos de Plataforma
Android
Agrega la siguiente configuración en android/app/build.gradle:
android {
defaultConfig {
minSdkVersion 21
}
configurations {
debugImplementation {
exclude group: 'io.objectbox', module: 'objectbox-android'
}
}
}
dependencies {
implementation 'io.objectbox:objectbox-android:5.0.0'
}
iOS
Configura el proyecto iOS en Xcode:
- Abre el proyecto en Xcode
- Ve a
Runner - PROJECT - Build Settings - Architectures - Architectures - Cambia
$(ARCHS_STANDARD)a$(ARCHS_STANDARD_64_BIT)
Características Principales
1. Gestión Multi-Colección
- Soporte para 6 colecciones simultáneas
- Wrappers tipados para cada colección
- Operaciones genéricas con type-safety
2. Claves Alternativas
- Hasta 3 claves alternativas por colección
- Extracción automática desde el campo JSON
data - Búsquedas rápidas sin parsear JSON
3. Búsqueda Vectorial HNSW
- Búsquedas de vecinos más cercanos
- Soporte para 3 índices HNSW por colección
- Ideal para geolocalización y búsquedas de similitud
4. Sincronización Offline
- Campo
eTransferpara tracking de cambios - Identificadores duales:
idMobile(offline) eidServer(sincronizado) - Métodos específicos para gestión de pendientes
5. Operaciones en Isolates
- Transacciones asíncronas para operaciones pesadas
- No bloquea el hilo principal
- Mejor rendimiento en lotes grandes
6. Debug y Admin
- Interfaz de administración ObjectBox en modo debug
- Logging detallado en desarrollo
- Soporte para base de datos en memoria (testing)
Respuestas Estandarizadas
Todos los métodos retornan un Map<String, dynamic> con la siguiente estructura:
Éxito
{
'dataOk': 'OK',
'data': <resultado>, // Puede ser T, List<T>, List<Map>, String, etc.
'totalItems': <int> // Solo en consultas que retornan listas
}
Error
{
'dataNOk': 'NOK',
'error': 'ER0XX <mensaje>' // Código de error + descripción
}
Códigos de Error
- ER060: Error al guardar registros
- ER061: Error al leer por ID
- ER062: Error al obtener registros con filtros
- ER063: Error al eliminar registros
- ER064: Error al actualizar como transferido
- ER065: Error al cambiar estado
- ER066: Error en claves alternativas
- ER067: Error al leer pendientes de transferencia
- ER068: Error en búsqueda HNSW
Ejemplos Completos
Para más ejemplos de implementación, visita los siguientes repositorios:
Implementación de CRUD Básico
- Ejemplo 1 - CRUD Completo
- Operaciones básicas de inserción, lectura, actualización y eliminación
- Gestión de múltiples colecciones
- Sincronización offline
Implementación con HNSW
- Ejemplo 2 - CRUD con Búsqueda Vectorial
- Búsquedas geoespaciales con HNSW
- Localización de puntos cercanos
- Implementación de claves alternativas
Métodos Principales de la API
| Método | Descripción |
|---|---|
initStore() |
Inicializa el store de ObjectBox |
closeStore() |
Cierra la conexión con la base de datos |
deleteData<T>() |
Elimina todos los registros de una colección |
guardarEnObjectBox<T>() |
Guarda o actualiza registros |
leerByIdObjectBox<T>() |
Lee un registro por ID |
obtenerObjectBox<T>() |
Obtiene registros con filtros |
eliminarObjectBox<T>() |
Elimina registros específicos |
cambiaEstadoObjectBox<T>() |
Cambia el estado de un registro |
leerByAlternativeKeyObjectBox<T>() |
Lee por clave alternativa |
leerByAlternativeHNSWKeyObjectBox<T>() |
Búsqueda vectorial HNSW |
leerPendientesTransferirObjectBox<T>() |
Lee registros pendientes de sync |
actualizoComoTransferidoObjectBox<T>() |
Marca registros como transferidos |
ultimoCreadoElObjectBox<T>() |
Obtiene el último timestamp |
Comandos de Desarrollo
# Obtener dependencias
flutter pub get
# Generar código de ObjectBox (después de modificar entidades)
flutter pub run build_runner build --delete-conflicting-outputs
# Ejecutar tests
flutter test
# Analizar código
flutter analyze
# Formatear código
dart format .
Contribuir
Las contribuciones son bienvenidas. Por favor:
- Haz fork del proyecto
- Crea una rama para tu feature (
git checkout -b feature/AmazingFeature) - Commit tus cambios (
git commit -m 'Add some AmazingFeature') - Push a la rama (
git push origin feature/AmazingFeature) - Abre un Pull Request
Soporte
Para reportar bugs o solicitar nuevas características, por favor abre un issue en el repositorio de GitHub.
Licencia
Este proyecto está licenciado bajo la licencia MIT. Consulta el archivo LICENSE para más detalles.
Versionado
- Versión actual: 2.0.0
- ObjectBox: ^5.0.0
- Branch principal: rc-6.0.0
Desarrollado por: Roble Sistemas Repositorio: https://github.com/RobleSistemas/flutter_objectbox_provider