Universal Bluetooth Plugin for Flutter

A comprehensive Flutter plugin that provides unified access to Classic Bluetooth, Bluetooth Low Energy (BLE), and iBeacon functionality across iOS and Android platforms.

corp : https://jamkrafters.com

🚀 Features

✅ Classic Bluetooth

  • Device scanning and discovery
  • Connection management (pairing/disconnection)
  • Bidirectional data communication
  • Connection state monitoring
  • Make device discoverable to other devices

✅ BLE (Bluetooth Low Energy)

  • BLE device scanning with service filtering
  • GATT connection management
  • Service and characteristic discovery
  • Read/Write characteristic values
  • Characteristic notifications and subscriptions
  • Connection state monitoring

✅ iBeacon Support

  • iBeacon advertising (iOS/Android)
  • iBeacon scanning and ranging
  • Proximity detection
  • Region monitoring

📦 Installation

Add this to your package's pubspec.yaml file:

dependencies:
  universal_bluetooth: ^0.0.3

🔧 Setup

Android Setup

Add the following permissions to your android/app/src/main/AndroidManifest.xml:

<!-- Basic Bluetooth permissions -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

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

<!-- Location permissions for BLE and Beacon scanning -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />

iOS Setup

Add the following to your ios/Runner/Info.plist:

<key>NSBluetoothAlwaysUsageDescription</key>
<string>This app needs access to Bluetooth to communicate with devices</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>This app needs access to Bluetooth to communicate with devices</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>This app needs location access for beacon functionality</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app needs location access for beacon functionality</string>

📱 Usage

1️⃣ Basic Setup and Status Check

import 'package:universal_bluetooth/universal_bluetooth.dart';

// Check if Bluetooth is available and enabled
final isAvailable = await UniversalBluetooth.isBluetoothAvailable;
final isEnabled = await UniversalBluetooth.isBluetoothEnabled;

// Request to enable Bluetooth
if (!isEnabled) {
  final enabled = await UniversalBluetooth.requestBluetoothEnable();
}

// Get my Bluetooth address
final address = await UniversalBluetooth.getBluetoothAddress();

2️⃣ Classic Bluetooth Usage

Device Scanning

// Start scanning for devices
await UniversalBluetooth.startBluetoothScan();

// Listen for discovered devices
UniversalBluetooth.bluetoothScanResults.listen((result) {
  print('Found device: ${result.device.name} (${result.device.address})');
  print('Signal strength: ${result.device.rssi}dBm');
});

// Listen for scan completion
UniversalBluetooth.bluetoothScanFinished.listen((_) {
  print('Scan completed');
});

// Stop scanning
await UniversalBluetooth.stopBluetoothScan();

Make Device Discoverable

// Make device discoverable for 300 seconds
final success = await UniversalBluetooth.startBluetoothDiscoverable(
  duration: 300
);

// Stop discoverable mode
await UniversalBluetooth.stopBluetoothDiscoverable();

Connection and Data Communication

// Connect to a device
await UniversalBluetooth.connectToBluetoothDevice(deviceId);

// Monitor connection state
UniversalBluetooth.bluetoothConnectionStateChanged(deviceId).listen((state) {
  switch (state) {
    case BluetoothConnectionState.connected:
      print('Connected');
      break;
    case BluetoothConnectionState.disconnected:
      print('Disconnected');
      break;
    case BluetoothConnectionState.connecting:
      print('Connecting...');
      break;
    case BluetoothConnectionState.error:
      print('Connection error');
      break;
  }
});

// Send data
final message = "Hello, Bluetooth!";
await UniversalBluetooth.sendBluetoothData(
  deviceId,
  message.codeUnits
);

// Receive data
UniversalBluetooth.bluetoothDataReceived(deviceId).listen((data) {
  final receivedMessage = String.fromCharCodes(data);
  print('Received message: $receivedMessage');
});

// Disconnect
await UniversalBluetooth.disconnectBluetoothDevice(deviceId);

3️⃣ BLE (Bluetooth Low Energy) Usage

BLE Scanning

// Start BLE scanning (scan for specific services)
await UniversalBluetooth.startBleScan(
  serviceUuids: ['180F'], // Battery Service UUID (optional)
  timeout: Duration(seconds: 30),
);

// Listen for discovered BLE devices
UniversalBluetooth.bleScanResults.listen((device) {
  print('Found BLE device: ${device.name} (RSSI: ${device.rssi})');
});

// Stop BLE scanning
await UniversalBluetooth.stopBleScan();

BLE Connection and GATT Operations

// Connect to BLE device
await UniversalBluetooth.connectToBleDevice(deviceId);

