Flutter Network Info Plus 鸿蒙适配要点总结

1 阅读4分钟

Flutter Network Info Plus 鸿蒙适配要点总结

概述

Network Info Plus 是一个流行的 Flutter 插件,用于获取设备的网络信息,包括 Wi-Fi 名称、BSSID、IP 地址等。本文将详细介绍该插件在鸿蒙(HarmonyOS)平台的适配实现,包括功能介绍、技术实现和注意事项。

创建ohos模块

flutter create . --org dev.fluttercommunity.plus --template=plugin --platforms=ohos

功能介绍

支持的网络信息获取功能

  1. Wi-Fi 名称 (SSID) - 获取当前连接的 Wi-Fi 网络名称
  2. Wi-Fi BSSID - 获取 Wi-Fi 基站的 MAC 地址
  3. IPv4 地址 - 获取设备的 IPv4 地址
  4. IPv6 地址 - 获取设备的 IPv6 地址
  5. 子网掩码 - 获取网络的子网掩码
  6. 网关 IP - 获取网络网关地址
  7. 广播 IP - 获取网络广播地址

API 接口

class NetworkInfo {
  Future<String?> getWifiName();      // 获取 Wi-Fi 名称
  Future<String?> getWifiBSSID();     // 获取 Wi-Fi BSSID
  Future<String?> getWifiIP();        // 获取 IPv4 地址
  Future<String?> getWifiIPv6();      // 获取 IPv6 地址
  Future<String?> getWifiSubmask();   // 获取子网掩码
  Future<String?> getWifiGatewayIP(); // 获取网关 IP
  Future<String?> getWifiBroadcast(); // 获取广播 IP
}

鸿蒙平台技术实现

1. 插件架构

鸿蒙端实现采用标准的 Flutter 插件架构:

export default class NetworkInfoPlusOhosPlugin implements FlutterPlugin, MethodCallHandler {
  private channel: MethodChannel | null = null;
  
  onAttachedToEngine(binding: FlutterPluginBinding): void {
    this.channel = new MethodChannel(binding.getBinaryMessenger(), 
      "dev.fluttercommunity.plus/network_info");
    this.channel.setMethodCallHandler(this);
  }
}

2. 核心 API 使用

Wi-Fi 信息获取

使用鸿蒙的 @kit.ConnectivityKit 中的 wifiManager

import { wifiManager } from '@kit.ConnectivityKit';

// 获取 Wi-Fi 名称
async getWifiName() {
  let wifiMessage = await wifiManager.getLinkedInfo().then(data => {
    return data.ssid;
  }).catch((error: number) => {
    console.info("get linked info error");
  });
  return wifiMessage;
}

// 获取 BSSID
async getWifiBSSID() {
  let wifiMessage = await wifiManager.getLinkedInfo().then(data => {
    if (data && data.bssid !== undefined && data.bssid !== null) {
      return data.bssid;
    }
    return null;
  });
  return wifiMessage;
}
IP 地址信息获取
// 获取 IPv4 地址
getWifiIPAddress() {
  const bytes = [
    wifiManager.getIpInfo().ipAddress >>> 24,
    (wifiManager.getIpInfo().ipAddress >>> 16) & 0xFF,
    (wifiManager.getIpInfo().ipAddress >>> 8) & 0xFF,
    wifiManager.getIpInfo().ipAddress & 0xFF
  ];
  return bytes.map(byte => byte.toString()).join('.');
}

// 获取 IPv6 地址
getIpV6() {
  let info = wifiManager.getIpv6Info().linkIpv6Address;
  return info === '' ? null : info;
}
网络计算功能
// IP 地址与整数转换
ipToInt(ip: string): number {
  let parts = ip.split('.');
  let num = 0;
  for (let i = 0; i < parts.length; i++) {
    num |= parseInt(parts[i], 10) << (24 - 8 * i);
  }
  return num;
}

// 计算广播地址
getBroadcastIP() {
  const ipInt = this.ipToInt(this.getWifiIPAddress() as string);
  const subnetMaskInt = this.ipToInt(this.getWifiSubnetMask() as string);
  const broadcastInt = (ipInt & subnetMaskInt) | (~subnetMaskInt);
  return this.intToIp(broadcastInt);
}

3. 方法调用处理

onMethodCall(call: MethodCall, result: MethodResult): void {
  switch (call.method) {
    case "wifiName":
      this.getWifiName().then(data => {
        result.success(data || null);
      });
      break;
    case "wifiBSSID":
      this.getWifiBSSID().then(data => {
        // 失败时返回默认 MAC 地址
        result.success(data || '02:00:00:00:00:00');
      });
      break;
    // ... 其他方法
  }
}

Flutter 端使用示例

基本使用

class _MyHomePageState extends State<MyHomePage> {
  final NetworkInfo _networkInfo = NetworkInfo();
  String _connectionStatus = 'Unknown';

