Flutter无网络时缓存数据,联网后自动同步至Firebase的实现方法
实现Flutter离线缓存num数据并自动同步Firebase的方案
核心思路
离线时将待同步的num数据存入本地缓存,监听网络状态恢复事件,触发后台同步逻辑;同步成功后清理对应本地缓存,失败则标记状态待后续重试。
具体实现步骤
1. 本地缓存选型与实现
推荐用Hive(轻量级NoSQL数据库,适合小型结构化数据),比SharedPreferences更灵活,支持自定义对象存储。
- 添加依赖到
pubspec.yaml:
dependencies: hive: ^2.2.3 hive_flutter: ^1.1.0 dev_dependencies: hive_generator: ^1.1.5 build_runner: ^2.1.11
- 初始化Hive(在
main()中执行):
void main() async { WidgetsFlutterBinding.ensureInitialized(); await Hive.initFlutter(); await Hive.openBox('sync_queue'); // 打开存储队列的盒子 runApp(MyApp()); }
- 封装缓存操作工具类:
class SyncCache { static final Box _box = Hive.box('sync_queue'); // 存入待同步的num数据 static Future<void> addToQueue(num data) async { final key = DateTime.now().millisecondsSinceEpoch.toString(); await _box.put(key, { 'data': data, 'timestamp': DateTime.now().toIso8601String(), 'status': 'pending', // pending/synced/failed 'retryCount': 0 }); } // 获取所有待同步数据(带key) static List<Map<String, dynamic>> getPendingData() { return _box.keys .map((key) => { 'key': key, ..._box.get(key) as Map<String, dynamic> }) .where((item) => item['status'] == 'pending') .toList(); } // 标记数据为已同步并删除 static Future<void> markAsSynced(String key) async { await _box.delete(key); } // 标记数据为同步失败,增加重试次数 static Future<void> markAsFailed(String key) async { final currentData = _box.get(key) as Map<String, dynamic>; await _box.put(key, { ...currentData, 'status': 'failed', 'retryCount': (currentData['retryCount'] ?? 0) + 1 }); } }
2. 网络状态监听
用connectivity_plus包监听网络切换,网络恢复时自动触发同步。
- 添加依赖:
dependencies: connectivity_plus: ^4.0.1
- 实现全局网络监听组件:
class NetworkListener extends StatefulWidget { final Widget child; const NetworkListener({super.key, required this.child}); @override State<NetworkListener> createState() => _NetworkListenerState(); } class _NetworkListenerState extends State<NetworkListener> { late StreamSubscription<ConnectivityResult> _subscription; @override void initState() { super.initState(); _subscription = Connectivity().onConnectivityChanged.listen((result) { if (result != ConnectivityResult.none) { SyncService.syncPendingData(); } }); } @override void dispose() { _subscription.cancel(); super.dispose(); } @override Widget build(BuildContext context) { return widget.child; } }
将该组件套在MaterialApp外层,实现全局监听:
runApp(NetworkListener(child: MyApp()));
3. 同步逻辑封装
创建同步服务类,处理与Firebase的交互及缓存状态更新:
class SyncService { // 批量同步待处理数据 static Future<void> syncPendingData() async { final pendingData = SyncCache.getPendingData(); for (var item in pendingData) { final key = item['key'] as String; try { // 发送数据到Firebase(示例用Firestore) await FirebaseFirestore.instance.collection('num_records').add({ 'value': item['data'], 'created_at': item['timestamp'] }); await SyncCache.markAsSynced(key); } catch (e) { await SyncCache.markAsFailed(key); // 超过3次重试则不再尝试 if (item['retryCount'] >= 3) { // 可记录错误日志或提示用户 } } } } }
4. 后台任务保障(可选)
如果需要应用退到后台仍能同步,用workmanager实现周期性任务:
- 添加依赖:
dependencies: workmanager: ^0.5.1
- 初始化与注册任务:
void callbackDispatcher() { Workmanager().executeTask((task, inputData) async { await SyncService.syncPendingData(); return Future.value(true); }); } void main() async { WidgetsFlutterBinding.ensureInitialized(); await Hive.initFlutter(); await Hive.openBox('sync_queue'); // 初始化workmanager Workmanager().initialize( callbackDispatcher, isInDebugMode: false, ); // 每15分钟检查一次待同步数据 Workmanager().registerPeriodicTask( 'sync_task', 'syncPendingData', frequency: const Duration(minutes: 15), ); runApp(NetworkListener(child: MyApp())); }
5. 日常数据发送逻辑
平时发送数据时先判断网络状态,有网直接发,无网存缓存:
Future<void> sendData(num data) async { final connectivityResult = await Connectivity().checkConnectivity(); if (connectivityResult != ConnectivityResult.none) { try { await FirebaseFirestore.instance.collection('num_records').add({ 'value': data, 'created_at': DateTime.now().toIso8601String() }); } catch (e) { // 在线发送失败也存入缓存 await SyncCache.addToQueue(data); } } else { await SyncCache.addToQueue(data); } }
注意事项
- 去重处理:存入缓存前可检查是否已有相同数据(比如根据num值和时间范围判断),避免重复同步。
- 指数退避重试:对失败数据可设置指数退避策略(如1分钟、2分钟、4分钟后重试),减少无效请求。
- 缓存清理:定期清理超过7天的失败数据,或提供手动清理入口。
- 权限配置:确保Firebase的写入权限配置正确,避免因权限问题导致同步失败。
内容的提问来源于stack exchange,提问作者BerkayCelikdemir
相关产品推荐
相关产品推荐

