Flutter 三方库 connectivity_plus 的鸿蒙化适配与网络状态管理实战

Flutter 三方库 connectivity_plus 的鸿蒙化适配与网络状态管理实战

欢迎加入开源鸿蒙跨平台社区: https://openharmonycrossplatform.csdn.net


一、背景与问题域

在移动应用开发中,网络连接状态的管理是构建健壮用户体验的核心课题。用户日常使用手机的过程中,网络状态变化是极为频繁的场景:进入电梯时信号衰减、地铁穿越基站覆盖边界、咖啡厅里从移动数据切换到公共 WiFi、地下车库完全失去信号------这些状态切换如果应用处理不当,轻则导致页面卡顿、白屏,重则导致用户操作数据丢失、核心流程中断。

传统开发中,开发者往往在每个需要网络请求的页面分别处理网络异常。这种做法存在三个显著缺陷:

第一,代码重复。每个涉及网络请求的页面都需要编写相似的网络检测逻辑,包括离线判断、网络恢复检测、错误提示等,造成大量冗余代码。

第二,状态不一致。分散在各页面的网络检测逻辑缺乏统一协调机制,可能出现部分页面已提示离线而其他页面仍在发起请求的情况,用户体验碎片化。

第三,维护成本高。当网络检测逻辑需要调整时(如增加 WiFi 切换到移动网络的特殊处理),需要在多个文件、多处位置逐一修改,极易遗漏。

针对上述问题,我们需要在 Flutter for OpenHarmony 项目中构建一套统一、完善的网络状态监听与响应体系。connectivity_plus 作为 Flutter 生态中网络连接状态监听领域最成熟的三方库,自然成为首选方案。


二、connectivity_plus 库的特性与 OH 平台能力评估

2.1 库的核心能力概述

connectivity_plus 由 Flutter Community 团队维护,是 Flutter 官方推荐的跨平台网络状态监听解决方案。该库的核心价值在于提供了一套统一抽象的 API,能够屏蔽底层各操作系统网络状态获取机制的差异,为开发者提供一致的使用体验。

主动查询能力 :通过 checkConnectivity() 方法,开发者可以随时获取设备当前的连接状态。这是异步方法,返回值为 List<ConnectivityResult> 枚举数组,原因是现代设备可能同时保持多种网络连接(例如手机同时连接 WiFi 和蓝牙网络)。

dart 复制代码
final results = await connectivity.checkConnectivity();
for (final result in results) {
  if (result == ConnectivityResult.wifi) {
    print('当前已连接 WiFi');
  }
}

被动监听能力 :通过 onConnectivityChanged 流,开发者可以订阅网络状态变化事件。与主动查询不同,被动监听不需要轮询,操作系统会在网络状态实际发生变化时主动通知应用,因此具有更好的实时性和更低的资源消耗。

dart 复制代码
connectivity.onConnectivityChanged.listen((results) {
  if (results.contains(ConnectivityResult.wifi)) {
    print('WiFi 已连接');
  } else if (results.contains(ConnectivityResult.none)) {
    print('当前无网络连接');
  }
});

2.2 支持的连接类型

connectivity_plus 对网络连接类型的抽象覆盖了主流应用场景:

枚举值 含义 OpenHarmony 支持情况
wifi 无线局域网 完全支持
mobile 蜂窝移动网络(4G/5G) 完全支持
ethernet 以太网有线网络 完全支持
bluetooth 蓝牙共享网络 完全支持
vpn 虚拟专用网络 完全支持
other 其他网络类型 完全支持
none 无有效网络连接 完全支持

从上表可以看出,connectivity_plus 所定义的七种连接类型在 OpenHarmony 平台均有对应实现。需要特别说明的是 none 的判定逻辑:当系统返回的列表为空,或列表中唯一元素为 ConnectivityResult.none 时,表明设备当前没有有效的网络连接。

2.3 选型决策依据

在评估网络状态监听方案时,我们主要从以下四个维度进行比较:

成熟度与稳定性。connectivity_plus 自 2018 年发布以来持续维护,API 设计经历了多个版本的迭代优化,整体稳定。相较于自研方案或新兴库,其行为边界清晰、已知问题有充分文档记录。

跨平台一致性。Android、iOS、Web、Linux、Windows、macOS 以及 OpenHarmony 共用同一套 Dart API。当业务同时覆盖多端时,开发者无需为每个平台分别学习不同的网络状态获取方式。

社区活跃度。该库由 Flutter Community 维护,在 pub.dev 上的周下载量超过百万,issues 响应及时。与 OpenHarmony 的适配工作由社区持续推进,版本更新中对 OH 平台的支持持续完善。

与 Riverpod 的协同性。connectivity_plus 本身仅提供基础的监听能力,高级功能(如网络恢复自动刷新、状态历史记录、便捷派生 Provider)需要开发者自行封装。我们选择将网络状态与 Riverpod 状态管理体系深度整合,实现更高级别的工程化实践。


