在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

概述

网络状态监听是指实时监测设备的网络连接状态。在移动应用中,网络状态的变化会直接影响应用的功能和用户体验。及时监听网络状态可以让应用做出相应的调整,例如缓存数据、显示离线提示、调整请求策略等。

本文将详细介绍如何在 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:获取网络状态变化的 Stream
  • startListening():开始监听网络状态变化
  • 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();
      },
    );
  }
}

通过这个示例,你可以快速实现一个网络状态监听系统,为你的应用提供离线感知能力。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