You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Flutter iOS端如何通过编程方式断开当前WiFi并连接到指定SSID的网络

Flutter iOS端如何通过编程方式断开当前WiFi并连接到指定SSID的网络

我来给你详细讲讲在Flutter的iOS端,怎么通过代码实现断开当前WiFi、连接指定SSID的需求吧,毕竟这类网络操作在IoT或者特定场景APP里太常见了~

一、先把必要的权限配置搞定

iOS对WiFi相关的操作权限卡得很严,得先把这些配置好,不然代码写了也白搭:

  • 位置权限:要获取当前连接的SSID,必须申请位置权限。你得在iOS项目的Info.plist里添加权限描述,告诉用户为什么需要这个权限:
    <!-- 前台使用位置权限(获取当前SSID必备) -->
    <key>NSLocationWhenInUseUsageDescription</key>
    <string>需要访问位置信息以获取当前WiFi状态,方便连接指定网络</string>
    <!-- 如果你的APP需要在后台也操作WiFi,就再加这个 -->
    <key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
    <string>需要在后台访问位置信息以管理WiFi连接状态</string>
    
  • 开发者后台与Xcode权限:还得在苹果开发者后台给你的App ID开启Hotspot ConfigurationWiFi Information权限,然后在Xcode的Signing & Capabilities面板里添加Hotspot Configuration能力,不然调用NEHotspotConfigurationManager会直接报错。

二、原生Swift代码实现WiFi的连接与断开

iOS原生提供的NEHotspotConfigurationManager就是干这个的,我们写个单例类封装所有操作:

import NetworkExtension
import SystemConfiguration.CaptiveNetwork

class WiFiManager: NSObject {
    static let shared = WiFiManager()
    
    // 连接指定SSID的WiFi网络
    func connectToWiFi(ssid: String, password: String, completion: @escaping (Error?) -> Void) {
        // 构造WiFi配置,isWEP设为false(一般都是WPA/WPA2)
        let configuration = NEHotspotConfiguration(ssid: ssid, passphrase: password, isWEP: false)
        // joinOnce设为false的话,APP退出后WiFi会保持连接;设为true则APP退出就断开
        configuration.joinOnce = false
        
        NEHotspotConfigurationManager.shared.apply(configuration) { error in
            completion(error)
        }
    }
    
    // 断开WiFi:可以指定SSID,也可以断开所有由当前APP配置的WiFi
    func disconnectWiFi(ssid: String? = nil, completion: @escaping (Error?) -> Void) {
        if let targetSSID = ssid {
            // 断开指定SSID的连接(仅限当前APP配置的)
            NEHotspotConfigurationManager.shared.removeConfiguration(forSSID: targetSSID) { error in
                completion(error)
            }
        } else {
            // 断开所有由当前APP配置的WiFi连接
            NEHotspotConfigurationManager.shared.removeAllConfigurations { error in
                completion(error)
            }
        }
    }
    
    // 获取当前连接的SSID(必须位置权限已授权才能拿到)
    func getCurrentSSID() -> String? {
        guard let interfaces = CNCopySupportedInterfaces() as? [String] else { return nil }
        for interface in interfaces {
            guard let networkInfo = CNCopyCurrentNetworkInfo(interface as CFString) as? [String: AnyObject] else { continue }
            return networkInfo[kCNNetworkInfoKeySSID as String] as? String
        }
        return nil
    }
}

这里要划重点:iOS不允许APP断开非当前APP配置的WiFi,只能断开由你的APP通过NEHotspotConfigurationManager连接的WiFi。如果用户当前连的是其他APP或者手动连的WiFi,你没法主动断开,只能通过连接新的SSID让系统自动切换。

三、Flutter端通过MethodChannel调用原生代码

Flutter没法直接操作iOS的WiFi API,所以得用MethodChannel和原生通信:

Flutter端代码

import 'package:flutter/services.dart';