三、数据模型设计

3.1 连接类型枚举

我们首先定义一套面向业务的数据模型,将 connectivity_plus 的 ConnectivityResult 枚举映射为更具业务语义的 ConnectionType 枚举:

dart 复制代码
/// 网络连接类型枚举
///
/// 覆盖 OpenHarmony 平台的主要网络形态,
/// 同时兼容 connectivity_plus 的 ConnectivityResult 定义
enum ConnectionType {
  /// 无线局域网
  wifi,

  /// 蜂窝移动网络(4G/5G)
  mobile,

  /// 以太网有线网络
  ethernet,

  /// 蓝牙共享网络
  bluetooth,

  /// 虚拟专用网络
  vpn,

  /// 其他网络类型
  other,

  /// 无网络连接
  none,
}

3.2 网络连接状态信息模型

ConnectivityInfo 是网络状态的核心数据载体,它不仅包含原始的连接类型列表,还提供了多个派生属性,简化页面层的使用:

dart 复制代码
/// 网络连接状态数据模型
class ConnectivityInfo {
  /// 当前网络连接类型列表
  /// 一个设备可能同时保持多种网络连接
  final List<ConnectionType> connectionTypes;

  /// 是否已连接(任意有效网络)
  final bool isConnected;

  /// 是否为高质量网络(WiFi 或以太网,适合大数据传输)
  final bool isHighQuality;

  /// 上次状态变化的时间戳
  final DateTime? lastChangedAt;

  /// 状态变化历史记录(用于离线时长计算等)
  final List<ConnectivityHistoryEntry> history;

  /// 获取主要连接类型(用于 UI 显示)
  /// 优先级:WiFi > 以太网 > 移动网络 > 其他
  ConnectionType? get primaryType {
    if (connectionTypes.isEmpty || connectionTypes.contains(ConnectionType.none)) {
      return null;
    }
    if (connectionTypes.contains(ConnectionType.wifi)) return ConnectionType.wifi;
    if (connectionTypes.contains(ConnectionType.ethernet)) return ConnectionType.ethernet;
    if (connectionTypes.contains(ConnectionType.mobile)) return ConnectionType.mobile;
    return connectionTypes.first;
  }

  /// 获取连接类型的友好显示名称
  String get connectionTypeName {
    final type = primaryType;
    if (type == null) return '无网络';
    switch (type) {
      case ConnectionType.wifi:    return 'WiFi';
      case ConnectionType.mobile: return '移动网络';
      case ConnectionType.ethernet: return '以太网';
      case ConnectionType.bluetooth: return '蓝牙';
      case ConnectionType.vpn:   return 'VPN';
      case ConnectionType.other: return '其他网络';
      case ConnectionType.none:   return '无网络';
    }
  }

  /// 判断是否为特定连接类型
  bool isType(ConnectionType type) => connectionTypes.contains(type);

  /// 计算离线持续时长
  /// 仅在当前处于离线状态时返回有意义的值
  Duration? get offlineDuration {
    if (isConnected) return null;
    final lastOffline = history
        .where((e) => e.fromConnected && !e.toConnected)
        .lastOrNull;
    if (lastOffline == null) return null;
    return DateTime.now().difference(lastOffline.timestamp);
  }
}

3.3 状态变化历史记录模型

为了支持离线时长计算、状态变化统计等高级功能,我们设计了 ConnectivityHistoryEntry 模型:

dart 复制代码
/// 网络状态变化历史条目
class ConnectivityHistoryEntry {
  /// 变化发生的时间戳
  final DateTime timestamp;

  /// 变化前的连接状态
  final bool fromConnected;

  /// 变化后的连接状态
  final bool toConnected;

  /// 变化前的连接类型列表
  final List<ConnectionType> fromTypes;

  /// 变化后的连接类型列表
  final List<ConnectionType> toTypes;

  /// 变化的语义类型
  final ConnectivityChangeType changeType;

  /// 是否为离线到联网的切换
  bool get wasReconnected => !fromConnected && toConnected;

  /// 是否为联网到离线的切换
  bool get wasDisconnected => fromConnected && !toConnected;
}

/// 网络状态变化类型枚举
enum ConnectivityChangeType {
  offlineToOnline,  // 离线 → 联网
  onlineToOffline,  // 联网 → 离线
  wifiToMobile,     // WiFi → 移动网络
  mobileToWifi,     // 移动网络 → WiFi
  wifiToEthernet,   // WiFi → 以太网
  ethernetToWifi,   // 以太网 → WiFi
  typeChanged,      // 其他类型的网络类型切换
  initial,          // 初始状态
}

3.4 OH 平台原生类型映射

OpenHarmony 原生网络状态以整数值形式表示,通过平台通道传递给 Flutter 层。以下是映射关系的对照说明:

