my_bluetooth 2.0.0 copy "my_bluetooth: ^2.0.0" to clipboard
my_bluetooth: ^2.0.0 copied to clipboard

PlatformAndroid

Android Classic Bluetooth discovery, RFCOMM connections, and byte or file transfer for Flutter printer and accessory apps.

example/lib/main.dart

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:my_bluetooth/my_bluetooth.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(home: BluetoothExamplePage());
  }
}

class BluetoothExamplePage extends StatefulWidget {
  const BluetoothExamplePage({super.key});

  @override
  State<BluetoothExamplePage> createState() => _BluetoothExamplePageState();
}

class _BluetoothExamplePageState extends State<BluetoothExamplePage> {
  XFile? image;

  final _myBluetooth = MyBluetooth();

  MPBluetoothAdapterState _adapterState = MPBluetoothAdapterState.unknown;
  StreamSubscription<MPBluetoothAdapterState>? _adapterStateStateSubscription;

  bool _isScanning = false;
  StreamSubscription<bool>? _isScanningSubscription;

  MPConnectionStateEnum _connectState = MPConnectionStateEnum.disconnected;
  StreamSubscription<ConnectionStateResponse>? _connectStateSubscription;

  StreamSubscription<List<BluetoothDevice>>? _scanResultsSubscription;
  List<BluetoothDevice> _scanResults = [];

  BluetoothCapabilities? _capabilities;
  BluetoothPermissionStatus? _scanPermissionStatus;
  BluetoothScanSession? _scanSession;
  StreamSubscription<List<BluetoothScanResult>>?
  _scanSessionResultsSubscription;
  final Map<String, int?> _rssiByRemoteId = {};

  @override
  void initState() {
    super.initState();
    if (!MyBluetooth.isSupported) return;
    _adapterStateStateSubscription = _myBluetooth.adapterState.listen((state) {
      _adapterState = state;
      if (mounted) {
        setState(() {});
      }
    }, onError: (e) {});
    _connectStateSubscription = _myBluetooth.connectionState.listen((state) {
      _connectState = state.connectionState;
      if (mounted) {
        setState(() {});
      }
    }, onError: (e) {});

    _isScanningSubscription = _myBluetooth.discoveryState.listen((state) {
      _isScanning = state;
      if (mounted) {
        setState(() {});
      }
    }, onError: (e) {});

    _scanResultsSubscription = _myBluetooth.scanResults.listen((results) {
      _scanResults = results;
      if (mounted) {
        setState(() {});
      }
    }, onError: (e) {});
  }

  @override
  void dispose() {
    _adapterStateStateSubscription?.cancel();
    _scanResultsSubscription?.cancel();
    _scanSessionResultsSubscription?.cancel();
    _connectStateSubscription?.cancel();
    _isScanningSubscription?.cancel();
    final scanSession = _scanSession;
    if (scanSession != null) {
      unawaited(scanSession.stop().catchError((Object _) {}));
    }

    super.dispose();
  }

