Flutter实战:如何用network_info_plus插件一键获取当前WiFi名称(附iOS/Android权限配置全流程)
Flutter跨平台开发实战:深度解析network_info_plus插件获取WiFi信息的完整方案
在智能家居设备配网、企业内网认证等场景中,自动获取设备当前连接的WiFi信息能显著提升用户体验。作为Flutter开发者,我们经常需要处理这类需求,但跨平台的权限差异和API限制往往让实现过程充满挑战。本文将带你从原理到实践,全面掌握在Flutter应用中获取WiFi信息的专业方案。
1. 技术选型与核心原理
在Flutter生态中,network_info_plus插件是目前最成熟的网络信息获取方案。它实际上是对原生平台API的封装:
- Android端:通过
WifiManager获取SSID/BSSID,需要ACCESS_NETWORK_STATE和定位权限 - iOS端:依赖
NEHotspotNetworkAPI,需要com.apple.developer.networking.wifi-info权限
技术难点:从Android 10(API 29)开始,Google加强了隐私保护,获取SSID必须配合定位权限。这是很多开发者容易忽略的关键点。
插件核心方法包括:
Future<String?> getWifiName(); // 获取SSID Future<String?> getWifiBSSID(); // 获取BSSID Future<String?> getWifiIP(); // 获取IPv4地址 Future<String?> getWifiIPv6(); // 获取IPv6地址 Future<String?> getWifiSubmask(); // 获取子网掩码2. 项目配置全流程
2.1 基础环境搭建
首先在pubspec.yaml中添加依赖:
dependencies: network_info_plus: ^6.1.3 permission_handler: ^10.4.0执行flutter pub get后,需要针对不同平台进行配置。
2.2 Android平台配置
修改android/app/src/main/AndroidManifest.xml:
<manifest> <!-- 基础网络权限 --> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!-- Android 10+ 必须的定位权限 --> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <!-- 针对不同API级别的兼容处理 --> <uses-permission-sdk-23 android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="30" /> </manifest>2.3 iOS平台配置
iOS的配置更为复杂,需要多个步骤:
- Entitlements文件配置
在Runner/Runner.entitlements中添加:
<dict> <key>com.apple.developer.networking.wifi-info</key> <true/> </dict>- Info.plist权限声明
添加定位权限描述:
<key>NSLocationWhenInUseUsageDescription</key> <string>需要获取WiFi信息用于设备配网</string> <key>NSLocationAlwaysAndWhenInUseUsageDescription</key> <string>需要获取WiFi信息用于设备配网</string>- Xcode工程配置
在Signing & Capabilities中添加Access WiFi Information能力。
3. 代码实现与最佳实践
3.1 基础获取实现
创建一个完整的WiFi信息获取组件:
import 'package:flutter/material.dart'; import 'package:network_info_plus/network_info_plus.dart'; import 'package:permission_handler/permission_handler.dart'; class WifiInfoWidget extends StatefulWidget { const WifiInfoWidget({super.key}); @override State<WifiInfoWidget> createState() => _WifiInfoWidgetState(); } class _WifiInfoWidgetState extends State<WifiInfoWidget> { final NetworkInfo _networkInfo = NetworkInfo(); Map<String, String?> _wifiData = {}; bool _isLoading = true; Future<void> _fetchWifiInfo() async { setState(() => _isLoading = true); // 检查并请求定位权限 if (!await _checkLocationPermission()) { setState(() => _isLoading = false); return; } // 并行获取所有网络信息 final results = await Future.wait([ _networkInfo.getWifiName(), _networkInfo.getWifiBSSID(), _networkInfo.getWifiIP(), _networkInfo.getWifiSubmask(), ]); setState(() { _wifiData = { 'SSID': results[0], 'BSSID': results[1], 'IP地址': results[2], '子网掩码': results[3], }; _isLoading = false; }); } Future<bool> _checkLocationPermission() async { final status = await Permission.location.status; if (status.isGranted) return true; final result = await Permission.location.request(); return result.isGranted; } @override void initState() { super.initState(); _fetchWifiInfo(); } @override Widget build(BuildContext context) { return Column( children: [ if (_isLoading) const CircularProgressIndicator() else ...[ for (final entry in _wifiData.entries) ListTile( title: Text(entry.key), subtitle: Text(entry.value ?? '未获取到数据'), ), ElevatedButton( onPressed: _fetchWifiInfo, child: const Text('刷新信息'), ) ] ], ); } }3.2 高级功能扩展
实时监听网络变化
Stream<Map<String, String?>> getWifiInfoStream() async* { final networkInfo = NetworkInfo(); while (true) { yield { 'SSID': await networkInfo.getWifiName(), 'IP': await networkInfo.getWifiIP(), }; await Future.delayed(const Duration(seconds: 5)); } }多平台兼容处理
Future<String?> getFormattedWifiName() async { final name = await NetworkInfo().getWifiName(); if (name == null) return null; // Android返回的SSID常带有引号 if (name.startsWith('"') && name.endsWith('"')) { return name.substring(1, name.length - 1); } return name; }4. 疑难问题解决方案
4.1 常见错误排查
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| Android返回null | 未授予定位权限 | 检查ACCESS_FINE_LOCATION权限 |
| iOS崩溃 | 缺少entitlements配置 | 确认Xcode中WiFi权限已添加 |
| 模拟器无数据 | 模拟器无真实WiFi模块 | 使用真机测试 |
| 返回"" | Android 12+限制 | 添加ACCESS_WIFI_STATE权限 |
4.2 性能优化建议
- 权限缓存:首次获取权限后缓存状态,避免重复请求
- 错误重试:网络不稳定时实现指数退避重试机制
- 数据本地缓存:非关键数据可考虑本地存储减少请求次数
Future<String?> getCachedWifiName() async { const cacheKey = 'last_wifi_name'; final prefs = await SharedPreferences.getInstance(); // 先从缓存读取 final cachedName = prefs.getString(cacheKey); if (cachedName != null) return cachedName; // 缓存不存在则从网络获取 final freshName = await NetworkInfo().getWifiName(); if (freshName != null) { await prefs.setString(cacheKey, freshName); } return freshName; }5. 企业级应用实践
在智能家居场景中,我们通常需要将WiFi信息传递给IoT设备。这里分享一个完整的配网流程实现:
class SmartConfigPage extends StatefulWidget { const SmartConfigPage({super.key}); @override State<SmartConfigPage> createState() => _SmartConfigPageState(); } class _SmartConfigPageState extends State<SmartConfigPage> { final _networkInfo = NetworkInfo(); String? _currentSsid; String? _currentPassword; bool _isConfiguring = false; Future<void> _startSmartConfig() async { if (_currentSsid == null || _currentPassword == null) return; setState(() => _isConfiguring = true); try { // 调用设备配网SDK await IotDevice.connect( ssid: _currentSsid!, password: _currentPassword!, ); // 配网成功后跳转 if (mounted) { Navigator.pushReplacement( context, MaterialPageRoute(builder: (_) => const ConfigSuccessPage()), ); } } catch (e) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('配网失败: ${e.toString()}')), ); } finally { if (mounted) setState(() => _isConfiguring = false); } } @override void initState() { super.initState(); _loadCurrentWifi(); } Future<void> _loadCurrentWifi() async { final name = await _networkInfo.getWifiName(); setState(() => _currentSsid = name); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('设备配网')), body: Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ TextField( decoration: const InputDecoration(labelText: 'WiFi名称'), controller: TextEditingController(text: _currentSsid), onChanged: (v) => _currentSsid = v, ), TextField( decoration: const InputDecoration(labelText: 'WiFi密码'), obscureText: true, onChanged: (v) => _currentPassword = v, ), const SizedBox(height: 20), ElevatedButton( onPressed: _isConfiguring ? null : _startSmartConfig, child: _isConfiguring ? const CircularProgressIndicator() : const Text('开始配网'), ), ], ), ), ); } }在实际项目中,我们发现Android 12+的设备需要额外处理NEARBY_WIFI_DEVICES权限,而iOS 16之后对后台WiFi访问有了更严格的限制。这些平台差异需要开发者在设计架构时就充分考虑。