OH 原生值 OH 类型名 对应 ConnectionType
0 NONE none
1 WIFI wifi
2 CELLULAR mobile
3 ETHERNET ethernet
4 VPN vpn
5 BLUETOOTH bluetooth
6 OTHER other

这一映射关系定义在 ConnectivityInfo.fromOHNative 工厂构造方法中,为未来通过 MethodChannel 直接调用 OH 原生 API 预留了扩展空间。


四、服务层实现

4.1 ConnectivityService 架构设计

ConnectivityService 是网络状态管理的核心服务类,采用单例模式确保全局唯一实例。其职责包括:初始化并启动网络状态监听、接收平台层推送的状态变化事件、管理状态历史记录、对外暴露网络状态流、以及维护网络恢复和断开的回调注册机制。

dart 复制代码
/// 网络连接状态服务
///
/// 负责网络状态监听、状态查询、历史管理及 OH 平台适配
class ConnectivityService {
  static ConnectivityService? _instance;
  static ConnectivityService get instance =>
      _instance ??= ConnectivityService._();

  ConnectivityService._();

  /// connectivity_plus 的 Connectivity 实例(跨平台统一入口)
  final Connectivity _connectivity = Connectivity();

  /// 当前网络状态
  ConnectivityInfo _currentInfo = ConnectivityInfo.empty();

  /// 网络状态变化流(广播,供多个订阅者使用)
  final StreamController<ConnectivityInfo> _connectivityController =
      StreamController<ConnectivityInfo>.broadcast();

  /// 网络状态变化历史
  final List<ConnectivityHistoryEntry> _history = [];

  /// 标记是否正在监听
  bool _isListening = false;

  /// 上一次连接状态(用于检测变化方向)
  bool? _lastConnected;

  /// 网络恢复时的回调列表
  final List<Function(ConnectivityInfo)> _onNetworkRestoredCallbacks = [];

  /// 网络断开时的回调列表
  final List<Function(ConnectivityInfo)> _onNetworkLostCallbacks = [];

  /// 获取网络状态流
  Stream<ConnectivityInfo> get connectivityStream =>
      _connectivityController.stream;

  /// 获取当前网络状态(同步访问)
  ConnectivityInfo get currentInfo => _currentInfo;

  /// 获取历史记录(不可变列表)
  List<ConnectivityHistoryEntry> get history =>
      List.unmodifiable(_history);
}

4.2 初始化与监听启动

服务的初始化流程在 init() 方法中完成,首先获取初始网络状态,然后启动被动监听:

dart 复制代码
/// 初始化并开始监听网络状态
Future<ConnectivityInfo> init() async {
  await _updateConnectivity();
  if (!_isListening) {
    _startListening();
  }
  return _currentInfo;
}

/// 启动网络状态监听
void _startListening() {
  if (_isListening) return;

  _connectivity.onConnectivityChanged.listen(
    _handleConnectivityChange,
    onError: (error) {
      debugPrint('[ConnectivityService] 监听错误: $error');
    },
  );

  _isListening = true;
  debugPrint('[ConnectivityService] 网络状态监听已启动');
}

4.3 网络状态变化处理

当操作系统推送网络状态变化事件时,_handleConnectivityChange 方法负责处理。其核心逻辑分为三步:更新内部状态、通知观察者、触发业务回调:

dart 复制代码
/// 处理网络状态变化
void _handleConnectivityChange(List<ConnectivityResult> results) {
  _updateConnectivity(results: results);

  // 通知所有订阅者
  _connectivityController.add(_currentInfo);

  // 检测离线 → 联网切换,触发自动刷新
  if (_currentInfo.isConnected && _lastConnected == false) {
    debugPrint('[ConnectivityService] 网络已恢复,触发 onNetworkRestored 回调');
    for (final callback in _onNetworkRestoredCallbacks) {
      try {
        callback(_currentInfo);
      } catch (e) {
        debugPrint('[ConnectivityService] onNetworkRestored 回调错误: $e');
      }
    }
  }

  // 检测联网 → 离线切换
  if (!_currentInfo.isConnected && _lastConnected == true) {
    debugPrint('[ConnectivityService] 网络已断开,触发 onNetworkLost 回调');
    for (final callback in _onNetworkLostCallbacks) {
      try {
        callback(_currentInfo);
      } catch (e) {
        debugPrint('[ConnectivityService] onNetworkLost 回调错误: $e');
      }
    }
  }

  _lastConnected = _currentInfo.isConnected;
}

4.4 回调注册与注销机制

服务层提供了注册和注销回调的公开接口,业务层可以根据需要订阅网络恢复或网络断开事件:

dart 复制代码
/// 注册网络恢复回调
void onNetworkRestored(Function(ConnectivityInfo) callback) {
  _onNetworkRestoredCallbacks.add(callback);
}

/// 注册网络断开回调
void onNetworkLost(Function(ConnectivityInfo) callback) {
  _onNetworkLostCallbacks.add(callback);
}

