import 'package:flutter/material.dart';
import 'package:wifi_scan/wifi_scan.dart';
import 'package:wifi_iot/wifi_iot.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:provider/provider.dart';
import 'package:network_info_plus/network_info_plus.dart'; // 新增:用于获取当前连接WiFi信息
import '../../../core/localization/app_localizations.dart';
import '../models/wifi.dart';
import '../viewmodels/WiFiListScreen_viewmodel.dart';
import '../../../core/network/tcp_client.dart';
import '../widgets/device_connection_dialog.dart';
class WiFiListScreen extends StatefulWidget {
const WiFiListScreen({super.key});
@override
WiFiListScreenState createState() => WiFiListScreenState();
}
class WiFiListScreenState extends State<WiFiListScreen> {
List<WiFiAccessPoint> accessPoints = [];
bool isScanning = false;
late WiFiViewModel viewModel;
final NetworkInfo _networkInfo = NetworkInfo(); // 新增:网络信息实例
// 已保存的网络数据
List<Map<String, String>> savedNetworks = [];
// 动态的可用网络列表,将从扫描结果更新
List<Map<String, String>> availableNetworks = [];
// 创建一个Map,用于存储每个SSID对应的最强信号接入点
Map<String, Map<String, String>> uniqueNetworksMap = {};
String? _currentConnectedSSID; // 新增:当前连接的WiFi SSID
String _connectionStatus = '未连接'; // 新增:连接状态文本
@override
void initState() {
super.initState();
_checkPermissionsAndGetCurrentWiFi(); // 修改:先获取当前连接状态
}
// 新增:检查权限并获取当前连接状态
Future<void> _checkPermissionsAndGetCurrentWiFi() async {
var status = await Permission.location.request();
if (status.isGranted) {
await _getCurrentConnectedWiFi(); // 先获取当前连接状态
_loadSavedNetworks();
_startScan();
} else {
print('Location permission denied');
}
}
// 新增:获取当前设备连接的WiFi信息
Future<void> _getCurrentConnectedWiFi() async {
try {
// 获取当前连接的WiFi名称
String? wifiName = await _networkInfo.getWifiName();
String? bssid = await _networkInfo.getWifiBSSID();
String? ipAddress = await _networkInfo.getWifiIP();
String? subnetMask = await _networkInfo.getWifiSubmask();
String? gateway = await _networkInfo.getWifiGatewayIP();
if (wifiName != null && wifiName.isNotEmpty && wifiName != 'null') {
// 在Android上,WiFi名称可能带有双引号,需要处理
if (wifiName.startsWith('"') && wifiName.endsWith('"')) {
wifiName = wifiName.substring(1, wifiName.length - 1);
}
setState(() {
_currentConnectedSSID = wifiName;
_connectionStatus = '已连接到 $wifiName';
});
} else {
setState(() {
_currentConnectedSSID = null;
_connectionStatus = '未连接';
});
}
} catch (e) {
setState(() {
_currentConnectedSSID = null;
_connectionStatus = '未连接';
});
}
}
Future<void> _loadSavedNetworks() async {
final vm = context.read<WiFiViewModel>();
viewModel = vm;
final wifiList = await vm.getWiFi();
setState(() {
savedNetworks = wifiList.map((wifi) {
final ssid = wifi.ssid ?? '未知网络';
final isConnected = ssid == _currentConnectedSSID;
return <String, String>{
'ssid': ssid,
'status': isConnected ? '已连接' : '未连接',
'bssid': (wifi.bssid ?? '').toString(),
'level': (wifi.level?.toString() ?? '-100').toString(),
'capabilities': wifi.capabilities ?? '',
'passord': wifi.password ?? '',
'frequency': (wifi.frequency?.toString() ?? '0').toString(),
};
}).toList();
});
}
Future<void> _startScan() async {
setState(() => isScanning = true);
// 获取扫描结果
accessPoints = await WiFiScan.instance.getScannedResults() ?? [];
setState(() {
uniqueNetworksMap.clear(); // 清空之前的记录
for (var ap in accessPoints) {
String ssid = ap.ssid.isNotEmpty ? ap.ssid : '隐藏网络';
int level = ap.level;
// 如果这个SSID还没有记录,或者当前接入点的信号比已记录的强
if (!uniqueNetworksMap.containsKey(ssid) ||
level > int.parse(uniqueNetworksMap[ssid]!['level']!)) {
final isConnected = ssid == _currentConnectedSSID;
// 如果是当前已经连接的网络,判断是否已经在已保存列表中
if (isConnected) {
final isAlreadySaved = savedNetworks.any(
(item) => item['ssid'] == ssid,
);
if (!isAlreadySaved) {
savedNetworks.add({
'ssid': ssid,
'status': '已连接',
'bssid': ap.bssid,
'level': level.toString(),
'capabilities': ap.capabilities,
'passord': '',
'frequency': ap.frequency.toString(),
});
// 从可用网络中移除
availableNetworks.removeWhere((item) => item['ssid'] == ssid);
// 存入数据库
viewModel.insertWiFi(
WiFi(
ssid: ssid,
bssid: ap.bssid ?? '',
capabilities: ap.capabilities ?? '',
level: level,
frequency: ap.frequency ?? 0,
password: '', // 因为获取不多到密码,暂时存空
lastConnected: DateTime.now().toIso8601String(),
),
);
}
continue; // 已处理,跳过后续逻辑
}
uniqueNetworksMap[ssid] = {
'ssid': ssid,
'status': isConnected ? '已连接' : '未连接',
'level': level.toString(),
'capabilities': ap.capabilities,
'bssid': ap.bssid,
'frequency': ap.frequency.toString(),
};
}
}
// 将Map的值转换成列表,并过滤掉已保存的网络(除非是当前连接的网络)
final savedSSIDs = savedNetworks
.map((network) => network['ssid'])
.toSet();
availableNetworks = uniqueNetworksMap.values.where((network) {
final ssid = network['ssid']!;
// 显示未保存的网络,或者是当前连接的网络(即使已保存也要显示在可用网络中)
return !savedSSIDs.contains(ssid) || ssid == _currentConnectedSSID;
}).toList();
isScanning = false;
});
}
// 根据信号强度获取连接状态描述
String _getConnectionStatus(int level) {
if (level >= -50) return '信号强';
if (level >= -60) return '信号良好';
if (level >= -70) return '信号一般';
return '信号较弱';
}
// 修改连接方法,添加网络状态更新逻辑
Future<void> _connectToWiFi(
Map<String, String> network,
String password,
) async {
final ssid = network['ssid']!;
final bssid = network['bssid']!;
// 尝试连接
final isConnected = await WiFiForIoTPlugin.connect(
ssid,
password: password,
bssid: bssid,
security: _getSecurity(network['capabilities'] ?? ''),
);
if (isConnected) {
// 更新当前连接状态
setState(() {
_currentConnectedSSID = ssid;
_connectionStatus = '已连接到 $ssid';
});
// 如果这个网络不在已保存列表中,添加它
final isAlreadySaved = savedNetworks.any((item) => item['ssid'] == ssid);
// 从可用网络移除
setState(() {
availableNetworks.removeWhere((item) => item['bssid'] == bssid);
// 更新已保存网络状态
savedNetworks = savedNetworks.map((item) {
if (item['ssid'] == ssid) {
return {...item, 'status': '已连接'};
} else {
return {...item, 'status': '未连接'};
}
}).toList();
if (!isAlreadySaved) {
savedNetworks.add({
'ssid': ssid,
'status': '已连接',
'bssid': bssid,
'level': network['level'] ?? '-100',
'capabilities': network['capabilities'] ?? '',
'frequency': network['frequency'] ?? '0',
});
}
});
if (!isAlreadySaved) {
// 保存到数据库
await viewModel.insertWiFi(
WiFi(
ssid: ssid,
bssid: bssid,
capabilities: network['capabilities'] ?? '',
level: int.tryParse(network['level'] ?? '-100') ?? -100,
password: password,
frequency: int.tryParse(network['frequency'] ?? '0') ?? 0,
lastConnected: DateTime.now().toIso8601String(),
),
);
}
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('已成功连接到 $ssid'),
backgroundColor: Colors.green,
),
);
}
} else {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('连接 $ssid 失败'), backgroundColor: Colors.red),
);
}
}
}
// 安全类型检测
NetworkSecurity _getSecurity(String capabilities) {
if (capabilities.contains('WPA2') || capabilities.contains('WPA')) {
return NetworkSecurity.WPA;
}
if (capabilities.contains('WEP')) return NetworkSecurity.WEP;
return NetworkSecurity.NONE;
}
// 新增:断开网络连接
Future<void> _disconnectFromWiFi(String ssid) async {
bool isDisconnected = await WiFiForIoTPlugin.disconnect();
if (isDisconnected) {
setState(() {
_currentConnectedSSID = null;
_connectionStatus = '未连接';
// 更新网络状态
for (var network in savedNetworks) {
if (network['ssid'] == ssid) {
network['status'] = '已保存';
}
}
});
if (mounted) {
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text('已断开与 $ssid 的连接')));
}
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 连接状态显示(动态显示当前连接状态)
Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
_connectionStatus,
style: TextStyle(
fontSize: 14,
color: _currentConnectedSSID != null
? Colors.green
: Colors.grey,
),
),
IconButton(
icon: const Icon(Icons.settings_ethernet, size: 32),
color: Colors.blue,
onPressed: _showDeviceConnectionDialog,
),
],
),
),
// 设备连接标题和搜索按钮
// 在设备连接标题区域的Row中替换按钮代码:
Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'设备连接',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
IconButton(
// 替换为图标按钮
onPressed: _startScan,
icon: isScanning
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(
Colors.blue,
),
),
)
: const Icon(Icons.refresh, color: Colors.blue),
tooltip: '刷新WiFi列表', // 添加提示文本
),
],
),
),
Expanded(
child: isScanning
? const Center(child: CircularProgressIndicator())
: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 已保存网络区域(包括已连接的)
_buildNetworkSection('已保存网络', savedNetworks, true),
// 可用网络区域
_buildNetworkSection(
'可用网络',
availableNetworks,
false,
),
// 如果没有网络显示空状态
if (savedNetworks.isEmpty &&
availableNetworks.isEmpty)
const Center(
child: Padding(
padding: EdgeInsets.only(top: 100),
child: Text(
'未发现网络',
style: TextStyle(
fontSize: 16,
color: Colors.grey,
),
),
),
),
],
),
),
),
],
),
),
);
}
Widget _buildNetworkSection(
String title,
List<Map<String, String>> networks,
bool isSavedSection,
) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Text(
title,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.black54,
),
),
),
// 网络列表
...networks
.map((network) => _buildNetworkItem(network, isSavedSection))
.toList(),
],
);
}
Widget _buildNetworkItem(Map<String, String> network, bool isSavedItem) {
final isConnected = network['status'] == '已连接';
return ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
leading: _getSignalIcon(int.tryParse(network['level'] ?? '-100') ?? -100),
title: Text(
network['ssid']!,
style: TextStyle(
fontSize: 16,
fontWeight: isConnected ? FontWeight.bold : FontWeight.normal,
color: isConnected ? Colors.green : Colors.black,
),
),
subtitle: network['level'] != null
? Text(
" ${_getConnectionStatus(int.tryParse(network['level'] ?? '-100') ?? -100)}",
)
: null,
trailing: Text(
network['status']!,
style: TextStyle(
fontSize: 14,
color: isConnected ? Colors.green : Colors.grey,
),
),
onTap: () {
if (isSavedItem && isConnected) {
_disconnectFromWiFi(network['ssid']!);
} else if (isSavedItem && !isConnected) {
if (network['passord'] != null && network['passord']!.isNotEmpty) {
_connectToWiFi(network, network['passord']!);
} else {
_showPasswordDialog(network);
}
} else if (!isConnected) {
_showPasswordDialog(network);
}
},
);
}
// 根据信号强度显示对应的WiFi图标
Widget _getSignalIcon(int level) {
Color color;
IconData icon;
if (level >= -50) {
color = Colors.green;
icon = Icons.wifi;
} else if (level >= -60) {
color = Colors.blue;
icon = Icons.wifi;
} else if (level >= -70) {
color = Colors.orange;
icon = Icons.wifi;
} else {
color = Colors.red;
icon = Icons.wifi;
}
return Icon(icon, color: color, size: 24);
}
void _showPasswordDialog(Map<String, String> network) {
TextEditingController passwordController = TextEditingController();
showDialog(
context: context,
builder: (context) => AlertDialog(
titlePadding: const EdgeInsets.fromLTRB(24, 24, 24, 10), // 调整标题内边距
contentPadding: const EdgeInsets.symmetric(
horizontal: 24,
vertical: 10,
), // 调整内容内边距
actionsPadding: const EdgeInsets.fromLTRB(24, 10, 24, 16), // 调整按钮内边距
title: Text(
"连接到 ${network['ssid']}",
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
content: TextField(
controller: passwordController,
obscureText: true,
style: const TextStyle(fontSize: 14),
decoration: const InputDecoration(
labelText: '密码',
labelStyle: TextStyle(fontSize: 14),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10)),
borderSide: BorderSide(color: Colors.grey),
),
contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 14),
hintText: '请输入WiFi密码',
hintStyle: TextStyle(fontSize: 14),
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
style: TextButton.styleFrom(foregroundColor: Colors.grey[700]),
child: const Text('取消', style: TextStyle(fontSize: 14)),
),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
_connectToWiFi(network, passwordController.text);
},
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).primaryColor,
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 10),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
),
child: const Text(
'连接',
style: TextStyle(fontSize: 14, color: Colors.white),
),
),
],
),
);
}
void _showDeviceConnectionDialog() {
TextEditingController ipController = TextEditingController();
TextEditingController portController = TextEditingController();
showDialog(
context: context,
builder: (context) => DeviceConnectionDialog(
ipController: ipController,
portController: portController,
onCancel: () {
Navigator.pop(context);
ScaffoldMessenger.of(
context,
).showSnackBar(const SnackBar(content: Text('连接已取消')));
},
onConfirm: () {
_connectToDevice(ipController.text, portController.text);
Navigator.pop(context);
},
),
);
}
// 设备连接逻辑
void _connectToDevice(String ip, String port) {
if (ip.isEmpty || port.isEmpty) {
ScaffoldMessenger.of(
context,
).showSnackBar(const SnackBar(content: Text('请填写IP地址和端口号')));
return;
}
viewModel.connectTcp(host: ip, port: int.tryParse(port) ?? 0);
}
}
改成comsuer的方式包裹