BLE Basicsflutter_blue_plusBeginnerflutter bluetooth tutorial

Getting Started with Bluetooth Low Energy in Flutter (2026 Complete Guide)

April 10, 2025·7 min read

Getting Started with Bluetooth Low Energy in Flutter

Bluetooth Low Energy (BLE) is the wireless protocol powering the modern IoT world — fitness trackers, smart home sensors, medical devices, industrial monitors, and custom hardware all use BLE to communicate with smartphones. If you're a Flutter developer looking to build apps that talk to hardware, BLE is the skill you need.

This guide gives you everything you need to go from zero BLE knowledge to understanding the protocol, the Flutter ecosystem, and the first practical steps to connecting your app to a real device.

TL;DR

BLE in Flutter is handled by flutter_blue_plus — the actively maintained successor to the deprecated flutter_blue. BLE uses a hierarchical data model called GATT (Services → Characteristics → Descriptors). Your app scans for advertising devices, connects, discovers services, then reads/writes/subscribes to characteristics. The complete Flutter BLE stack is well-supported in 2026 for Android and iOS.


What Is Bluetooth Low Energy?

Bluetooth Low Energy is not the same as classic Bluetooth. It's a separate protocol introduced with Bluetooth 4.0 in 2010, designed specifically for devices that need to transmit small amounts of data while running on a coin cell battery for months or years.

Where classic Bluetooth is built for streaming (audio, file transfer), BLE is built for sensing and controlling — reading a temperature every second, sending a command to unlock a door, receiving heart rate data from a fitness band. For a full comparison of the two protocols, see our BLE vs Classic Bluetooth guide.

BLE devices fall into two roles:

  • Central — your Flutter app. It scans, connects, and initiates communication.
  • Peripheral — the hardware device. It advertises its presence and exposes data.

The GATT Data Model: How BLE Organises Data

Before writing a single line of Flutter code, you need to understand GATT — the Generic Attribute Profile. GATT defines how data is structured and accessed on a BLE device.

Every BLE peripheral exposes a hierarchy:

Services are logical groupings of related functionality. A heart rate monitor might have a Heart Rate Service and a Battery Service. Each service has a UUID — either a standardised 16-bit UUID (like 0x180D for Heart Rate) or a custom 128-bit UUID for proprietary devices.

Characteristics are the individual data points within a service. The Heart Rate Service contains a Heart Rate Measurement characteristic (0x2A37) that holds the current BPM reading. Each characteristic has properties defining how you can interact with it: read, write, notify, indicate.

Descriptors are metadata about characteristics. The most important is the Client Characteristic Configuration Descriptor (CCCD, 0x2902) which you write to in order to enable notifications.

For a deep dive into the GATT hierarchy with real examples, see our BLE GATT Profiles Explained guide.


Setting Up flutter_blue_plus

flutter_blue_plus is the standard Flutter BLE package in 2026. The original flutter_blue is deprecated — if you're still using it, see our flutter_blue vs flutter_blue_plus migration guide.

Add to pubspec.yaml:

dependencies:
  flutter_blue_plus: ^1.32.0
  permission_handler: ^11.3.0

For a full comparison of all Flutter BLE packages, see Flutter BLE Packages Compared.


Platform Permissions

BLE requires explicit permissions on both platforms. Getting this wrong is the number one cause of "BLE not working" issues. See our complete Flutter BLE Permissions Guide for the full setup — here's the quick version:

Android (AndroidManifest.xml):

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

iOS (Info.plist):

<key>NSBluetoothAlwaysUsageDescription</key>
<string>This app uses Bluetooth to connect to BLE devices.</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>This app uses Bluetooth to connect to BLE devices.</string>

Step 1: Scanning for Devices

Scanning puts your phone's Bluetooth radio in listen mode, capturing advertising packets from nearby BLE peripherals. For a complete scanning implementation with filtering, deduplication, and UI, see our Flutter BLE Scanning Guide.

Basic scan:

import 'package:flutter_blue_plus/flutter_blue_plus.dart';

Future<void> startScan() async {
  await FlutterBluePlus.startScan(timeout: const Duration(seconds: 15));

  FlutterBluePlus.scanResults.listen((results) {
    for (ScanResult result in results) {
      print('${result.device.platformName} | RSSI: ${result.rssi}');
    }
  });
}

Always check adapter state first:

final state = await FlutterBluePlus.adapterState.first;
if (state != BluetoothAdapterState.on) {
  print('Bluetooth is off or unavailable');
  return;
}

Step 2: Connecting to a Device