class WiFiConnector {
  // 定义MethodChannel的名称,要和原生端保持一致
  static const MethodChannel _channel = MethodChannel('com.yourapp.wifi_connector');

  // 连接指定SSID的WiFi
  static Future<void> connectToWiFi(String ssid, String password) async {
    try {
      await _channel.invokeMethod(
        'connectToWiFi',
        {'ssid': ssid, 'password': password},
      );
    } on PlatformException catch (e) {
      throw Exception('WiFi连接失败: ${e.message}');
    }
  }

  // 断开WiFi:可选指定SSID,不传则断开所有APP配置的WiFi
  static Future<void> disconnectWiFi([String? ssid]) async {
    try {
      await _channel.invokeMethod(
        'disconnectWiFi',
        {'ssid': ssid},
      );
    } on PlatformException catch (e) {
      throw Exception('WiFi断开失败: ${e.message}');
    }
  }

  // 获取当前连接的SSID
  static Future<String?> getCurrentSSID() async {
    try {
      return await _channel.invokeMethod('getCurrentSSID');
    } on PlatformException catch (e) {
      throw Exception('获取当前SSID失败: ${e.message}');
    }
  }
}

iOS原生端注册MethodChannel

AppDelegate.swift里注册通道,处理Flutter的调用:

import UIKit
import Flutter

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    guard let controller = window?.rootViewController as? FlutterViewController else {
      fatalError("RootViewController is not a FlutterViewController")
    }
    
    // 注册MethodChannel,名称要和Flutter端一致
    let wifiChannel = FlutterMethodChannel(
      name: "com.yourapp.wifi_connector",
      binaryMessenger: controller.binaryMessenger
    )
    
    // 处理Flutter发来的方法调用
    wifiChannel.setMethodCallHandler { [weak self] call, result in
      guard let self = self else { return }
      switch call.method {
      case "connectToWiFi":
        guard let args = call.arguments as? [String: String],
              let ssid = args["ssid"],
              let password = args["password"] else {
          result(FlutterError(
            code: "INVALID_ARGS",
            message: "参数错误,请传入正确的SSID和密码",
            details: nil
          ))
          return
        }
        WiFiManager.shared.connectToWiFi(ssid: ssid, password: password) { error in
          if let error = error {
            result(FlutterError(
              code: "CONNECT_FAILED",
              message: error.localizedDescription,
              details: nil
            ))
          } else {
            result(nil)
          }
        }
      case "disconnectWiFi":
        let args = call.arguments as? [String: String]
        let targetSSID = args?["ssid"]
        WiFiManager.shared.disconnectWiFi(ssid: targetSSID) { error in
          if let error = error {
            result(FlutterError(
              code: "DISCONNECT_FAILED",
              message: error.localizedDescription,
              details: nil
            ))
          } else {
            result(nil)
          }
        }
      case "getCurrentSSID":
        let currentSSID = WiFiManager.shared.getCurrentSSID()
        result(currentSSID)
      default:
        // 处理未实现的方法
        result(FlutterMethodNotImplemented)
      }
    }
    
    GeneratedPluginRegistrant.register(with: self)
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

四、几个容易踩坑的注意事项

  • iOS版本限制NEHotspotConfigurationManager是从iOS 11开始支持的,如果你的APP要兼容iOS 10及以下,得做版本判断,跳过这部分逻辑。
  • 非APP配置的WiFi无法断开:iOS系统限制,APP只能断开自己通过NEHotspotConfigurationManager连接的WiFi,没法断开用户手动连的或者其他APP连的,这点一定要注意,别白费功夫。
  • SSID前缀匹配的问题:如果你是要匹配SSID前缀(比如开头是"MY_WIFI_"的),iOS 13以后没法直接扫描所有可用SSID了,只能获取当前连接的SSID。如果必须扫描,得申请Access WiFi Information的特殊权限,这个得向苹果提交申请,一般只有特定场景(比如企业级APP)才能通过。

内容来源于stack exchange

火山引擎 最新活动