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
89
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