Future<void> connectToDevice(BluetoothDevice device) async {
  await device.connect(timeout: const Duration(seconds: 15));
  print('Connected to ${device.platformName}');

  // Always listen to connection state changes
  device.connectionState.listen((state) {
    print('Connection state: $state');
    if (state == BluetoothConnectionState.disconnected) {
      // Handle disconnect — trigger reconnect if needed
    }
  });
}

Step 3: Discovering Services and Characteristics

After connecting, discover the GATT structure:

Future<void> discoverServices(BluetoothDevice device) async {
  List<BluetoothService> services = await device.discoverServices();

  for (BluetoothService service in services) {
    print('Service: ${service.serviceUuid}');
    for (BluetoothCharacteristic char in service.characteristics) {
      print('  Char: ${char.characteristicUuid}');
      print('  Can read: ${char.properties.read}');
      print('  Can write: ${char.properties.write}');
      print('  Notifies: ${char.properties.notify}');
    }
  }
}

Step 4: Reading and Writing Characteristics

// Read
List<int> value = await characteristic.read();
print('Value: $value');

// Write
await characteristic.write([0x01], withoutResponse: false);

// Enable notifications for real-time data
await characteristic.setNotifyValue(true);
characteristic.lastValueStream.listen((data) {
  print('Notification received: $data');
});

For complete examples including multi-byte parsing, error handling, and real-time UI updates, see our Flutter BLE Read & Write Characteristics guide.


Choosing the Right Hardware to Test With

If you don't have a BLE device yet, you have several options. For custom projects, the most common choices are ESP32 (cheap, built-in WiFi+BLE) and Arduino Nano 33 BLE (cleaner BLE API, nRF52840 chip). See our ESP32 vs Arduino for Flutter BLE comparison to choose the right board.

For testing without any hardware, use the free nRF Connect app (iOS/Android) to simulate a BLE peripheral.


Flutter vs Native and Other Frameworks

If you're evaluating whether Flutter is the right choice for your BLE project, see our Flutter vs React Native for BLE comparison and Flutter BLE vs Native Android breakdown. The short answer: Flutter with flutter_blue_plus is the best choice for most teams in 2026.


What to Learn Next

This guide covers the fundamentals. The full journey to production BLE apps involves:

  1. BLE GATT Profiles Explained — Deep dive into Services, Characteristics, and UUIDs
  2. Flutter BLE Scanning Guide — Complete scanning implementation with filters and deduplication
  3. Flutter BLE Permissions — Full Android 12+ and iOS setup
  4. Read & Write Characteristics — Notifications, multi-byte data, error handling
  5. Build a Complete Flutter BLE App — Full project from scratch

For a structured, video-based path from these fundamentals all the way to shipping production hardware apps, the BLE Flutter Course covers every step with real hardware, full source code, and production edge cases that tutorials never reach.


Frequently Asked Questions

What is the best Flutter package for BLE in 2026? flutter_blue_plus. It's the actively maintained successor to the deprecated flutter_blue, with full Android 12+ and modern iOS support. See our Flutter BLE Packages Comparison for a full breakdown.

Does Flutter support BLE on both Android and iOS? Yes. flutter_blue_plus supports Android API 21+ and iOS 12+, covering the vast majority of active devices. The API is unified — the same Dart code works on both platforms.

What is GATT in BLE? GATT (Generic Attribute Profile) is the data model BLE uses to organise information. Devices expose Services, which contain Characteristics (the actual data points), which have Descriptors (metadata). Your Flutter app navigates this hierarchy to read, write, and subscribe to data. See BLE GATT Profiles Explained for the full guide.

Why is BLE not working on my Android device? The most common cause is missing or incorrect permissions — especially on Android 12+ where BLUETOOTH_SCAN and BLUETOOTH_CONNECT are required. See our Flutter BLE Permissions guide for the complete fix.

What hardware should I use to test my Flutter BLE app? ESP32 for custom projects (cheap, powerful, built-in BLE+WiFi). Arduino Nano 33 BLE for cleaner firmware code. nRF Connect app for testing without any hardware. See ESP32 vs Arduino for Flutter BLE.

Is there a Flutter BLE course? Yes — the BLE Flutter Course is a dedicated course for Flutter developers building BLE apps, covering everything from fundamentals to production deployment with real hardware examples.

Full Course Available

Ready to master BLE with Flutter?

Go from zero to production-ready BLE apps. The complete course covers scanning, connecting, GATT communication, custom hardware, background processing, and deploying real BLE-powered Flutter apps.

Enroll in the Course