Flutter(Dart)环境下获取手机IPv4与IPv6地址的可行方案及权限问题咨询
我来帮你解决这几个困扰你的问题,都是基于实际开发中的经验总结:
如果你不想依赖第三方服务器,有两种可靠的原生方案:
1. 枚举所有网络接口获取地址
Dart的dart:io库自带NetworkInterface类,可以直接枚举设备上的所有活跃网络接口,从中筛选出非回环的IPv4/IPv6地址:
import 'dart:io'; Future<List<String>> getLocalIps() async { final List<String> ips = []; final interfaces = await NetworkInterface.list( includeLoopback: false, includeLinkLocal: true, type: InternetAddressType.any, ); for (var interface in interfaces) { for (var addr in interface.addresses) { // 排除链路本地地址(可选,根据需求调整) if (!addr.isLinkLocal) { ips.add(addr.address); } } } return ips; }
这个方法能获取到设备上所有可用的网络IP,但需要注意:如果设备同时连接了Wi-Fi和移动数据,会返回多个地址,你可能需要根据当前活跃网络进一步筛选。
2. 模拟Kotlin的DatagramSocket方式获取路由IP
虽然RawDatagramSocket没有connect方法,但可以用Socket发起一个到公共地址的连接(不需要成功建立,只要本地网络栈分配了出口IP),从而拿到当前用于对外通信的IP:
import 'dart:io'; Future<String?> getActiveIp() async { try { // 连接到公共DNS服务器(53端口是DNS服务端口) final socket = await Socket.connect( '8.8.8.8', 53, timeout: const Duration(seconds: 3) ); final activeIp = socket.address.address; socket.close(); return activeIp; } catch (e) { // 连接失败时,可以尝试IPv6地址 try { final socket = await Socket.connect( '2001:4860:4860::8888', 53, timeout: const Duration(seconds: 3) ); final activeIp = socket.address.address; socket.close(); return activeIp; } catch (ipv6Error) { print('Failed to get active IP: $ipv6Error'); return null; } } }
这个方法的优势是能直接拿到当前路由使用的出口IP,适合需要知道“设备对外访问时使用的IP”的场景,而且完全不需要第三方服务器返回数据,只是利用本地网络栈的连接逻辑。
ip route get命令权限被拒的问题 你遇到的权限问题是因为Android 10+对系统命令的执行权限做了严格限制,ip route属于netlink操作,普通APP即使声明了INTERNET、ACCESS_NETWORK_STATE等权限,也无法直接执行该命令。而ConnectBot能做到,大概率是因为它使用了Android原生网络API而非直接执行shell命令,或者在某些特殊场景下(比如root设备)获取了额外权限。
推荐的解决方案是:
通过Android原生API获取路由信息
在Flutter中通过MethodChannel调用Android原生代码,利用系统提供的ConnectivityManager获取当前网络的链路属性,从而拿到对应的IP地址,不需要执行任何shell命令,也不需要额外的危险权限。
第一步:Android原生端代码(Kotlin)
class MainActivity : FlutterActivity() { private val CHANNEL = "com.example.ip_channel" override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result -> if (call.method == "getCurrentRouteIp") { val ip = getCurrentRouteIp() if (ip != null) { result.success(ip) } else { result.error("NO_IP", "No active network IP found", null) } } else { result.notImplemented() } } } private fun getCurrentRouteIp(): String? { val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager val activeNetwork = connectivityManager.activeNetwork ?: return null val linkProperties = connectivityManager.getLinkProperties(activeNetwork) ?: return null // 遍历所有链路地址,返回第一个非回环的IP linkProperties.linkAddresses.forEach { address -> val ipAddress = address.address if (!ipAddress.isLoopbackAddress) { return ipAddress.hostAddress } } return null } }
第二步:Flutter端调用代码
import 'package:flutter/services.dart'; class IpUtils { static const MethodChannel _channel = MethodChannel('com.example.ip_channel'); static Future<String?> getCurrentRouteIp() async { try { final String? ip = await _channel.invokeMethod('getCurrentRouteIp'); return ip; } on PlatformException catch (e) { print('Error getting route IP: ${e.message}'); return null; } } }
这种方案完全符合Android的权限规范,只需要在AndroidManifest.xml中保留ACCESS_NETWORK_STATE权限即可,不需要额外申请危险权限,也能在私有网络环境下正常工作。
内容的提问来源于stack exchange,提问作者Vsevolod

