Flutter应用连接IoT扬声器WiFi后无法发送GET/POST请求求助
问题:Flutter应用内连接IoT扬声器WiFi后无法发送GET/POST请求
我正在开发一款Flutter应用,用于连接带WiFi功能的IoT扬声器设备,需通过GET/POST API与设备通信并上传音频文件。通过安卓系统自带WiFi选项连接扬声器WiFi时,GET/POST请求(包括音频上传)均正常;但使用wiflutter包在应用内连接该WiFi后,完全无法发送任何GET/POST请求。
尝试过的WiFi相关包
package:wiflutter/wiflutter.dart:可成功连接WiFi,但连接后无法发送GET/POST请求package:wifi_scan/wifi_scan.dart:仅支持WiFi扫描,无连接功能package:wifi/wifi.dart:已过时,不支持空安全package:wifi_connector/wifi_connector.dart:依赖过时的Kotlin插件版本,无法解决依赖问题
WiFi连接代码
import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:nspeak/RecordingPage.dart'; import 'package:wifi_scan/wifi_scan.dart'; import 'package:wiflutter/wiflutter.dart'; import 'package:wiflutter/enums/enterprise_certificate_enum.dart'; // import 'package:wifi_iot/wifi_iot.dart'; -- 不支持空安全,无法使用 // import 'package:wifi_connector/wifi_connector.dart'; -- 使用旧版Kotlin插件,无法使用 // import 'package:wifi_configuration/wifi_configuration.dart'; -- 不支持空安全,无法使用 // import 'package:wifi/wifi.dart'; -- 不支持空安全,无法使用 scanNspeakwifi() async { resultcanstarscan = await WiFiScan.instance.canStartScan(); if (resultcanstarscan == CanStartScan.yes) { scansuccess = await WiFiScan.instance.startScan(); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( padding: EdgeInsets.only(bottom: 25), duration: Durations.extralong4, backgroundColor: Colors.yellow, content: Center( child: Text('扫描中', style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold, color: Colors.black)), ), ), ); } } else { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( padding: EdgeInsets.only(bottom: 25, left: 30, right: 30), duration: Durations.extralong4, backgroundColor: Colors.red, content: Center( child: Text('无法启动扫描,请检查是否已授予所有必要权限!', style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold)), ), ), ); } } cangetresults = await WiFiScan.instance.canGetScannedResults(); if (cangetresults == CanGetScannedResults.yes) { scanresults = await WiFiScan.instance.getScannedResults(); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( padding: EdgeInsets.only(bottom: 25, left: 20, right: 20), duration: Durations.extralong4, backgroundColor: Colors.green, content: Center( child: Text('扫描完成!点击「获取结果」查看附近的NSpeak设备', style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold)), ), ), ); } } else { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( padding: EdgeInsets.only(bottom: 25, left: 20, right: 20), duration: Durations.extralong4, backgroundColor: Colors.red, content: Center( child: Text('无扫描结果,请检查是否已授予所有必要权限!', style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold)), ), ), ); } } } getNspeakwifi() async { var resultsavail = await WiFiScan.instance.onScannedResultsAvailable; var checkresult = await resultsavail.isEmpty; if (!checkresult) { setState(() { wifiNetworks = scanresults; _shownetworks = true; }); } else { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( padding: EdgeInsets.only(bottom: 25, left: 20, right: 20), duration: Durations.extralong4, backgroundColor: Colors.red, content: Center( child: Text('附近无NSpeak设备!', style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold)), ), ), ); } } } Widget displayNspeak() { var value; nspeakdevices = []; /*每次点击「获取结果」都重新初始化列表,避免重复添加WiFi热点*/ for (int i = 0; i < wifiNetworks.length;) { var entry = wifiNetworks[i]; if (entry.ssid.contains("NSPEAK")) { value = entry.ssid; print("已加入列表"); nspeakdevices.add(value); } i++; } if (nspeakdevices.isNotEmpty) { return ListView.builder( itemCount: nspeakdevices.length, itemBuilder: (BuildContext context, int index) { var nspeakvalue = nspeakdevices[index]; return Row( children: [ Expanded(child: ListTile(title: Text(nspeakvalue))), TextButton( onPressed: () async { var con = await wiflutter.connect( ssid: nspeakvalue, password: '********', ); }, child: Text("连接", style: TextStyle(fontSize: 10)), ), TextButton( onPressed: () async { var con = await wiflutter.disconnect(); }, child: Text("断开", style: TextStyle(fontSize: 10)), ), ], ); }, ); } else { return const Text("附近无NSpeak设备"); } }
音频文件上传代码
fileupload() async { try { var headersget = {'Authorization': 'Authorization text'}; requestget = http.Request('GET', Uri.parse('http://192.168.4.1/getlogin')); requestget.headers.addAll(headersget); http.StreamedResponse responseget = await requestget.send(); if (responseget.statusCode == 200) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( padding: EdgeInsets.only(bottom: 50, left: 50), duration: Durations.extralong4, backgroundColor: Colors.white, content: Text('登录成功!', style: TextStyle(fontSize: 15, color: Colors.black, fontWeight: FontWeight.bold)), ), ); } else { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( padding: EdgeInsets.only(bottom: 50, left: 50), duration: Durations.extralong4, content: Text('登录失败', style: TextStyle(fontSize: 15, color: Colors.black, fontWeight: FontWeight.bold)), ), ); } var headerspost = {'Content-Type': 'multipart/form-data;', 'Authorization': 'authorization text'}; requestpost = http.MultipartRequest('POST', Uri.parse('http://192.168.4.1/upload')); requestpost.files.add(await http.MultipartFile.fromPath('mp3', audiofilepath)); print("文件添加成功"); requestpost.headers.addAll(headerspost); print("请求头添加成功"); http.StreamedResponse responsepost = await requestpost.send(); print("POST请求已发送"); if (responsepost.statusCode == 200 || responsepost.statusCode == 302) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( padding: EdgeInsets.only(bottom: 50, left: 50), duration: Durations.extralong4, backgroundColor: Colors.white, content: Text('文件上传成功!', style: TextStyle(fontSize: 15, color: Colors.black, fontWeight: FontWeight.bold)), ), ); } else { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( padding: EdgeInsets.only(bottom: 50, left: 50), duration: Durations.extralong4, content: Text('文件上传失败', style: TextStyle(fontSize: 15, color: Colors.black, fontWeight: FontWeight.bold)), ), ); } } catch (e) { print('音频上传错误: $e'); } }
解决方案
1. 检查并补全权限
Android 10+版本下,应用内连接WiFi后需额外权限才能访问本地网络,在AndroidManifest.xml中添加:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <uses-permission android:name="android.permission.INTERNET" /> <!-- Android 10+ 扫描WiFi必须的位置权限 --> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
同时确保应用已动态申请这些权限。
2. 验证连接有效性
wiflutter的connect方法返回成功不代表网络已稳定,添加连接后校验:
var con = await wiflutter.connect(ssid: nspeakvalue, password: '********'); // 检查当前连接的SSID是否匹配 var currentSSID = await wiflutter.getCurrentWifiSSID(); if (currentSSID == nspeakvalue) { // 等待网络稳定 await Future.delayed(const Duration(seconds: 2)); // ping设备测试连通性 try { final result = await InternetAddress.lookup('192.168.4.1'); if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) { print('设备可连通'); } } on SocketException catch (_) { print('无法连通设备'); } }
3. 允许明文HTTP请求
如果IoT设备使用未加密的HTTP,Android 9+默认会拦截,需配置网络安全规则:
- 在
android/app/src/main/res/xml创建network_security_config.xml:
<?xml version="1.0" encoding="utf-8"?> <network-security-config> <domain-config cleartextTrafficPermitted="true"> <domain includeSubdomains="true">192.168.4.1</domain> </domain-config> </network-security-config>
- 在
AndroidManifest.xml的application标签中引用:
<application ... android:networkSecurityConfig="@xml/network_security_config">
4. 替换WiFi连接包
若第三方包问题无法解决,可直接编写Android原生Kotlin代码实现WiFi连接,通过MethodChannel与Flutter通信,精准控制连接流程。
内容的提问来源于stack exchange,提问作者Sanath balthar
相关产品推荐
相关产品推荐