/// 移除网络恢复回调
void removeOnNetworkRestored(Function(ConnectivityInfo) callback) {
  _onNetworkRestoredCallbacks.remove(callback);
}

/// 移除网络断开回调
void removeOnNetworkLost(Function(ConnectivityInfo) callback) {
  _onNetworkLostCallbacks.remove(callback);
}

这种基于回调列表的设计允许应用同时注册多个网络恢复/断开处理逻辑,且各处理逻辑之间相互独立、不相互干扰。

4.5 历史记录管理

历史记录管理逻辑嵌入在 _updateConnectivity 方法中,当检测到连接状态发生实质性变化(而非仅仅类型切换)时,记录一条历史条目:

dart 复制代码
/// 更新当前网络状态
Future<void> _updateConnectivity({List<ConnectivityResult>? results}) async {
  try {
    List<ConnectivityResult> connectivityResults;
    if (results != null) {
      connectivityResults = results;
    } else {
      connectivityResults = await _connectivity.checkConnectivity();
    }

    _recordHistory(connectivityResults);

    _currentInfo = ConnectivityInfo.fromConnectivityResults(
      connectivityResults,
      lastChangedAt: DateTime.now(),
      history: List.unmodifiable(_history),
    );
  } catch (e) {
    debugPrint('[ConnectivityService] 获取网络状态失败: $e');
  }
}

历史记录数量限制为最近 50 条,防止内存占用无限增长。当超过此上限时,最早的记录会被移除。


五、状态管理集成

5.1 Riverpod Provider 体系

我们将网络状态封装为 Riverpod 的 Notifier,使其能够与 Riverpod 生态无缝集成。ConnectivityNotifier 在 provider 首次被监听时自动初始化网络监听,并在状态变化时自动更新:

dart 复制代码
/// 网络连接状态状态类
class ConnectivityState {
  final ConnectivityInfo connectivityInfo;
  final bool isListening;
  final List<ConnectivityHistoryEntry> history;
  final int changeCount;

  const ConnectivityState({
    required this.connectivityInfo,
    this.isListening = false,
    this.history = const [],
    this.changeCount = 0,
  });

  factory ConnectivityState.initial() => const ConnectivityState(
    connectivityInfo: ConnectivityInfo(
      connectionTypes: [],
      isConnected: false,
      isHighQuality: false,
    ),
  );

  bool get isConnected => connectivityInfo.isConnected;
  bool get isOffline => !connectivityInfo.isConnected;
  String get connectionTypeName => connectivityInfo.connectionTypeName;

  ConnectivityState copyWith({
    ConnectivityInfo? connectivityInfo,
    bool? isListening,
    List<ConnectivityHistoryEntry>? history,
    int? changeCount,
  }) {
    return ConnectivityState(
      connectivityInfo: connectivityInfo ?? this.connectivityInfo,
      isListening: isListening ?? this.isListening,
      history: history ?? this.history,
      changeCount: changeCount ?? this.changeCount,
    );
  }
}

/// 网络连接状态 Notifier
class ConnectivityNotifier extends Notifier<ConnectivityState> {
  StreamSubscription<ConnectivityInfo>? _subscription;

  @override
  ConnectivityState build() {
    _initConnectivity();
    return ConnectivityState.initial();
  }

  Future<void> _initConnectivity() async {
    await ConnectivityService.instance.init();

    final info = ConnectivityService.instance.currentInfo;
    state = state.copyWith(
      connectivityInfo: info,
      isListening: true,
    );

    _subscription?.cancel();
    _subscription = ConnectivityService.instance.connectivityStream.listen(
      _onConnectivityChanged,
      onError: (error) { /* 错误处理 */ },
    );
  }

  void _onConnectivityChanged(ConnectivityInfo info) {
    state = state.copyWith(
      connectivityInfo: info,
      history: ConnectivityService.instance.history,
      changeCount: state.changeCount + 1,
    );
  }

  Future<void> checkConnectivity() async {
    final info = await ConnectivityService.instance.checkConnectivity();
    state = state.copyWith(connectivityInfo: info);
  }

  void clearHistory() {
    ConnectivityService.instance.clearHistory();
    state = state.copyWith(history: []);
  }
}

/// 网络连接状态 Provider
final connectivityProvider =
    NotifierProvider<ConnectivityNotifier, ConnectivityState>(() {
  return ConnectivityNotifier();
});

5.2 便捷派生 Provider

为了简化页面层代码,我们基于 connectivityProvider 定义了多个派生 Provider,分别对应不同的判断维度:

dart 复制代码
/// 是否已连接
final isConnectedProvider = Provider<bool>((ref) {
  return ref.watch(connectivityProvider).isConnected;
});

/// 是否离线
final isOfflineProvider = Provider<bool>((ref) {
  return ref.watch(connectivityProvider).isOffline;
});

