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

Flutter/Bloc应用:恢复网络后Hive本地数据同步至远程API的最佳实践

Flutter/Bloc架构下离线数据同步的最佳实践

针对你遇到的本地Hive数据向远程API同步的场景,把逻辑放在initState里存在局限性(比如页面销毁后无法触发、仅在页面初始化时执行一次),以下是更可靠的实现方案:

1. 用全局Cubit/Bloc统一管理同步逻辑

将同步逻辑从UI层剥离,放到全局的OfflineSyncCubit中(通过依赖注入如get_it提供实例),在应用启动时初始化,同时监听网络状态变化,确保网络恢复时自动触发同步,而非仅依赖页面初始化。

示例代码:

class OfflineSyncCubit extends Cubit<SyncState> {
  final HiveInterface hive;
  final ApiService apiService;
  final Connectivity connectivity;
  StreamSubscription? _connectivitySubscription;

  OfflineSyncCubit({
    required this.hive,
    required this.apiService,
    required this.connectivity,
  }) : super(SyncInitial()) {
    _initSync();
  }

  void _initSync() async {
    // 应用启动时先检查网络,有网则立即同步
    final status = await connectivity.checkConnectivity();
    if (status != ConnectivityResult.none) {
      await _syncLocalTasks();
    }
    // 监听网络状态切换,从无网变有网时触发同步
    _connectivitySubscription = connectivity.onConnectivityChanged.listen((status) {
      if (status != ConnectivityResult.none) {
        _syncLocalTasks();
      }
    });
  }

  Future<void> _syncLocalTasks() async {
    emit(SyncLoading());
    try {
      final taskBox = await hive.openBox('offline_tasks');
      final tasks = taskBox.values.toList().cast<OfflineTask>();
      
      for (final task in tasks) {
        // 发送请求(需保证接口幂等,用task.id作为唯一标识)
        await apiService.submitTask(task);
        // 同步成功后删除本地任务
        await taskBox.delete(task.id);
      }
      
      emit(SyncSuccess());
    } catch (e) {
      emit(SyncFailure(error: e.toString()));
    }
  }

  @override
  Future<void> close() {
    _connectivitySubscription?.cancel();
    return super.close();
  }
}

// Hive离线任务实体
@HiveType(typeId: 0)
class OfflineTask extends HiveObject {
  @HiveField(0)
  final String id;
  @HiveField(1)
  final String content;
  @HiveField(2)
  final int retryCount;
  @HiveField(3)
  final DateTime createdAt;

  OfflineTask({
    required this.id,
    required this.content,
    this.retryCount = 0,
    required this.createdAt,
  });
}

2. 实现重试机制与幂等性保障

  • 幂等性:每个离线任务生成唯一ID,服务端根据ID判断是否已处理,避免重复提交导致数据冗余。
  • 指数退避重试:同步失败时(如网络波动),按照1s、2s、4s的指数间隔重试,同时记录重试次数,超过阈值则标记为失败,等待用户手动触发重试。

3. 同步状态的UI反馈

在UI层监听OfflineSyncCubit的状态,给用户明确的同步状态提示:

BlocBuilder<OfflineSyncCubit, SyncState>(
  builder: (context, state) {
    if (state is SyncLoading) {
      return Padding(
        padding: EdgeInsets.all(8),
        child: CircularProgressIndicator(size: 20),
      );
    } else if (state is SyncFailure) {
      return TextButton(
        onPressed: () => context.read<OfflineSyncCubit>()._syncLocalTasks(),
        child: Text('重试同步'),
      );
    }
    return SizedBox.shrink();
  },
)

4. 任务提交时的即时处理

用户提交数据时,先判断网络状态:无网则直接存入Hive;有网则先尝试发送,失败后仍存入Hive兜底:

class MessageBloc extends Bloc<MessageEvent, MessageState> {
  final OfflineSyncCubit offlineSyncCubit;
  final ApiService apiService;
  final Connectivity connectivity;

  MessageBloc({
    required this.offlineSyncCubit,
    required this.apiService,
    required this.connectivity,
  }) : super(MessageInitial()) {}

  void sendMessage(String content) async {
    final task = OfflineTask(
      id: Uuid().v4(),
      content: content,
      createdAt: DateTime.now(),
    );
    final networkStatus = await connectivity.checkConnectivity();
    
    if (networkStatus == ConnectivityResult.none) {
      final box = await Hive.openBox('offline_tasks');
      await box.put(task.id, task);
    } else {
      try {
        await apiService.submitTask(task);
      } catch (e) {
        // 发送失败存入本地,等待后续同步
        final box = await Hive.openBox('offline_tasks');
        await box.put(task.id, task);
      }
    }
  }
}

5. 避免阻塞UI线程

所有Hive操作和网络请求都用异步方法执行,若批量同步任务较多,可考虑用Isolate处理,防止UI卡顿。

内容的提问来源于stack exchange,提问作者Gwaine

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 06:01:00