NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
pub.dev
Ultra-simple offline-first Flutter package. Just get(), save(), and syncAll(). Automatic local storage with Hive, manual sync when you want.
Last release 1 months ago
03 Sep 2026
Release timing varies
gaps range from 8 days to 6 months
Most releases are documented
notes for 20 of 24 stable releases
Nothing withdrawn
no release was ever pulled
1 years old
24 releases · first in 2025
One column per month.
syncAll(maxConcurrent:) limita cuántos managers usan red/Hive al mismo tiempo.
syncAll(maxConcurrent:) limita cuántos managers usan red/Hive al mismo tiempo.syncAll() reutilizan el mismo Future en curso en lugar de retornar un resultado vacío.GlobalConfig.autoSyncEnabled permite desactivar polling/reconexión internos cuando la aplicación usa su propio scheduler.Nuevo CacheManager.shouldSyncMany() para evaluar varios catálogos con una sola lectura de Hive.
syncAllCacheManager.shouldSyncMany() para evaluar varios catálogos con una sola lectura de Hive.OnlineOfflineManager.isCacheFresh() y cacheFreshness() para saber si un catálogo puede omitir la red.syncAll() acepta skipFresh y precomputedFreshness para saltar managers con caché reciente (ideal para sync automático).Síntoma: Al estar sin conexión, el ApiClient hacía hasta 3 intentos con esperas de 2 s y 4 s (total ≥ 6 s por request) para errores como Failed host l
ApiClient reintentaba requests cuando no hay internetSíntoma: Al estar sin conexión, el ApiClient hacía hasta 3 intentos con esperas de 2 s y 4 s (total ≥ 6 s por request) para errores como Failed host lookup o Network is unreachable. Esto generaba futures pendientes que saturaban el canal de plataforma de Flutter y podían causar ANR en Android.
Causa raíz: La lógica de retry en _executeWithRetry() trataba todos los errores de red por igual. failed host lookup no es un error transitorio — indica que no hay internet y reintentar es inútil.
Solución: Se añade detección de isNoInternet (errores DNS y red no disponible). Cuando se detecta este tipo de error, el ciclo de retry aborta de inmediato igual que si se hubieran agotado los intentos.
Síntoma: Al arrancar la app, los logs mostraban 🔌 [Connectivity] Inicializando servicio global... dos veces, seguido de dos emisiones 📡 Emitiendo camb
ConnectivityService se inicializaba dos veces en paraleloSíntoma: Al arrancar la app, los logs mostraban 🔌 [Connectivity] Inicializando servicio global... dos veces, seguido de dos emisiones 📡 Emitiendo cambio: true. Esto podía provocar que el AutoSync intentara un sync extra justo después de que el sync de cold_start terminara.
Causa raíz: Race condition en initializeGlobal(). El guard if (_globalIsInitialized) return se comprueba antes de cualquier await, pero _globalIsInitialized = true se escribe después de varios awaits. Dos llamadas en paralelo pasaban el guard simultáneamente (ambas veían _globalIsInitialized = false) y ambas completaban la inicialización.
Solución: Se introdujo un Completer<void>? _globalInitCompleter estático. La primera llamada crea el Completer y procede. Cualquier llamada posterior que llegue mientras la inicialización está en curso espera el mismo Future en lugar de correr en paralelo. Resultado: la init corre exactamente una vez, se emite exactamente un evento online=true al arrancar y el AutoSync no dispara un sync innecesario.
Síntoma: Al llamar syncAll(retryFailed: true), managers sin endpoint configurado (auth, user_profile, etc.) aparecían como fallos reales y se reintent
Síntoma: Al llamar syncAll(retryFailed: true), managers sin endpoint configurado (auth, user_profile, etc.) aparecían como fallos reales y se reintentaban 2 veces con backoff (≈ 27 s perdidos por sync manual), apareciendo además en el panel de catálogos fallidos de la UI.
Causa raíz 1 — SyncService.sync(): cuando endpoint == null, el método salía con return sin cambiar el status (SyncStatus.idle) ni guardar el error. Al leerlo en syncAll(), status != SyncStatus.success → se interpretaba como fallo de red.
Causa raíz 2 — bloque retry en syncAll(): el filtro de managers a reintentar no excluía los que tenían error benigno "sin endpoint".
Solución:
SyncService.sync() ahora llama _updateStatus(SyncStatus.error) y guarda _lastSyncError = SyncService.noEndpointErrorMessage cuando endpoint == null.SyncService.noEndpointErrorMessage es una constante pública ('Sin endpoint configurado') para que los consumidores puedan filtrar por ella.OnlineOfflineManager.syncAll() filtra del retry cualquier manager cuyo SyncResult.error == SyncService.noEndpointErrorMessage.Impacto: cero cambios en la API pública; managers sin endpoint siguen reportando SyncResult(success: false, error: 'Sin endpoint configurado') — el mismo valor que ya filtraba la app con _isBenignNoEndpointSyncMessage.
README actualizado con todas las nuevas funciones de v3.4.0: syncAll() con parámetros opcionales, SyncResult.itemCount, SyncRunRecord y historial de s
syncAll() con parámetros opcionales, SyncResult.itemCount, SyncRunRecord y historial de sync.^3.4.0.Esta versión corrige el problema raíz por el que syncAll() siempre reportaba éxito aunque un catálogo hubiera fallado, y agrega historial de sync cons
Esta versión corrige el problema raíz por el que syncAll() siempre reportaba éxito aunque un catálogo hubiera fallado, y agrega historial de sync consultable.
syncAll() reportaba éxito aunque fallaraProblema: SyncService.sync() capturaba cualquier error internamente sin relanzarlo, por lo que syncAll() siempre construía SyncResult(success: true) sin importar lo que ocurriera en la red.
Solución: SyncService ahora guarda el error y el conteo de registros internamente. syncAll() lee el estado real después de llamar a sync() y construye SyncResult con success real.
SyncServiceString? get lastSyncError // null si el último sync fue exitoso
int get lastDownloadCount // registros descargados en el último sync OK
SyncResult con conteo de registrosNuevo campo opcional itemCount — backward-compatible, código existente no se afecta:
class SyncResult {
final bool success;
final String? error;
final int? itemCount; // ← NUEVO: registros descargados del servidor
}
syncAll() con retry y progreso opcionalesTres nuevos parámetros opcionales con defaults — llamadas sin parámetros funcionan exactamente igual:
await OnlineOfflineManager.syncAll(
retryFailed: true, // reintenta managers fallidos (default: false)
retryAttempts: 2, // intentos extra con backoff 3s/6s (default: 2)
trigger: 'manual', // etiqueta para auditoría (default: 'auto')
onProgress: (box, ok, count) {
print('$box: ${ok ? "✓ $count registros" : "✗ error"}');
},
);
Nota: ApiClient ya tenía retry HTTP interno (3 intentos, backoff 2s/4s/6s). Este retry es de nivel superior — reintenta el manager completo si falló tras los reintentos HTTP.
CacheManager: historial de sync runsNueva clase SyncRunRecord y cuatro nuevos métodos. El historial se guarda en el box _cache_metadata ya existente (sin box adicional) y se limita a 50 registros:
// Nuevo modelo
class SyncRunRecord {
final String boxName;
final DateTime startedAt;
final DateTime finishedAt;
final bool success;
final int itemCount;
final String? error;
final String trigger; // 'manual' | 'auto' | 'reconnect'
int get durationMs;
}
// Nuevos métodos
await CacheManager.saveSyncRun(record);
await CacheManager.getLastSyncRun('concepts'); // → SyncRunRecord?
await CacheManager.getRecentSyncRuns(limit: 50); // → List<SyncRunRecord>
await CacheManager.clearOldSyncRuns(keepLast: 50);
syncAll() llama saveSyncRun() automáticamente al terminar cada manager — no requiere ningún cambio en el código de la app.
sync() no lanza excepciones (mismo comportamiento que antes)syncAll() sin parámetros: idéntico al anteriorSyncResult.success y SyncResult.error: mismos tiposCacheManager.getLastSyncTime() / updateLastSyncTime(): intactosNothing published for this version
Se corrigió un problema donde al volver de offline a online, a veces se ejecutaban múltiples sincronizaciones simultáneas causando doble POST de los m
Se corrigió un problema donde al volver de offline a online, a veces se ejecutaban múltiples sincronizaciones simultáneas causando doble POST de los mismos datos.
Cambios implementados:
Protección en syncAll(): Se agregó un flag _isSyncingAll para prevenir múltiples ejecuciones simultáneas de syncAll(). Ahora si se llama desde diferentes lugares al mismo tiempo (reconexión automática, WorkManager, llamada manual), solo se ejecuta una vez.
Protección en _handleReconnection(): Ya existía un flag _isHandlingReconnection, pero ahora está mejor documentado y garantiza que solo una reconexión se procese a la vez.
WorkManager con ExistingWorkPolicy.replace: Se aseguró que syncWhenConnected() use un nombre fijo y reemplace tareas existentes en lugar de crear múltiples tareas duplicadas.
Resultado: Ahora hay protección en 3 niveles para evitar ejecuciones simultáneas y garantizar que cada dato se sincronice solo una vez.
Ahora puedes controlar qué managers hacen POST (subida) y cuáles solo hacen GET (descarga):
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)
developer.log() para que aparezcan en logcat incluso con la app cerradaboxName contiene "asistencia" o "attendance", se habilita uploadEnabled: true automáticamente en backgroundWORKMANAGER_SETUP.md con guía completa de configuraciónOnlineOfflineManager ahora acepta parámetro uploadEnabled (default: false)SyncService respeta uploadEnabled y omite upload si es falseBackgroundSyncService guarda y restaura uploadEnabled en SharedPreferencesApiClient usando developer.log() para backgroundNothing published for this version
Se agregaron múltiples endpoints de verificación para redes que bloquean Google/Cloudflare
Se corrigió un problema donde la sincronización al reconectar fallaba porque connectivity_plus detectaba la conexión antes de que estuviera realmente
Se corrigió un problema donde la sincronización al reconectar fallaba porque connectivity_plus detectaba la conexión antes de que estuviera realmente disponible.
ConnectivityService.hasRealConnection() que hace un ping HTTP real para verificar conectividadreconnectDelaySeconds en GlobalConfig.init() (default: 3 segundos)verifyRealConnection en GlobalConfig.init() (default: true)// 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)
);
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...
Ahora puedes sincronizar datos incluso cuando la app está completamente cerrada usando WorkManager.
Ahora puedes sincronizar datos incluso cuando la app está completamente cerrada usando WorkManager.
BackgroundSyncService.initialize()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());
}
| 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) |
init() ahora es async y acepta enableBackgroundSyncinitSync() para inicialización síncronasaveForBackgroundSync()loadFromPrefs()updateToken() ahora es async y actualiza SharedPreferencesworkmanager: ^0.9.0+3 - Para tareas en backgroundshared_preferences: ^2.2.2 - Para persistir configuraciónAgregar en android/app/src/main/AndroidManifest.xml:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
Sincronización Periódica: Se ejecuta automáticamente cada 10 minutos cuando hay conexión a internet
OnlineOfflineManagerGlobalConfig.syncMinutes (por defecto 10 minutos)syncAll() cada 10 minutos cuando hay internetREADME.md y README_SUPER_SIMPLE.md con información sobre sincronización automáticaGlobalConfig.syncMinutes cambiado de 5 a 10 minutos por defectoOnlineOfflineManagerdisposeAutoSync() agregado para limpieza de recursosEsta versión es una reescritura completa enfocada en simplicidad extrema.
Esta versión es una reescritura completa enfocada en simplicidad extrema.
// 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',
);
// 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
| 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é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 |
// 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) { ... }
await OnlineOfflineManager.debugInfo();
// Imprime info detallada de todos los managers y boxes
final estados = await OnlineOfflineManager.getAllSyncInfo();
for (final entry in estados.entries) {
print('${entry.key}: ${entry.value.synced}/${entry.value.total}');
}
SyncConfig - Ya no se necesitagetAll() - Reemplazado por get()getSync() / getLocal() - Reemplazados por getSynced() / getPending()sync(), forceSync(), syncNow() - Reemplazados por syncAll()syncAllManagers() - Reemplazado por syncAll()get() - Ahora es manual con syncAll()GlobalConfigget() siempre es instantáneo - Lee datos locales sin esperarsyncAll()Sincronización ultra-inteligente con verificación previa
Nuevas características:
Mejoras técnicas:
Correcciones:
Nuevos métodos:
Beneficios:
Esta versión introduce sincronización ultra-inteligente y prevención de duplicados para una experiencia de sincronización perfecta.
cleanDuplicates() para limpieza manualmaxPagesPerSync, syncTimeoutMinutes_downloadFromServer() vs _downloadFromServerManual()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
);
cleanDuplicates() - Limpieza de Duplicados// Limpiar duplicados manualmente
await manager.cleanDuplicates();
// 📱 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,
);
🔄 Sincronización manual no sincronizaba
🔄 Registros se multiplicaban en la interfaz
🔄 Descargas masivas en cada reinicio
🧠 Sincronización ultra-inteligente
🧹 Limpieza automática de duplicados
⚡ Sincronización manual siempre funciona
📊 Logs detallados con estadísticas
// ✅ Tu código actual funciona sin cambios
final manager = OnlineOfflineManager(
boxName: 'datos',
endpoint: 'https://api.ejemplo.com/datos',
);
// 🚀 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();
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();
}
}
Sincronización inteligente optimizada con caché
Esta versión introduce sincronización inteligente optimizada y configuraciones de rendimiento para una experiencia de desarrollo aún mejor.
SyncConfig.frequent, SyncConfig.occasional, SyncConfig.rare, SyncConfig.manualSyncConfig.frequent - Para datos que cambian frecuentementefinal 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 raramentefinal manager = OnlineOfflineManager(
boxName: 'config',
endpoint: 'https://api.ejemplo.com/config',
syncConfig: SyncConfig.rare, // Sincroniza cada hora
);
SyncConfig.manual - Para sincronización manualfinal manager = OnlineOfflineManager(
boxName: 'reports',
endpoint: 'https://api.ejemplo.com/reports',
syncConfig: SyncConfig.manual, // Solo sincroniza manualmente
);
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();
// 🎯 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();
}
SyncConfig.frequentgetAllWithSync()SyncConfig.occasionalgetAllFast() + forceSync() cuando sea necesarioSyncConfig.raregetAllFast() + sincronización manualSyncConfig.manualgetAllFast() + forceSync() solo cuando sea necesario🔄 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
⚡ Usando datos en caché (sincronización omitida)
Tiempo: ~50-100ms por consulta
// ✅ 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
);
// 🚀 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()
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();
}
}
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.
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.
{data: [...], total: N}data sin configuración adicionaltotal y page// ✨ AHORA FUNCIONA AUTOMÁTICAMENTE
// Respuesta del servidor: {data: [...], total: 100}
// getFromServer() retorna directamente: [...]
final datos = await manager.getFromServer();
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();
{data: [...], total: N, page: 1}[{...}, {...}]{id: 1, name: "..."}_extractNestedData() para respuestas anidadasgetDirectFromServer() para acceso directo al servidorgetAll() vs getFromServer() vs getAllWithSync()?// 🚀 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; });
}
}
// 🔄 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');
}
}
getAll() optimizado para UIgetFromServer() y getAllWithSync()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();
}
}
Nothing published for this version
Nothing published for this version
ConnectivityService: Corregido para funcionar correctamente en dispositivos reales
ConnectivityService: Mejorado para funcionar en entornos de test
Nombre del paquete: betuko_offline_sync - Librería Flutter offline-first
betuko_offline_sync - Librería Flutter offline-firstYour coding agent can read these notes before it upgrades. Set up the MCP server →