  Future<bool> _prepareScanPermission() async {
    final capabilities = await _myBluetooth.getCapabilities();
    if (mounted) {
      setState(() => _capabilities = capabilities);
    }
    if (!capabilities.supported || !capabilities.adapterAvailable) {
      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(content: Text('Bluetooth is unavailable.')),
        );
      }
      return false;
    }

    var permissions = await _myBluetooth.checkPermissions();
    var scanStatus = permissions.forOperation(BluetoothOperation.scan);
    if (mounted) {
      setState(() => _scanPermissionStatus = scanStatus);
    }
    if (scanStatus == BluetoothPermissionStatus.denied) {
      if (!mounted) return false;
      final accepted = await showDialog<bool>(
        context: context,
        builder: (context) => AlertDialog(
          title: const Text('Nearby devices permission'),
          content: const Text(
            'Scanning is needed to find a Classic Bluetooth printer. '
            'The example does not use scan results to derive location.',
          ),
          actions: [
            TextButton(
              onPressed: () => Navigator.pop(context, false),
              child: const Text('Cancel'),
            ),
            FilledButton(
              onPressed: () => Navigator.pop(context, true),
              child: const Text('Continue'),
            ),
          ],
        ),
      );
      if (accepted != true) return false;
      permissions = await _myBluetooth.requestPermissions(
        BluetoothOperation.scan,
      );
      scanStatus = permissions.forOperation(BluetoothOperation.scan);
      if (mounted) {
        setState(() => _scanPermissionStatus = scanStatus);
      }
    }

    return scanStatus == BluetoothPermissionStatus.granted ||
        scanStatus == BluetoothPermissionStatus.notRequired;
  }

  Future<void> _startScanSession() async {
    try {
      if (!await _prepareScanPermission()) return;
      await _scanSessionResultsSubscription?.cancel();
      await _scanSession?.stop();

      final session = await _myBluetooth.startScanSession(
        options: const BluetoothScanOptions(
          filter: BluetoothScanFilter(includeUnnamed: false),
          timeout: Duration(seconds: 20),
          staleAfter: Duration(seconds: 5),
          maxResults: 20,
        ),
      );
      _scanSession = session;
      _scanSessionResultsSubscription = session.results.listen((results) {
        if (!mounted) return;
        setState(() {
          _scanResults = results.map((result) => result.device).toList();
          _rssiByRemoteId
            ..clear()
            ..addEntries(
              results.map(
                (result) => MapEntry(result.device.remoteId, result.rssi),
              ),
            );
        });
      });
    } on MyBluetoothException catch (error) {
      if (!mounted) return;
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Scan failed: ${error.kind.name}')),
      );
    }
  }

  Future<void> _stopDiscovery() async {
    final session = _scanSession;
    _scanSession = null;
    await _scanSessionResultsSubscription?.cancel();
    _scanSessionResultsSubscription = null;
    if (session != null) {
      await session.stop();
    } else {
      await _myBluetooth.stopScan();
    }
  }

  @override
  Widget build(BuildContext context) {
    if (!MyBluetooth.isSupported) {
      return const Scaffold(
        body: Center(
          child: Text('my_bluetooth supports Android Classic Bluetooth only.'),
        ),
      );
    }
    return Scaffold(
      appBar: AppBar(title: const Text('Plugin example app | My Bluetooth')),
      body: Center(
        child: SingleChildScrollView(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              const Text("Step 1. On/Off Bluetooth"),
              Text("State Bluetooth Adapter: ${_adapterState.toString()}"),
              TextButton(
                onPressed: () async {
                  await _myBluetooth.turnOn();
                },
                child: const Text("turnOn"),
              ),
              const Text("Step 2: Scan device"),
              Text(" is Scanning : ${_isScanning.toString()}"),
              Text(
                'Adapter available: '
                '${_capabilities?.adapterAvailable ?? "not checked"}',
              ),
              Text(
                'Scan permission: '
                '${_scanPermissionStatus?.name ?? "not checked"}',
              ),
              const SizedBox(height: 20),
              const Text("Step 3: Connect"),
              Text("is connecting: $_connectState"),
              TextButton(
                onPressed: () async {
                  debugPrint('${await _myBluetooth.disconnect()}');
                },
                child: const Text("disconnect"),
              ),
              const SizedBox(height: 20),
              Row(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Expanded(
                    flex: 1,
                    child: Column(
                      mainAxisSize: MainAxisSize.max,
                      mainAxisAlignment: MainAxisAlignment.start,
                      children: [
                        TextButton(
                          onPressed: () async {
                            var bondedDevices =
                                await _myBluetooth.bondedDevices;
                            setState(() {
                              _scanResults = bondedDevices;
                            });
                            debugPrint(
                              'bondedDevices: ${bondedDevices.length}',
                            );
                          },
                          child: const Text("getBondedDevices"),
                        ),
                        TextButton(
                          onPressed: () async {
                            await _scanSessionResultsSubscription?.cancel();
                            _scanSessionResultsSubscription = null;
                            _scanSession = null;
                            _rssiByRemoteId.clear();
                            await _myBluetooth.startScan(
                              withKeywords: [
                                // "Nailpop", "Nailpop Pro",
                                // "Snap# Kiosk", "DMP"
                                //     "image box"
                              ],
                            );
                          },
                          child: const Text("startScan"),
                        ),
                        TextButton(
                          onPressed: _startScanSession,
                          child: const Text("startScanSession (V2)"),
                        ),
                        TextButton(
                          onPressed: () async {
                            await _stopDiscovery();
                          },
                          child: const Text("stopScan"),
                        ),
                      ],
                    ),
                  ),
                  SizedBox(
                    width: MediaQuery.of(context).size.width / 3 * 2,
                    height: 140,
                    child: ListView.builder(
                      scrollDirection: Axis.horizontal,
                      shrinkWrap: true,
                      itemCount: _scanResults.length,
                      itemBuilder: (BuildContext context, int index) {
                        final item = _scanResults[index];
                        return GestureDetector(
                          onTap: () async {
                            debugPrint('click ${item.remoteId}');
                            debugPrint(
                              '${await _myBluetooth.connect(remoteId: item.remoteId)}',
                            );
                          },
                          child: Container(
                            margin: const EdgeInsets.symmetric(horizontal: 20),
                            padding: const EdgeInsets.all(20),
                            decoration: BoxDecoration(
                              color: Colors.blueAccent.withValues(alpha: 0.2),
                            ),
                            child: Column(
                              mainAxisSize: MainAxisSize.min,
                              children: [
                                Text(
                                  "Name: ${item.platformName}",
                                  style: const TextStyle(
                                    fontWeight: FontWeight.bold,
                                  ),
                                ),
                                Text("ID: ${item.remoteId}"),
                                Text("bondState: ${item.bondState}"),
                                Text("type: ${item.type}"),
                                Text(
                                  "RSSI: "
                                  "${_rssiByRemoteId[item.remoteId] ?? 'n/a'}",
                                ),
                              ],
                            ),
                          ),
                        );
                      },
                    ),
                  ),
                ],
              ),

              const Text(
                "SEND TEXT TO DEVICE",
                style: TextStyle(fontWeight: FontWeight.w800),
              ),
              TextButton(
                onPressed: () async {
                  await _myBluetooth.sendText(value: "Hello World");
                },
                child: const Text("1 | Send text 'Hello World'"),
              ),

              //Send File
              const Text(
                "HOW TO SEND FILE?",
                style: TextStyle(fontWeight: FontWeight.w800),
              ),
              TextButton(
                onPressed: () async {
                  final ImagePicker picker = ImagePicker();
                  image = await picker.pickImage(source: ImageSource.gallery);

                  debugPrint(image?.path);
                },
                child: const Text("Step 1 | Choose file"),
              ),

              const SizedBox(height: 10),
              TextButton(
                onPressed: () async {
                  if (image != null) {
                    debugPrint(
                      '${await _myBluetooth.sendFile(pathImage: image?.path)}',
                    );
                  } else {
                    ScaffoldMessenger.of(context).showSnackBar(
                      const SnackBar(
                        content: Text("You need to choose file first"),
                      ),
                    );
                  }
                },
                child: const Text("Step 3 | Sent file"),
              ),

              //Send File
              const Text(
                "NOTE!!!! You can use function 'sendCmd' to send any thing.",
                style: TextStyle(fontWeight: FontWeight.w500),
              ),
            ],
          ),
        ),
      ),
    );
  }
}
5
likes
160
points
9
downloads
screenshot

Documentation

API reference

Publisher

verified publisherwongcoupon.com

Weekly Downloads

Android Classic Bluetooth discovery, RFCOMM connections, and byte or file transfer for Flutter printer and accessory apps.

Repository (GitHub)
View/report issues

Topics

#bluetooth #bluetooth-classic #rfcomm #printer #flutter-plugin

Funding

Consider supporting this project:

buymeacoffee.com
ko-fi.com
github.com

License

MIT (license)

Dependencies

flutter

More

Packages that depend on my_bluetooth

Packages that implement my_bluetooth