/// 连接类型名称(用于 UI 显示)
final connectionTypeNameProvider = Provider<String>((ref) {
  return ref.watch(connectivityProvider).connectionTypeName;
});

/// 是否为高质量网络(WiFi/以太网,适合大流量传输)
final isHighQualityNetworkProvider = Provider<bool>((ref) {
  return ref.watch(connectivityProvider).connectivityInfo.isHighQuality;
});

/// 是否为 WiFi
final isWifiProvider = Provider<bool>((ref) {
  return ref.watch(connectivityProvider).connectivityInfo
      .isType(ConnectionType.wifi);
});

/// 是否为移动网络
final isMobileNetworkProvider = Provider<bool>((ref) {
  return ref.watch(connectivityProvider).connectivityInfo
      .isType(ConnectionType.mobile);
});

页面层使用时,只需引入对应的 Provider 即可,无需关心底层的服务初始化和状态同步细节:

dart 复制代码
class _MyPageState extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    // 简单判断是否离线
    final isOffline = ref.watch(isOfflineProvider);

    // 获取连接类型文字
    final typeName = ref.watch(connectionTypeNameProvider);

    return isOffline
        ? _buildOfflineView(typeName)
        : _buildOnlineView();
  }
}

六、网络恢复自动刷新机制

6.1 问题背景与设计目标

当设备从离线状态恢复网络连接时,用户最直接的期待是应用能够自动重新加载最新数据。以新闻阅读应用为例:用户在地铁中打开应用,浏览到一半地铁到站恢复网络,此时应用应当自动刷新列表,而非让用户手动下拉刷新。同样的场景也适用于电商商品列表、社交信息流、实时报价等需要持续保持数据新鲜度的场景。

自动刷新机制的设计目标是:网络恢复后,无需用户操作,应用自动触发已注册的数据刷新任务,并给予用户适当的视觉反馈。

6.2 NetworkRefreshManager 的实现

NetworkRefreshManagerNotifier 实现了注册-监听-触发的完整流程:

dart 复制代码
/// 网络刷新管理器状态
class NetworkRefreshManagerState {
  final Map<String, NetworkRefreshCallback> callbacks;
  final int refreshCount;
  final DateTime? lastRefreshTime;

  const NetworkRefreshManagerState({
    this.callbacks = const {},
    this.refreshCount = 0,
    this.lastRefreshTime,
  });

  NetworkRefreshManagerState copyWith({
    Map<String, NetworkRefreshCallback>? callbacks,
    int? refreshCount,
    DateTime? lastRefreshTime,
  }) {
    return NetworkRefreshManagerState(
      callbacks: callbacks ?? this.callbacks,
      refreshCount: refreshCount ?? this.refreshCount,
      lastRefreshTime: lastRefreshTime ?? this.lastRefreshTime,
    );
  }
}

/// 网络刷新管理器 Notifier
class NetworkRefreshManagerNotifier extends Notifier<NetworkRefreshManagerState> {
  @override
  NetworkRefreshManagerState build() {
    // 监听网络恢复事件
    ref.listen<ConnectivityState>(connectivityProvider, (previous, next) {
      if (previous != null && !previous.isConnected && next.isConnected) {
        _triggerAutoRefresh();
      }
    });
    return const NetworkRefreshManagerState();
  }

  /// 注册自动刷新任务
  void register({
    required String key,
    required Future<void> Function() onRefresh,
  }) {
    final callback = NetworkRefreshCallback(
      key: key,
      onRefresh: onRefresh,
      lastRefreshTime: DateTime.now(),
    );
    final newCallbacks = Map<String, NetworkRefreshCallback>.from(state.callbacks);
    newCallbacks[key] = callback;
    state = state.copyWith(callbacks: newCallbacks);
  }

  /// 触发自动刷新
  Future<void> _triggerAutoRefresh() async {
    if (state.callbacks.isEmpty) return;

    for (final callback in state.callbacks.values) {
      try {
        await callback.onRefresh();
      } catch (e) {
        // 单个刷新失败不影响其他任务
      }
    }

    state = state.copyWith(
      refreshCount: state.refreshCount + 1,
      lastRefreshTime: DateTime.now(),
    );
  }
}

/// 网络刷新管理器 Provider
final networkRefreshManagerProvider =
    NotifierProvider<NetworkRefreshManagerNotifier,
        NetworkRefreshManagerState>(() {
  return NetworkRefreshManagerNotifier();
});

6.3 页面集成示例

在具体的页面中,开发者只需要在初始化时注册刷新任务,网络恢复后刷新会自动触发:

dart 复制代码
class PostListPage extends ConsumerStatefulWidget {
  @override
  ConsumerState<PostListPage> createState() => _PostListPageState();
}

