flutter_peerlink_plugin 0.0.1 copy "flutter_peerlink_plugin: ^0.0.1" to clipboard
flutter_peerlink_plugin: ^0.0.1 copied to clipboard

A Flutter plugin for peer-to-peer data transfer in android and iOS.

Flutter PeerLink Plugin #

A Flutter plugin for peer-to-peer communication between nearby devices using platform-native APIs. Supports both Android (Nearby Connections API) and iOS (MultipeerConnectivity framework) for local network discovery, connections, and data transfer.

Features #

Device Discovery - Discover nearby devices on the same network ✅ Advertising - Make your device discoverable to others ✅ Peer-to-Peer Connections - Connect directly to discovered devices ✅ Data Transfer - Send bytes and streaming data between devices ✅ Connection Strategies - Support for Star, Point-to-Point, and Cluster topologies ✅ Thread-Safe - Efficient background processing for large data transfers ✅ Stream Protocol - Custom chunked streaming for large file transfers ✅ Event-Based - Reactive streams for device discovery and data reception

Platform Support #

Platform API Version
Android Nearby Connections Android 5.0+ (API 21+)
iOS MultipeerConnectivity iOS 13.0+

⚠️ Important: Android and iOS devices cannot discover or connect to each other. They use different underlying protocols (Nearby Connections vs MultipeerConnectivity).

Installation #

Add this to your pubspec.yaml:

dependencies:
  flutter_peerlink_plugin: ^0.0.1

Or install from your local repository:

dependencies:
  flutter_peerlink_plugin:
    path: ../flutter_peerlink_plugin

Then run:

flutter pub get

Platform Setup #

Android Setup #

1. Update AndroidManifest.xml

Add required permissions in android/app/src/main/AndroidManifest.xml:

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Bluetooth permissions -->
    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

    <!-- Android 12+ (API 31+) Bluetooth permissions -->
    <uses-permission android:name="android.permission.BLUETOOTH_SCAN"
        android:usesPermissionFlags="neverForLocation" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

    <!-- WiFi permissions -->
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />

    <!-- Location permissions (required for Nearby Connections) -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

    <!-- Android 10+ (API 29+) -->
    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />

    <!-- Android 13+ (API 33+) Nearby WiFi Devices -->
    <uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES"
        android:usesPermissionFlags="neverForLocation" />

    <!-- Foreground service permission -->
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE" />

    <application>
        <!-- Your app content -->

        <!-- Foreground service for background connections -->
        <service
            android:name="com.peerlink.flutter_peerlink_plugin.service.PeerlinkForegroundService"
            android:enabled="true"
            android:exported="false"
            android:foregroundServiceType="connectedDevice" />
    </application>
</manifest>

2. Update build.gradle

Ensure minimum SDK version in android/app/build.gradle:

android {
    defaultConfig {
        minSdkVersion 21  // Android 5.0+
        targetSdkVersion 34
    }
}

3. Google Play Services

The plugin uses Google Play Services Nearby Connections API. Most devices have this pre-installed, but you may need to ensure Play Services are up to date on test devices.

iOS Setup #

1. Update Info.plist

Add required permissions and configurations in ios/Runner/Info.plist:

<dict>
    <!-- Bluetooth Usage Description -->
    <key>NSBluetoothAlwaysUsageDescription</key>
    <string>This app uses Bluetooth to discover and connect to nearby devices</string>

    <key>NSBluetoothPeripheralUsageDescription</key>
    <string>This app uses Bluetooth to discover and connect to nearby devices</string>

    <!-- Local Network Usage Description (iOS 14+) -->
    <key>NSLocalNetworkUsageDescription</key>
    <string>This app uses the local network to discover nearby devices</string>

    <!-- Bonjour Services (required for MultipeerConnectivity) -->
    <key>NSBonjourServices</key>
    <array>
        <!-- Replace with your service type (max 15 chars) -->
        <string>_peerlink._tcp</string>
    </array>

    <!-- Background Modes (optional, for background connections) -->
    <key>UIBackgroundModes</key>
    <array>
        <string>nearby-interaction</string>
    </array>
</dict>

⚠️ Important: Update the NSBonjourServices array with your actual service type used in initialize(). iOS requires the service type to be:

  • 1-15 characters
  • Lowercase alphanumeric and hyphens only
  • Format: _servicename._tcp

2. Deployment Target

Ensure minimum iOS version in ios/Podfile:

platform :ios, '13.0'

3. Service Type Limitations

iOS has strict service type requirements:

  • Maximum 15 characters
  • Lowercase letters, numbers, and hyphens only
  • The plugin automatically sanitizes Android-style service IDs

Example conversions:

  • my_app_servicemy-app-service
  • MyAppServiceWithLongNamemyappservicewi ✅ (truncated)
  • com.example.appcom-example-ap ✅ (sanitized)

Usage #

1. Initialize the Plugin #

import 'package:flutter_peerlink_plugin/flutter_peerlink_plugin.dart';

final plugin = FlutterPeerlinkPlugin.instance;

