You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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:

  1. 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" />
    
  2. Disable Battery Optimization: Go to Settings > Apps > Your App > Battery Usage > Allow Background Activity—Huawei's default settings kill background P2P processes.
  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 09:57:35