fultter wifi_scan三方库鸿蒙版本部署使用效果展示
基础环境
Flutter 社区地址: https://atomgit.com/CPF-Flutter/flutter_flutter
三方库地址:https://atomgit.com/oh-flutter/wifi_scan
适配后地址:https://atomgit.com/oh-flutter/wifi_scan
鸿蒙版本:7.0

演示GIF

演示的鸿蒙系统版本

库的概述
wifi_scan 原本是 flutternetwork/WiFiFlutter 提供的 Flutter WiFi 扫描插件,官方版本支持 Android、iOS 两个平台。通过社区的努力,现阶段已经支持鸿蒙方向。
主要功能
用于扫描附近可见 WiFi 接入点的 Flutter 插件。
WiFi 扫描
通过 WiFiScan.instance.startScan() 一行调用,由鸿蒙原生 wifiManager.startScan() 实现。扫描完成后可通过 getScannedResults() 获取扫描结果。
扫描结果监听
通过 onScannedResultsAvailable 流监听扫描结果变化,当新的扫描结果可用时自动推送更新。
权限检查
提供 canStartScan() 和 canGetScannedResults() 方法检查是否可以执行扫描操作,支持自动请求权限。
API 说明
| API | 描述 | 参数 | 返回值 | OpenHarmony支持 |
|---|---|---|---|---|
canStartScan() | 检查是否可以启动扫描 | askPermissions: bool | Future<CanStartScan> | 是 |
startScan() | 启动WiFi扫描 | 无 | Future<bool> | 是 |
canGetScannedResults() | 检查是否可以获取扫描结果 | askPermissions: bool | Future<CanGetScannedResults> | 是 |
getScannedResults() | 获取扫描结果 | 无 | Future<List<WiFiAccessPoint>> | 是 |
onScannedResultsAvailable | 扫描结果可用时的流 | 无 | Stream<List<WiFiAccessPoint>> | 是 |
核心代码
下面挑四个关键的代码块来说明。
Dart层入口
这一段在 lib/wifi_scan.dart 里。
class WiFiScan {
static const MethodChannel _channel = MethodChannel('wifi_scan');
static const EventChannel _eventChannel = EventChannel('wifi_scan/onScannedResultsAvailable');
static WiFiScan? _instance;
static WiFiScan get instance => _instance ??= WiFiScan._();
Future<CanStartScan> canStartScan({bool askPermissions = false}) async {
final int code = await _channel.invokeMethod('canStartScan', <String, dynamic>{
'askPermissions': askPermissions,
});
return CanStartScan.values[code];
}
Future<bool> startScan() async {
return await _channel.invokeMethod('startScan');
}
Future<CanGetScannedResults> canGetScannedResults({bool askPermissions = false}) async {
final int code = await _channel.invokeMethod('canGetScannedResults', <String, dynamic>{
'askPermissions': askPermissions,
});
return CanGetScannedResults.values[code];
}
Future<List<WiFiAccessPoint>> getScannedResults() async {
final List<dynamic> results = await _channel.invokeMethod('getScannedResults');
return results.map((e) => WiFiAccessPoint.fromMap(e)).toList();
}
Stream<List<WiFiAccessPoint>> get onScannedResultsAvailable {
return _eventChannel.receiveBroadcastStream().map((event) {
final List<dynamic> results = event;
return results.map((e) => WiFiAccessPoint.fromMap(e)).toList();
});
}
}
MethodChannel 的通道名固定为 wifi_scan,EventChannel 为 wifi_scan/onScannedResultsAvailable,和各平台原生端保持一致。这一层是纯 Dart 代码,跨平台通用。
鸿蒙端 MethodCallHandlerImpl
文件在 ohos/src/main/ets/components/plugin/WifiScanPlugin.ets。
import {
FlutterPlugin,
FlutterPluginBinding,
MethodCall,
MethodCallHandler,
MethodChannel,
EventChannel,
EventSink,
StreamHandler,
} from '@ohos/flutter_ohos';
import { wifiManager } from '@kit.ConnectivityKit';
import { abilityAccessCtrl, bundleManager, Permissions } from '@kit.AbilityKit';
export default class WifiScanPlugin implements FlutterPlugin, MethodCallHandler, StreamHandler {
private static readonly CHANNEL_NAME: string = "wifi_scan";
private static readonly EVENT_CHANNEL_NAME: string = "wifi_scan/onScannedResultsAvailable";
private methodChannel: MethodChannel | null = null;
private eventChannel: EventChannel | null = null;
private eventSink: EventSink | null = null;
private scanStateCallback: ((value: number) => void) | null = null;
onAttachedToEngine(binding: FlutterPluginBinding): void {
this.methodChannel = new MethodChannel(binding.getBinaryMessenger(), WifiScanPlugin.CHANNEL_NAME);
this.methodChannel.setMethodCallHandler(this);
this.eventChannel = new EventChannel(binding.getBinaryMessenger(), WifiScanPlugin.EVENT_CHANNEL_NAME);
this.eventChannel.setStreamHandler(this);
}
onDetachedFromEngine(binding: FlutterPluginBinding): void {
this.unregisterScanListener();
if (this.eventSink !== null) {
this.eventSink.endOfStream();
}
if (this.methodChannel !== null) {
this.methodChannel.setMethodCallHandler(null);
}
this.methodChannel = null;
if (this.eventChannel !== null) {
this.eventChannel.setStreamHandler(null);
}
this.eventChannel = null;
this.eventSink = null;
}
onMethodCall(call: MethodCall, result: MethodResult): void {
switch (call.method) {
case "canStartScan":
this.handleCanStartScan(call, result);
break;
case "startScan":
this.handleStartScan(result);
break;
case "canGetScannedResults":
this.handleCanGetScannedResults(call, result);
break;
case "getScannedResults":
this.handleGetScannedResults(result);
break;
default:
result.notImplemented();
break;
}
}
onListen(args: Object, events: EventSink): void {
this.eventSink = events;
this.registerScanListener();
this.pushScannedResults();
}
onCancel(args: Object): void {
this.unregisterScanListener();
if (this.eventSink !== null) {
this.eventSink.endOfStream();
}
this.eventSink = null;
}
}
这是鸿蒙适配的核心,实现了鸿蒙的 FlutterPlugin 接口。onAttachedToEngine 时创建 MethodChannel 和 EventChannel,onDetachedFromEngine 时解除注册,避免内存泄漏。
鸿蒙端扫描实现
文件在 ohos/src/main/ets/components/plugin/WifiScanPlugin.ets。
private handleStartScan(result: MethodResult): void {
try {
if (!this.hasPermission('ohos.permission.SET_WIFI_INFO' as Permissions)) {
result.error("WifiScanPlugin.Security", "SET_WIFI_INFO permission is not granted", null);
return;
}
if (!wifiManager.isWifiActive()) {
result.success(false);
return;
}
wifiManager.startScan();
result.success(true);
} catch (error) {
const err = error as BusinessError;
if (err.code === 201) {
result.error("WifiScanPlugin.Security", err.message, null);
} else {
result.success(false);
}
}
}
private handleGetScannedResults(result: MethodResult): void {
try {
if (!this.hasPermission('ohos.permission.GET_WIFI_INFO' as Permissions)) {
result.error("WifiScanPlugin.Security", "GET_WIFI_INFO permission is not granted", null);
return;
}
result.success(this.buildScannedResults());
} catch (error) {
const err = error as BusinessError;
if (err.code === 201) {
result.error("WifiScanPlugin.Security", err.message, null);
} else {
result.error("WifiScanPlugin", err.message, null);
}
}
}
private buildScannedResults(): Object[] {
const result: Object[] = [];
try {
if (!this.hasPermission('ohos.permission.GET_WIFI_INFO' as Permissions)) {
return result;
}
const scanInfos: wifiManager.WifiScanInfo[] = wifiManager.getScanInfoList();
for (let i = 0; i < scanInfos.length; i++) {
const info: wifiManager.WifiScanInfo = scanInfos[i];
result.push(this.toWireMap(info));
}
} catch (error) {
// WLAN off / scan cache empty => empty list, mirroring Android scanResults
}
return result;
}
private toWireMap(info: wifiManager.WifiScanInfo): Map<string, Object | null> {
const map = new Map<string, Object | null>();
map.set('ssid', info.ssid);
map.set('bssid', info.bssid);
map.set('capabilities', info.capabilities);
map.set('frequency', info.frequency);
map.set('level', info.rssi);
map.set('timestamp', info.timestamp);
map.set('standard', null);
map.set('centerFrequency0', info.centerFrequency0);
map.set('centerFrequency1', info.centerFrequency1);
map.set('channelWidth', this.toChannelWidthCode(info.channelWidth));
map.set('isPasspoint', null);
map.set('operatorFriendlyName', null);
map.set('venueName', null);
map.set('is80211mcResponder', null);
return map;
}
把 Dart 发来的 startScan 和 getScannedResults 方法映射到鸿蒙 ArkUI 的 wifiManager API。扫描结果通过 getScanInfoList() 获取,然后转换成 Dart 端能识别的 Map 格式。
权限检查实现
文件在 ohos/src/main/ets/components/plugin/WifiScanPlugin.ets。
private computeCanCode(): number {
try {
if (!this.isStaSupported()) {
return 0;
}
} catch (error) {
return 0;
}
if (!this.hasPermission('ohos.permission.GET_WIFI_INFO' as Permissions)
|| !this.hasPermission('ohos.permission.SET_WIFI_INFO' as Permissions)) {
return 2;
}
return 1;
}
private isStaSupported(): boolean {
return wifiManager.isFeatureSupported(0x0001);
}
private hasPermission(permission: Permissions): boolean {
try {
const atManager = abilityAccessCtrl.createAtManager();
const bundleInfo = bundleManager.getBundleInfoForSelfSync(
bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION);
const tokenId: number = bundleInfo.appInfo.accessTokenId;
const status = atManager.verifyAccessTokenSync(tokenId, permission);
return status === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED;
} catch (error) {
return false;
}
}
Can-codes 在 HarmonyOS 上的映射规则:
- 0 (notSupported):STA/scan 能力不支持或 WLAN 服务缺失
- 1 (yes):GET/SET_WIFI_INFO 是 system_grant 权限,安装时已授予
- 2 (noLocationPermissionRequired):缺少 WiFi 权限时返回
使用示例
引入依赖的时候鸿蒙必须用 git 分支,不能直接写版本号。
dependencies:
flutter:
sdk: flutter
wifi_scan:
git:
url: "https://atomgit.com/oh-flutter/wifi_scan.git"
ref: "0.4.1+2-ohos-1.0.0-beta.1"
在鸿蒙工程的 module.json5 中需要声明以下权限:
{
"requestPermissions": [
{
"name": "ohos.permission.GET_WIFI_INFO"
},
{
"name": "ohos.permission.SET_WIFI_INFO"
}
]
}
基础 WiFi 扫描调用示例:
import 'package:wifi_scan/wifi_scan.dart';
void _startScan() async {
// 检查平台扫描支持情况
final can = await WiFiScan.instance.canStartScan(askPermissions: true);
switch(can) {
case CanStartScan.yes:
// 启动扫描
final isScanning = await WiFiScan.instance.startScan();
if (isScanning) {
print('扫描已启动');
}
break;
case CanStartScan.noLocationPermissionRequired:
print('需要位置权限');
break;
case CanStartScan.notSupported:
print('不支持扫描');
break;
}
}
void _getScannedResults() async {
final can = await WiFiScan.instance.canGetScannedResults(askPermissions: true);
switch(can) {
case CanGetScannedResults.yes:
final accessPoints = await WiFiScan.instance.getScannedResults();
for (var ap in accessPoints) {
print('SSID: ${ap.ssid}, BSSID: ${ap.bssid}, Level: ${ap.level}');
}
break;
// ... 处理其他情况
}
}
监听扫描结果变化:
List<WiFiAccessPoint> accessPoints = [];
StreamSubscription<List<WiFiAccessPoint>>? subscription;
void _startListening() {
subscription = WiFiScan.instance.onScannedResultsAvailable.listen((results) {
setState(() {
accessPoints = results;
});
});
}
void dispose() {
subscription?.cancel();
super.dispose();
}
WiFiAccessPoint 数据结构
| 字段名 | 类型 | 描述 | OpenHarmony支持 |
|---|---|---|---|
ssid | String | WiFi网络的SSID(网络名称) | 是 |
bssid | String | WiFi接入点的BSSID(MAC地址) | 是(注意:无GET_WIFI_PEERS_MAC权限时可能为随机值) |
capabilities | String | 网络的安全能力描述 | 是 |
frequency | int | 频率(MHz) | 是 |
level | int | 信号强度(dBm) | 是 |
timestamp | int | 扫描时间戳(微秒) | 是 |
standard | int? | WiFi标准(802.11a/b/g/n/ac/ax等) | 否(返回null) |
centerFrequency0 | int? | 中心频率0(用于80+80/160MHz) | 是 |
centerFrequency1 | int? | 中心频率1(用于80+80/160MHz) | 是 |
channelWidth | int? | 信道宽度(20/40/80/160MHz) | 是 |
isPasspoint | bool? | 是否为Passpoint网络 | 否(返回null) |
operatorFriendlyName | String? | 运营商友好名称 | 否(返回null) |
venueName | String? | 场地名称 | 否(返回null) |
is80211mcResponder | bool? | 是否支持802.11mc RTT响应 | 否(返回null) |
CanStartScan 枚举值
| 值 | 描述 | OpenHarmony映射 |
|---|---|---|
yes | 可以启动扫描 | 已授予WiFi权限且STA能力支持 |
noLocationPermissionRequired | 需要位置权限 | 缺少WiFi权限(system_grant权限未授予) |
notSupported | 不支持扫描 | STA能力不支持或WLAN服务缺失 |
CanGetScannedResults 枚举值
| 值 | 描述 | OpenHarmony映射 |
|---|---|---|
yes | 可以获取扫描结果 | 已授予WiFi权限且STA能力支持 |
noLocationPermissionRequired | 需要位置权限 | 缺少WiFi权限(system_grant权限未授予) |
notSupported | 不支持获取扫描结果 | STA能力不支持或WLAN服务缺失 |
使用说明

启动扫描
调用 startScan() 触发完整的 WiFi 扫描。如果扫描成功启动,该方法返回 true。
获取扫描结果
调用 getScannedResults() 获取最新可用的扫描结果。返回一个 WiFiAccessPoint 对象列表,包含 SSID、BSSID、信号强度、频率等信息。
扫描结果可用事件
当新的扫描结果可用时,onScannedResultsAvailable 流会发出新的数据。
OpenHarmony 平台差异说明
- 如果没有受限制的
ohos.permission.GET_WIFI_PEERS_MAC权限,系统会返回随机化的 BSSID。 WifiScanInfo不包含 Passpoint、运营商、场地、802.11mc 字段,因此对应字段返回null。GET_WIFI_INFO和SET_WIFI_INFO为system_grant权限,在安装时授予,无需运行时弹窗授权。
新增特性
- 新增 OpenHarmony(ohos)平台支持,基于
@ohos.wifiManager实现,与 Android/iOS 平台接口行为保持一致。
更多推荐




所有评论(0)