bluetooth_link

A Flutter plugin to connect and stream data from classic Bluetooth (SPP) devices on Android.

Note: iOS is currently not supported. Future support may be added.

Features

  • List paired (bonded) Bluetooth devices
  • Connect to classic Bluetooth devices
  • Stream incoming data via EventChannel
  • Disconnect easily

Getting Started

Add this plugin to your pubspec.yaml:

dependencies:
  bluetooth_link: ^0.0.1

Then run:

flutter pub get

Permissions

In your Android AndroidManifest.xml:

<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />

Usage

import 'package:bluetooth_link/bluetooth_link.dart';

final bluetooth = BluetoothLink();

// List bonded devices
List<Map<String, String>> devices = await bluetooth.getBondedDevices();

// Connect to the first one
await bluetooth.connect(devices.first['address']!);

// Listen to data
bluetooth.onDataReceived((data) {
  print('Received: \$data');
});

// Listen for incoming data
final subscription = bluetooth.onDataReceived.listen((event) {
  if (event['type'] == 'data') {
    print("Received: ${event['data']}");
  } else if (event['type'] == 'error') {
    print("Error: ${event['message']}");
  } else if (event['type'] == 'disconnected') {
    print("Disconnected: ${event['message']}");
  }
});

// Check connection status
bool connected = await bluetooth.isConnected();
print("Is connected: $connected");

// Disconnect when done
await bluetooth.disconnect();
subscription.cancel();

Example UI

See full working Flutter example inside example/lib/main.dart:

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final bluetooth = BluetoothLink();
  List<Map<String, String>> devices = [];
  String? selectedAddress;
  String incomingData = '';

  @override
  void initState() {
    super.initState();
    bluetooth.onDataReceived((data) {
      setState(() => incomingData += data);
    });
    loadDevices();
  }

  Future<void> loadDevices() async {
    devices = await bluetooth.getBondedDevices();
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Bluetooth Link')),
        body: Column(
          children: [
            DropdownButton<String>(
              hint: Text('Select device'),
              value: selectedAddress,
              items: devices.map((d) => DropdownMenuItem(
                value: d['address'],
                child: Text(d['name']!),
              )).toList(),
              onChanged: (value) async {
                selectedAddress = value;
                await bluetooth.connect(value!);
                setState(() {});
              },
            ),
            Text('Received: \$incomingData'),
            ElevatedButton(
              onPressed: () => bluetooth.disconnect(),
              child: Text('Disconnect'),
            ),
          ],
        ),
      ),
    );
  }
}

Contributing

Pull requests are welcome.


License

MIT License