flutter实战之常用模块network_info_plus模块及其应用
1. network_info_plus简介与集成
1.1 什么是network_info_plus模块
network_info_plus
是一个用于访问网络相关信息如网络状态以及Wi-Fi信息的Flutter插件。它是一个可跨平台利用的插件,可以在iOS和Android系统中提供输入和输出网络连接的信息。
network_info_plus
模块的核心功能是对网络连接状态进行检查和监控。它可以获取当前的网络连接类型,如Wi-Fi,移动网络或无网连接,并可以获取当前连接的Wi-Fi网络的相关信息,如SSID,BSSID和安全类型。network_info_plus 可以用于各种需要获取或检查网络信息的场景,例如:
- 在用户网络连接状态发生变化时,动态调整应用的功能或行为。
- 获取Wi-Fi信息,帮助实现特定的功能,如 Wi-Fi定位。
1.2 如何在Flutter项目中添加network_info_plus模块
要在Flutter项目中集成 network_info_plus 模块,你需要在项目的 pubspec.yaml 文件中添加如下依赖:
dependencies: network_info_plus: ^4.0.1 # 其它模块...
flutter pub get
笔者写本博文时最新版本为 4.0.1,后续相关 API 以该版本为依据。如果入喉改版想要直接使用新的版本也可以使用一下命令添加到你的项目中:
flutter pub add network_info_plus
2. 基本用法与功能
2.1 使用network_info_plus获取网络状态
network_info_plus
插件使我们能方便地获取设备当前的网络状态。这些信息可以用于决定如何优化你的应用的性能和行为(比如在没有网络连接的情况下缓存数据而不是试图从服务器拉取数据)。
以下是如何使用network_info_plus
获取网络状态的简单步骤:
步骤
- 导入 network_info_plus 和创建 NetworkInfo 实例:
import 'package:network_info_plus/network_info_plus.dart'; final networkInfo = NetworkInfo();
- 使用
networkInfo.getDataConnectionStatus()
方法获取当前的网络状态。
DataConnectionStatus status = await networkInfo.getDataConnectionStatus(); switch (status) { case DataConnectionStatus.connected: print('Connected to network.'); break; case DataConnectionStatus.disconnected: print('Disconnected from network.'); break; }
getDataConnectionStatus()
方法将返回一个DataConnectionStatus
枚举,这个枚举代表设备当前的网络连接状态。你可以用这个状态来判断设备是否连接到了网络。
注意事项
network_info_plus
插件需要网络状态的权限。在Android上,你需要在AndroidManifest.xml
文件的<manifest>
标签中添加android.permission.ACCESS_NETWORK_STATE
权限。- 插件可能无法检测到一些特定类型的网络连接,比如 VPN 或代理。在这种情况下,
getDataConnectionStatus()
方法可能会返回DataConnectionStatus.disconnected
,即使设备实际上是连接到网络的。 - 如果用户在设备的设置中关闭了网络,
getDataConnectionStatus()
方法也会返回DataConnectionStatus.disconnected
。
2.2 获取当前的Wi-Fi信息
network_info_plus
插件还可以用于获取当前设备连接的Wi-Fi的信息,如SSID、BSSID和信号强度。这些信息对于实现对不同Wi-Fi环境的自适应优化,如降低高负载操作或提示对用户进行手动操作,具有很大的价值。
下面我们将详细介绍如何使用network_info_plus
插件来获取这些Wi-Fi信息。
- 创建一个新的
WifiInfo
实例。
final wifiInfo = WifiInfo();
该实例会用于后续的Wi-Fi信息获取操作。
- 使用
wifiInfo
实例的方法获取Wi-Fi网络的详细信息。
- 获取 SSID:
String ssid = await wifiInfo.getWifiName(); print('SSID: $ssid');
SSID (Service Set Identifier) 是一个用于标识Wi-Fi网络名称的字符串。通过获取SSID,你可以知道设备当前连接到了哪一个Wi-Fi网络。你可以在应用中显示这个信息,或者根据特定的SSID以选择性地执行某些操作。
- 获取 BSSID:
String bssid = await wifiInfo.getWifiBSSID(); print('BSSID: $bssid');
BSSID (Basic Service Set Identifier) 是一个用于标识Wi-Fi接入点(如路由器)的字符串。这个字符串通常是接入点的MAC地址。你可以使用BSSID来识别特定的Wi-Fi接入点,并根据这个识别信息执行应用内的操作。
- 获取 信号强度:
int signalStrength = await wifiInfo.getWifiSignalStrength(); print('Signal strength: $signalStrength');
信号强度是一个整数值,表示设备与Wi-Fi接入点之间的信号质量。较高的数值意味着更好的信号质量。你可以根据信号强度值预测当前的网络质量,并据此调整应用的行为,例如在信号质量较差时限制大型文件的下载。
2.3 监听网络状态的变化
在某些应用场景中,你可能需要实时了解设备的网络连接状态。network_info_plus
插件提供了一种简便的方式来监听设备的网络状态变化。当设备的网络状态发生变化时,你可以在应用中执行相应的操作(如提示用户连接到Wi-Fi或重新请求数据)。
下面我们将详细介绍如何使用network_info_plus
插件来实现网络状态的监听和响应。
- 创建一个新的
DataConnection
类的实例。
final dataConnection = DataConnection();
DataConnection
类提供了一些方法来检查和监听设备的网络连接状态。
- 使用
dataConnectionStatusStream
方法监听网络状态变化。
StreamSubscription<DataConnectionStatus> streamSubscription = dataConnection.onDataConnectionStatusChanged.listen((DataConnectionStatus status) { switch (status) { case DataConnectionStatus.connected: print('Connected to network.'); break; case DataConnectionStatus.disconnected: print('Disconnected from network.'); break; } });
onDataConnectionStatusChanged
是一个 Stream<DataConnectionStatus>
类型,通过 listen
方法可以监听网络状态变化。当网络状态发生变化时,这个流会发出 DataConnectionStatus.connected
或 DataConnectionStatus.disconnected
事件,表示设备已连接或断开网络。你可以在这些事件中执行相应的操作,以实现对设备网络状态的响应。
- 在不再需要监听网络状态变化时,取消监听。
streamSubscription.cancel();
当你不再需要监听网络状态变化(例如,应用被关闭或切换到了后台运行),请记得取消网络状态监听,以避免应用的资源浪费。
经过上述步骤,你已经成功实现了网络状态的监听和响应。通过network_info_plus
插件,你的应用可以更加智能地适应用户的网络环境,提供更好的用户体验。
3. 高级功能与操作
3.1 判断网络类型
在开发移动应用时,我们可能需要知道设备当前连接的网络类型,因为不同的网络类型可能会有不同的传输速度和费用。例如,Wi-Fi可能传输速度更高,而移动数据网络可能需要用户付费。使用network_info_plus
插件,我们可以轻松地获取这些信息。
下面是判断网络类型的步骤:
- 导入
network_info_plus
插件。
import 'package:network_info_plus/network_info_plus.dart';
- 创建一个
NetworkInfoPlus
类的实例。
final networkInfo = NetworkInfoPlus();
- 使用
networkInfo
实例的方法来获取网络类型。
final networkType = await networkInfo.getConnectionType();
- 使用Switch语句来判断网络类型并执行相应操作。
switch (networkType) { case NetworkType.wifi: print("The device is connected to Wi-Fi."); break; case NetworkType.mobile: print("The device is using mobile data."); break; case NetworkType.none: print("The device is not connected to any network."); break; }
在上面的代码中:
- 我们首先导入了
network_info_plus
插件。 - 然后,我们创建了一个
NetworkInfoPlus
类的实例,这个实例会被用于后续的网络类型获取操作。 - 使用
getConnectionType
方法来获取网络类型。这个方法将返回一个异步的Future<NetworkType>
对象,然后我们用await
关键字等待Future
对象的完成,并将结果赋值给networkType
。 - 最后,使用一个
switch
语句判断网络类型并执行相应的操作。NetworkType
是一个枚举类型,其可能的值包括wifi
(表示设备当前连接的是Wi-Fi)、mobile
(表示设备当前正在使用移动数据网络)和none
(表示设备当前没有连接任何网络)。
3.2 获取数据网络信息
除了获取网络类型,network_info_plus
插件还可以获取设备当前使用的数据网络的其他信息,如移动运营商的名称、网络频率、网络技术类型等。
下面我们将详细介绍如何使用network_info_plus
插件来获取数据网络的各类信息。
- 导入
network_info_plus
插件。
import 'package:network_info_plus/network_info_plus.dart';
- 创建一个新的
DataConnection
实例。
final dataConnectionInfo = DataConnection.instance;
- 获取移动运营商的名称
String operatorName = await dataConnectionInfo.getCarrierName(); print('Operator: $operatorName');
- 获取网络的频率
DataConnectionFrequency frequency = await dataConnectionInfo.getFrequency(); print('Frequency: $frequency');
- 获取网络技术类型
DataConnectionTechnology technology = await dataConnectionInfo.getTechnology(); print('Technology: $technology');
在上述的代码中:
- 我们首先导入
network_info_plus
插件。 - 根据初始化
DataConnection
的实例,我们可以获取设备当前使用的数据网络的相关信息。 - 接着,我们使用
getCarrierName
方法来获取移动运营商的名称。这个方法会返回一个异步的Future<String>
对象。我们用await
关键字等待Future
对象的完成,并将结果赋值给operatorName
。 - 然后使用
getFrequency
方法来获取设备的网络频率。 这个方法将返回一个异步的Future<DataConnectionFrequency>
对象。包括low
(低)、medium
(中)和high
(高). - 最后,使用
getTechnology
方法来获取设备使用的网络技术类型。这个方法将返回一个异步的Future<DataConnectionTechnology>
对象。包括gprs
(GPRS)、edge
(EDGE)、hspa
(HSPA)等等。
根据获取的数据网络信息,你可以在应用中显示这些信息,或根据相关信息执行定制化的操作。
4. 网络优先的数据加载策略应用
在实际的开发应用中,我们往往需要优先考虑使用网络数据,如果网络不可用,我们再去加载本地的缓存数据。下面的示例说明了如何使用 network_info_plus
插件实现这个功能:
import 'package:network_info_plus/network_info_plus.dart'; // 创建 NetworkInfoPlus 的一个实例 final networkInfo = NetworkInfoPlus(); // 创建数据加载的函数 Future<Data> loadData() async { Data data; // 首先,我们检查设备的网络状态。 NetworkType networkType = await networkInfo.getConnectionType(); // 如果有网络连接,那么我们首先尝试从网络加载数据。 if(networkType != NetworkType.none) { // 尝试从网络加载数据 try { data = await loadFromNetwork(); // 如果网络加载成功,那么我们将数据缓存到本地。 await cacheData(data); } catch(e) { //如果网络加载数据失败,那么我们尝试从本地缓存加载数据。 data = await loadFromCache(); } } else { // 如果没有网络连接,那么我们只能从本地缓存加载数据。 data = await loadFromCache(); } return data; }
在本例子中:
- 我们首先导入了
network_info_plus
插件。 - 接着创建了一个异步方法
loadData
,该方法返回一个Data
对象的Future
,Data
是一个示例类,你需要将其换成你具体的数据类。 - 通过使用
networkInfo.getConnectionType()
方法来获取当前网络的类型。 - 根据获取的网络类型,我们设计各种数据加载的策略。如果设备在线,我们优先尝试从网络加载数据,并在成功后将数据缓存在本地。如果网络加载失败,将尝试从本地缓存数据加载。 如果设备不在线,则直接尝试从本地缓存数据来加载。
这个网络优先的数据加载策略在实际的应用开发中是非常常见的,能帮助我们设计出更好的用户体验。
5. 阅读官方给出的案例
这是一个Flutter应用程序,其主要功能是获取并展示有关Wi-Fi网络的一些详细信息。现在我们将逐段解析。
- 为桌面设备设置平台覆盖以避免异常
void _enablePlatformOverrideForDesktop() { if (!kIsWeb && (Platform.isWindows || Platform.isLinux)) { debugDefaultTargetPlatformOverride = TargetPlatform.fuchsia; } } void main() { _enablePlatformOverrideForDesktop(); runApp(const MyApp()); }
这段代码为桌面设备设置了平台覆盖,以防止在这些平台上运行时引起异常。然后在 main()
方法中调用此功能,并启动 MyApp。
- 创建应用程序的主要部件
class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( useMaterial3: true, colorSchemeSeed: const Color(0x9f4376f8), ), home: const MyHomePage(title: 'Flutter Demo Home Page'), ); } }
这段代码定义了MyApp类,它是一个无状态部件,返回一个MaterialApp对象作为应用程序的主题。其中设定了 App 的标题和主题,并指定了 MyHomePage 作为首页。
- 创建 MyHomePage StatefulWidget
class MyHomePage extends StatefulWidget { const MyHomePage({Key? key, this.title}) : super(key: key); final String? title; @override State<MyHomePage> createState() => _MyHomePageState(); }
这段代码定义了 Stateful Widget MyHomePage,它可能会改变状态。创建状态对象 _MyHomePageState
来保存可能会改变的信息。
- _MyHomePageState 包含状态和网络信息对象
class _MyHomePageState extends State<MyHomePage> { String _connectionStatus = 'Unknown'; final NetworkInfo _networkInfo = NetworkInfo(); @override void initState() { super.initState(); _initNetworkInfo(); } // ... }
这段代码是 build()
方法,它指定了如何在屏幕上显示网络连接的详细信息。Scaffold
是 Flutter 的基本布局结构,包含 AppBar
和 body
。
- 获取并显示网络信息
@Override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('NetworkInfoPlus example'), elevation: 4, ), body: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ const Text( 'Network info', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 16), Text(_connectionStatus), ], )), ); }
- 异步获取网络信息
Future<void> _initNetworkInfo() async { String? wifiName, wifiBSSID, wifiIPv4, wifiIPv6, wifiGatewayIP, wifiBroadcast, wifiSubmask; try {...} ... setState(() { _connectionStatus = 'Wifi Name: $wifiName\n' 'Wifi BSSID: $wifiBSSID\n' 'Wifi IPv4: $wifiIPv4\n' 'Wifi IPv6: $wifiIPv6\n' 'Wifi Broadcast: $wifiBroadcast\n' 'Wifi Gateway: $wifiGatewayIP\n' 'Wifi Submask: $wifiSubmask\n'; }); }
最后,_initNetworkInfo()
方法用于异步获取网络信息。它将收集的各种网络信息赋值给 _connectionStatus
并调用 setState()
更新 UI。如果获取任何详细信息产生异常,都会捕获并记录,然后将失败消息设为该详细信息。
- 以下是该案例完整代码:
import 'dart:async'; import 'dart:developer' as developer; import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:network_info_plus/network_info_plus.dart'; // 设置平台覆盖以避免运行时异常 void _enablePlatformOverrideForDesktop() { if (!kIsWeb && (Platform.isWindows || Platform.isLinux)) { debugDefaultTargetPlatformOverride = TargetPlatform.fuchsia; } } // 主函数 void main() { _enablePlatformOverrideForDesktop(); runApp(const MyApp()); } // 创建 MyApp StatefulWidget class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); // 重写 build 方法 @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( useMaterial3: true, colorSchemeSeed: const Color(0x9f4376f8), ), home: const MyHomePage(title: 'Flutter Demo Home Page'), ); } } // 创建 MyHomePage StatefulWidget class MyHomePage extends StatefulWidget { const MyHomePage({Key? key, this.title}) : super(key: key); final String? title; @override State<MyHomePage> createState() => _MyHomePageState(); } // 创建 _MyHomePageState State class _MyHomePageState extends State<MyHomePage> { String _connectionStatus = 'Unknown'; final NetworkInfo _networkInfo = NetworkInfo(); @override void initState() { super.initState(); _initNetworkInfo(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('NetworkInfoPlus example'), elevation: 4, ), body: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ const Text( '网络信息', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 16), Text(_connectionStatus), ], ), ), ); } // 定义一个异步函数 _initNetworkInfo(),用于获取网络信息 Future<void> _initNetworkInfo() async { String? wifiName, wifiBSSID, wifiIPv4, wifiIPv6, wifiGatewayIP, wifiBroadcast, wifiSubmask; // 获取 WiFi 名称 try { if (!kIsWeb && Platform.isIOS) { var status = await _networkInfo.getLocationServiceAuthorization(); if (status == LocationAuthorizationStatus.notDetermined) { status = await _networkInfo.requestLocationServiceAuthorization(); } if (status == LocationAuthorizationStatus.authorizedAlways || status == LocationAuthorizationStatus.authorizedWhenInUse) { wifiName = await _networkInfo.getWifiName(); } else { wifiName = await _networkInfo.getWifiName(); } } else { wifiName = await _networkInfo.getWifiName(); } } on PlatformException catch (e) { developer.log('Failed to get Wifi Name', error: e); wifiName = 'Failed to get Wifi Name'; } // 获取 WiFi BSSID try { if (!kIsWeb && Platform.isIOS) { var status = await _networkInfo.getLocationServiceAuthorization(); if (status == LocationAuthorizationStatus.notDetermined) { status = await _networkInfo.requestLocationServiceAuthorization(); } if (status == LocationAuthorizationStatus.authorizedAlways || status == LocationAuthorizationStatus.authorizedWhenInUse) { wifiBSSID = await _networkInfo.getWifiBSSID(); } else { wifiBSSID = await _networkInfo.getWifiBSSID(); } } else { wifiBSSID = await _networkInfo.getWifiBSSID(); } } on PlatformException catch (e) { developer.log('Failed to get Wifi BSSID', error: e); wifiBSSID = 'Failed to get Wifi BSSID'; } // 获取 WiFi IPv4 地址 try { wifiIPv4 = await _networkInfo.getWifiIP(); } on PlatformException catch (e) { developer.log('Failed to get Wifi IPv4', error: e); wifiIPv4 = 'Failed to get Wifi IPv4'; } // 获取 WiFi IPv6 地址 try { if (!Platform.isWindows) { wifiIPv6 = await _networkInfo.getWifiIPv6(); } } on PlatformException catch (e) { developer.log('Failed to get Wifi IPv6', error: e); wifiIPv6 = 'Failed to get Wifi IPv6'; } // 获取 WiFi 子网掩码 try { if (!Platform.isWindows) { wifiSubmask = await _networkInfo.getWifiSubmask(); } } on PlatformException catch (e) { developer.log('Failed to get Wifi submask address', error: e); wifiSubmask = 'Failed to get Wifi submask address'; } // 获取 WiFi 广播地址 try { if (!Platform.isWindows) { wifiBroadcast = await _networkInfo.getWifiBroadcast(); } } on PlatformException catch (e) { developer.log('Failed to get Wifi broadcast', error: e); wifiBroadcast = 'Failed to get Wifi broadcast'; } // 获取 WiFi 网关 IP 地址 try { if (!Platform.isWindows) { wifiGatewayIP = await _networkInfo.getWifiGatewayIP(); } } on PlatformException catch (e) { developer.log('Failed to get Wifi gateway address', error: e); wifiGatewayIP = 'Failed to get Wifi gateway address'; } // 更新界面状态 setState(() { _connectionStatus = 'WiFi 名称: $wifiName\n' 'WiFi BSSID: $wifiBSSID\n' 'WiFi IPv4: $wifiIPv4\n' 'WiFi IPv6: $wifiIPv6\n' 'WiFi 广播地址: $wifiBroadcast\n' 'WiFi 网关 IP 地址: $wifiGatewayIP\n' 'WiFi 子网掩码: $wifiSubmask\n'; }); } }