// Discover services
final services = await UniversalBluetooth.discoverBleServices(deviceId);

// Get characteristics for a service
final characteristics = await UniversalBluetooth.getBleCharacteristics(
  deviceId,
  serviceUuid,
);

// Read characteristic value
final data = await UniversalBluetooth.readBleCharacteristic(
  deviceId,
  serviceUuid,
  characteristicUuid,
);

// Write to characteristic
await UniversalBluetooth.writeBleCharacteristic(
  deviceId,
  serviceUuid,
  characteristicUuid,
  [0x01, 0x02, 0x03],
);

// Subscribe to characteristic notifications
await UniversalBluetooth.subscribeBleCharacteristic(
  deviceId,
  serviceUuid,
  characteristicUuid,
);

// Listen for characteristic value changes
UniversalBluetooth.bleCharacteristicValueChanged(
  deviceId,
  serviceUuid,
  characteristicUuid,
).listen((data) {
  print('Characteristic value changed: $data');
});

// Disconnect BLE device
await UniversalBluetooth.disconnectBleDevice(deviceId);

4️⃣ iBeacon Usage

iBeacon Advertising

// Request location permission (required for beacon functionality)
final hasPermission = await UniversalBluetooth.requestLocationPermission();

// Start advertising as iBeacon
await UniversalBluetooth.startBeaconAdvertising(
  uuid: 'E2C56DB5-DFFB-48D2-B060-D0F5A71096E0',
  major: 1,
  minor: 100,
  identifier: 'MyBeacon',
);

// Stop beacon advertising
await UniversalBluetooth.stopBeaconAdvertising();

iBeacon Scanning

// Start scanning for beacons
await UniversalBluetooth.startBeaconScanning(
  uuids: ['E2C56DB5-DFFB-48D2-B060-D0F5A71096E0'],
);

// Listen for discovered beacons
UniversalBluetooth.beaconScanResults.listen((beacon) {
  print('Found beacon: ${beacon.uuid}');
  print('Major: ${beacon.major}, Minor: ${beacon.minor}');
  print('Distance: ${beacon.distance}m');
  print('Proximity: ${beacon.proximity}');
});

// Stop beacon scanning
await UniversalBluetooth.stopBeaconScanning();

📊 Data Models

BluetoothDevice

class BluetoothDevice {
  final String id;           // Device ID
  final String name;         // Device name
  final String address;      // MAC address
  final bool isConnected;    // Connection status
  final int? rssi;          // Signal strength
  final List<String> serviceUuids; // Service UUID list
}

BleDevice

class BleDevice {
  final String id;           // Device ID
  final String name;         // Device name
  final String address;      // MAC address
  final int rssi;           // Signal strength
  final List<String> serviceUuids; // Service UUID list
  final bool isConnectable; // Whether device is connectable
}

BeaconDevice

class BeaconDevice {
  final String uuid;         // Beacon UUID
  final int major;          // Major value
  final int minor;          // Minor value
  final int rssi;           // Signal strength
  final double? distance;   // Estimated distance (meters)
  final BeaconProximity proximity; // Proximity
}

enum BeaconProximity { immediate, near, far, unknown }

BluetoothConnectionState

enum BluetoothConnectionState {
  disconnected,  // Disconnected
  connecting,    // Connecting
  connected,     // Connected
  disconnecting, // Disconnecting
  error,         // Error
}

🔍 Debug and Extension Functions

// Get list of connected devices
final connectedDevices = await UniversalBluetoothExtensions.getConnectedDevices();

// Send test data
final result = await UniversalBluetoothExtensions.testDataSend(deviceId);

// Set global method call handler
UniversalBluetoothExtensions.setMethodCallHandler((call) async {
  // Handle custom method calls
});

🎯 Platform Support

Feature Android iOS
Classic Bluetooth
BLE
iBeacon Advertising
iBeacon Scanning

📋 Requirements

  • Flutter: 3.3.0+
  • Dart: 3.8.1+
  • Android: API level 21+ (Android 5.0+)
  • iOS: 11.0+

⚠️ Important Notes

  1. Permissions: Android 12+ requires new Bluetooth permissions
  2. Location Permission: Required for BLE scanning and iBeacon functionality
  3. Background Operation: Additional setup may be required for background operation
  4. iOS Limitations: iOS doesn't provide access to actual Bluetooth MAC addresses for privacy reasons

📝 Complete Example

import 'package:flutter/material.dart';
import 'package:universal_bluetooth/universal_bluetooth.dart';

class BluetoothExample extends StatefulWidget {
  @override
  _BluetoothExampleState createState() => _BluetoothExampleState();
}

class _BluetoothExampleState extends State<BluetoothExample> {
  List<BluetoothScanResult> devices = [];
  bool isScanning = false;

