网络状态监听 - 鸿蒙Flutter离线感知能力应用



概述
网络状态监听是指实时监测设备的网络连接状态。在移动应用中,网络状态的变化会直接影响应用的功能和用户体验。及时监听网络状态可以让应用做出相应的调整,例如缓存数据、显示离线提示、调整请求策略等。
本文将详细介绍如何在 Flutter 应用中实现网络状态监听,包括使用 connectivity_plus 包、构建网络状态服务类、实时更新 UI 等,并提供完整的代码示例。
为什么需要网络状态监听
场景分析
在实际应用中,网络状态变化是非常常见的:
- 用户从 Wi-Fi 环境移动到移动网络环境
- 用户进入地下停车场或电梯,网络信号变差
- 用户手动关闭移动数据或 Wi-Fi
- 网络临时中断后恢复
应用影响
网络状态变化会对应用产生以下影响:
- 功能可用性:某些功能需要网络连接才能使用
- 数据同步:离线状态下的数据需要在恢复网络后同步
- 用户体验:网络中断时需要给出友好提示
- 性能优化:根据网络类型调整资源加载策略
监听的好处
实现网络状态监听可以带来以下好处:
- 及时通知用户网络状态变化
- 根据网络状态调整应用行为
- 提供离线缓存和数据同步功能
- 优化资源加载策略,减少流量消耗
使用 connectivity_plus 包
connectivity_plus 是 Flutter 社区最常用的网络状态监听包,支持 iOS、Android、Web 等多个平台。
安装依赖
在 pubspec.yaml 中添加依赖:
dependencies:
connectivity_plus: ^5.0.0
然后运行 flutter pub get 安装依赖。
基本用法
import 'package:connectivity_plus/connectivity_plus.dart';
class NetworkStatusExample {
final Connectivity _connectivity = Connectivity();
void checkStatus() async {
var result = await _connectivity.checkConnectivity();
print('当前网络状态: $result');
}
void startListening() {
_connectivity.onConnectivityChanged.listen((result) {
print('网络状态变化: $result');
});
}
}
网络状态类型
connectivity_plus 提供了以下网络状态类型:
| 状态 | 说明 |
|---|---|
ConnectivityResult.wifi |
设备连接到 Wi-Fi 网络 |
ConnectivityResult.mobile |
设备使用移动数据网络 |
ConnectivityResult.none |
设备没有网络连接 |
构建网络状态服务类
将网络状态监听逻辑封装为一个服务类,可以方便地在整个应用中使用。
实现代码
import 'package:connectivity_plus/connectivity_plus.dart';
class NetworkService {
final Connectivity _connectivity = Connectivity();
StreamSubscription<ConnectivityResult>? _subscription;
Stream<ConnectivityResult> get onNetworkStatusChanged {
return _connectivity.onConnectivityChanged;
}
void startListening(void Function(ConnectivityResult) callback) {
_subscription = _connectivity.onConnectivityChanged.listen(callback);
}
void stopListening() {
_subscription?.cancel();
_subscription = null;
}
Future<bool> isConnected() async {
var result = await _connectivity.checkConnectivity();
return result != ConnectivityResult.none;
}
Future<bool> isWifi() async {
var result = await _connectivity.checkConnectivity();
return result == ConnectivityResult.wifi;
}
Future<bool> isMobile() async {
var result = await _connectivity.checkConnectivity();
return result == ConnectivityResult.mobile;
}
Future<String> getNetworkType() async {
var result = await _connectivity.checkConnectivity();
switch (result) {
case ConnectivityResult.wifi:
return 'Wi-Fi';
case ConnectivityResult.mobile:
return '移动网络';
case ConnectivityResult.none:
return '无网络';
default:
return '未知';
}
}
}
代码说明
onNetworkStatusChanged:获取网络状态变化的 StreamstartListening():开始监听网络状态变化stopListening():停止监听,释放资源isConnected():检查是否有网络连接isWifi():检查是否是 Wi-Fi 连接isMobile():检查是否是移动网络连接getNetworkType():获取网络类型的字符串描述
使用示例
final networkService = NetworkService();
// 检查当前网络状态
bool connected = await networkService.isConnected();
print('是否连接网络: $connected');
// 获取网络类型
String type = await networkService.getNetworkType();
print('网络类型: $type');
// 监听网络状态变化
networkService.startListening((result) {
print('网络状态变化: $result');
});
// 停止监听
networkService.stopListening();
实时更新 UI
使用 StreamBuilder 可以实时根据网络状态更新 UI。
实现代码
class NetworkStatusWidget extends StatelessWidget {
final NetworkService networkService;
const NetworkStatusWidget({super.key, required this.networkService});
Widget build(BuildContext context) {
return StreamBuilder<ConnectivityResult>(
stream: networkService.onNetworkStatusChanged,
initialData: ConnectivityResult.none,
builder: (context, snapshot) {
ConnectivityResult status = snapshot.data ?? ConnectivityResult.none;
return Container(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
color: _getStatusColor(status),
child: Row(
children: [
Icon(_getStatusIcon(status), color: Colors.white),
const SizedBox(width: 8),
Text(
_getStatusText(status),
style: const TextStyle(color: Colors.white),
),
],
),
);
},
);
}
Color _getStatusColor(ConnectivityResult status) {
switch (status) {
case ConnectivityResult.wifi:
return Colors.green;
case ConnectivityResult.mobile:
return Colors.orange;
case ConnectivityResult.none:
return Colors.red;
default:
return Colors.grey;
}
}
IconData _getStatusIcon(ConnectivityResult status) {
switch (status) {
case ConnectivityResult.wifi:
return Icons.wifi;
case ConnectivityResult.mobile:
return Icons.signal_cellular_alt;
case ConnectivityResult.none:
return Icons.signal_cellular_off;
default:
return Icons.network_check;
}
}
String _getStatusText(ConnectivityResult status) {
switch (status) {
case ConnectivityResult.wifi:
return 'Wi-Fi连接';
case ConnectivityResult.mobile:
return '移动网络';
case ConnectivityResult.none:
return '无网络连接';
default:
return '未知网络';
}
}
}
使用示例
class MyApp extends StatelessWidget {
final NetworkService networkService = NetworkService();
MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('网络状态监听示例')),
body: Column(
children: [
NetworkStatusWidget(networkService: networkService),
const Expanded(child: Center(child: Text('应用内容'))),
],
),
),
);
}
}
全局网络状态管理
将网络状态管理集成到全局状态管理中,可以在应用的任何地方访问网络状态。
使用 ChangeNotifier
import 'package:flutter/foundation.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
class NetworkProvider extends ChangeNotifier {
final Connectivity _connectivity = Connectivity();
StreamSubscription<ConnectivityResult>? _subscription;
ConnectivityResult _status = ConnectivityResult.none;
ConnectivityResult get status => _status;
bool get isConnected => _status != ConnectivityResult.none;
bool get isWifi => _status == ConnectivityResult.wifi;
bool get isMobile => _status == ConnectivityResult.mobile;
NetworkProvider() {
_init();
}
Future<void> _init() async {
_status = await _connectivity.checkConnectivity();
notifyListeners();
_subscription = _connectivity.onConnectivityChanged.listen((result) {
_status = result;
notifyListeners();
});
}
void dispose() {
_subscription?.cancel();
super.dispose();
}
}
使用 Provider
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => NetworkProvider(),
child: const MyApp(),
),
);
}
class MyWidget extends StatelessWidget {
const MyWidget({super.key});
Widget build(BuildContext context) {
var networkProvider = Provider.of<NetworkProvider>(context);
if (!networkProvider.isConnected) {
return const Text('当前无网络连接');
}
if (networkProvider.isMobile) {
return const Text('当前使用移动网络');
}
return const Text('当前使用Wi-Fi网络');
}
}
离线缓存和数据同步
网络状态监听的一个重要应用场景是实现离线缓存和数据同步。
实现代码
class DataSyncService {
final NetworkService _networkService;
final List<dynamic> _pendingData = [];
DataSyncService(this._networkService) {
_init();
}
void _init() {
_networkService.startListening((result) async {
if (result != ConnectivityResult.none && _pendingData.isNotEmpty) {
await _syncPendingData();
}
});
}
Future<void> addData(dynamic data) async {
bool connected = await _networkService.isConnected();
if (connected) {
await _sendData(data);
} else {
_pendingData.add(data);
print('数据已缓存,等待网络恢复后同步');
}
}
Future<void> _sendData(dynamic data) async {
// 发送数据到服务器
print('发送数据: $data');
}
Future<void> _syncPendingData() async {
print('开始同步缓存数据,共 ${_pendingData.length} 条');
for (var data in _pendingData) {
try {
await _sendData(data);
} catch (e) {
print('同步失败: $e');
break;
}
}
_pendingData.clear();
print('缓存数据同步完成');
}
}
使用示例
final networkService = NetworkService();
final dataSyncService = DataSyncService(networkService);
// 添加数据(会自动判断网络状态)
await dataSyncService.addData({'name': 'John'});
await dataSyncService.addData({'name': 'Jane'});
根据网络状态调整请求策略
根据网络状态调整请求策略可以优化用户体验和减少流量消耗。
实现代码
class SmartApiClient {
final NetworkService _networkService;
final String baseUrl;
SmartApiClient({
required this.baseUrl,
required NetworkService networkService,
}) : _networkService = networkService;
Future<dynamic> get(String path, {bool allowCache = true}) async {
bool connected = await _networkService.isConnected();
if (!connected) {
if (allowCache) {
return await _getFromCache(path);
} else {
throw Exception('无网络连接');
}
}
bool isMobile = await _networkService.isMobile();
final response = await http.get(
Uri.parse('$baseUrl$path'),
headers: {
'Cache-Control': isMobile ? 'max-age=3600' : 'no-cache',
},
);
if (allowCache) {
await _saveToCache(path, response.body);
}
return jsonDecode(response.body);
}
Future<dynamic> _getFromCache(String path) async {
// 从缓存获取数据
print('从缓存获取: $path');
return {'cached': true, 'path': path};
}
Future<void> _saveToCache(String path, String data) async {
// 保存数据到缓存
print('保存到缓存: $path');
}
}
使用示例
final networkService = NetworkService();
final apiClient = SmartApiClient(
baseUrl: 'https://api.example.com',
networkService: networkService,
);
// 获取数据(会根据网络状态调整策略)
var data = await apiClient.get('/users');
UI 层的网络状态提示
在 UI 层提供友好的网络状态提示,可以提升用户体验。
实现代码
class NetworkStatusBanner extends StatefulWidget {
const NetworkStatusBanner({super.key});
State<NetworkStatusBanner> createState() => _NetworkStatusBannerState();
}
class _NetworkStatusBannerState extends State<NetworkStatusBanner> {
late NetworkService _networkService;
ConnectivityResult _status = ConnectivityResult.none;
void initState() {
super.initState();
_networkService = NetworkService();
_initNetworkStatus();
_startListening();
}
void dispose() {
_networkService.stopListening();
super.dispose();
}
Future<void> _initNetworkStatus() async {
_status = await _networkService.checkConnectivity();
setState(() {});
}
void _startListening() {
_networkService.startListening((result) {
setState(() {
_status = result;
_showNetworkNotification(result);
});
});
}
void _showNetworkNotification(ConnectivityResult status) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(_getStatusText(status)),
backgroundColor: _getStatusColor(status),
duration: const Duration(seconds: 3),
),
);
}
String _getStatusText(ConnectivityResult status) {
switch (status) {
case ConnectivityResult.wifi:
return '已连接到 Wi-Fi';
case ConnectivityResult.mobile:
return '已切换到移动网络';
case ConnectivityResult.none:
return '网络连接已断开';
default:
return '网络状态变化';
}
}
Color _getStatusColor(ConnectivityResult status) {
switch (status) {
case ConnectivityResult.wifi:
return Colors.green;
case ConnectivityResult.mobile:
return Colors.orange;
case ConnectivityResult.none:
return Colors.red;
default:
return Colors.grey;
}
}
Widget build(BuildContext context) {
if (_status == ConnectivityResult.none) {
return Container(
color: Colors.red,
padding: const EdgeInsets.all(16),
child: const Center(
child: Text(
'当前无网络连接,部分功能可能不可用',
style: TextStyle(color: Colors.white),
),
),
);
}
return const SizedBox.shrink();
}
}
使用示例
class MyPage extends StatelessWidget {
const MyPage({super.key});
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('页面标题')),
body: Column(
children: [
const NetworkStatusBanner(),
Expanded(
child: ListView(
children: const [
ListTile(title: Text('内容1')),
ListTile(title: Text('内容2')),
ListTile(title: Text('内容3')),
],
),
),
],
),
);
}
}
网络状态监听最佳实践
1. 在应用启动时初始化网络监听
在应用启动时初始化网络服务,确保应用始终能够感知网络状态变化。
2. 在页面生命周期中管理监听的订阅和取消
在 initState() 中开始监听,在 dispose() 中取消监听,避免内存泄漏。
3. 使用 StreamBuilder 实时更新 UI
使用 StreamBuilder 可以自动响应网络状态变化,实时更新 UI。
4. 提供离线缓存功能
在离线状态下,使用缓存数据提升用户体验。
5. 显示友好的网络状态提示
使用 SnackBar 或顶部横幅提示用户网络状态变化。
6. 根据网络状态调整请求策略
根据网络类型调整缓存策略和资源加载策略。
7. 全局状态管理
将网络状态集成到全局状态管理中,方便在应用的任何地方访问。
注意事项
1. connectivity_plus 的局限性
connectivity_plus 只能检测设备是否连接到网络,不能检测网络是否真正可用。例如,设备可能连接到 Wi-Fi 但无法访问互联网。
2. 需要额外检测网络可用性
如果需要检测网络是否真正可用,可以发送一个简单的请求来验证:
Future<bool> isNetworkAvailable() async {
try {
final response = await http.get(Uri.parse('https://api.example.com/ping'));
return response.statusCode == 200;
} catch (e) {
return false;
}
}
3. iOS 权限配置
在 iOS 上,需要在 Info.plist 中添加权限配置:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
4. Android 权限配置
在 Android 上,需要在 AndroidManifest.xml 中添加权限:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
总结
网络状态监听是构建健壮移动应用的重要组成部分。通过合理使用 connectivity_plus 包和构建网络状态服务类,可以实现应用的离线感知能力,提升用户体验。
在实际开发中,需要结合离线缓存、数据同步和请求策略调整等功能,构建一个完整的网络状态管理系统。
完整示例代码
以下是一个完整的网络状态管理示例:
import 'package:flutter/material.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
class NetworkService {
final Connectivity _connectivity = Connectivity();
StreamSubscription<ConnectivityResult>? _subscription;
Stream<ConnectivityResult> get onNetworkStatusChanged {
return _connectivity.onConnectivityChanged;
}
void startListening(void Function(ConnectivityResult) callback) {
_subscription = _connectivity.onConnectivityChanged.listen(callback);
}
void stopListening() {
_subscription?.cancel();
_subscription = null;
}
Future<bool> isConnected() async {
var result = await _connectivity.checkConnectivity();
return result != ConnectivityResult.none;
}
Future<String> getNetworkType() async {
var result = await _connectivity.checkConnectivity();
switch (result) {
case ConnectivityResult.wifi:
return 'Wi-Fi';
case ConnectivityResult.mobile:
return '移动网络';
case ConnectivityResult.none:
return '无网络';
default:
return '未知';
}
}
}
class NetworkStatusBanner extends StatelessWidget {
final NetworkService networkService;
const NetworkStatusBanner({super.key, required this.networkService});
Widget build(BuildContext context) {
return StreamBuilder<ConnectivityResult>(
stream: networkService.onNetworkStatusChanged,
initialData: ConnectivityResult.none,
builder: (context, snapshot) {
var status = snapshot.data ?? ConnectivityResult.none;
if (status == ConnectivityResult.none) {
return Container(
color: Colors.red,
padding: const EdgeInsets.all(12),
child: const Text(
'无网络连接',
style: TextStyle(color: Colors.white),
textAlign: TextAlign.center,
),
);
}
return const SizedBox.shrink();
},
);
}
}
通过这个示例,你可以快速实现一个网络状态监听系统,为你的应用提供离线感知能力。
更多推荐



所有评论(0)