class _PostListPageState extends ConsumerState<PostListPage> {
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      // 注册网络恢复时的自动刷新任务
      ref.read(networkRefreshManagerProvider.notifier).register(
        key: 'post_list',
        onRefresh: () async {
          await ref.read(postProvider.notifier).loadPosts();
        },
      );
    });
  }

  void _onNetworkRestored() {
    // 网络恢复时直接触发刷新,无需用户手动操作
    ref.read(networkRefreshManagerProvider.notifier).refreshAll();
    showSnackBar(context, '网络已恢复,正在刷新...');
  }
}

七、离线提示 UI 组件

7.1 网络状态横幅组件

ConnectivityBanner 是一个包装型组件,将任意子组件包裹其中,同时在顶部或底部渲染网络状态提示。它能自动检测网络状态变化,分别展示离线横幅和网络恢复通知:

dart 复制代码
/// 网络状态横幅组件
///
/// 使用方式:
/// ```dart
/// Scaffold(
///   body: ConnectivityBanner(
///     showOfflineBanner: true,
///     showRestoredNotification: true,
///     restoredNotificationDuration: 3,
///     child: MyPageContent(),
///   ),
/// )
/// ```
class ConnectivityBanner extends ConsumerStatefulWidget {
  final Widget child;
  final bool showOfflineBanner;
  final bool showRestoredNotification;
  final int restoredNotificationDuration;

  const ConnectivityBanner({
    super.key,
    required this.child,
    this.showOfflineBanner = true,
    this.showRestoredNotification = true,
    this.restoredNotificationDuration = 3,
  });
}

横幅的视觉设计采用梯度配色:离线状态使用红橙渐变色传达警示感,网络恢复使用绿色渐变传达积极信号。恢复通知在展示指定时长后自动消失,避免干扰用户操作。

7.2 网络状态指示器

ConnectivityIndicator 是一个紧凑的状态指示组件,适合嵌入 AppBar 或抽屉菜单中:

dart 复制代码
/// 简洁的网络状态指示器
class ConnectivityIndicator extends ConsumerWidget {
  final double size;
  final bool showLabel;
  final VoidCallback? onTap;

  const ConnectivityIndicator({
    super.key,
    this.size = 20,
    this.showLabel = false,
    this.onTap,
  });

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final state = ref.watch(connectivityProvider);
    final isConnected = state.isConnected;
    final info = state.connectivityInfo;

    IconData icon;
    Color color;

    if (isConnected) {
      switch (info.primaryType) {
        case ConnectionType.wifi:
          icon = Icons.wifi;
          color = Colors.green;
          break;
        case ConnectionType.mobile:
          icon = Icons.signal_cellular_alt;
          color = Colors.blue;
          break;
        case ConnectionType.ethernet:
          icon = Icons.settings_ethernet;
          color = Colors.teal;
          break;
        default:
          icon = Icons.signal_wifi_4_bar;
          color = Colors.grey;
      }
    } else {
      icon = Icons.wifi_off;
      color = Colors.red;
    }

    return GestureDetector(
      onTap: onTap,
      child: Row(
        mainAxisSize: MainAxisSize.min,
        children: [
          Icon(icon, size: size, color: color),
          if (showLabel) ...[
            const SizedBox(width: 4),
            Text(
              info.connectionTypeName,
              style: TextStyle(fontSize: size * 0.7, color: color),
            ),
          ],
        ],
      ),
    );
  }
}

在 AppBar 中的使用效果:在工作台页面右上角显示网络状态图标,点击后弹出详情底部菜单,展示当前连接类型、离线时长、状态变化次数等信息。

7.3 网络感知型 FutureBuilder

NetworkAwareFutureBuilder 是面向网络请求场景的便捷封装。当网络不可用时,它显示友好的离线提示而非原始错误;网络恢复时,自动重新发起请求:

dart 复制代码
/// 网络状态友好的 FutureBuilder 包装器
class NetworkAwareFutureBuilder<T> extends ConsumerWidget {
  final Future<T> Function() future;
  final Widget Function(BuildContext context, T data) onSuccess;
  final Widget Function(BuildContext context)? onLoading;
  final Widget Function(BuildContext context, ConnectivityInfo? info)?
      onNetworkError;
  final Widget Function(BuildContext context, Object error, StackTrace?)?
      onError;

  const NetworkAwareFutureBuilder({
    super.key,
    required this.future,
    required this.onSuccess,
    this.onLoading,
    this.onNetworkError,
    this.onError,
  });

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final state = ref.watch(connectivityProvider);

    if (!state.isConnected) {
      return onNetworkError?.call(context, state.connectivityInfo) ??
          _DefaultNetworkErrorWidget(info: state.connectivityInfo);
    }

    return FutureBuilder<T>(
      future: future(),
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.waiting ||
            snapshot.connectionState == ConnectionState.active) {
          return onLoading?.call(context) ??
              const Center(child: CircularProgressIndicator());
        }
        if (snapshot.hasError) {
          return onError?.call(context, snapshot.error!, snapshot.stackTrace) ??
              _DefaultErrorWidget(error: snapshot.error!);
        }
        return onSuccess(context, snapshot.data as T);
      },
    );
  }
}