  @override
  void initState() {
    super.initState();
    _initBluetooth();
  }

  Future<void> _initBluetooth() async {
    // Check and request Bluetooth enable
    final isEnabled = await UniversalBluetooth.isBluetoothEnabled;
    if (!isEnabled) {
      await UniversalBluetooth.requestBluetoothEnable();
    }

    // Listen for scan results
    UniversalBluetooth.bluetoothScanResults.listen((result) {
      setState(() {
        final index = devices.indexWhere(
          (device) => device.device.address == result.device.address,
        );
        if (index >= 0) {
          devices[index] = result;
        } else {
          devices.add(result);
        }
      });
    });
  }

  Future<void> _startScan() async {
    setState(() {
      devices.clear();
      isScanning = true;
    });
    await UniversalBluetooth.startBluetoothScan();
  }

  Future<void> _stopScan() async {
    await UniversalBluetooth.stopBluetoothScan();
    setState(() {
      isScanning = false;
    });
  }

  Future<void> _connectDevice(String deviceId) async {
    try {
      await UniversalBluetooth.connectToBluetoothDevice(deviceId);
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Connecting to device...')),
      );
    } catch (e) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Connection failed: $e')),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Universal Bluetooth Example'),
        actions: [
          IconButton(
            icon: Icon(isScanning ? Icons.stop : Icons.search),
            onPressed: isScanning ? _stopScan : _startScan,
          ),
        ],
      ),
      body: ListView.builder(
        itemCount: devices.length,
        itemBuilder: (context, index) {
          final result = devices[index];
          final device = result.device;
          return ListTile(
            title: Text(device.name.isNotEmpty ? device.name : 'Unknown Device'),
            subtitle: Text('${device.address}\nRSSI: ${device.rssi}dBm'),
            trailing: ElevatedButton(
              onPressed: () => _connectDevice(device.id),
              child: Text('Connect'),
            ),
            leading: Icon(Icons.bluetooth),
          );
        },
      ),
    );
  }
}

📖 API Reference

Core Methods

Method Description
isBluetoothAvailable Check if Bluetooth is available on device
isBluetoothEnabled Check if Bluetooth is currently enabled
requestBluetoothEnable() Request user to enable Bluetooth
getBluetoothAddress() Get device Bluetooth address

Classic Bluetooth

Method Description
startBluetoothScan() Start scanning for Classic Bluetooth devices
stopBluetoothScan() Stop Classic Bluetooth scanning
startBluetoothDiscoverable({duration}) Make device discoverable
stopBluetoothDiscoverable() Stop discoverable mode
connectToBluetoothDevice(deviceId) Connect to a Classic Bluetooth device
disconnectBluetoothDevice(deviceId) Disconnect from a Classic Bluetooth device
sendBluetoothData(deviceId, data) Send data to connected Classic Bluetooth device

Bluetooth Low Energy

Method Description
startBleScan({serviceUuids, timeout}) Start BLE scanning with optional filters
stopBleScan() Stop BLE scanning
connectToBleDevice(deviceId) Connect to a BLE device
disconnectBleDevice(deviceId) Disconnect from a BLE device
discoverBleServices(deviceId) Discover services on connected BLE device
getBleCharacteristics(deviceId, serviceUuid) Get characteristics for a service
readBleCharacteristic(deviceId, serviceUuid, characteristicUuid) Read characteristic value
writeBleCharacteristic(deviceId, serviceUuid, characteristicUuid, data) Write to characteristic
subscribeBleCharacteristic(deviceId, serviceUuid, characteristicUuid) Subscribe to characteristic notifications
unsubscribeBleCharacteristic(deviceId, serviceUuid, characteristicUuid) Unsubscribe from characteristic notifications

iBeacon

Method Description
isBeaconSupported Check if iBeacon is supported
startBeaconAdvertising({uuid, major, minor, identifier}) Start advertising as iBeacon
stopBeaconAdvertising() Stop beacon advertising
startBeaconScanning({uuids}) Start scanning for beacons
stopBeaconScanning() Stop beacon scanning
requestLocationPermission() Request location permission for beacon functionality

🔗 Additional Resources

For complete examples and advanced usage, check the /example folder which includes:

  • Classic Bluetooth scanning and connection
  • Bidirectional data communication
  • BLE device connection and GATT operations
  • iBeacon advertising and scanning
  • Permission management
  • Error handling

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

📄 License

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

🆘 Support

If you encounter any issues or have questions, please file an issue on the GitHub repository.


Universal Bluetooth Plugin makes it easy to integrate all Bluetooth functionality into your Flutter applications, from simple device connections to complex IoT applications.