Flutter for OpenHarmony 实战:network_info_plus 网络扫描与隐私合规深度适配

Flutter for OpenHarmony 实战:network_info_plus 网络扫描与隐私合规深度适配

前言

做 IoT(物联网)配网、局域网文件互传(类似华为分享)、或简单的 WiFi 测速 App 时,我们需要获取当前连接的 WiFi SSID (名称)BSSID (Mac 地址) 以及本机的 IP 地址

但在 HarmonyOS NEXT 这个极其看重隐私合规的系统中,SSID 已经不再是一个简单的字符串,它被视为用户的物理轨迹隐私 。如果应用在未授权情况下频繁扫描网络,将面临应用商店下架的风险。network_info_plus 插件为我们封装了跨平台的调用逻辑,但在鸿蒙上落地,你还需要处理一些特有的权限"潜规则"。


一、 深度视角:WiFi 信息为何与位置挂钩?

1.1 "WiFi 列表 == 地理位置"

鸿蒙系统(以及 Android 12+)遵循一套安全原则:由于全球绝大部分 WiFi AP 的地理位置已被云端数据库索引,通过获取 BSSID (Mac 地址),应用可以推算出误差在 50 米内的位置。

因此,在鸿蒙上获取网络详情,本质上是对 ohos.permission.LOCATION 的挑战。

1.2 鸿蒙 API 12+ 的隐私变更

  • SSID 屏蔽 :若未开启系统 GPS 开关且未授予精确位置权限,API 将返回 <unknown ssid>
  • IP 格式:鸿蒙底层同时支持 IPv4 和 IPv6,插件会自动选择当前活跃的本地 IP。

二、 工程实战:合规的"全家桶"方案

2.1 快速安装与依赖配置

在鸿蒙 NEXT (API 12+) 生态中,通用插件直接在真机运行往往需要对应的 _ohos 适配包来建立底层的 MethodChannel 桥接。

命令行安装:
bash 复制代码
# 1. 安装跨平台主插件
flutter pub add network_info_plus

# 2. ⚡️ 关键:安装鸿蒙真机适配包
flutter pub add network_info_plus_ohos

# 3. 安装鸿蒙专项权限管理插件
flutter pub add permission_handler_ohos
pubspec.yaml 最终配置参考:

安装完成后,你的 dependencies 列表应当包含以下三项,这是确保网络扫描功能在鸿蒙真机上"跑得通"的铁三角:

yaml 复制代码
dependencies:
  # ... 其他依赖
  network_info_plus: ^7.0.0      # 提供跨平台一致的 API 接口
  network_info_plus_ohos: any    # 💡 必选:建立鸿蒙真机底层桥接
  permission_handler_ohos: any   # 💡 必选:处理鸿蒙 user_grant 权限弹窗

2.2 权限组合配置 (module.json5)

在鸿蒙中,地理位置属于 user_grant 权限。除了声明权限名,你必须 提供 reason(理由)和 usedScene(使用场景),否则编译会报错。

json5 复制代码
"requestPermissions": [
  { "name": "ohos.permission.INTERNET" },
  { "name": "ohos.permission.GET_WIFI_INFO" }, 
  {
    "name": "ohos.permission.LOCATION",
    "reason": "$string:location_reason", // 💡 必须在 string.json 中定义
    "usedScene": {
      "abilities": ["EntryAbility"],
      "when": "inuse"
    }
  },
  {
    "name": "ohos.permission.APPROXIMATELY_LOCATION",
    "reason": "$string:location_reason",
    "usedScene": {
      "abilities": ["EntryAbility"],
      "when": "inuse"
    }
  }
]

2.3 优雅的权限检查代码 (专项适配版)

由于通用插件在鸿蒙 NEXT 早期版本可能存在行为不一致,推荐使用专项适配插件 permission_handler_ohos

dart 复制代码
import 'package:permission_handler_ohos/permission_handler_ohos.dart';

Future<void> initNetworkScan() async {
  // 1. 直接申请鸿蒙系统定位权限字符串
  final status = await PermissionHandlerOhos.requestPermission("ohos.permission.LOCATION");
  
  if (status == PermissionStatusOhos.granted) {
    // 2. 只有位置权限也被授予,SSID 才不会返回 <unknown>
    final info = NetworkInfo();
    final ssid = await info.getWifiName();
    print('当前连接的 WiFi: $ssid');
  } else {
    // 🔔 告知用户:不授权位置权限,我们就没法帮你完成配网哦
  }
}

三、 高级应用场景:智能投屏子网校验

在构建局域网投屏功能时,第一步是判断手机与电视是否在同一个网段。

3.1 同子网判断逻辑

dart 复制代码
Future<bool> isSameSubnet(String deviceIp) async {
  final info = NetworkInfo();
  final myIp = await info.getWifiIP(); // e.g., 192.168.3.15
  if (myIp == null) return false;

  final myPrefix = myIp.substring(0, myIp.lastIndexOf('.'));
  final devicePrefix = deviceIp.substring(0, deviceIp.lastIndexOf('.'));
  
  return myPrefix == devicePrefix; // 判断 C 段是否一致
}

3.2 监听网络状态变更

配合 connectivity_plus 使用,每当用户切换 WiFi,自动刷新 NetworkInfo