await plugin.initialize(
  serviceId: 'my-app',  // Use short, simple names (iOS: max 15 chars)
  deviceName: 'My Device',
  strategy: ConnectionStrategy.cluster,  // or .star, .pointToPoint
  autoAcceptOutgoingConnections: true,
);

2. Request Permissions #

// Check permissions
bool hasPermissions = await plugin.checkPermissions();

if (!hasPermissions) {
  // Request permissions
  bool granted = await plugin.requestPermissions();

  if (!granted) {
    print('Permissions denied');
    return;
  }
}

3. Start Discovery & Advertising #

// Start advertising (make this device discoverable)
await plugin.startAdvertising();

// Start discovering nearby devices
await plugin.startDiscovery();

// Listen for discovered devices
plugin.discoveryStream.listen((devices) {
  print('Discovered ${devices.length} devices');
  for (var device in devices) {
    print('- ${device.name} (${device.id})');
  }
});

4. Connect to a Device #

// Connect to a discovered device
await plugin.connect(device.id);

// Listen for connection changes
plugin.connectionStream.listen((connections) {
  for (var conn in connections) {
    if (conn.state == LinkedDeviceState.connected) {
      print('Connected to ${conn.name}');
    } else if (conn.state is LinkedDeviceStateFailed) {
      print('Connection failed: ${(conn.state as LinkedDeviceStateFailed).errorCode}');
    }
  }
});

5. Accept/Reject Incoming Connections #

// Automatically accept outgoing connections (set in initialize)
// For incoming connections, manually accept/reject:

plugin.connectionStream.listen((connections) {
  for (var conn in connections) {
    if (conn.isIncoming && conn.state == LinkedDeviceState.connecting) {
      // User decides to accept or reject
      await plugin.acceptConnection(conn.id);
      // or
      await plugin.rejectConnection(conn.id);
    }
  }
});

6. Send Bytes (Simple Data) #

// Send small data payloads
final data = Uint8List.fromList([1, 2, 3, 4, 5]);
final payloadId = await plugin.sendBytes(device.id, data);
print('Sent bytes with payloadId: $payloadId');

// Receive bytes on the other device
plugin.onBytePayload.listen((payload) {
  print('Received ${payload.chunk.length} bytes from ${payload.deviceId}');
});

7. Stream Large Data #

For large data transfers (files, media, etc.), use streaming:

// Start a stream
final payloadId = await plugin.startStream(device.id);

// Send data in chunks
const chunkSize = 8192;  // 8 KB chunks
for (int i = 0; i < largeData.length; i += chunkSize) {
  final end = (i + chunkSize < largeData.length) ? i + chunkSize : largeData.length;
  final chunk = largeData.sublist(i, end);
  await plugin.sendChunk(payloadId, chunk);
}

// Finish the stream
await plugin.finishStream(payloadId);

// Or cancel if needed
await plugin.cancelStream(payloadId);

8. Receive Streams #

plugin.onStreamPayload.listen((payload) {
  if (payload.isIdle) {
    print('Stream started: ${payload.id}');
    // Initialize buffer, open file, etc.
  } else if (payload.isActive) {
    print('Received chunk: ${payload.chunk.length} bytes');
    // Append to buffer, write to file, etc.
  } else if (payload.isCompleted) {
    print('Stream completed: ${payload.id}');
    // Finalize file, close buffer, etc.
  } else if (payload.isCanceled) {
    print('Stream canceled: ${payload.id}');
  } else if (payload.isFailed) {
    print('Stream failed: ${payload.id}');
  }
});

9. Cleanup #

// Stop discovery/advertising
await plugin.stopDiscovery();
await plugin.stopAdvertising();

// Disconnect from a device
await plugin.disconnect(device.id);

// Dispose the plugin
plugin.dispose();

Connection Strategies #

The plugin supports three connection strategies:

Cluster (Default) #

ConnectionStrategy.cluster
  • All-to-all mesh network
  • Best for: Group chat, collaborative apps
  • Max peers: 8 devices

Star #

ConnectionStrategy.star
  • One central hub, others connect to it
  • Best for: Multiplayer games with a host
  • Max peers: 8 devices

Point-to-Point #

ConnectionStrategy.pointToPoint
  • Direct 1-to-1 connection
  • Best for: File transfers, private chat
  • Max peers: 1 device

Foreground Service (Android Only) #

To keep connections alive in the background on Android:

// Start foreground service
await plugin.startForegroundService(
  title: 'PeerLink Active',
  message: 'Connected to nearby devices',
);

// Stop foreground service
await plugin.stopForegroundService();

Note: Foreground service is not supported on iOS. iOS requires background modes in Info.plist, but connections may still drop when the app is backgrounded.

API Reference #

Methods #

