You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 15:47:39