  Future<void> _initNetworkInfo() async {
    String? wifiName, wifiBSSID, wifiIPv4;
    
    try {
      // 鸿蒙平台特殊处理
      if (Platform.operatingSystem == 'ohos') {
        wifiName = await _networkInfo.getWifiName();
        wifiBSSID = await _networkInfo.getWifiBSSID();
      }
      
      wifiIPv4 = await _networkInfo.getWifiIP();
      
      setState(() {
        _connectionStatus = 'Wifi Name: $wifiName\n'
            'Wifi BSSID: $wifiBSSID\n'
            'Wifi IPv4: $wifiIPv4\n';
      });
    } catch (e) {
      print('获取网络信息失败: $e');
    }
  }
}

权限处理

// 权限请求(主要针对 Android/iOS)
if (!kIsWeb && (Platform.isAndroid || Platform.isIOS || Platform.operatingSystem == 'ohos')) {
  final permissionStatus = await Permission.locationWhenInUse.request();
  
  if (permissionStatus.isGranted) {
    wifiBSSID = await _networkInfo.getWifiBSSID();
  } else {
    // 鸿蒙平台权限被拒绝时的特殊处理
    if (Platform.operatingSystem == 'ohos') {
      try {
        wifiBSSID = await _networkInfo.getWifiBSSID();
      } catch (e) {
        wifiBSSID = 'Unauthorized to get Wifi BSSID';
      }
    }
  }
}

权限配置

鸿蒙权限要求

module.json5 中添加必要权限:

{
  "requestPermissions": [
    {
      "name": "ohos.permission.GET_WIFI_INFO",
      "reason": "$string:wifi_info_reason",
      "usedScene": {
        "abilities": ["EntryAbility"],
        "when": "inuse"
      }
    },
    {
      "name": "ohos.permission.GET_NETWORK_INFO",
      "reason": "$string:network_info_reason",
      "usedScene": {
        "abilities": ["EntryAbility"],
        "when": "inuse"
      }
    },
    {
      "name": "ohos.permission.INTERNET",
      "reason": "$string:internet_reason",
      "usedScene": {
        "abilities": ["EntryAbility"],
        "when": "inuse"
      }
    }
  ]
}

权限说明文件

string.json 中添加权限说明:

{
  "string": [
    {
      "name": "wifi_info_reason",
      "value": "获取WiFi信息用于网络状态显示"
    },
    {
      "name": "network_info_reason",
      "value": "获取网络信息用于连接状态检测"
    },
    {
      "name": "internet_reason",
      "value": "访问网络用于数据传输"
    }
  ]
}

注意事项

1. 平台差异

  • 权限模型:鸿蒙的权限模型与 Android/iOS 有所不同,需要在 module.json5 中声明
  • API 差异:鸿蒙使用 wifiManager 而非 Android 的 WifiManager
  • 返回值处理:某些情况下鸿蒙 API 返回的数据格式可能与其他平台不同

2. 错误处理

// 建议的错误处理模式
try {
  let result = await wifiManager.getLinkedInfo();
  return result.ssid || null;
} catch (error) {
  console.error("获取WiFi信息失败:", error);
  return null;
}

3. 调试建议

  • 使用详细的日志输出来跟踪问题
  • 检查设备的 Wi-Fi 连接状态
  • 验证权限是否正确配置
  • 测试不同的网络环境

4. 性能考虑

  • 避免频繁调用网络信息 API
  • 考虑缓存机制减少重复请求
  • 异步处理避免阻塞 UI 线程

5. 兼容性

  • 确保在不同鸿蒙版本上的兼容性
  • 处理 API 不可用的情况
  • 提供合理的默认值和错误信息

最佳实践

1. 统一的错误处理

Future<String?> _safeGetNetworkInfo(Future<String?> Function() getter, String errorMsg) async {
  try {
    return await getter();
  } on PlatformException catch (e) {
    developer.log(errorMsg, error: e);
    return 'Failed to get network info';
  }
}

2. 平台特定逻辑

if (Platform.operatingSystem == 'ohos') {
  // 鸿蒙特定处理逻辑
} else if (Platform.isAndroid) {
  // Android 特定处理逻辑
}

3. 权限检查

Future<bool> _checkPermissions() async {
  if (Platform.operatingSystem == 'ohos') {
    // 鸿蒙权限检查逻辑
    return true; // 简化示例
  }
  return await Permission.locationWhenInUse.isGranted;
}

总结

Network Info Plus 的鸿蒙适配展示了如何将 Flutter 插件扩展到新平台的完整流程。通过合理的架构设计、详细的错误处理和平台特定的优化,可以为鸿蒙用户提供与其他平台一致的网络信息获取体验。

在实际开发中,建议:

  1. 充分测试各种网络环境
  2. 处理好权限和异常情况
  3. 保持与其他平台的 API 一致性
  4. 关注鸿蒙系统的更新和 API 变化

这种跨平台适配的经验也可以应用到其他 Flutter 插件的鸿蒙适配工作中。