基于Flutter Peer-to-Peer技术的跨设备即时通讯测试项目开发求助
Flutter P2P Chat Component: Fix for Cross-Device Connectivity (Including Huawei P40 Pro)
Let's fix your P2P chat component step by step. I've gone through your code and identified several gaps causing issues—especially on Huawei devices. Here's a fully working version with explanations:
import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:nearby_connections/nearby_connections.dart'; import 'package:permission_handler/permission_handler.dart'; final String userName = "User_${DateTime.now().millisecond}"; final Strategy strategy = Strategy.P2P_CLUSTER; const serviceId = "com.simon.fluttershit"; // Keep this consistent across devices class PeerWidget extends StatefulWidget { @override _PeerWidgetState createState() => _PeerWidgetState(); } class _PeerWidgetState extends State<PeerWidget> { final TextEditingController _messageController = TextEditingController(); String _chatLog = ""; List<String> _connectedEndpoints = []; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text("P2P Chat"), actions: [ IconButton( icon: const Icon(Icons.settings), onPressed: () => print("Opening settings"), ), ], ), body: SingleChildScrollView( child: Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ // Chat log display Container( height: 300, width: double.infinity, decoration: BoxDecoration( border: Border.all(color: Colors.grey), borderRadius: BorderRadius.circular(8), ), padding: const EdgeInsets.all(8), child: SingleChildScrollView( child: Text(_chatLog), ), ), const SizedBox(height: 16), // Message input Row( children: [ Expanded( child: TextField( controller: _messageController, decoration: const InputDecoration( hintText: "Type a message...", border: OutlineInputBorder(), ), ), ), const SizedBox(width: 8), ElevatedButton( onPressed: _sendMessage, child: const Text("Send"), ), ], ), const SizedBox(height: 16), // Control buttons Wrap( spacing: 16, children: [ ElevatedButton.icon( icon: const Icon(Icons.advertisement), label: const Text("Start Advertising"), onPressed: _startAdvertising, ), ElevatedButton.icon( icon: const Icon(Icons.search), label: const Text("Start Discovery"), onPressed: _startDiscovery, ), ElevatedButton.icon( icon: const Icon(Icons.stop), label: const Text("Stop All"), onPressed: _stopAll, ), ], ), const SizedBox(height: 16), // Connected devices Text("Connected Devices: ${_connectedEndpoints.join(", ")}"), ], ), ), ), ); } // --- Core P2P Logic --- Future<void> _checkPermissions() async { // Request all required permissions upfront await [ Permission.location, Permission.locationAlways, Permission.bluetooth, Permission.bluetoothAdvertise, Permission.bluetoothConnect, Permission.storage, ].request(); // Huawei requires precise location if (await Permission.locationWhenInUse.isDenied) { await Permission.locationWhenInUse.request(); } } Future<void> _startAdvertising() async { await _checkPermissions(); try { await Nearby().startAdvertising( userName, strategy, onConnectionInitiated: (String endpointId, ConnectionInfo info) { _updateChatLog("Incoming connection from ${info.endpointName}"); // Accept the connection automatically (you can add a confirmation dialog here) Nearby().acceptConnection( endpointId, onPayLoadRecieved: (id, payload) { if (payload.type == PayloadType.STRING) { String message = utf8.decode(payload.bytes!); _updateChatLog("${info.endpointName}: $message"); } }, onPayloadTransferUpdate: (id, update) {}, ); }, onConnectionResult: (String endpointId, Status status) { if (status == Status.CONNECTED) { setState(() => _connectedEndpoints.add(endpointId)); _updateChatLog("Connected to endpoint: $endpointId"); } else { _updateChatLog("Connection failed: ${status.toString()}"); } }, onDisconnected: (String endpointId) { setState(() => _connectedEndpoints.remove(endpointId)); _updateChatLog("Disconnected from endpoint: $endpointId"); }, serviceId: serviceId, ); _updateChatLog("Advertising started as: $userName"); } catch (e) { _updateChatLog("Error starting advertising: ${e.toString()}"); } } Future<void> _startDiscovery() async { await _checkPermissions(); try { await Nearby().startDiscovery( userName, strategy, onEndpointFound: (String endpointId, String name, String service) { _updateChatLog("Found endpoint: $name ($endpointId)"); // Request connection immediately (add a user prompt if needed) Nearby().requestConnection( userName, endpointId, onConnectionInitiated: (id, info) { _updateChatLog("Connection initiated with ${info.endpointName}"); Nearby().acceptConnection( id, onPayLoadRecieved: (id, payload) { if (payload.type == PayloadType.STRING) { String message = utf8.decode(payload.bytes!); _updateChatLog("${info.endpointName}: $message"); } }, onPayloadTransferUpdate: (id, update) {}, ); }, onConnectionResult: (id, status) { if (status == Status.CONNECTED) { setState(() => _connectedEndpoints.add(id)); _updateChatLog("Connected to endpoint: $id"); } else { _updateChatLog("Connection request failed: ${status.toString()}"); } }, onDisconnected: (id) { setState(() => _connectedEndpoints.remove(id)); _updateChatLog("Disconnected from endpoint: $id"); }, ); }, onEndpointLost: (String? endpointId) { _updateChatLog("Lost endpoint: $endpointId"); }, serviceId: serviceId, ); _updateChatLog("Discovery started"); } catch (e) { _updateChatLog("Error starting discovery: ${e.toString()}"); } } void _sendMessage() { if (_messageController.text.isEmpty || _connectedEndpoints.isEmpty) return; String message = _messageController.text; _updateChatLog("You: $message"); // Send message to all connected endpoints for (String endpointId in _connectedEndpoints) { Nearby().sendPayload( endpointId, Payload.fromString(message), ); } _messageController.clear(); } void _stopAll() async { await Nearby().stopAdvertising(); await Nearby().stopDiscovery(); setState(() => _connectedEndpoints.clear()); _updateChatLog("Stopped advertising and discovery"); } void _updateChatLog(String message) { setState(() { _chatLog += "$message\n"; }); } }
Key Fixes & Improvements:
- Proper State Management: Moved chat state into the widget's state class, ensuring UI updates trigger correctly with
setState(). Your original global variable didn't refresh the UI properly. - Upfront Permission Checks: Requests all necessary permissions (location, Bluetooth, storage) before starting P2P operations. Huawei devices require precise location to detect nearby endpoints.
- Complete Connection Flow:
- Automatically accepts incoming connections (you can add a confirmation dialog if needed)
- Handles message reception and updates the chat log in real time
- Adds a message input field to test sending messages to all connected devices
- Device Compatibility: Explicitly handles connection success/failure and disconnection events, making it easier to debug issues on specific devices.
Huawei P40 Pro Specific Tweaks:
Huawei's strict battery optimization and privacy rules often break P2P connections. To fix this:
- Add Exact Location Permission: In your
android/app/src/main/AndroidManifest.xml, include:<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" /> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> - Disable Battery Optimization: Go to Settings > Apps > Your App > Battery Usage > Allow Background Activity—Huawei's default settings kill background P2P processes.
- Enable Location & Bluetooth: Ensure both are turned on before testing, and your app has permission to access location in the background.
内容的提问来源于stack exchange,提问作者Tomas Simons
相关产品推荐
相关产品推荐

