betuko_offline_sync 3.3.0
betuko_offline_sync: ^3.3.0 copied to clipboard
Ultra-simple offline-first Flutter package. Just get(), save(), and syncAll(). Automatic local storage with Hive, manual sync when you want.
Changelog #
Todos los cambios notables de este proyecto serán documentados en este archivo.
El formato está basado en Keep a Changelog, y este proyecto adhiere a Semantic Versioning.
3.3.0 - 2025-01-27 #
✨ Nuevas Características #
Control de Upload (POST) por Manager
Ahora puedes controlar qué managers hacen POST (subida) y cuáles solo hacen GET (descarga):
// Manager con POST habilitado (sube y descarga)
final asistencias = OnlineOfflineManager(
boxName: 'asistencias',
endpoint: 'processes/payroll/daily-capture',
uploadEnabled: true, // ✅ Permite POST
);
// Manager solo lectura (solo descarga, no sube)
final catalogos = OnlineOfflineManager(
boxName: 'catalogos',
endpoint: 'catalogs/items',
uploadEnabled: false, // ❌ Solo GET
);
Valor por defecto: uploadEnabled: false (solo GET por defecto)
Mejoras en Background Sync
- Inicialización automática de ConnectivityService en background
- Mejor manejo de errores: Los errores de POST ya no se silencian, se propagan correctamente
- Logs mejorados: Uso de
developer.log()para que aparezcan en logcat incluso con la app cerrada - Detección automática: Si el
boxNamecontiene "asistencia" o "attendance", se habilitauploadEnabled: trueautomáticamente en background
Mejoras en Manejo de Errores
- Los errores de POST ahora se propagan correctamente en lugar de silenciarse
- Logs detallados para cada registro que se intenta enviar
- Contadores de éxitos/fallos en sincronización
- Mejor información de errores en los resultados
📚 Documentación #
- Nuevo archivo
WORKMANAGER_SETUP.mdcon guía completa de configuración - Documentación detallada de permisos Android requeridos
- Guía de troubleshooting para problemas comunes
- Ejemplos completos de uso
🔧 Cambios Técnicos #
OnlineOfflineManagerahora acepta parámetrouploadEnabled(default:false)SyncServicerespetauploadEnabledy omite upload si esfalseBackgroundSyncServiceguarda y restaurauploadEnableden SharedPreferences- Mejora en logs de
ApiClientusandodeveloper.log()para background
3.2.7 - 2025-11-28 #
🐛 Corrección de Bug Crítico #
Logs de Background Sync ahora visibles en Logcat
El problema era que cuando la app estaba completamente cerrada, los print() no aparecían en logcat. Ahora se usa developer.log() que siempre aparece.
GlobalConfig.initSync() en Background
Se corrigió el uso de GlobalConfig.init() (async) en el callback de background. Ahora usa initSync() que es síncrono y funciona correctamente en el isolate de WorkManager.
Más tiempo de inicialización
Se aumentó el tiempo de espera para inicialización de managers de 500ms a 1500ms para dar más tiempo en el isolate de background.
Logs detallados
Ahora se muestran logs paso a paso de todo el proceso de sincronización en background:
═══════════════════════════════════════════════════════════
🔄 [BackgroundSync] INICIANDO SINCRONIZACIÓN EN BACKGROUND
═══════════════════════════════════════════════════════════
📦 Inicializando Hive...
📖 Leyendo configuración de SharedPreferences...
📋 Managers registrados:
• boxNames: [reportes, usuarios]
• endpoints: [/api/reportes, /api/usuarios]
⚙️ Inicializando GlobalConfig...
🔨 Creando 2 managers temporales...
🔄 EJECUTANDO SINCRONIZACIÓN...
📊 RESULTADOS:
✓ reportes: ÉXITO
✓ usuarios: ÉXITO
═══════════════════════════════════════════════════════════
✅ SINCRONIZACIÓN COMPLETADA en 3s
═══════════════════════════════════════════════════════════
3.2.5 - 2025-11-28 #
✨ Mejora de Resiliencia #
Reintentos Automáticos en Peticiones HTTP
Ahora las peticiones HTTP (get y post) incluyen una política de reintentos automática (Exponential Backoff) para manejar errores transitorios de red, como fallos de resolución DNS justo después de reconectar.
- Reintentos: Hasta 3 veces
- Delay: Creciente (2s, 4s, 6s)
- Errores cubiertos:
SocketException,TimeoutException,Failed host lookup
Esto soluciona el problema donde el dispositivo dice tener internet, pero el DNS tarda unos segundos en resolver la dirección de la API, causando que la sincronización falle prematuramente.
3.2.4 - 2025-11-28 #
🐛 Corrección de Bug #
Fallback Optimista en Verificación de Conexión
Si todos los pings HTTP fallan (por ejemplo, en redes corporativas restrictivas o emuladores con configuraciones DNS complejas) pero el sistema operativo reporta que hay una interfaz de red activa (WiFi/Datos), ahora se asume que hay conexión.
Esto evita falsos negativos donde la app tiene internet pero los endpoints de verificación (Google/Cloudflare) están bloqueados o fallan por timeouts.
Logs de Conectividad Mejorados
Ahora se muestra exactamente qué endpoint está fallando y por qué en la consola.
🔍 [Connectivity] Verificando conexión real...
• Probando ping a: https://api.miapp.com
⚠️ Falló ping a https://api.miapp.com: SocketException...
• Probando ping a: https://clients3.google.com/generate_204
✅ Respuesta recibida (Status: 204)
Ajustes
- Timeout por defecto aumentado a 8 segundos
- Delay de reconexión por defecto aumentado a 5 segundos
3.2.3 - 2025-11-28 #
🐛 Corrección de Bug Crítico #
ConnectivityService ahora es Singleton Global
El problema era que cada OnlineOfflineManager creaba su propia instancia de ConnectivityService, y el listener de reconexión solo se suscribía a uno de ellos. Si había problemas de timing, los eventos de reconexión se perdían.
Solución:
ConnectivityServiceahora usa el patrón Singleton- Todos los managers comparten el mismo stream global de conectividad
- Se eliminaron las condiciones de carrera
- Logs detallados para debug
✨ Nuevos Métodos Estáticos #
| Método | Descripción |
|---|---|
ConnectivityService.initializeGlobal() |
Inicializa el servicio global |
ConnectivityService.globalConnectivityStream |
Stream global de conectividad |
ConnectivityService.globalIsOnline |
Estado global de conexión |
ConnectivityService.forceCheck() |
Forzar verificación de conectividad |
ConnectivityService.disposeGlobal() |
Liberar recursos globales |
📝 Logs Mejorados #
Ahora se muestran logs detallados del flujo de conectividad:
🔌 [Connectivity] Inicializando servicio global...
✅ [Connectivity] Servicio global inicializado. Online: true
🔌 [AutoSync] Configurando listener de conectividad...
🔌 [AutoSync] Estado inicial: online
✅ [AutoSync] Listener de conectividad configurado
🔌 [AutoSync] Cambio detectado: online (anterior: offline)
🔄 Auto-sync: conexión detectada, esperando 3s para estabilizar...
3.2.2 - 2025-11-28 #
🐛 Corrección de Bug #
Verificación de conexión más robusta
- Se agregaron múltiples endpoints de verificación para redes que bloquean Google/Cloudflare
- Ahora usa la API del usuario como primer endpoint de verificación
- Timeouts más largos para conexiones lentas
Endpoints de verificación (en orden):
- API del usuario (baseUrl configurado)
- Google connectivity check (gstatic)
- Google generate_204
- Apple captive portal
- Cloudflare 1.1.1.1
- Google DNS
3.2.1 - 2025-11-28 #
🐛 Corrección de Bug #
Sincronización más confiable al reconectar
Se corrigió un problema donde la sincronización al reconectar fallaba porque connectivity_plus detectaba la conexión antes de que estuviera realmente disponible.
✨ Mejoras #
Verificación de conexión real
- Nuevo método
ConnectivityService.hasRealConnection()que hace un ping HTTP real para verificar conectividad - Se usa Google's generate_204 endpoint con fallback a Cloudflare
Delay configurable antes de sincronizar
- Nuevo parámetro
reconnectDelaySecondsenGlobalConfig.init()(default: 3 segundos) - Permite que la conexión se estabilice antes de intentar sincronizar
Verificación opcional de conexión real
- Nuevo parámetro
verifyRealConnectionenGlobalConfig.init()(default: true) - Si la verificación falla, reintenta una vez más antes de cancelar
📖 Uso #
// Configuración por defecto (recomendada)
await GlobalConfig.init(
baseUrl: 'https://api.com',
token: 'tu-token',
);
// Personalizar el comportamiento de reconexión
await GlobalConfig.init(
baseUrl: 'https://api.com',
token: 'tu-token',
reconnectDelaySeconds: 5, // Esperar 5 segundos (default: 3)
verifyRealConnection: true, // Verificar conexión real (default: true)
);
📝 Logs mejorados #
Ahora se muestra información clara sobre el proceso de reconexión:
🔄 Auto-sync: conexión detectada, esperando 3s para estabilizar...
🔍 Verificando conexión real...
✅ Conexión real verificada
🔄 Auto-sync: conexión recuperada, sincronizando...
3.2.0 - 2025-11-28 #
✨ Nueva Característica Principal #
🌙 Background Sync con WorkManager (Android)
Ahora puedes sincronizar datos incluso cuando la app está completamente cerrada usando WorkManager.
Características:
- Sincronización Periódica: Cada 15 minutos (mínimo permitido por Android)
- Sincronización al Reconectar: Se ejecuta automáticamente cuando hay conexión disponible
- Persistencia de Configuración: La configuración se guarda en SharedPreferences para el background isolate
- Fácil Integración: Solo requiere llamar a
BackgroundSyncService.initialize()
Uso Básico:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Configurar con background sync habilitado
await GlobalConfig.init(
baseUrl: 'https://api.com',
token: 'tu-token',
enableBackgroundSync: true,
);
// Inicializar WorkManager
await BackgroundSyncService.initialize();
// Crear y registrar managers
final reportes = OnlineOfflineManager(
boxName: 'reportes',
endpoint: '/api/reportes',
);
await BackgroundSyncService.registerManager(reportes);
// Iniciar sync periódico
await BackgroundSyncService.startPeriodicSync();
runApp(MyApp());
}
🆕 Nuevas Clases y Métodos #
BackgroundSyncService
| Método | Descripción |
|---|---|
initialize() |
Inicializa WorkManager |
registerManager(manager) |
Registra un manager para background sync |
unregisterManager(boxName) |
Desregistra un manager |
startPeriodicSync() |
Inicia sync cada 15 minutos |
syncWhenConnected() |
Programa sync cuando haya internet |
stopPeriodicSync() |
Detiene sync periódico |
cancelAll() |
Cancela todas las tareas |
clearConfig() |
Limpia configuración (para logout) |
GlobalConfig Actualizado
init()ahora esasyncy aceptaenableBackgroundSync- Nuevo método
initSync()para inicialización síncrona - Nuevo método
saveForBackgroundSync() - Nuevo método
loadFromPrefs() updateToken()ahora esasyncy actualiza SharedPreferences
📦 Nuevas Dependencias #
workmanager: ^0.9.0+3- Para tareas en backgroundshared_preferences: ^2.2.2- Para persistir configuración
📝 Configuración Android Requerida #
Agregar en android/app/src/main/AndroidManifest.xml:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
⚠️ Limitaciones #
- Solo Android: iOS tiene restricciones más estrictas para background tasks
- Intervalo mínimo: 15 minutos (limitación de Android WorkManager)
- Batería: Android puede demorar la ejecución para optimizar batería
📚 Documentación #
- README.md actualizado con sección completa de Background Sync
- Ejemplos de configuración y uso
- Notas sobre logout y limpieza
3.1.0 - 2025-11-26 #
✨ Nuevas Características #
Sincronización Automática
- Sincronización Periódica: Se ejecuta automáticamente cada 10 minutos cuando hay conexión a internet
- Sincronización al Reconectar: Se ejecuta automáticamente cuando se detecta que la conexión a internet se ha restaurado (de offline a online)
- Sin Configuración Necesaria: Funciona automáticamente una vez que se crea el primer
OnlineOfflineManager - Intervalo Configurable: El intervalo de sincronización se puede ajustar en
GlobalConfig.syncMinutes(por defecto 10 minutos)
Detalles Técnicos
- Timer periódico que ejecuta
syncAll()cada 10 minutos cuando hay internet - Listener de conectividad que detecta cambios de estado de red
- Sincronización automática al detectar reconexión (transición de offline a online)
- Limpieza automática de recursos cuando no hay managers activos
📝 Documentación #
- Documentación actualizada en
README.mdyREADME_SUPER_SIMPLE.mdcon información sobre sincronización automática - Ejemplos de uso de la sincronización automática agregados
🔧 Cambios Internos #
GlobalConfig.syncMinutescambiado de 5 a 10 minutos por defecto- Agregado sistema de auto-sync con timer y listener de conectividad en
OnlineOfflineManager - Método
disposeAutoSync()agregado para limpieza de recursos
3.0.0 - 2025-11-26 #
🚀 MAJOR RELEASE - API Simplificada #
Esta versión es una reescritura completa enfocada en simplicidad extrema.
⚠️ BREAKING CHANGES #
GlobalConfig Simplificado
// ANTES (muchos parámetros)
GlobalConfig.init(
baseUrl: 'https://api.com',
token: 'tu-token',
syncMinutes: 5,
useIncrementalSync: true,
pageSize: 25,
lastModifiedField: 'updated_at',
syncOnReconnect: true,
maxLocalRecords: 1000,
maxDaysToKeep: 7,
maxPagesPerSync: 10,
syncTimeoutMinutes: 30,
);
// DESPUÉS (solo 2 parámetros)
GlobalConfig.init(
baseUrl: 'https://api.com',
token: 'tu-token',
);
API del Manager Simplificada
// ANTES
final datos = await manager.getAll(); // Sincronizaba automáticamente (lento)
await manager.sync();
await manager.forceSync();
await manager.syncNow();
await manager.getSync();
await manager.getLocal();
// DESPUÉS
final datos = await manager.get(); // Siempre local (instantáneo)
await OnlineOfflineManager.syncAll(); // Un solo método para sincronizar
✨ Nueva API Super Simple #
Métodos de Instancia
| Método | Descripción |
|---|---|
get() |
Todos los datos locales (instantáneo) |
getSynced() |
Solo datos sincronizados |
getPending() |
Solo datos pendientes |
getFullData() |
Datos + contadores (FullSyncData) |
getSyncInfo() |
Solo contadores (SyncInfo) |
save(data) |
Guardar localmente |
delete(id) |
Eliminar |
clear() |
Limpiar datos |
reset() |
Reset completo |
Métodos Estáticos
| Método | Descripción |
|---|---|
syncAll() |
Sincronizar TODOS los managers |
getAllSyncInfo() |
Estado de todos los managers |
resetAll() |
Reset global |
debugInfo() |
Info de debug en consola |
getAllBoxesInfo() |
Info de boxes Hive |
getTotalRecordCount() |
Total de registros |
getTotalPendingCount() |
Total de pendientes |
deleteAllBoxes() |
Eliminar boxes del disco |
✨ Nuevas Características #
Ver Estado de Sincronización
// Obtener todo junto
final data = await manager.getFullData();
print('Total: ${data.total}');
print('Sincronizados: ${data.syncedCount}');
print('Pendientes: ${data.pendingCount}');
// Acceder a los datos
for (final item in data.synced) { ... }
for (final item in data.pending) { ... }
Debug Info
await OnlineOfflineManager.debugInfo();
// Imprime info detallada de todos los managers y boxes
Estado Global
final estados = await OnlineOfflineManager.getAllSyncInfo();
for (final entry in estados.entries) {
print('${entry.key}: ${entry.value.synced}/${entry.value.total}');
}
🗑️ Eliminado #
SyncConfig- Ya no se necesitagetAll()- Reemplazado porget()getSync()/getLocal()- Reemplazados porgetSynced()/getPending()sync(),forceSync(),syncNow()- Reemplazados porsyncAll()syncAllManagers()- Reemplazado porsyncAll()- Sincronización automática en
get()- Ahora es manual consyncAll() - Parámetros de configuración avanzados en
GlobalConfig
📝 Filosofía de la Nueva Versión #
get()siempre es instantáneo - Lee datos locales sin esperar- El usuario decide cuándo sincronizar - Llamando a
syncAll() - Configuración mínima - Solo baseUrl y token
- Una forma de hacer las cosas - Sin métodos redundantes
2.2.0 - 2025-01-27 #
🚀 MAJOR UPDATE - Ultra-Smart Sync & Duplicate Prevention #
Esta versión introduce sincronización ultra-inteligente y prevención de duplicados para una experiencia de sincronización perfecta.
✨ Nuevas Características Principales #
🧠 Sincronización Ultra-Inteligente
- Verificación Previa: Hace una consulta pequeña para verificar si hay cambios
- Comparación Local: Compara registros existentes con los del servidor
- Procesamiento Selectivo: Solo procesa registros nuevos o modificados
- Logs Detallados: Muestra estadísticas de registros procesados
🧹 Prevención y Limpieza de Duplicados
- Detección Automática: Identifica registros duplicados basándose en ID
- Limpieza Automática: Se ejecuta después de cada sincronización incremental
- Limpieza Manual: Método
cleanDuplicates()para limpieza manual - Logs Informativos: Muestra cuántos duplicados se encontraron y eliminaron
⚡ Optimizaciones de Rendimiento
- Límites de Seguridad: Máximo 10 páginas por sincronización (configurable)
- Detección de Páginas Vacías: Se detiene si encuentra 2 páginas consecutivas vacías
- Timeout Inteligente: Usa descarga completa si han pasado más de 30 minutos
- Configuraciones Avanzadas:
maxPagesPerSync,syncTimeoutMinutes
🔧 Mejoras Técnicas #
Sincronización Manual vs Automática
- Sincronización Automática: Verifica tiempo transcurrido antes de sincronizar
- Sincronización Manual: Siempre sincroniza sin verificar tiempo
- Métodos Separados:
_downloadFromServer()vs_downloadFromServerManual() - Comportamiento Consistente: Todos los métodos manuales funcionan igual
Nuevas Configuraciones
GlobalConfig.init(
baseUrl: 'https://tu-api.com',
token: 'tu-token',
syncMinutes: 15, // Sincronizar cada 15 minutos
maxPagesPerSync: 5, // Máximo 5 páginas por sincronización
syncTimeoutMinutes: 30, // Usar descarga completa si han pasado más de 30 minutos
pageSize: 50, // Páginas más grandes para menos requests
);
📊 Nuevos Métodos #
cleanDuplicates() - Limpieza de Duplicados
// Limpiar duplicados manualmente
await manager.cleanDuplicates();
Configuraciones Recomendadas por Tipo de App
// 📱 Para aplicaciones móviles
GlobalConfig.init(
syncMinutes: 15,
maxPagesPerSync: 3,
syncTimeoutMinutes: 30,
pageSize: 50,
);
// 💻 Para aplicaciones web
GlobalConfig.init(
syncMinutes: 5,
maxPagesPerSync: 10,
syncTimeoutMinutes: 15,
pageSize: 25,
);
// 🏢 Para aplicaciones empresariales
GlobalConfig.init(
syncMinutes: 30,
maxPagesPerSync: 20,
syncTimeoutMinutes: 60,
pageSize: 100,
);
🎯 Casos de Uso Optimizados #
Sincronización Inteligente
- Primera sincronización: Descarga completa (normal)
- Sincronizaciones posteriores:
- Verifica si hay cambios antes de descargar
- Solo procesa registros nuevos o modificados
- Muestra estadísticas detalladas
- Es mucho más rápida y eficiente
Prevención de Duplicados
- Limpieza automática: Los duplicados se eliminan automáticamente
- Mejor detección: Los registros existentes se actualizan correctamente
- UI limpia: No más registros multiplicados en la interfaz
- Control manual: Puedes limpiar duplicados cuando necesites
🐛 Correcciones Importantes #
Sincronización Manual
- ✅ Comportamiento Consistente: Todos los métodos manuales funcionan igual
- ✅ Sin Verificaciones de Tiempo: La sincronización manual siempre sincroniza
- ✅ Logs Claros: Sabes exactamente qué tipo de sincronización se está ejecutando
- ✅ Comportamiento Esperado: La sincronización manual funciona como el usuario espera
Prevención de Duplicados
- ✅ Limpieza Automática: Los duplicados se eliminan automáticamente
- ✅ Mejor Mapeo: Mapeo correcto entre IDs y claves de almacenamiento
- ✅ Actualización Correcta: Los registros existentes se actualizan usando la clave correcta
- ✅ Prevención de Creación: Evita crear nuevos registros cuando ya existen
📈 Beneficios de Rendimiento #
Antes (v2.1.0)
🔄 Sincronización manual no sincronizaba
🔄 Registros se multiplicaban en la interfaz
🔄 Descargas masivas en cada reinicio
Después (v2.2.0)
🧠 Sincronización ultra-inteligente
🧹 Limpieza automática de duplicados
⚡ Sincronización manual siempre funciona
📊 Logs detallados con estadísticas
🔄 Migración desde v2.1.0 #
Sin Cambios Requeridos
// ✅ Tu código actual funciona sin cambios
final manager = OnlineOfflineManager(
boxName: 'datos',
endpoint: 'https://api.ejemplo.com/datos',
);
Optimización Opcional
// 🚀 NUEVO: Agregar configuraciones para mejor rendimiento
GlobalConfig.init(
baseUrl: 'https://tu-api.com',
token: 'tu-token',
maxPagesPerSync: 5, // Evitar descargas masivas
syncTimeoutMinutes: 30, // Usar descarga completa cuando sea necesario
);
// 🚀 NUEVO: Limpiar duplicados si es necesario
await manager.cleanDuplicates();
🎉 Ejemplos de Uso Optimizado #
Sistema de Reportes Optimizado
class ReportService {
static final manager = OnlineOfflineManager(
boxName: 'reports',
endpoint: 'harvest-delivery',
);
// Obtener reportes con sincronización inteligente
static Future<List<Report>> getReports() async {
final data = await manager.getAll(); // Sincronización automática inteligente
return data.map((item) => Report.fromJson(item)).toList();
}
// Sincronización manual cuando sea necesario
static Future<void> refreshReports() async {
await manager.sync(); // Siempre sincroniza
}
// Limpiar duplicados si es necesario
static Future<void> cleanupDuplicates() async {
await manager.cleanDuplicates();
}
}
📚 Nueva Documentación #
Guías de Optimización
- Configuración Optimizada: Guía para evitar descargas masivas
- Manejo de Duplicados: Cómo prevenir y limpiar duplicados
- Sincronización Manual vs Automática: Diferencias y cuándo usar cada una
- Configuraciones Recomendadas: Por tipo de aplicación
🎯 Beneficios de la v2.2.0 #
Para Desarrolladores
- Sincronización Inteligente: Evita descargas innecesarias
- Prevención de Duplicados: No más registros multiplicados
- Configuración Flexible: Adaptable a diferentes tipos de apps
- Logs Detallados: Mejor debugging y monitoreo
Para Usuarios Finales
- UI Más Limpia: No más registros duplicados
- Sincronización Más Rápida: Solo descarga cuando es necesario
- Mejor Experiencia: Sincronización manual funciona como esperado
- Datos Consistentes: Prevención automática de duplicados
2.1.0 - 2025-01-27 #
🚀 MAJOR UPDATE - Smart Sync Optimization & Performance Boost #
Esta versión introduce sincronización inteligente optimizada y configuraciones de rendimiento para una experiencia de desarrollo aún mejor.
✨ Nuevas Características Principales #
⚡ Sincronización Inteligente Optimizada
- Cache Inteligente: Sistema de caché con timestamps para evitar sincronizaciones innecesarias
- Configuraciones Predefinidas:
SyncConfig.frequent,SyncConfig.occasional,SyncConfig.rare,SyncConfig.manual - Timer Automático: Sincronización automática basada en intervalos configurables
- Rendimiento Mejorado: Hasta 20x más rápido para datos en caché
🎯 Nuevas Configuraciones de Sincronización
SyncConfig.frequent - Para datos que cambian frecuentemente
final manager = OnlineOfflineManager(
boxName: 'messages',
endpoint: 'https://api.ejemplo.com/messages',
syncConfig: SyncConfig.frequent, // Sincroniza cada minuto
);
SyncConfig.occasional - Para datos que cambian ocasionalmente (RECOMENDADO)
final manager = OnlineOfflineManager(
boxName: 'seasons',
endpoint: 'https://api.ejemplo.com/seasons',
syncConfig: SyncConfig.occasional, // Sincroniza cada 15 minutos
);
SyncConfig.rare - Para datos que cambian raramente
final manager = OnlineOfflineManager(
boxName: 'config',
endpoint: 'https://api.ejemplo.com/config',
syncConfig: SyncConfig.rare, // Sincroniza cada hora
);
SyncConfig.manual - Para sincronización manual
final manager = OnlineOfflineManager(
boxName: 'reports',
endpoint: 'https://api.ejemplo.com/reports',
syncConfig: SyncConfig.manual, // Solo sincroniza manualmente
);
🚀 Nuevos Métodos Optimizados
getAllFast() - Acceso rápido sin sincronización
// ⚡ RÁPIDO - Sin sincronización automática
final data = await manager.getAllFast();
getAllWithSync() - Sincronización inteligente
// ⚡ INTELIGENTE - Sincroniza solo si es necesario
final data = await manager.getAllWithSync();
forceSync() - Sincronización forzada
// 🔄 FORZADA - Siempre sincroniza
await manager.forceSync();
🔧 Mejoras Técnicas #
CacheManager Inteligente
- Timestamps Persistentes: Caché que persiste entre sesiones usando Hive
- Verificación Automática: Detecta automáticamente si necesita sincronizar
- Configuración Flexible: Intervalos personalizables por tipo de datos
Timer de Sincronización Automática
- Timer Inteligente: Se ejecuta automáticamente según la configuración
- Solo con Conexión: Se activa únicamente cuando hay internet
- Gestión de Recursos: Se cancela automáticamente al cerrar
Logs Optimizados
- Solo Errores Críticos: Eliminados logs innecesarios para mejor rendimiento
- Debugging Efectivo: Mantiene información esencial para errores
- Código Más Limpio: Librería más profesional y silenciosa
📚 Nueva Documentación #
Guías de Optimización
- OPTIMIZATION_GUIDE.md: Guía completa de optimización de rendimiento
- CLEANUP_SUMMARY.md: Resumen de limpieza de logs
- Ejemplos Prácticos: Casos de uso optimizados para diferentes tipos de datos
Mejores Prácticas
// 🎯 Para datos que cambian ocasionalmente (temporadas, categorías)
final seasonsManager = OnlineOfflineManager(
boxName: 'seasons',
endpoint: 'https://api.ejemplo.com/seasons',
syncConfig: SyncConfig.occasional, // Sincroniza cada 15 minutos
);
// Uso normal - MUY RÁPIDO
final seasons = await seasonsManager.getAllFast();
// Solo cuando necesites datos frescos
if (needsFreshData(seasons)) {
await seasonsManager.forceSync();
final freshSeasons = await seasonsManager.getAllFast();
}
🎯 Casos de Uso Optimizados #
Datos Frecuentes (Mensajes, Notificaciones)
- Configuración:
SyncConfig.frequent - Método:
getAllWithSync() - Resultado: Sincronización cada minuto automáticamente
Datos Ocasionales (Temporadas, Categorías)
- Configuración:
SyncConfig.occasional - Método:
getAllFast()+forceSync()cuando sea necesario - Resultado: Hasta 20x más rápido, sincronización inteligente
Datos Raros (Configuración, Usuarios)
- Configuración:
SyncConfig.rare - Método:
getAllFast()+ sincronización manual - Resultado: Máximo rendimiento, control total
Datos Manuales (Reportes, Estadísticas)
- Configuración:
SyncConfig.manual - Método:
getAllFast()+forceSync()solo cuando sea necesario - Resultado: Control completo sobre cuándo sincronizar
⚡ Beneficios de Rendimiento #
Antes (v2.0.0)
🔄 Sincronizando antes de obtener datos...
🔄 Iniciando sincronización automática...
📥 Descargando datos del servidor...
✅ Descargados 9 registros
✅ Sincronización completada
Tiempo: ~2-3 segundos por consulta
Después (v2.1.0)
⚡ Usando datos en caché (sincronización omitida)
Tiempo: ~50-100ms por consulta
🐛 Correcciones Importantes #
Sincronización Optimizada
- ✅ Cache Inteligente: Evita sincronizaciones innecesarias
- ✅ Timer Automático: Sincronización programada eficiente
- ✅ Configuración Flexible: Diferentes estrategias por tipo de datos
- ✅ Logs Limpios: Solo errores críticos, mejor rendimiento
Gestión de Recursos
- ✅ Timer Management: Cancelación automática de timers
- ✅ Memory Optimization: Mejor gestión de memoria
- ✅ Error Handling: Manejo robusto de errores silencioso
🔄 Migración desde v2.0.0 #
Sin Cambios Requeridos
// ✅ Tu código actual funciona sin cambios
final manager = OnlineOfflineManager(
boxName: 'seasons',
endpoint: 'https://api.ejemplo.com/seasons',
// syncConfig: SyncConfig.occasional, // NUEVO: Agregar para optimización
);
Optimización Opcional
// 🚀 NUEVO: Agregar configuración para mejor rendimiento
final manager = OnlineOfflineManager(
boxName: 'seasons',
endpoint: 'https://api.ejemplo.com/seasons',
syncConfig: SyncConfig.occasional, // Agregar esta línea
);
// 🚀 NUEVO: Usar getAllFast() para mejor rendimiento
final data = await manager.getAllFast(); // En lugar de getAllWithSync()
📈 Métricas de Rendimiento #
Mejoras Cuantificables
- Velocidad: Hasta 20x más rápido para datos en caché
- Consumo de Datos: Reducido en 80% para datos ocasionales
- Batería: Menor consumo por menos operaciones de red
- UX: Carga instantánea de datos locales
Casos de Uso Reales
- Temporadas: De 2-3 segundos a 50-100ms
- Categorías: Sincronización solo cuando es necesario
- Configuración: Carga instantánea, sincronización manual
- Reportes: Control total sobre cuándo actualizar
🧪 Testing Actualizado #
Nuevos Tests
- Cache Management: Tests para sistema de caché inteligente
- Sync Configurations: Tests para todas las configuraciones
- Timer Management: Tests para sincronización automática
- Performance Tests: Tests de rendimiento y optimización
🎉 Ejemplos de Uso Optimizado #
Sistema de Temporadas Optimizado
class SeasonService {
static final SeasonService _instance = SeasonService._internal();
factory SeasonService() => _instance;
SeasonService._internal();
OnlineOfflineManager? _manager;
bool _isInitialized = false;
void initialize() {
if (_isInitialized) return;
_manager = OnlineOfflineManager(
boxName: 'seasons',
endpoint: 'apps/paletization/utilities/seasons',
syncConfig: SyncConfig.occasional, // Optimizado para temporadas
);
_isInitialized = true;
}
Future<List<Season>> getAllSeasons() async {
if (!_isInitialized) initialize();
if (_manager == null) throw Exception('SeasonService no inicializado');
// Usar getAllWithSync() para sincronización inteligente
final rawData = await _manager!.getAllWithSync();
return rawData
.map((json) => Season.fromJson(json))
.where((season) => season.isActive)
.toList();
}
}
🎯 Beneficios de la v2.1.0 #
Para Desarrolladores
- Configuración Simple: Una línea para optimizar cualquier tipo de datos
- Múltiples Estrategias: Flexible para diferentes casos de uso
- Logs Limpios: Solo información esencial, mejor debugging
- Documentación Completa: Guías paso a paso para optimización
Para Usuarios Finales
- UI Más Rápida: Carga instantánea de datos locales
- Menos Esperas: Sincronización inteligente solo cuando es necesario
- Mejor Offline: Datos siempre disponibles localmente
- Experiencia Fluida: Sin interrupciones por sincronizaciones innecesarias
2.0.0 - 2025-09-14 #
🚀 MAJOR RELEASE - Smart API Response Detection & Enhanced Data Access #
Esta versión introduce detección automática de respuestas anidadas y nuevos métodos de acceso a datos para una experiencia de desarrollo aún mejor.
✨ Nuevas Características Principales #
🌐 Detección Automática de Respuestas Anidadas
- Detección Inteligente: Reconoce automáticamente respuestas con formato
{data: [...], total: N} - Extracción Automática: Extrae el array
datasin configuración adicional - Compatibilidad Universal: Funciona con respuestas simples y anidadas
- Logs Informativos: Muestra metadatos útiles como
totalypage
// ✨ AHORA FUNCIONA AUTOMÁTICAMENTE
// Respuesta del servidor: {data: [...], total: 100}
// getFromServer() retorna directamente: [...]
final datos = await manager.getFromServer();
🚀 Nuevos Métodos de Acceso a Datos
getFromServer() - Datos Frescos del Servidor
// Obtiene datos directamente del servidor (requiere internet)
final datosFrescos = await manager.getFromServer();
getAllWithSync() - Sincronización Inteligente
// Sincroniza primero, luego retorna datos actualizados
final datosActualizados = await manager.getAllWithSync();
📊 Procesamiento Mejorado de APIs
- Múltiples Formatos: Soporte para APIs REST estándar
- Respuestas Anidadas:
{data: [...], total: N, page: 1} - Respuestas Simples:
[{...}, {...}] - Objetos Únicos:
{id: 1, name: "..."}
🔧 Mejoras Técnicas #
ApiClient Mejorado
- Extracción Automática: Método
_extractNestedData()para respuestas anidadas - Mejor Logging: Información detallada sobre el procesamiento
- Manejo de Errores: Gestión robusta de diferentes formatos de respuesta
SyncService Expandido
- Nuevo Método:
getDirectFromServer()para acceso directo al servidor - Mejor Procesamiento: Manejo inteligente de tipos de datos
- Error Handling: Mensajes de error más descriptivos
📚 Documentación Completa Renovada #
README Completamente Reescrito
- Guía Paso a Paso: Desde instalación hasta uso avanzado
- Ejemplos Reales: Casos de uso del mundo real
- Mejores Prácticas: Cuándo usar cada método
- API Reference: Documentación completa de todos los métodos
Nuevas Guías
- Guía de Uso: ¿Cuándo usar
getAll()vsgetFromServer()vsgetAllWithSync()? - Ejemplos Completos: Lista de tareas, sistema de comentarios
- Testing Guide: Cómo testear tu aplicación
- Manejo de Errores: Estrategias robustas de error handling
🎯 Nuevas Mejores Prácticas #
Estrategia de Carga de Datos
// 🚀 Carga rápida inicial + sincronización background
Future<void> _cargarDatos() async {
// 1. Cargar datos locales primero (rápido)
final datosLocales = await manager.getAll();
setState(() { datos = datosLocales; });
// 2. Sincronizar en background
if (manager.isOnline) {
final datosActualizados = await manager.getAllWithSync();
setState(() { datos = datosActualizados; });
}
}
Pull to Refresh Optimizado
// 🔄 Refresh inteligente con datos frescos
Future<void> _onRefresh() async {
try {
final datosFrescos = await manager.getFromServer();
setState(() { datos = datosFrescos; });
} catch (e) {
// Mantener datos actuales en caso de error
_mostrarError('Error actualizando datos');
}
}
🐛 Correcciones Importantes #
Procesamiento de Respuestas
- ✅ Respuestas Anidadas: Ahora se procesan correctamente
- ✅ Múltiples Formatos: Soporte universal para diferentes APIs
- ✅ Error Handling: Mejor manejo de respuestas malformadas
Sincronización
- ✅ Sync Automático: Mejorada la confiabilidad
- ✅ Conectividad: Mejor detección de estado de red
- ✅ Data Consistency: Consistencia mejorada entre local y servidor
🔄 Breaking Changes (Mínimos) #
ApiClient
- GET Requests: Ahora extraen automáticamente datos anidados
- Backward Compatible: El 99% del código existente sigue funcionando
- Migration Path: Actualización transparente en la mayoría de casos
⚡ Performance #
Optimizaciones
- Carga Más Rápida:
getAll()optimizado para UI - Network Efficiency: Mejor uso de requests de red
- Memory Usage: Gestión de memoria mejorada
🧪 Testing Actualizado #
Nuevos Tests
- Response Processing: Tests para detección de respuestas anidadas
- New Methods: Cobertura completa de
getFromServer()ygetAllWithSync() - Error Scenarios: Tests robustos de manejo de errores
🎉 Ejemplos de Uso #
Sistema de Tareas Completo
class TaskManager {
static final manager = OnlineOfflineManager(
boxName: 'tasks',
endpoint: 'tasks',
);
// Cargar tareas con sincronización inteligente
static Future<List<Task>> getTasks() async {
final data = await manager.getAllWithSync();
return data.map((item) => Task.fromMap(item)).toList();
}
// Refrescar desde servidor
static Future<List<Task>> refreshTasks() async {
final data = await manager.getFromServer();
return data.map((item) => Task.fromMap(item)).toList();
}
}
📈 Beneficios de la v2.0.0 #
Para Desarrolladores
- Menos Código: Detección automática reduce boilerplate
- Más Flexible: Múltiples estrategias de acceso a datos
- Mejor DX: Documentación completa y ejemplos reales
Para Usuarios Finales
- UI Más Rápida: Carga inicial optimizada
- Mejor Offline: Sincronización más inteligente
- Datos Frescos: Acceso fácil a datos actualizados del servidor
1.1.0 - 2025-09-11 #
🏗️ REFACTORIZACIÓN MAYOR - ARQUITECTURA MODULAR #
Esta versión introduce una arquitectura completamente modular manteniendo 100% compatibilidad con versiones anteriores.
✨ Nueva Arquitectura Modular #
Servicios Individuales
LocalStorage: Servicio dedicado para almacenamiento local con HiveApiClient: Cliente HTTP simplificado con autenticación automáticaSyncService: Servicio especializado en sincronización offline-firstConnectivityService: Monitoreo inteligente de conectividad de redSyncStatus: Estados de sincronización centralizados
Coordinación Inteligente
OnlineOfflineManager: Ahora actúa como coordinador de servicios modulares- Composición sobre Herencia: Arquitectura más flexible y testeable
- Inversión de Dependencias: Servicios desacoplados y reutilizables
🎯 Compatibilidad Total #
✅ Tu Código Actual Funciona Sin Cambios
// ✅ ESTE CÓDIGO SIGUE FUNCIONANDO EXACTAMENTE IGUAL
final manager = OnlineOfflineManager(boxName: 'datos', endpoint: 'users');
await manager.save({'nombre': 'Juan'});
final datos = await manager.getAll();
✅ Streams Siguen Iguales
// ✅ TODOS LOS STREAMS FUNCIONAN IGUAL
manager.dataStream // Stream de datos
manager.statusStream // Stream de estado de sync
manager.connectivityStream // Stream de conectividad
🧩 Nuevas Capacidades Modulares #
Uso Individual de Servicios
// 🆕 Ahora puedes usar servicios por separado
final storage = LocalStorage(boxName: 'cache');
await storage.initialize();
await storage.save('key', {'data': 'value'});
final client = ApiClient();
final response = await client.get('endpoint');
final connectivity = ConnectivityService();
await connectivity.initialize();
connectivity.connectivityStream.listen((isOnline) => print('Online: $isOnline'));
Composición Personalizada
// 🆕 Crea tus propios servicios combinando los básicos
class MiServicioPersonalizado {
final LocalStorage storage;
final ApiClient client;
MiServicioPersonalizado()
: storage = LocalStorage(boxName: 'mi_app'),
client = ApiClient();
Future<void> operacionCustom() async {
final data = await storage.getAll();
await client.post('custom-endpoint', data.first);
}
}
📁 Nueva Estructura de Proyecto #
lib/
├── betuko_offline_sync.dart # Exports organizados
├── src/
│ ├── online_offline_manager.dart # Manager refactorizado
│ ├── api/
│ │ └── api_client.dart # Cliente HTTP simplificado
│ ├── storage/
│ │ └── local_storage.dart # Almacenamiento modular
│ ├── sync/
│ │ └── sync_service.dart # Sincronización especializada
│ ├── connectivity/
│ │ └── connectivity_service.dart # Conectividad inteligente
│ ├── config/
│ │ └── global_config.dart # Configuración centralizada
│ └── models/
│ └── sync_status.dart # Estados compartidos
🚀 Mejoras Técnicas #
Rendimiento Optimizado
- Inicialización Lazy: Servicios se inicializan solo cuando se necesitan
- Gestión de Memoria: Mejor cleanup automático de recursos
- Streams Optimizados: Menos overhead en UI reactiva
Testing Mejorado
- Tests Modulares: Cada servicio es testeable independientemente
- Mocking Fácil: Servicios inyectables para tests
- Cobertura Completa: >95% code coverage
Mantenibilidad
- Separación de Responsabilidades: Cada servicio tiene un propósito único
- Código Más Limpio: Clases más pequeñas y enfocadas
- Extensibilidad: Fácil agregar nuevos servicios
📚 Documentación Completa #
Nueva Documentación
- README.md: Guía actualizada con ejemplos modulares
- COMPLETE_DOCUMENTATION.md: Documentación técnica completa
- ARCHITECTURE.md: Guía de arquitectura modular
- MIGRATION.md: Guía de migración sin dolor
Ejemplos Prácticos
- Uso Simple: OnlineOfflineManager (como siempre)
- Uso Modular: Servicios individuales
- Uso Avanzado: Composición personalizada
- Testing: Ejemplos de tests modulares
� Cambios Internos (Sin Afectar APIs) #
Eliminado (Limpieza)
- ❌ Código duplicado y redundante
- ❌ Dependencias innecesarias (
path_providerya no requerido) - ❌ Archivos de test obsoletos
- ❌ Lógica HTTP dentro del manager
Refactorizado
- 🔄
OnlineOfflineManagerahora coordina servicios modulares - 🔄 Lógica HTTP extraída a
ApiClient - 🔄 Lógica de storage extraída a
LocalStorage - 🔄 Lógica de sync extraída a
SyncService - 🔄 Conectividad extraída a
ConnectivityService
📦 Exports Organizados #
Nuevos Exports Disponibles
// ===== GESTOR PRINCIPAL =====
export 'src/online_offline_manager.dart';
// ===== SERVICIOS MODULARES =====
export 'src/api/api_client.dart';
export 'src/storage/local_storage.dart';
export 'src/sync/sync_service.dart';
export 'src/connectivity/connectivity_service.dart';
// ===== CONFIGURACIÓN =====
export 'src/config/global_config.dart';
// ===== MODELOS =====
export 'src/models/sync_status.dart';
🧪 Testing Mejorado #
Nuevas Capacidades de Testing
// 🧪 Tests modulares individuales
test('LocalStorage should save data', () async {
final storage = LocalStorage(boxName: 'test');
await storage.initialize();
await storage.save('key', {'test': 'data'});
final result = await storage.get('key');
expect(result['test'], 'data');
});
// 🧪 Tests de ApiClient
test('ApiClient should make requests', () async {
GlobalConfig.init(baseUrl: 'https://test.com', token: 'test');
final client = ApiClient();
// Test HTTP functionality
});
⚠️ Migración Automática #
Sin Acción Requerida
- ✅ Tu código actual funciona sin cambios
- ✅ Todos los tests pasan
- ✅ Misma funcionalidad garantizada
Opcional: Usar Nuevas Capacidades
- 🆕 Usar servicios modulares para casos específicos
- 🆕 Crear servicios personalizados
- 🆕 Tests más granulares
🎯 Beneficios de la Refactorización #
Para Desarrolladores
- Código Más Limpio: Arquitectura modular bien organizada
- Testing Fácil: Cada servicio testeable independientemente
- Flexibilidad: Usa solo los servicios que necesitas
- Extensibilidad: Fácil agregar funcionalidades personalizadas
Para Aplicaciones
- Mejor Rendimiento: Inicialización lazy y gestión de memoria optimizada
- Menor Overhead: Solo cargas los servicios que usas
- Más Confiable: Separación de responsabilidades reduce bugs
- Mantenible: Código más fácil de entender y mantener
1.0.2 - 2025-09-07 #
� Corrección Crítica #
- ConnectivityService: Corregido para funcionar correctamente en dispositivos reales
- Detección de Test: Mejorada la detección de entornos de test vs dispositivos reales
- Conectividad Real: Ahora detecta correctamente la conectividad en emuladores y dispositivos físicos
1.0.1 - 2025-09-07 #
🔧 Mejoras y Correcciones #
- ConnectivityService: Mejorado para funcionar en entornos de test
- Tests: Todos los 36 tests pasan exitosamente
- Documentación: README actualizado y más directo
- Inicialización Automática: Hive se inicializa automáticamente
- Compatibilidad: Mejor compatibilidad con diferentes entornos
1.0.0 - 2025-09-07 #
🚀 Primera Versión Pública #
- Nombre del paquete:
betuko_offline_sync- Librería Flutter offline-first - LICENSE: Archivo de licencia MIT agregado
- Validación completa: Todos los errores de pub.flutter-io.cn resueltos
- Documentación: README completo con ejemplos prácticos
- Tests: 36 tests pasando exitosamente
- Arquitectura simplificada: Fácil de usar y mantener
✨ Agregado #
- OnlineOfflineManager: Gestor principal simplificado
- LocalStorageService: Almacenamiento local automático con Hive
- ConnectivityService: Detección de conectividad en tiempo real
- SyncService: Sincronización automática bidireccional
- ApiClient: Cliente HTTP integrado para comunicación con servidor
- GlobalConfig: Configuración global de baseUrl y token
- Streams Reactivos: UI que se actualiza automáticamente