WiFi 通信
1. WiFi 连接管理
// 添加依赖
// pubspec.yaml
dependencies:
wifi_iot: ^0.3.18
// 使用示例
import 'package:wifi_iot/wifi_iot.dart';
class WiFiManager {
// 获取 WiFi 状态
Future<bool> isWiFiEnabled() async {
return await WiFiForIoTPlugin.isEnabled();
}
// 开启/关闭 WiFi
Future<bool> setWiFiEnabled(bool enabled) async {
return await WiFiForIoTPlugin.setEnabled(enabled);
}
// 连接 WiFi
Future<bool> connectToWiFi(String ssid, String password) async {
return await WiFiForIoTPlugin.connect(
ssid,
password: password,
security: NetworkSecurity.WPA,
);
}
// 扫描 WiFi
Future<List<WifiNetwork>> scanWiFiNetworks() async {
return await WiFiForIoTPlugin.loadWifiList();
}
}
2. Socket 通信
class SocketClient {
Socket? _socket;
Future<void> connect(String host, int port) async {
try {
_socket = await Socket.connect(host, port);
print('Connected to: ${_socket?.remoteAddress.address}:${_socket?.remotePort}');
// 监听数据
_socket?.listen(
(List<int> data) {
print('Received: ${String.fromCharCodes(data)}');
},
onError: (error) {
print('Error: $error');
_socket?.destroy();
},
onDone: () {
print('Connection closed');
_socket?.destroy();
},
);
} catch (e) {
print('Failed to connect: $e');
}
}
void sendData(String data) {
_socket?.write(data);
}
void disconnect() {
_socket?.destroy();
}
}
蓝牙通信
1. 蓝牙管理
// 添加依赖
// pubspec.yaml
dependencies:
flutter_blue_plus: ^1.5.2
// 使用示例
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
class BluetoothManager {
FlutterBluePlus flutterBlue = FlutterBluePlus.instance;
// 检查蓝牙状态
Future<bool> isBluetoothEnabled() async {
return await flutterBlue.isOn;
}
// 扫描设备
Stream<List<ScanResult>> scanDevices() {
// 开始扫描
flutterBlue.startScan(timeout: Duration(seconds: 4));
// 返回扫描结果流
return flutterBlue.scanResults;
}
// 连接设备
Future<void> connectToDevice(BluetoothDevice device) async {
try {
await device.connect();
print('Connected to ${device.name}');
} catch (e) {
print('Failed to connect: $e');
}
}
// 发送数据
Future<void> sendData(
BluetoothDevice device,
List<int> data,
Guid serviceUuid,
Guid characteristicUuid,
) async {
List<BluetoothService> services = await device.discoverServices();
for (BluetoothService service in services) {
if (service.uuid == serviceUuid) {
for (BluetoothCharacteristic characteristic in service.characteristics) {
if (characteristic.uuid == characteristicUuid) {
await characteristic.write(data);
break;
}
}
}
}
}
}
2. BLE 通信示例
class BLEExample extends StatefulWidget {
@override
_BLEExampleState createState() => _BLEExampleState();
}
class _BLEExampleState extends State<BLEExample> {
final BluetoothManager _bluetoothManager = BluetoothManager();
List<ScanResult> _scanResults = [];
bool _isScanning = false;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('BLE Example'),
),
body: Column(
children: [
// 扫描按钮
ElevatedButton(
onPressed: () {
setState(() {
_isScanning = true;
});
_bluetoothManager.scanDevices().listen((results) {
setState(() {
_scanResults = results;
});
});
},
child: Text(_isScanning ? 'Scanning...' : 'Start Scan'),
),
// 设备列表
Expanded(
child: ListView.builder(
itemCount: _scanResults.length,
itemBuilder: (context, index) {
final result = _scanResults[index];
return ListTile(
title: Text(result.device.name ?? 'Unknown'),
subtitle: Text(result.device.id.toString()),
trailing: Text('${result.rssi} dBm'),
onTap: () => _connectToDevice(result.device),
);
},
),
),
],
),
);
}
void _connectToDevice(BluetoothDevice device) async {
try {
await _bluetoothManager.connectToDevice(device);
// 连接成功后的操作
} catch (e) {
print('Connection failed: $e');
}
}
}
完整通信示例
class DeviceCommunicationDemo extends StatefulWidget {
@override
_DeviceCommunicationDemoState createState() => _DeviceCommunicationDemoState();
}
class _DeviceCommunicationDemoState extends State<DeviceCommunicationDemo> {
final WiFiManager _wifiManager = WiFiManager();
final BluetoothManager _bluetoothManager = BluetoothManager();
final SocketClient _socketClient = SocketClient();
List<WifiNetwork> _wifiNetworks = [];
List<ScanResult> _bluetoothDevices = [];
@override
void initState() {
super.initState();
_initCommunication();
}
Future<void> _initCommunication() async {
// 检查 WiFi 状态
bool wifiEnabled = await _wifiManager.isWiFiEnabled();
if (!wifiEnabled) {
await _wifiManager.setWiFiEnabled(true);
}
// 检查蓝牙状态
bool bluetoothEnabled = await _bluetoothManager.isBluetoothEnabled();
if (!bluetoothEnabled) {
// 提示用户开启蓝牙
}
}
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
child: Scaffold(
appBar: AppBar(
title: Text('Device Communication'),
bottom: TabBar(
tabs: [
Tab(text: 'WiFi'),
Tab(text: 'Bluetooth'),
],
),
),
body: TabBarView(
children: [
// WiFi 页面
_buildWiFiPage(),
// 蓝牙页面
_buildBluetoothPage(),
],
),
),
);
}
Widget _buildWiFiPage() {
return Column(
children: [
ElevatedButton(
onPressed: () async {
final networks = await _wifiManager.scanWiFiNetworks();
setState(() {
_wifiNetworks = networks;
});
},
child: Text('Scan WiFi Networks'),
),
Expanded(
child: ListView.builder(
itemCount: _wifiNetworks.length,
itemBuilder: (context, index) {
final network = _wifiNetworks[index];
return ListTile(
title: Text(network.ssid ?? 'Unknown'),
subtitle: Text('Signal: ${network.level} dBm'),
onTap: () => _connectToWiFi(network),
);
},
),
),
],
);
}
Widget _buildBluetoothPage() {
return Column(
children: [
ElevatedButton(
onPressed: () {
_bluetoothManager.scanDevices().listen((results) {
setState(() {
_bluetoothDevices = results;
});
});
},
child: Text('Scan Bluetooth Devices'),
),
Expanded(
child: ListView.builder(
itemCount: _bluetoothDevices.length,
itemBuilder: (context, index) {
final device = _bluetoothDevices[index];
return ListTile(
title: Text(device.device.name ?? 'Unknown'),
subtitle: Text(device.device.id.toString()),
trailing: Text('${device.rssi} dBm'),
onTap: () => _connectToBluetoothDevice(device.device),
);
},
),
),
],
);
}
void _connectToWiFi(WifiNetwork network) async {
// 显示密码输入对话框
final password = await _showPasswordDialog();
if (password != null) {
final connected = await _wifiManager.connectToWiFi(
network.ssid!,
password,
);
if (connected) {
// 连接成功,可以进行 Socket 通信
await _socketClient.connect('192.168.1.1', 8080);
}
}
}
void _connectToBluetoothDevice(BluetoothDevice device) async {
try {
await _bluetoothManager.connectToDevice(device);
// 连接成功后的操作
} catch (e) {
print('Connection failed: $e');
}
}
Future<String?> _showPasswordDialog() async {
return showDialog<String>(
context: context,
builder: (context) {
String? password;
return AlertDialog(
title: Text('Enter WiFi Password'),
content: TextField(
obscureText: true,
onChanged: (value) => password = value,
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text('Cancel'),
),
TextButton(
onPressed: () => Navigator.pop(context, password),
child: Text('Connect'),
),
],
);
},
);
}
}
最佳实践
- 权限两步走:manifest/Info.plist 先声明,运行时再动态申请;被拒绝后给”去设置页开启”的引导(直接跳系统设置页),别只 toast 一句”权限不足”
- BLE 扫描设超时:
startScan(timeout: Duration(seconds: 5~10)),扫到目标设备立刻stopScan()——持续扫描是耗电大户 - 串口四参数必须和硬件手册对上:波特率/数据位/校验/停止位(实战案例的 9600/8N1),错一个就收不到数据;联调先拿串口调试助手确认硬件 OK,再查代码
- 流式数据做粘包/拆包:串口和 BLE 通知都是字节流,一次回调不一定是一帧完整数据;按协议的帧头帧尾切分,别假设”一次回调就是一条消息”
注意事项
- Android 和 iOS 蓝牙表现不同:iOS 后台蓝牙要开
bluetooth-centralbackground mode,Info.plist 的用途描述要写具体(“需要您的位置”这种会被拒);Android 12+ 蓝牙扫描要BLUETOOTH_SCAN运行时权限,不再是位置权限 - 大数据分包传:BLE 单包 MTU 默认 20 字节(协商后可到 512),传固件/图片必须分包+序号+重传——别指望一次写完
- 断开是常态:设备走出范围、电量低都会断;实现”断线自动重连 + 退避”,UI 实时显示连接状态,别让用户对着断开的设备点”发送”
- 敏感数据别明文传:WiFi 配网时的 SSID/密码、开锁指令,至少做一层对称加密——抓包工具看 BLE 广播跟看明信片一样
实战案例
设备通信我做过最实在的就是 flutter_brightness 的串口。光照传感器通过串口上报数据,Dart 侧用 flutter_libserialport:SerialPort.availablePorts 枚举端口,connect('COM3') 打开,波特率 9600、8 数据位、无校验、1 停止位——这四个参数必须和硬件手册对上,错一个就收不到数据,联调时先拿串口调试助手确认硬件没问题,再查代码。数据是持续流,用 broadcast StreamController<String> 往外抛,UI 层 listen 更新亮度。设备通信和 HTTP 最大的不同:没有”请求-响应”,只有”持续的数据流”,所以心智模型要从 Future 切到 Stream,断线重连、粘包处理都是必修课。
总结
设备通信是移动应用中常见的功能,通过合理使用 WiFi 和蓝牙通信,可以实现设备间的数据交换。在实现时需要注意权限处理、错误处理和性能优化等方面。