使用示例:

dart 复制代码
NetworkAwareFutureBuilder<List<Post>>(
  future: () => postService.fetchPosts(),
  onSuccess: (context, posts) => PostListView(posts: posts),
  onLoading: () => const Center(child: CircularProgressIndicator()),
  onNetworkError: (context, info) => OfflineView(
    message: '网络不可用,无法加载内容',
    onRetry: () => ref.read(postProvider.notifier).loadPosts(),
  ),
)

八、网络状态管理页面

独立的网络状态管理页面 (/connectivity) 提供了网络状态的全景视图,包括四个核心区域:

当前状态卡片:以全宽渐变卡片展示连接状态、连接类型和所有活跃的网络类型标签。

网络信息卡片:展示监听状态、变化次数、OH 平台适配状态等元信息。

自动刷新设置卡片:提供注册示例刷新任务和手动触发刷新的操作入口。

状态变化历史列表:逆序展示最近的网络状态切换记录,包括切换时间戳、变化方向和描述文字。

页面入口定义在 workspace_page.dart 的工作台网格中,通过 WorkspaceItem 配置路由跳转:

dart 复制代码
WorkspaceItem(
  id: 'connectivity',
  title: '网络状态',
  description: '网络连接监听与离线提示',
  icon: Icons.wifi,
  color: Colors.cyan,
  route: '/connectivity',
)

路由在 router.dart 中配置:

dart 复制代码
GoRoute(
  path: '/connectivity',
  name: 'connectivity',
  pageBuilder: (context, state) => OHRouteTransitionPage(
    key: state.pageKey,
    child: const ConnectivityPage(),
  ),
)

九、OH 平台权限体系说明

9.1 Flutter 应用层权限模型

connectivity_plus 在 OpenHarmony 平台的网络功能实现依赖于 Flutter 框架层与 OH 原生系统之间的平台通道。平台通道的通信在 OH 系统层面由系统服务管理,应用无需在 module.json5 中额外声明互联网权限------这一点与 Android 平台需要在 AndroidManifest.xml 中声明 ACCESS_NETWORK_STATE 权限形成对比。

Flutter for OpenHarmony 的架构中,网络能力作为基础平台能力默认对应用开放。connectivity_plus 通过 MethodChannel 向原生侧查询网络状态,返回值由 OH 的网络管理子系统提供,该子系统本身是系统级服务,无需应用授权。

9.2 OH 平台适配要点

connectivity_plus 在 OH 平台的表现与 Android 平台高度一致,但在实现机制上存在差异:

监听机制 。Android 平台通过 ConnectivityManagerregisterNetworkCallback 实现实时监听;iOS 平台通过 Reachability 机制;OpenHarmony 平台则通过 wifinet connection 等系统 API 实现。connectivity_plus 在各平台的适配层屏蔽了这些差异,Flutter 层使用统一的 onConnectivityChanged 流接口。

类型映射 。OH 平台的网络类型返回值定义在 @ohos.net.connection 模块中,其枚举值与 connectivity_plus 的 ConnectivityResult 枚举存在一一对应关系,这一映射在 Dart 层完成,对业务代码透明。

平台检测 。部分场景需要判断当前是否运行在 OH 平台,我们通过 Platform.operatingSystem 属性检测:

dart 复制代码
bool _isOHEnvironment() {
  try {
    final os = Platform.operatingSystem.toLowerCase();
    return os.contains('harmony') ||
        os.contains('openharmony') ||
        os.contains('ohos');
  } catch (e) {
    return false;
  }
}

十、工作台集成示例

workspace_page.dart 中,网络状态管理已深度集成至工作台页面。主要体现在三个层面:

AppBar 指示器 。工作台页面右上角的 AppBar 区域包含一个 ConnectivityIndicator,实时反映当前网络状态。用户点击指示器后,弹出底部菜单展示详细信息。

横幅提示。当设备处于离线状态时,工作台顶部显示红色横幅,告知用户当前处于离线模式。横幅底部提供"重试"按钮,用户点击后重新检查网络状态。

自动刷新联动 。通过 ref.listen 监听 connectivityProvider 的状态变化,当检测到从离线到联网的切换时,自动调用 _onNetworkRestored 方法,显示恢复通知并触发数据刷新。

dart 复制代码
void _initConnectivityListener() {
  ref.listen<ConnectivityState>(connectivityProvider, (previous, next) {
    if (previous != null && !previous.isConnected && next.isConnected) {
      _onNetworkRestored();
    }
    if (previous != null && previous.isConnected && !next.isConnected) {
      _onNetworkLost();
    }
  });
}

十一、验证与测试

11.1 静态分析

在编写过程中,所有代码文件均通过 Dart 静态分析工具检查,无编译错误或严重警告:

