flutter_objectbox_provider 1.0.0 copy "flutter_objectbox_provider: ^1.0.0" to clipboard
flutter_objectbox_provider: ^1.0.0 copied to clipboard

A robust ObjectBox database management layer for Flutter applications with alternative keys, HNSW vector search, offline sync capabilities, and transaction management.

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)
  • ColeccionObjectBox1
  • ColeccionObjectBox2
  • ColeccionObjectBox3
  • ColeccionObjectBox4
  • ColeccionObjectBox5

Cada colección tiene su propia QueryPropertiesWrapper correspondiente:

  • ColeccionObjBoxWrapper para ColeccionObjectBox
  • ColeccionObjBox1Wrapper para ColeccionObjectBox1
  • ColeccionObjBox2Wrapper para ColeccionObjectBox2
  • ColeccionObjBox3Wrapper para ColeccionObjectBox3
  • ColeccionObjBox4Wrapper para ColeccionObjectBox4
  • ColeccionObjBox5Wrapper para ColeccionObjectBox5

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 Server
  • idMobile (String): ID provisional generado en el dispositivo cuando se trabaja offline
  • data (String): Datos del registro en formato JSON
  • dataOriginal (String): Datos originales para control de cambios
  • estado (String): Estado del registro ('A' = Activo, 'B' = Borrado, etc.)
  • creadoEl (String): Fecha de creación en formato timestamp
  • idAuth (String): ID del usuario autenticado
  • coleccionAuth (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 alternativa
  • alternativeKey1 (String): Segunda clave alternativa
  • alternativeKey2 (String): Tercera clave alternativa

Claves HNSW para Búsqueda Vectorial #

Para búsquedas de vecinos más cercanos (ej: localidades cercanas por coordenadas):

  • alternativeHnswKey (List
  • alternativeHnswKey1 (List
  • alternativeHnswKey2 (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:

  1. Abre el proyecto en Xcode
  2. Ve a Runner - PROJECT - Build Settings - Architectures - Architectures
  3. 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 eTransfer para tracking de cambios
  • Identificadores duales: idMobile (offline) e idServer (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 #

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:

  1. Haz fork del proyecto
  2. Crea una rama para tu feature (git checkout -b feature/AmazingFeature)
  3. Commit tus cambios (git commit -m 'Add some AmazingFeature')
  4. Push a la rama (git push origin feature/AmazingFeature)
  5. 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

0
likes
130
points
72
downloads

Publisher

unverified uploader

Weekly Downloads

A robust ObjectBox database management layer for Flutter applications with alternative keys, HNSW vector search, offline sync capabilities, and transaction management.

Repository (GitHub)
View/report issues

Topics

#database #objectbox #offline-first #vector-search #persistence

Documentation

API reference

License

MIT (license)

Dependencies

flat_buffers, flutter, flutter_models_provider, flutter_utils_providers, objectbox, objectbox_flutter_libs, path, path_provider, uuid

More

Packages that depend on flutter_objectbox_provider