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 Configuration和WiFi 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