bash 复制代码
flutter analyze lib/models/connectivity_info.dart \
             lib/services/connectivity_service.dart \
             lib/providers/connectivity_provider.dart \
             lib/widgets/connectivity_banner.dart \
             lib/pages/connectivity_page.dart

11.2 测试场景矩阵

测试场景 操作步骤 预期结果
WiFi 连接检测 将设备连接至 WiFi 网络 AppBar 显示绿色 WiFi 图标,详情显示"已连接 - WiFi"
WiFi 断开检测 关闭设备的 WiFi 开关 顶部出现红色离线横幅,AppBar 图标变为灰色
移动网络检测 关闭 WiFi,仅启用移动数据 AppBar 显示蓝色移动网络图标
网络恢复刷新 离线状态下重新连接网络 出现绿色恢复通知,数据自动刷新
状态历史记录 多次切换网络状态 历史列表正确记录每次切换的 timestamp 和类型

11.3 鸿蒙设备运行验证

在鸿蒙设备上运行应用后,网络状态监听功能正常触发。AppBar 中的 ConnectivityIndicator 能够正确反映设备的实际网络状态,WiFi 连接时显示绿色图标,离线时显示红色图标并附有文字标签。点击指示器弹出的底部菜单完整展示当前连接类型、历史变化记录和手动刷新操作入口。

这是我的运行截图:


十二、项目文件结构

复制代码
lib/
├── models/
│   └── connectivity_info.dart         # 连接类型枚举、网络状态数据模型、
│                                      # 变化历史记录模型
├── services/
│   └── connectivity_service.dart     # 网络状态服务:监听、查询、回调管理
├── providers/
│   └── connectivity_provider.dart    # Riverpod Notifier、便捷 Provider、
│                                      # 自动刷新管理器
├── widgets/
│   └── connectivity_banner.dart      # 网络状态横幅、指示器、对话框、
│                                      # NetworkAwareFutureBuilder
├── pages/
│   ├── connectivity_page.dart        # 独立网络状态管理页面
│   └── workspace_page.dart           # 工作台页面(已集成网络状态监听)
└── routing/
    └── router.dart                   # go_router 路由配置(/connectivity 路由)

pubspec.yaml                         # 依赖声明:connectivity_plus ^6.0.3

十三、总结

本文围绕 connectivity_plus 在 Flutter for OpenHarmony 项目中的集成与适配,构建了一套从底层服务到上层 UI 的完整网络状态管理体系。核心技术要点可归纳为以下五个方面:

connectivity_plus 的 OH 平台适配:通过统一的 Dart API 接口屏蔽底层平台差异,实现 WiFi、移动网络、以太网、蓝牙等多种网络类型的监听能力,OH 平台无需额外权限配置。

数据模型与类型映射 :设计了 ConnectionType 枚举、ConnectivityInfo 数据模型和 ConnectivityHistoryEntry 历史记录模型,将原始的 connectivity_plus 结果转换为面向业务的语义化数据结构。

Riverpod 状态管理整合 :通过 ConnectivityNotifier 将网络状态接入 Riverpod 体系,并衍生出 isConnectedProviderisWifiProvider 等多个便捷 Provider,简化页面层代码。

网络恢复自动刷新 :通过 NetworkRefreshManagerNotifier 实现注册式自动刷新机制,网络恢复时无需用户手动操作,应用自动触发已注册的数据刷新任务。

完整的离线提示 UI :提供了 ConnectivityBanner 横幅组件、ConnectivityIndicator 状态指示器、OfflinePromptDialog 对话框和 NetworkAwareFutureBuilder 异步封装,覆盖了从即时提示到页面级状态展示的完整 UI 需求。

本方案已在实际 OpenHarmony 设备上验证通过,网络状态监听、网络恢复自动刷新、离线提示等核心功能运行稳定。

相关推荐
不会写DN2 小时前
通过eino-ext如何正常indexer RAG?
网络·面试·go
思麟呀2 小时前
网络层IP协议
linux·服务器·网络·网络协议·tcp/ip·计算机网络
MonkeyKing3 小时前
InheritedWidget 原理与性能
flutter
SilentSamsara3 小时前
TLS/HTTPS 实战:证书链、握手与生产配置
网络·数据库·网络协议·http·https
U盘失踪了3 小时前
URL 统一资源定位符详解
网络
爱学习的小囧3 小时前
ESXi/vCenter 批量开关虚拟机完整教程 | PowerCLI 一键 + 原生脚本循环,新手也能落地
运维·网络·数据库·esxi
bbq粉刷匠3 小时前
网络基础概念
网络·tcp/ip·计算机网络
liulian09163 小时前
【Flutter For OpenHarmony】Flutter 三方库 flutter_secure_storage 的鸿蒙化适配指南
flutter·华为·学习方法·harmonyos
路溪非溪4 小时前
Wireshark抓取以太网MAC帧并进行分析
linux·网络·驱动开发·wireshark