dart 复制代码
Connectivity().onConnectivityChanged.listen((result) {
  if (result == ConnectivityResult.wifi) {
    refreshWifiDetails();
  }
});

四、 鸿蒙环境下的避坑指南 (FAQ)

4.1 为什么一直返回 <unknown ssid>null

自查清单

  1. 是否在真机运行?
    • 核心痛点:鸿蒙模拟器(Simulator)是通过宿主电脑网卡虚拟出来的网络,不具备真实的 WiFi 链路层属性。
    • 现象 :即便权限全部授予,getWifiName() 依然会返回 null
    • 对策 :开发此类功能必须使用真机。若需在模拟器演示,建议在代码中判断获取结果,若为空则填入"模拟数据"以保证 UI 完整性。
  2. 系统 GPS 开关开了吗? 即使给了应用权限,如果下拉控制中心的"位置信息"总开关是关的,SSID 依然被屏蔽。
  3. API 20 的特殊性:针对 API 20 以后的 SDK,建议显式设置请求频率,避免被系统判定为"恶意扫描"。

4.2 IPv6 地址解析

挑战getWifiIP 有时会返回一段长长的十六进制地址。
方案 :在业务逻辑中,对返回结果进行正则校验,优先过滤出符合 ^([0-9]{1,3}\.){3}[0-9]{1,3}$ 格式的 IPv4 地址。


五、 完整示例代码

以下代码演示了如何在鸿蒙应用中获取并展示当前连接的 WiFi 详细信息:

dart 复制代码
import 'package:flutter/material.dart';
import 'package:network_info_plus/network_info_plus.dart';
import 'package:permission_handler_ohos/permission_handler_ohos.dart';

class NetworkInfoPlusPage extends StatefulWidget {
  const NetworkInfoPlusPage({super.key});

  @override
  State<NetworkInfoPlusPage> createState() => _NetworkInfoPlusPageState();
}

class _NetworkInfoPlusPageState extends State<NetworkInfoPlusPage> {
  final NetworkInfo _networkInfo = NetworkInfo();
  String _wifiName = "正在获取...";
  String _wifiIP = "正在获取...";

  @override
  void initState() {
    super.initState();
    _loadNetworkInfo();
  }

  Future<void> _loadNetworkInfo() async {
    // 💡 专项适配:使用 permission_handler_ohos 申请鸿蒙原生权限
    final status = await PermissionHandlerOhos.requestPermission("ohos.permission.LOCATION");

    if (status == PermissionStatusOhos.granted) {
      final wifiName = await _networkInfo.getWifiName();
      final wifiIP = await _networkInfo.getWifiIP();

      setState(() {
        // 💡 模拟器适配:如果获取不到真实数据,则展示模拟数据供演示
        if (wifiName == null && wifiIP == null) {
          _wifiName = "Harmony_Guest_WiFi (模拟数据)";
          _wifiIP = "192.168.3.15";
        } else {
          _wifiName = wifiName ?? "未连接或 SSID 被隐藏";
          _wifiIP = wifiIP ?? "未知";
        }
      });
    } else {
      setState(() {
        _wifiName = "位置权限未授予 (${status.name})";
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('鸿蒙网络信息实战')),
      body: Center(
        child: Card(
          margin: const EdgeInsets.all(20),
          child: Padding(
            padding: const EdgeInsets.all(20),
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                const Icon(Icons.wifi, size: 60, color: Colors.blue),
                const SizedBox(height: 20),
                Text('当前 WiFi: $_wifiName', style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
                const SizedBox(height: 10),
                Text('本地 IP 地址: $_wifiIP'),
                const SizedBox(height: 20),
                ElevatedButton(
                  onPressed: _loadNetworkInfo,
                  child: const Text('刷新网络状态'),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

六、 总结

network_info_plus 虽然只是一个小小的插件,但它牵动着鸿蒙最核心的隐私机制。对于 IoT 和工具类应用开发者,处理好 "权限引导 -> 隐私合规 -> 数据解析" 这一闭环,是打造极致用户体验的基石。


🌐 欢迎加入开源鸿蒙跨平台社区开源鸿蒙跨平台开发者社区

相关推荐
funnycoffee1232 小时前
华为,华三交换机开启snmp的命令
服务器·华为·华为snmp·华三snmp
早點睡3902 小时前
基础入门 Flutter for OpenHarmony:AlertDialog 对话框组件详解
flutter·harmonyos
Geoking.2 小时前
SPI 与 API 的区别详解
网络
lbb 小魔仙2 小时前
【HarmonyOS】React Native实战项目+自定义Hooks开发指南
react native·华为·harmonyos
张雨zy2 小时前
HarmonyOS 鸿蒙网络层封装实践:构建稳健的HTTP请求客户端
http·华为·harmonyos
lbb 小魔仙2 小时前
【HarmonyOS】React Native实战项目+输入格式化掩码Hook
react native·华为·harmonyos
专注VB编程开发20年2 小时前
PLC协议:Modbus.Device(NModbus4)和手动 Socket.BeginConnect (APM异步编程模型)对比
网络·网络协议·tcp/ip·plc
哈__2 小时前
基础入门 Flutter for OpenHarmony:image_picker 图片选择详解
flutter
lbb 小魔仙2 小时前
【HarmonyOS】React Native实战项目+关键词高亮搜索Hook
react native·华为·harmonyos