Flutter 三方库 connectivity_plus 的鸿蒙化适配指南:网络状态实时监听,离线提示贴心守护,让你的应用更懂网络
嗨,亲爱的小伙伴们!今天要和大家分享一个超超超实用的话题——如何在 Flutter for OpenHarmony 应用中实现网络状态的实时监听、友好的离线提示,以及酷炫的网络恢复自动刷新功能!✨你有没有遇到过这样的场景呀?用户在地铁上刷着你的应用,突然进入隧道没网了,结果应用还在那傻傻地转圈圈,半天之后才弹出一个冷冰冰的"加载失败";或者用户从无网环境回到有网环境,应用却不知道自动刷新数据,还要
Flutter 三方库 connectivity_plus 的鸿蒙化适配指南:网络状态实时监听,离线提示贴心守护,让你的应用更懂网络
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
前言:网络是个"小妖精",需要好好"伺候"
嗨,亲爱的小伙伴们!今天要和大家分享一个超超超实用的话题——如何在 Flutter for OpenHarmony 应用中实现网络状态的实时监听、友好的离线提示,以及酷炫的网络恢复自动刷新功能!✨
你有没有遇到过这样的场景呀?用户在地铁上刷着你的应用,突然进入隧道没网了,结果应用还在那傻傻地转圈圈,半天之后才弹出一个冷冰冰的"加载失败";或者用户从无网环境回到有网环境,应用却不知道自动刷新数据,还要用户手动去点刷新按钮——这种体验,真的会让用户好感度直接归零呢!😱
别担心!connectivity_plus 库来啦!它就像一位贴心的"网络管家",时刻关注着设备的网络状态变化,一旦发现网络"离家出走",立刻温柔地提醒用户;等网络"回家"了,又会自动帮你把数据刷新好——简直是完美的小助手!💕
特别是在鸿蒙生态蓬勃发展的今天,掌握网络状态监听能力,已经成为 Flutter 开发者必备的技能之一。接下来,就让博主手把手教你们玩转这个强大的库吧!准备好了吗?Let’s go!🚀
一、为什么 connectivity_plus 是鸿蒙开发者的"网络守护神"?
1.1 认识这位"网络小天使"
connectivity_plus 是 Flutter 生态中最权威、最流行的网络状态监听库,由 fluttercommunity.dev 官方维护。它就像一位长着翅膀的"网络小天使",时刻守护着你的应用与网络世界的连接。无论是 Android 的 WiFi、iOS 的移动网络,还是现在鸿蒙的各种网络形态,它都能轻松驾驭!🎯
在鸿蒙开发中,这位"小天使"的价值尤为突出。想象一下,你的应用需要在 WiFi 环境下下载大文件、在移动网络下提醒用户注意流量消耗、在离线时展示缓存数据——这些贴心的功能,都离不开网络状态监听的支撑!
1.2 鸿蒙网络监听的"特殊挑战"
不过,话说回来,鸿蒙系统的网络体系可不是一般的"对手"。它和 Android、iOS 虽然有些相似之处,但在权限模型、网络状态映射、事件响应机制等方面都有着独特的个性。就像三个性格不同的朋友,虽然都很友好,但相处方式可不一样呢!🌸
举个例子,同样是监听网络状态变化,Android 用的是 ConnectivityManager,iOS 用的是 SCNetworkReachability,而鸿蒙呢?人家有自己的 @ohos.net.connection 模块!所以,适配工作绝对不是简单的"拿来主义",需要我们做一些"本土化"的工作~
二、技术方案:三重奏——Service + Provider + Widgets
2.1 优雅架构,层层递进
在鸿蒙平台上使用 connectivity_plus,我们不只是简单地调用 API 就完事了。为了让代码更优雅、更易维护、更易扩展,博主采用了"三重奏"架构:
- ConnectivityService:负责底层网络状态监听,是整个架构的"基石"
- ConnectivityProvider:用 Riverpod 进行状态管理,是连接 Service 和 UI 的"桥梁"
- ConnectivityWidgets:提供可复用的 UI 组件,是展示给用户看的"门面"
这种架构就像一个井然有序的小团队,每个人各司其职,配合得天衣无缝!🎭
2.2 核心功能一览表
让我们来看看这套方案能为你带来哪些强大功能吧:
| 功能模块 | 描述 | 鸿蒙支持度 |
|---|---|---|
| 网络状态监听 | WiFi/移动网络/以太网/无连接 | ✅ 完美支持 |
| 状态变化回调 | 离线→联网、联网→离线、网络类型切换 | ✅ 实时响应 |
| 历史记录 | 记录网络状态变化历史 | ✅ 最多保存 50 条 |
| 自动刷新 | 网络恢复后自动触发数据刷新 | ✅ 可自定义任务 |
| 离线提示 | 红色横幅提醒用户 | ✅ 支持自动消失 |
| 恢复通知 | 绿色横幅告知用户网络已恢复 | ✅ 带动画效果 |
2.3 关键数据模型
在开始写代码之前,先让我们认识一下几个核心的数据模型,它们就像是整个系统的"骨架",支撑着所有功能的运行!
/// 网络连接类型枚举
enum ConnectionType {
wifi, /// WiFi 无线网络
mobile, /// 移动网络(4G/5G)
ethernet, /// 以太网有线网络
bluetooth, /// 蓝牙共享网络
vpn, /// VPN 虚拟专用网络
other, /// 其他网络类型
none, /// 无网络连接
}
/// 网络连接状态数据模型
class ConnectivityInfo {
final List<ConnectionType> connectionTypes; /// 当前网络连接类型列表
final bool isConnected; /// 是否已连接(任意网络)
final bool isHighQuality; /// 是否为高质量网络(WiFi/以太网)
final DateTime? lastChangedAt; /// 上次状态变化时间戳
final List<ConnectivityHistoryEntry> history; /// 网络变化历史记录
/// 判断是否为特定连接类型
bool isType(ConnectionType type) => connectionTypes.contains(type);
/// 获取主要的连接类型(用于显示)
ConnectionType? get primaryType { ... }
}
/// 网络状态变化历史记录条目
class ConnectivityHistoryEntry {
final DateTime timestamp; /// 变化时间戳
final bool fromConnected; /// 变化前的连接状态
final bool toConnected; /// 变化后的连接状态
final ConnectivityChangeType changeType; /// 变化类型
/// 是否从离线变为联网
bool get wasReconnected => !fromConnected && toConnected;
/// 是否从联网变为离线
bool get wasDisconnected => fromConnected && !toConnected;
}
这些模型设计得是不是很贴心呀?它们不仅封装了原始的网络状态,还提供了很多便捷的派生属性,让我们在业务代码中使用起来超级方便!🎀
三、手把手实战:打造你的网络状态监控系统
3.1 项目配置第一步
首先,我们需要在项目的 pubspec.yaml 中添加依赖。鸿蒙环境下,标准版的 connectivity_plus 已经支持得很好啦:
dependencies:
flutter:
sdk: flutter
# 网络状态监听库 - OpenHarmony 兼容
connectivity_plus: ^6.0.3
# 状态管理 - 用 Riverpod 让状态管理更优雅
flutter_riverpod: ^2.5.1
添加完依赖后,执行激动人心的命令:
flutter pub get
如果一切顺利,你就会看到控制台输出一堆绿色的成功信息,那种感觉,就像收到了心仪已久的礼物一样开心!🎁
3.2 基石层:ConnectivityService 实现
现在,让我们来构建整个系统的"基石"——ConnectivityService。这个服务负责所有底层的网络状态监听工作,是整个架构中最核心的部分!
import 'dart:async';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
import '../models/connectivity_info.dart';
/// 网络连接状态服务
class ConnectivityService {
static ConnectivityService? _instance;
static ConnectivityService get instance => _instance ??= ConnectivityService._();
ConnectivityService._();
final Connectivity _connectivity = Connectivity();
ConnectivityInfo _currentInfo = ConnectivityInfo.empty();
final StreamController<ConnectivityInfo> _connectivityController =
StreamController<ConnectivityInfo>.broadcast();
final List<ConnectivityHistoryEntry> _history = [];
bool _isListening = false;
bool? _lastConnected;
Stream<ConnectivityInfo> get connectivityStream => _connectivityController.stream;
ConnectivityInfo get currentInfo => _currentInfo;
List<ConnectivityHistoryEntry> get history => List.unmodifiable(_history);
bool get isConnected => _currentInfo.isConnected;
bool get isListening => _isListening;
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] 网络状态监听已启动');
}
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;
}
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),
);
debugPrint('[ConnectivityService] 网络状态已更新: $_currentInfo');
} catch (e) {
debugPrint('[ConnectivityService] 获取网络状态失败: $e');
}
}
void _recordHistory(List<ConnectivityResult> results) {
final wasConnected = _lastConnected ?? _currentInfo.isConnected;
final nowConnected = results.isNotEmpty && !results.contains(ConnectivityResult.none);
if (wasConnected == nowConnected && _history.isNotEmpty) {
return;
}
final fromTypes = _currentInfo.connectionTypes;
final toTypes = results.map(_mapResultToType).toList();
ConnectivityChangeType changeType;
if (!wasConnected && nowConnected) {
changeType = ConnectivityChangeType.offlineToOnline;
} else if (wasConnected && !nowConnected) {
changeType = ConnectivityChangeType.onlineToOffline;
} else {
if (fromTypes.contains(ConnectionType.wifi) && toTypes.contains(ConnectionType.mobile)) {
changeType = ConnectivityChangeType.wifiToMobile;
} else if (fromTypes.contains(ConnectionType.mobile) && toTypes.contains(ConnectionType.wifi)) {
changeType = ConnectivityChangeType.mobileToWifi;
} else {
changeType = ConnectivityChangeType.typeChanged;
}
}
final entry = ConnectivityHistoryEntry(
timestamp: DateTime.now(),
fromConnected: wasConnected,
toConnected: nowConnected,
fromTypes: fromTypes,
toTypes: toTypes,
changeType: changeType,
);
_history.add(entry);
if (_history.length > 50) {
_history.removeAt(0);
}
debugPrint('[ConnectivityService] 记录历史: ${entry.description}');
}
ConnectionType _mapResultToType(ConnectivityResult result) {
switch (result) {
case ConnectivityResult.wifi:
return ConnectionType.wifi;
case ConnectivityResult.bluetooth:
return ConnectionType.bluetooth;
case ConnectivityResult.ethernet:
return ConnectionType.ethernet;
case ConnectivityResult.mobile:
return ConnectionType.mobile;
case ConnectivityResult.vpn:
return ConnectionType.vpn;
case ConnectivityResult.other:
return ConnectionType.other;
case ConnectivityResult.none:
return ConnectionType.none;
case ConnectivityResult.satellite:
return ConnectionType.other;
}
}
final List<Function(ConnectivityInfo)> _onNetworkRestoredCallbacks = [];
final List<Function(ConnectivityInfo)> _onNetworkLostCallbacks = [];
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);
}
Future<ConnectivityInfo> checkConnectivity() async {
await _updateConnectivity();
return _currentInfo;
}
void clearHistory() {
_history.clear();
}
void dispose() {
stopListening();
_connectivityController.close();
_onNetworkRestoredCallbacks.clear();
_onNetworkLostCallbacks.clear();
debugPrint('[ConnectivityService] 资源已释放');
}
}
mixin ConnectivityListenerMixin {
void onNetworkRestored(ConnectivityInfo info) {}
void onNetworkLost(ConnectivityInfo info) {}
ConnectivityService get _connectivityService => ConnectivityService.instance;
void initConnectivityListener() {
_connectivityService.onNetworkRestored(onNetworkRestored);
_connectivityService.onNetworkLost(onNetworkLost);
}
void disposeConnectivityListener() {
_connectivityService.removeOnNetworkRestored(onNetworkRestored);
_connectivityService.removeOnNetworkLost(onNetworkLost);
}
}
哇,这个 Service 是不是超级强大?它不仅监听网络状态变化,还记录历史、提供回调机制,甚至还有一个贴心的 Mixin 让页面可以快速集成!💕
3.3 桥梁层:ConnectivityProvider 实现
有了 Service 这个"基石",接下来我们需要一座"桥梁"来连接 Service 和 UI 层——那就是 ConnectivityProvider!用 Riverpod 来做状态管理,真的是再合适不过了~
import 'dart:async';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../models/connectivity_info.dart';
import '../services/connectivity_service.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,
);
}
}
class ConnectivityNotifier extends Notifier<ConnectivityState> {
StreamSubscription<ConnectivityInfo>? _subscription;
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,
);
ConnectivityService.instance.onNetworkRestored(_onNetworkRestored);
ConnectivityService.instance.onNetworkLost(_onNetworkLost);
}
void _onConnectivityChanged(ConnectivityInfo info) {
final history = ConnectivityService.instance.history;
state = state.copyWith(
connectivityInfo: info,
history: history,
changeCount: state.changeCount + 1,
);
}
void _onNetworkRestored(ConnectivityInfo info) {
state = state.copyWith(
connectivityInfo: info,
history: ConnectivityService.instance.history,
changeCount: state.changeCount + 1,
);
}
void _onNetworkLost(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: []);
}
}
final connectivityProvider = NotifierProvider<ConnectivityNotifier, ConnectivityState>(() {
return ConnectivityNotifier();
});
final isConnectedProvider = Provider<bool>((ref) {
return ref.watch(connectivityProvider).isConnected;
});
final isOfflineProvider = Provider<bool>((ref) {
return ref.watch(connectivityProvider).isOffline;
});
final connectionTypeNameProvider = Provider<String>((ref) {
return ref.watch(connectivityProvider).connectionTypeName;
});
class NetworkRefreshCallback {
final String key;
final Future<void> Function() onRefresh;
final DateTime? lastRefreshTime;
const NetworkRefreshCallback({
required this.key,
required this.onRefresh,
this.lastRefreshTime,
});
}
class NetworkRefreshManagerState {
final Map<String, NetworkRefreshCallback> callbacks;
final int refreshCount;
final DateTime? lastRefreshTime;
const NetworkRefreshManagerState({
this.callbacks = const {},
this.refreshCount = 0,
this.lastRefreshTime,
});
}
class NetworkRefreshManagerNotifier extends Notifier<NetworkRefreshManagerState> {
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);
}
void unregister(String key) {
final newCallbacks = Map<String, NetworkRefreshCallback>.from(state.callbacks);
newCallbacks.remove(key);
state = state.copyWith(callbacks: newCallbacks);
}
Future<void> refreshAll() async {
await _triggerAutoRefresh();
}
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(),
);
}
}
final networkRefreshManagerProvider =
NotifierProvider<NetworkRefreshManagerNotifier, NetworkRefreshManagerState>(() {
return NetworkRefreshManagerNotifier();
});
太棒了!这个 Provider 不仅管理着网络状态,还提供了一个 networkRefreshManagerProvider 来管理网络恢复后的自动刷新任务——是不是超级贴心?🎈
3.4 门面层:精美 UI 组件
现在,让我们来打造精美的 UI 组件,让用户能够直观地看到网络状态变化!这些组件就像一个个精美的"小礼物",让用户感受到你的用心~
3.4.1 ConnectivityBanner:离线提示与恢复通知
这是一个超级实用的横幅组件,在离线时显示红色提醒,网络恢复时显示绿色通知,还带动画效果呢!
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../models/connectivity_info.dart';
import '../providers/connectivity_provider.dart';
class ConnectivityBanner extends ConsumerStatefulWidget {
final Widget child;
final bool showOfflineBanner;
final bool showRestoredNotification;
final int? offlineBannerDuration;
final int restoredNotificationDuration;
const ConnectivityBanner({
super.key,
required this.child,
this.showOfflineBanner = true,
this.showRestoredNotification = true,
this.offlineBannerDuration,
this.restoredNotificationDuration = 3,
});
ConsumerState<ConnectivityBanner> createState() => _ConnectivityBannerState();
}
class _ConnectivityBannerState extends ConsumerState<ConnectivityBanner> {
bool _showRestored = false;
bool _wasOffline = false;
Widget build(BuildContext context) {
final connectivityState = ref.watch(connectivityProvider);
final isConnected = connectivityState.isConnected;
if (!_wasOffline && !isConnected) {
_wasOffline = true;
} else if (_wasOffline && isConnected && widget.showRestoredNotification) {
_showRestored = true;
_wasOffline = false;
Future.delayed(Duration(seconds: widget.restoredNotificationDuration), () {
if (mounted) {
setState(() {
_showRestored = false;
});
}
});
}
return Column(
children: [
if (_showRestored && widget.showRestoredNotification)
_buildRestoredBanner(context),
if (!isConnected && widget.showOfflineBanner)
_buildOfflineBanner(context, connectivityState),
Expanded(child: widget.child),
],
);
}
Widget _buildOfflineBanner(BuildContext context, ConnectivityState state) {
return AnimatedContainer(
duration: const Duration(milliseconds: 300),
width: double.infinity,
padding: EdgeInsets.only(
top: MediaQuery.of(context).padding.top + 8,
left: 16,
right: 16,
bottom: 12,
),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Colors.red[400]!,
Colors.red[600]!,
],
),
boxShadow: [
BoxShadow(
color: Colors.red.withValues(alpha: 0.3),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
child: SafeArea(
bottom: false,
child: Row(
children: [
const Icon(
Icons.signal_wifi_off,
color: Colors.white,
size: 24,
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
const Text(
'网络连接已断开',
style: TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 2),
Text(
_getOfflineMessage(state),
style: TextStyle(
color: Colors.white.withValues(alpha: 0.9),
fontSize: 13,
),
),
],
),
),
],
),
),
);
}
Widget _buildRestoredBanner(BuildContext context) {
return AnimatedContainer(
duration: const Duration(milliseconds: 300),
width: double.infinity,
padding: EdgeInsets.only(
top: MediaQuery.of(context).padding.top + 8,
left: 16,
right: 16,
bottom: 12,
),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Colors.green[400]!,
Colors.green[600]!,
],
),
boxShadow: [
BoxShadow(
color: Colors.green.withValues(alpha: 0.3),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
child: SafeArea(
bottom: false,
child: Row(
children: [
const Icon(
Icons.signal_wifi_4_bar,
color: Colors.white,
size: 24,
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
const Text(
'网络已恢复',
style: TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 2),
Text(
'正在刷新数据...',
style: TextStyle(
color: Colors.white.withValues(alpha: 0.9),
fontSize: 13,
),
),
],
),
),
const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
),
],
),
),
);
}
String _getOfflineMessage(ConnectivityState state) {
if (state.connectivityInfo.offlineDuration != null) {
final duration = state.connectivityInfo.offlineDuration!;
if (duration.inMinutes > 0) {
return '已离线 ${duration.inMinutes} 分钟';
} else {
return '已离线 ${duration.inSeconds} 秒';
}
}
return '请检查您的网络连接';
}
}
这个横幅组件是不是超级精美?红色的离线提醒、绿色的恢复通知,还有渐变背景和阴影效果,简直是"颜值与实力并存"!🌟
3.4.2 ConnectivityIndicator:小巧的网络状态指示器
如果你只需要一个小巧的指示器放在 AppBar 里,那 ConnectivityIndicator 就再合适不过了~
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,
});
Widget build(BuildContext context, WidgetRef ref) {
final connectivityState = ref.watch(connectivityProvider);
final isConnected = connectivityState.isConnected;
final info = connectivityState.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.signal_wifi_off;
color = Colors.red;
}
final indicator = 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,
),
),
],
],
);
if (onTap != null) {
return GestureDetector(
onTap: onTap,
child: indicator,
);
}
return indicator;
}
}
小巧精致,放在 AppBar 里刚刚好,用户一眼就能看到当前的网络状态!🌸
3.5 集成到应用中
现在,让我们把这些组件集成到应用中,看看效果如何!
首先,在你的 main.dart 中用 ProviderScope 包裹整个应用:
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'widgets/connectivity_banner.dart';
void main() {
runApp(
ProviderScope(
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter OH Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: ConnectivityBanner(
child: HomePage(),
),
);
}
}
然后,在需要网络恢复自动刷新的页面中,注册刷新任务:
class MyDataPage extends ConsumerStatefulWidget {
const MyDataPage({super.key});
ConsumerState<MyDataPage> createState() => _MyDataPageState();
}
class _MyDataPageState extends ConsumerState<MyDataPage> {
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
ref.read(networkRefreshManagerProvider.notifier).register(
key: 'myData',
onRefresh: () async {
await ref.read(myDataProvider.notifier).loadData();
},
);
});
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('数据页面'),
actions: [
ConnectivityIndicator(showLabel: true),
const SizedBox(width: 16),
],
),
body: _buildBody(),
);
}
Widget _buildBody() {
final isOffline = ref.watch(isOfflineProvider);
if (isOffline) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.wifi_off, size: 80, color: Colors.grey[400]),
const SizedBox(height: 24),
Text(
'网络连接不可用',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.grey[700],
),
),
],
),
);
}
return _buildDataList();
}
}
哇,这样就完成了!整个应用现在拥有了完整的网络状态监听、离线提示和自动刷新功能!🎉
四、避坑指南:那些年我们踩过的网络监听"地雷"
4.1 权限配置的"小秘密"
在鸿蒙平台上使用网络功能,权限配置可是非常重要的哦!虽然 connectivity_plus 本身不需要特别的权限,但如果你的应用需要访问网络,记得在 module.json5 中声明:
"requestPermissions": [
{
"name": "ohos.permission.INTERNET"
},
{
"name": "ohos.permission.GET_NETWORK_INFO"
}
]
这两个权限是网络相关功能的基础,可别忘了加上哦~
4.2 模拟器的"虚假网络"
如果你在鸿蒙模拟器上测试,可能会发现网络状态永远显示"已连接"——这不是你的代码有问题,而是模拟器本身就一直模拟网络连接状态!
建议:最终的功能验证一定要在真实设备上进行!毕竟,真实的网络环境才能考验出真正的实力!💪
4.3 状态监听的"生命周期"
在使用 ConnectivityService 的回调时,一定要注意生命周期管理!如果在页面销毁时没有移除回调,可能会导致内存泄漏哦!
别怕,我们已经贴心地提供了 ConnectivityListenerMixin,只要在页面中这样使用就好:
class _MyPageState extends State<MyPage> with ConnectivityListenerMixin {
void initState() {
super.initState();
initConnectivityListener();
}
void dispose() {
disposeConnectivityListener();
super.dispose();
}
void onNetworkRestored(ConnectivityInfo info) {
debugPrint('网络恢复啦!');
}
}
这样就安全啦!🔒
五、完整示例:网络状态监控中心
最后,博主给大家准备了一个"大礼包"——一个完整的网络状态监控中心界面。这个界面可以展示网络状态详情、历史记录、自动刷新设置等,功能超级丰富!
这是我的运行截图:
六、总结与展望
好啦,今天的分享就到这里!通过本文,我们学会了:
- 如何配置 connectivity_plus 依赖,让它在鸿蒙平台上顺利运行
- 如何设计优雅的三层架构(Service + Provider + Widgets),让代码更易维护
- 如何实现网络状态实时监听,包括 WiFi/移动网络/无连接的状态变化
- 如何打造精美的离线提示和恢复通知,提升用户体验
- 如何实现网络恢复后的自动数据刷新,让用户感受到你的贴心
- 如何规避权限配置、模拟器测试、生命周期管理等常见的"坑"
网络状态监听是提升应用用户体验的重要一环,但绝不是最后一环。希望大家能够举一反三,将这些知识应用到实际项目中,打造出更加懂用户、懂网络的优质应用!
最后,如果你在实践过程中遇到任何问题,欢迎在评论区留言交流。也欢迎大家关注博主的其他文章,我们会持续带来更多 Flutter 鸿蒙化的实战技术分享!
我们下期再见!拜拜~ 👋
本文同步发布于开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐

所有评论(0)