Method Description Returns
initialize(...) Initialize the plugin Future<void>
dispose() Clean up resources void
checkPermissions() Check if permissions granted Future<bool>
requestPermissions() Request required permissions Future<bool>
startAdvertising() Start advertising Future<void>
stopAdvertising() Stop advertising Future<void>
startDiscovery() Start discovering devices Future<void>
stopDiscovery() Stop discovering Future<void>
connect(deviceId) Connect to device Future<void>
acceptConnection(deviceId) Accept incoming connection Future<void>
rejectConnection(deviceId) Reject incoming connection Future<void>
disconnect(deviceId) Disconnect from device Future<void>
sendBytes(deviceId, bytes) Send bytes payload Future<int> (payloadId)
startStream(deviceId) Start stream transfer Future<int> (payloadId)
sendChunk(payloadId, chunk) Send stream chunk Future<void>
finishStream(payloadId) Finish stream normally Future<void>
cancelStream(payloadId) Cancel stream transfer Future<void>
startForegroundService(...) Start foreground service (Android) Future<void>
stopForegroundService() Stop foreground service (Android) Future<void>

Properties #

Property Type Description
discoveredDevices List<PeerDevice> Current discovered devices
connectedDevices List<LinkedDevice> Current connections
discoveryStream Stream<List<PeerDevice>> Device discovery events
connectionStream Stream<List<LinkedDevice>> Connection state events
onBytePayload Stream<BytePayload> Incoming bytes data
onStreamPayload Stream<StreamPayload> Incoming stream data

Models #

PeerDevice

class PeerDevice {
  final String id;        // Device identifier
  final String name;      // Device name
}

LinkedDevice

class LinkedDevice {
  final String id;              // Device identifier
  final String name;            // Device name
  final LinkedDeviceState state; // Connection state
  final bool isIncoming;        // True if remote initiated
}

LinkedDeviceState

enum LinkedDeviceState {
  disconnected,
  connecting,
  connected,
  failed(String errorCode),
}

BytePayload

class BytePayload {
  final String deviceId;     // Sender device ID
  final Uint8List chunk;     // Data received
}

StreamPayload

class StreamPayload {
  final int id;              // Stream payloadId
  final String deviceId;     // Sender device ID
  final Uint8List chunk;     // Data chunk (empty for state changes)
  final bool isCompleted;    // Stream finished successfully
  final bool isCanceled;     // Stream was canceled
  final bool isFailed;       // Stream failed
}

Platform Differences #

Feature Android iOS
Discovery Protocol Google Nearby Connections MultipeerConnectivity
Service ID Flexible string Max 15 chars, alphanumeric + hyphen
Cross-Platform ❌ No ❌ No
Foreground Service ✅ Yes ❌ No (returns ERROR)
Background Connections ✅ Reliable ⚠️ Limited
Auto-Chunking ✅ Yes Custom implementation
Max Peers 8 8

Android-Specific Notes #

  • Requires Google Play Services
  • Location permission required (even though not used for location)
  • Foreground service keeps connections alive in background
  • Works over Bluetooth and WiFi Direct

iOS-Specific Notes #

  • Service type automatically sanitized from Android format
  • Foreground service not supported (use background modes)
  • Connections may drop when app backgrounds
  • Works over Bluetooth LE and WiFi Direct
  • Requires NSBonjourServices in Info.plist

Troubleshooting #

Android #

Problem: Discovery not working Solution:

  • Ensure all permissions granted (especially location)
  • Check Bluetooth and WiFi are enabled
  • Verify Google Play Services are installed and up to date

Problem: Connections drop in background Solution:

  • Use startForegroundService() to keep connections alive

iOS #

Problem: "Service type invalid" error Solution:

  • Service ID must be max 15 characters
  • Only lowercase alphanumeric and hyphens allowed
  • The plugin auto-sanitizes, but very long IDs get truncated

Problem: Permission prompts not appearing Solution:

  • Add all required keys to Info.plist
  • Ensure descriptions are user-friendly
  • Check NSBonjourServices includes your service type

Problem: Devices not discovering each other Solution:

  • Ensure both devices use the same service ID
  • Check WiFi and Bluetooth are enabled
  • Verify both devices on same network (for WiFi Direct)

Example App #

See the example/ directory for a complete demo app that shows:

  • Device discovery and advertising
  • Connection management
  • Bytes and stream transfers
  • Permission handling
  • Progress tracking for large transfers

Run the example:

cd example
flutter run

Security Considerations #

  • Encryption: Both platforms use encrypted connections (TLS on Android, built-in on iOS)
  • Authentication: No built-in authentication - implement your own handshake
  • Validation: Always validate received data before use
  • Privacy: Avoid including sensitive data in device names or service IDs

Limitations #

  1. No Cross-Platform: Android ↔ iOS devices cannot communicate
  2. Network Required: Devices must be on same network or within Bluetooth range
  3. Max Peers: Limited to 8 simultaneous connections
  4. Background (iOS): Connections may drop when app backgrounds
  5. Service ID (iOS): Max 15 characters

Contributing #

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests if applicable
  5. Submit a pull request

License #

This project is licensed under the MIT License - see the LICENSE file for details.

Support #

Acknowledgments #

4
likes
160
points
122
downloads

Publisher

unverified uploader

Weekly Downloads

A Flutter plugin for peer-to-peer data transfer in android and iOS.

Repository (GitHub)
View/report issues

Documentation

API reference

License

Apache-2.0 (license)

Dependencies

flutter

More

Packages that depend on flutter_peerlink_plugin

Packages that implement flutter_peerlink_plugin