Flutter近场通信插件light_flutter_nearby_connections的使用
Flutter近场通信插件light_flutter_nearby_connections的使用
本项目是一个轻量级且自定义版本的此仓库。该项目基于Google Nearby Connections API,作为与原生Android和iOS API以及Flutter之间的接口。
特性
- 对等设备发现
- 广告
- 浏览
- 对等设备通信
- 数据消息
包
示例代码
以下是使用light_flutter_nearby_connections
插件的完整示例代码:
import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import 'dart:io';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter_styled_toast/flutter_styled_toast.dart';
import 'package:flutter/material.dart';
import 'package:light_flutter_nearby_connections/light_flutter_nearby_connections.dart';
import 'package:permission_handler/permission_handler.dart';
void main() {
runApp(MyApp());
}
Route<dynamic> generateRoute(RouteSettings settings) {
switch (settings.name) {
case '/':
return MaterialPageRoute(builder: (_) => Home());
case 'browser':
return MaterialPageRoute(
builder: (_) => DevicesListScreen(deviceType: DeviceType.browser));
case 'advertiser':
return MaterialPageRoute(
builder: (_) => DevicesListScreen(deviceType: DeviceType.advertiser));
default:
return MaterialPageRoute(
builder: (_) => Scaffold(
body: Center(
child: Text('No route defined for ${settings.name}')),
));
}
}
class MyApp extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
onGenerateRoute: generateRoute,
initialRoute: '/',
);
}
}
class Home extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Expanded(
child: InkWell(
onTap: () {
Navigator.pushNamed(context, 'browser');
},
child: Container(
color: Colors.red,
child: Center(
child: Text(
'BROWSER',
style: TextStyle(color: Colors.white, fontSize: 40),
)),
),
),
),
Expanded(
child: InkWell(
onTap: () {
Navigator.pushNamed(context, 'advertiser');
},
child: Container(
color: Colors.green,
child: Center(
child: Text(
'ADVERTISER',
style: TextStyle(color: Colors.white, fontSize: 40),
)),
),
),
),
],
),
);
}
}
enum DeviceType { advertiser, browser }
class DevicesListScreen extends StatefulWidget {
const DevicesListScreen({required this.deviceType});
final DeviceType deviceType;
[@override](/user/override)
_DevicesListScreenState createState() => _DevicesListScreenState();
}
class _DevicesListScreenState extends State<DevicesListScreen> {
List<Device> devices = [];
List<Device> connectedDevices = [];
late NearbyService nearbyService;
late StreamSubscription subscription;
late StreamSubscription receivedDataSubscription;
bool isInit = false;
[@override](/user/override)
void initState() {
super.initState();
init();
}
[@override](/user/override)
void dispose() {
subscription.cancel();
receivedDataSubscription.cancel();
nearbyService.stopBrowsingForPeers();
nearbyService.stopAdvertisingPeer();
super.dispose();
}
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.deviceType.toString().substring(11).toUpperCase()),
),
backgroundColor: Colors.white,
body: ListView.builder(
itemCount: getItemCount(),
itemBuilder: (context, index) {
final device = widget.deviceType == DeviceType.advertiser
? connectedDevices[index]
: devices[index];
return Container(
margin: EdgeInsets.all(8.0),
child: Column(
children: [
Row(
children: [
Expanded(
child: GestureDetector(
onTap: () => _onTabItemListener(device),
child: Column(
children: [
Text(device.deviceName),
Text(
getStateName(device.state),
style: TextStyle(
color: getStateColor(device.state)),
),
],
crossAxisAlignment: CrossAxisAlignment.start,
),
)),
// 请求连接
GestureDetector(
onTap: () => _onButtonClicked(device),
child: Container(
margin: EdgeInsets.symmetric(horizontal: 8.0),
padding: EdgeInsets.all(8.0),
height: 35,
width: 100,
color: getButtonColor(device.state),
child: Center(
child: Text(
getButtonStateName(device.state),
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold),
),
),
),
)
],
),
SizedBox(
height: 8.0,
),
Divider(
height: 1,
color: Colors.grey,
)
],
),
);
}));
}
String getStateName(SessionState state) {
switch (state) {
case SessionState.notConnected:
return "断开连接";
case SessionState.connecting:
return "等待中";
default:
return "已连接";
}
}
Future<void> askPermissions() async {
final info = await DeviceInfoPlugin().androidInfo;
var withPermissions = true;
var errors = [];
final explicitPermissions = [
Permission.locationWhenInUse, // 始终?
Permission.location, // 始终?
// Android 12及以上
if (info.version.sdkInt >= 31) Permission.bluetoothAdvertise,
if (info.version.sdkInt >= 31) Permission.bluetoothConnect,
if (info.version.sdkInt >= 31) Permission.bluetoothScan,
// Android 13及以上
if (info.version.sdkInt >= 33) Permission.nearbyWifiDevices,
];
try {
if (explicitPermissions.isNotEmpty) {
final other = await explicitPermissions.request();
final locationStatus = await Permission.location.status;
if (!locationStatus.isGranted) {
errors.add("位置未启用");
}
final otherPermissions = !other.values.any((element) => !element.isGranted);
withPermissions &= otherPermissions;
if (!otherPermissions) {
errors.add("某些权限未授予");
}
log("requestPermissions granted: $other");
}
} catch (e) {
errors.add("请求权限时发生错误");
}
if (errors.isNotEmpty) {
log("ERROR: requestPermissions failed: $errors");
showDialog(
context: context,
builder: (context) => AlertDialog(
content: Text("错误:\n\n${errors.join("\n")}"),
));
}
}
String getButtonStateName(SessionState state) {
switch (state) {
case SessionState.notConnected:
case SessionState.connecting:
return "连接";
default:
return "断开连接";
}
}
Color getStateColor(SessionState state) {
switch (state) {
case SessionState.notConnected:
return Colors.black;
case SessionState.connecting:
return Colors.grey;
default:
return Colors.green;
}
}
Color getButtonColor(SessionState state) {
switch (state) {
case SessionState.notConnected:
case SessionState.connecting:
return Colors.green;
default:
return Colors.red;
}
}
_onTabItemListener(Device device) {
if (device.state == SessionState.connected) {
showDialog(
context: context,
builder: (BuildContext context) {
final myController = TextEditingController();
return AlertDialog(
title: Text("发送消息"),
content: TextField(controller: myController),
actions: [
TextButton(
child: Text("取消"),
onPressed: () {
Navigator.of(context).pop();
},
),
TextButton(
child: Text("发送"),
onPressed: () {
nearbyService.sendMessage(device.deviceId, myController.text);
myController.text = '';
},
)
],
);
});
}
}
int getItemCount() {
if (widget.deviceType == DeviceType.advertiser) {
return connectedDevices.length;
} else {
return devices.length;
}
}
_onButtonClicked(Device device) {
switch (device.state) {
case SessionState.notConnected:
nearbyService.invitePeer(
deviceID: device.deviceId,
deviceName: device.deviceName,
);
break;
case SessionState.connected:
nearbyService.disconnectPeer(deviceID: device.deviceId);
break;
case SessionState.connecting:
break;
}
}
void init() async {
nearbyService = NearbyService();
String devInfo = '';
await askPermissions();
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
if (Platform.isAndroid) {
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
devInfo = androidInfo.model;
}
if (Platform.isIOS) {
IosDeviceInfo iosInfo = await deviceInfo.iosInfo;
devInfo = iosInfo.localizedModel;
}
await nearbyService.init(
serviceType: 'mpconn',
deviceName: devInfo,
strategy: Strategy.Wi_Fi_P2P,
callback: (isRunning) async {
if (isRunning) {
if (widget.deviceType == DeviceType.browser) {
await nearbyService.stopBrowsingForPeers();
await Future.delayed(Duration(microseconds: 200));
await nearbyService.startBrowsingForPeers();
} else {
await nearbyService.stopAdvertisingPeer();
await nearbyService.stopBrowsingForPeers();
await Future.delayed(Duration(microseconds: 200));
await nearbyService.startAdvertisingPeer();
await nearbyService.startBrowsingForPeers();
}
}
});
subscription = nearbyService.stateChangedSubscription(callback: (devicesList) {
devicesList.forEach((element) {
print(
" deviceId: ${element.deviceId} | deviceName: ${element.deviceName} | state: ${element.state}");
if (Platform.isAndroid) {
if (element.state == SessionState.connected) {
nearbyService.stopBrowsingForPeers();
} else {
nearbyService.startBrowsingForPeers();
}
}
});
setState(() {
devices.clear();
devices.addAll(devicesList);
connectedDevices.clear();
connectedDevices.addAll(devicesList
.where((d) => d.state == SessionState.connected)
.toList());
});
});
receivedDataSubscription = nearbyService.dataReceivedSubscription(callback: (data) {
print("dataReceivedSubscription: ${jsonEncode(data)}");
showToast(jsonEncode(data),
context: context,
axis: Axis.horizontal,
alignment: Alignment.center,
position: StyledToastPosition.bottom);
});
}
}
更多关于Flutter近场通信插件light_flutter_nearby_connections的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter近场通信插件light_flutter_nearby_connections的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
light_flutter_nearby_connections
是一个用于 Flutter 的插件,它允许开发者在其应用程序中实现近场通信(Nearby Connections)功能。这个插件使用了 Google 的 Nearby Connections API,支持设备之间的点对点连接(P2P),而无需连接到互联网。
主要功能
- 发现附近的设备:查找并连接到附近的设备。
- 发送和接收数据:在设备之间传输数据。
- 支持多种连接策略:包括星型拓扑和点对点拓扑。
- 跨平台支持:支持 Android 和 iOS 平台。
使用步骤
1. 添加依赖
首先,在 pubspec.yaml
文件中添加 light_flutter_nearby_connections
依赖:
dependencies:
flutter:
sdk: flutter
light_flutter_nearby_connections: ^1.0.0
然后,运行 flutter pub get
安装依赖。
2. 初始化插件
在你的 Dart 代码中导入插件并初始化:
import 'package:light_flutter_nearby_connections/nearby_connections.dart';
Nearby nearby = Nearby();
3. 配置权限
确保在 AndroidManifest.xml
中添加以下权限:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<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" />
对于 iOS,在 Info.plist
中添加以下权限:
<key>NSBluetoothAlwaysUsageDescription</key>
<string>We need Bluetooth access to connect to nearby devices</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>We need Bluetooth access to connect to nearby devices</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>We need location access to connect to nearby devices</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>We need location access to connect to nearby devices</string>
4. 启动设备发现
使用 startAdvertising
和 startDiscovery
方法来启动设备发现和广告。
// 启动广告
nearby.startAdvertising(
deviceName: 'MyDevice',
strategy: Strategy.P2P_CLUSTER,
onConnectionInitiated: (String id, ConnectionInfo info) {
// 处理连接初始化
},
onConnectionResult: (String id, ConnectionResult result) {
// 处理连接结果
},
onDisconnected: (String id) {
// 处理断开连接
},
);
// 启动发现
nearby.startDiscovery(
deviceName: 'MyDevice',
strategy: Strategy.P2P_CLUSTER,
onEndpointFound: (String id, String name, String serviceId) {
// 处理发现设备
},
onEndpointLost: (String id) {
// 处理设备丢失
},
);
5. 建立连接
在 onConnectionInitiated
回调中接受连接请求:
nearby.acceptConnection(
id: id,
onPayLoadRecieved: (String id, Payload payload) {
// 处理接收到的数据
},
onPayloadTransferUpdate: (String id, PayloadTransferUpdate update) {
// 处理数据传输更新
},
);
6. 发送数据
使用 sendPayload
方法发送数据:
nearby.sendPayload(
id: id,
payload: Payload.bytes([1, 2, 3, 4]),
);
7. 断开连接
使用 disconnectFromEndpoint
方法断开连接:
nearby.disconnectFromEndpoint(id: id);