flutter_bluetooth_serial_plus
Bluetooth Classic serial (RFCOMM/SPP) on Android, plus Zebra printer BLE connections on Android and iOS.
Zebra ZD421 and other Link-OS printers over BLE
Pairing in system settings is not a connection to the printing channel.
Use ZebraBle for a printer in LE controller mode, not
BluetoothConnection.toAddress() (which remains Android Classic/SPP).
The printer must expose Zebra's BLE parser service; support depends on its
hardware and firmware. The standard BLE feature is marketed primarily for
configuration: this API checks the actual service instead of assuming printing
support from the model name.
On Android request permissions first:
final granted = await FlutterBluetoothSerial.instance.ensurePermissions();
if (!granted) throw StateError('Bluetooth permission denied');
Android 12+ requires Nearby devices (scan/connect) permission; Android 6–11 requires precise location permission and the system location service enabled for scanning. Enable Bluetooth in system settings. The plugin manifest declares the permissions; the host application's merged manifest must retain them.
On iOS add NSBluetoothAlwaysUsageDescription to the host app's Info.plist;
also add NSBluetoothPeripheralUsageDescription if targeting iOS 12. The value
must describe why the app needs Bluetooth. Authorization is requested by
CoreBluetooth. Only the new Zebra BLE API is supported on iOS; generic SPP,
bonded-device listing and adapter-control APIs remain Android-only.
// Keep the scan subscription to cancel it when the screen is disposed.
final scan = ZebraBle.instance.scan().listen((device) {
print('${device.name}: ${device.address}');
});
// After selecting a discovered device:
await scan.cancel();
final printer = await ZebraBle.instance.connect(selectedDevice.address);
try {
await printer.printZpl('^XA^FO30,30^A0N,30,30^FDPrueba Zebra^FS^XZ');
// Or reuse the exact bytes that already work over TCP:
// await printer.write(bytes);
} finally {
await printer.close();
}
Android identifiers are MAC addresses; iOS uses peripheral UUIDs returned by
the BLE scan, not the printer's MAC. Scanning includes all BLE devices because
some Zebra firmware does not advertise the parser service. Connection fails
with service_not_found / characteristic_not_found if the parser is unavailable.
One BLE connection is supported at a time. Writes are serialized and fragmented
with response acknowledgements (conservative 20-byte payloads on Android;
platform write-size limits on iOS). Default timeouts are 10 seconds for scanning,
20 seconds for connecting and 60 seconds per write; they are configurable.
input receives printer indications when available. A completed write confirms
BLE delivery, not a physical printed label. Large labels/images can be slow.
On a timeout/disconnection, a job may be partially or fully received: the library
does not retry or fall back to another transport automatically.
The example app includes Impresora Zebra (BLE) with scanning, service validation, editable ZPL and a test-label button. On Android it also offers manual MAC connection and bonded-device selection without requiring a scan advertisement. iOS opens this screen directly and also retrieves connected BLE peripherals with known services when available. Test first with a small label, then the same job that works over your network. Physical printing must be verified with the actual printer and mobile device.
Protocol references: Zebra BLE application note and official BLE example.
Features
The first goal of this project, update by @Angelica was making an interface for Serial Port Protocol (HC-05 Adapter). Now the plugin features:
-
Adapter status monitoring,
-
Turning adapter on and off,
-
Opening settings,
-
Discovering devices (and requesting discoverability),
-
Listing bonded devices and pairing new ones,
-
Connecting to multiple devices at the same time,
-
Sending and receiving data (multiple connections).
The plugin (for now) uses Serial Port profile for moving data over RFCOMM, so make sure there is running Service Discovery Protocol that points to SP/RFCOMM channel of the device. There could be max up to 7 Bluetooth connections.
The original serial API supports Android only; Zebra BLE also supports iOS.
Funding
Your contribution will help drive the development of quality tools for the Flutter and Dart developer community. Any amount will be appreciated. Thank you for your continued support!
Getting Started
Depending
# Add dependency to `pubspec.yaml` of your project.
dependencies:
# ...
flutter_bluetooth_serial_plus: ^0.5.7
Installing
flutter pub get
Importing
import 'package:flutter_bluetooth_serial_plus/flutter_bluetooth_serial_plus.dart';
Usage
You should look to the Dart code of the library (mostly documented functions) or to the examples code.
// Some simplest connection :F
try {
BluetoothConnection connection = await BluetoothConnection.toAddress(address);
print('Connected to the device');
connection.input.listen((Uint8List data) {
print('Data incoming: ${ascii.decode(data)}');
connection.output.add(data); // Sending data
if (ascii.decode(data).contains('!')) {
connection.finish(); // Closing connection
print('Disconnecting by local host');
}
}).onDone(() {
print('Disconnected by remote request');
});
}
catch (exception) {
print('Cannot connect, exception occured');
}
Note: Work is underway to make the communication easier than operations on byte streams. See #41 for discussion about the topic.
Examples
Check out example application with connections with both Arduino HC-05 and Raspberry Pi (RFCOMM) Bluetooth interfaces.
| Main screen and options | Discovery and connecting | Simple chat with server | Background connection |
|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
To-do list
- Add some utils to easier manage
BluetoothConnection(see discussion #41), - Allow connection method/protocol/UUID specification,
- Listening/server mode,
- Recognizing and displaying
BluetoothClassof device, - Maybe integration with
flutter_blueone day ;
You might also want to check milestones.



