Skip to main content
RapidDev - Software Development Agency
flutterflow-tutorials

How to Integrate External Health Monitoring Devices in FlutterFlow

Connect Bluetooth Low Energy (BLE) health monitoring devices to a FlutterFlow app using the flutter_blue_plus package in a Custom Widget. Scan for nearby devices, filter by the GATT service UUID for the device type (0x180D for heart rate, 0x1810 for blood pressure, 0x1808 for glucose), connect to the device, subscribe to characteristic notifications, and parse the incoming data bytes according to the GATT specification. Store readings in Firestore health_readings collection and display in a dashboard. Always implement auto-reconnect logic because BLE devices disconnect frequently.

What you'll learn

  • How BLE GATT profiles work and which service UUIDs to use for common health device types
  • How to scan, connect, and subscribe to a BLE health device using flutter_blue_plus in FlutterFlow
  • How to parse raw BLE characteristic data bytes for heart rate and blood pressure readings
  • How to store health readings in Firestore and display historical trends with charts
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner10 min read90-120 minFlutterFlow Standard+ (Custom Widget required)March 2026RapidDev Engineering Team
TL;DR

Connect Bluetooth Low Energy (BLE) health monitoring devices to a FlutterFlow app using the flutter_blue_plus package in a Custom Widget. Scan for nearby devices, filter by the GATT service UUID for the device type (0x180D for heart rate, 0x1810 for blood pressure, 0x1808 for glucose), connect to the device, subscribe to characteristic notifications, and parse the incoming data bytes according to the GATT specification. Store readings in Firestore health_readings collection and display in a dashboard. Always implement auto-reconnect logic because BLE devices disconnect frequently.

Read data from BLE health devices directly in your FlutterFlow app

Medical-grade health monitoring devices — heart rate monitors, blood pressure cuffs, pulse oximeters, glucose meters, and body temperature sensors — communicate over Bluetooth Low Energy (BLE) using standardized GATT (Generic Attribute Profile) profiles. Each device type uses a specific service UUID and transmits data in a defined binary format. Your FlutterFlow app can connect to these devices, subscribe to real-time characteristic updates, and parse the binary data into meaningful readings. This tutorial covers the BLE communication pattern using flutter_blue_plus and the specific parsing logic for the two most common health devices: heart rate monitors (GATT 0x180D) and blood pressure monitors (GATT 0x1810).

Prerequisites

  • A FlutterFlow project with Standard plan or higher for Custom Code
  • A BLE-compatible health monitoring device for testing (or use a heart rate app on a smartwatch that exposes GATT services)
  • iOS: add NSBluetoothAlwaysUsageDescription to Info.plist. Android: add BLUETOOTH, BLUETOOTH_ADMIN, and ACCESS_FINE_LOCATION permissions to AndroidManifest.xml

Step-by-step guide

1

Create the BLE device scanner Custom Widget

In FlutterFlow, go to Custom Code → Custom Widgets → Add Widget. Name it BLEDeviceScanner. Add the pubspec dependency flutter_blue_plus: ^1.30.0. The widget scans for nearby BLE devices, filters by the target service UUID, and displays a list of discovered devices. Initialize FlutterBluePlus and call FlutterBluePlus.startScan(withServices: [Guid('0000180D-0000-1000-8000-00805F9B34FB')]) to scan only for heart rate devices — the long form of the service UUID 0x180D. Listen to FlutterBluePlus.scanResults and update a local state list of devices (name + RSSI signal strength). Add a Connect button next to each device. On connect tap: await device.connect(autoConnect: false). After connection, discover services with device.discoverServices(). Find the Heart Rate Measurement characteristic (UUID 0x2A37) and call characteristic.setNotifyValue(true). Listen to characteristic.onValueReceived to get real-time heart rate data bytes.

ble_heart_rate.dart
1// BLEDeviceScanner - simplified connection + data reading
2import 'package:flutter_blue_plus/flutter_blue_plus.dart';
3
4// Heart Rate service UUID and characteristic UUID
5const HEART_RATE_SERVICE = '0000180d-0000-1000-8000-00805f9b34fb';
6const HEART_RATE_MEASUREMENT = '00002a37-0000-1000-8000-00805f9b34fb';
7
8Future<void> connectAndReadHeartRate(BluetoothDevice device,
9 Function(int heartRate) onReading) async {
10 await device.connect(autoConnect: false, timeout: Duration(seconds: 15));
11
12 final services = await device.discoverServices();
13 final hrService = services.firstWhere(
14 (s) => s.serviceUuid.toString() == HEART_RATE_SERVICE
15 );
16
17 final hrChar = hrService.characteristics.firstWhere(
18 (c) => c.characteristicUuid.toString() == HEART_RATE_MEASUREMENT
19 );
20
21 await hrChar.setNotifyValue(true);
22 hrChar.onValueReceived.listen((bytes) {
23 final heartRate = parseHeartRate(bytes);
24 onReading(heartRate);
25 });
26}
27
28int parseHeartRate(List<int> bytes) {
29 // GATT Heart Rate Measurement format:
30 // Byte 0: flags. Bit 0 = 0 means HR value is UINT8, = 1 means UINT16
31 final isUint16 = (bytes[0] & 0x01) != 0;
32 return isUint16 ? (bytes[2] << 8) + bytes[1] : bytes[1];
33}

Expected result: The Custom Widget scans for nearby BLE heart rate devices, shows them in a list with signal strength, and starts streaming heart rate values after the user taps Connect.

2

Parse blood pressure readings from a BLE BP monitor

Blood pressure monitors use GATT service UUID 0x1810 (Blood Pressure) and characteristic UUID 0x2A35 (Blood Pressure Measurement). Unlike heart rate which sends continuous notifications, blood pressure sends a single reading after measurement. Set characteristic.setNotifyValue(true) and wait for the onValueReceived event which fires once when the user completes a measurement on the device. Parse the bytes: Byte 0 is the flags byte. Bytes 1-2 are systolic pressure as a SFLOAT (IEEE-11073 16-bit float). Bytes 3-4 are diastolic. Bytes 5-6 are mean arterial pressure. If flag bit 2 is set, bytes 7-8 contain pulse rate. Create a Custom Function parseBPReading(List<int> bytes) that decodes the SFLOAT values: the SFLOAT format uses the upper 4 bits as a signed exponent and lower 12 bits as a mantissa. Return a Map with systolic, diastolic, and pulseRate values.

Expected result: After the user takes a measurement on the blood pressure cuff, the parsed systolic/diastolic values and pulse rate appear in the FlutterFlow app within 2 seconds.

3

Store readings in Firestore and display a dashboard

After receiving and parsing a health reading in the Custom Widget callback, call a FlutterFlow Action from the Custom Widget using the widget's action callback parameter. The action: (1) creates a Firestore document in health_readings/{userId}/readings with fields: deviceType (String, 'heart_rate' or 'blood_pressure'), value (Map: {heartRate: 75} or {systolic: 120, diastolic: 80}), unit (String, 'bpm' or 'mmHg'), timestamp (Timestamp). Create a HealthDashboard page with a Backend Query on health_readings/{userId}/readings ordered by timestamp descending, limit 100. Display the latest reading prominently (large text widget). Add a Custom Widget using fl_chart LineChart or BarChart for historical trend visualization — bind to the readings list from the Backend Query. Add a 'Connect Device' button that opens a bottom sheet containing the BLEDeviceScanner Custom Widget.

Expected result: Readings from the physical BLE device appear immediately in Firestore and update the dashboard chart in real time using the Firestore real-time listener.

4

Handle BLE disconnections with auto-reconnect

BLE devices disconnect when: the device goes out of range, the device turns off automatically after a session, the OS kills the BLE connection to save battery, or the measurement is complete (for single-reading devices). In the Custom Widget, listen to device.connectionState stream. When the state changes to BluetoothConnectionState.disconnected: update a local variable connectionStatus to 'Disconnected' and show a red indicator in the UI. Implement auto-reconnect: if autoReconnect app state is true, wait 3 seconds and call connectAndReadHeartRate again. Show a 'Reconnecting...' indicator with a CircularProgressIndicator during the attempt. After 3 failed reconnect attempts, stop retrying and show a Toast: 'Device disconnected. Tap Connect to reconnect.' Display connection status prominently in the UI — a green dot for connected, yellow for reconnecting, red for disconnected.

Expected result: When the BLE device disconnects, the app shows a clear status indicator and automatically attempts to reconnect up to 3 times before asking the user to reconnect manually.

Complete working example

ble_health_devices.txt
1BLE HEALTH DEVICE INTEGRATION IN FLUTTERFLOW
2
3GATT SERVICE UUIDs (common health devices):
4 0x180D Heart Rate Service
5 0x2A37 Heart Rate Measurement (notify)
6 0x1810 Blood Pressure Service
7 0x2A35 Blood Pressure Measurement (indicate)
8 0x1808 Glucose Service
9 0x2A18 Glucose Measurement (notify)
10 0x1822 Pulse Oximeter Service
11 0x2A5F PLX Spot-Check Measurement
12 0x1809 Health Thermometer
13 0x2A1C Temperature Measurement (indicate)
14
15CONNECTION FLOW:
161. FlutterBluePlus.startScan(withServices: [serviceUUID])
172. Show scanResults list user taps Connect
183. device.connect(autoConnect: false)
194. device.discoverServices()
205. Find characteristic by UUID
216. characteristic.setNotifyValue(true)
227. Listen to characteristic.onValueReceived
238. Parse bytes per GATT spec call action callback
24
25DATA PARSING:
26 Heart Rate (0x2A37):
27 Byte 0: flags (bit 0 = HR format: 0=uint8, 1=uint16)
28 Bytes 1-2: heart rate value (BPM)
29
30 Blood Pressure (0x2A35):
31 Byte 0: flags
32 Bytes 1-2: systolic (IEEE-11073 SFLOAT)
33 Bytes 3-4: diastolic (SFLOAT)
34 Bytes 5-6: mean arterial pressure (SFLOAT)
35
36FIRESTORE STRUCTURE:
37 health_readings/{userId}/readings/{readingId}
38 deviceType: 'heart_rate' | 'blood_pressure'
39 value: { heartRate: 72 } or { systolic: 120, diastolic: 80 }
40 unit: 'bpm' | 'mmHg'
41 deviceName: 'Polar H10'
42 timestamp: Timestamp
43
44CONNECTION STATUS UI:
45 Green dot: connected, streaming
46 Yellow + spinner: reconnecting
47 Red dot: disconnected

Common mistakes

Why it's a problem: Not implementing auto-reconnect logic, leaving users with a frozen screen when the device disconnects

How to avoid: Listen to device.connectionState stream. On disconnect, update connection status UI immediately (red dot, message). Implement auto-reconnect: wait 3 seconds, attempt reconnect, retry up to 3 times. If all retries fail, show a clear manual reconnect prompt. Always show connection status prominently in the UI.

Why it's a problem: Using the short UUID form (0x180D) instead of the full GATT UUID when scanning or filtering

How to avoid: Always use the full UUID format in scan filters and when comparing service/characteristic UUIDs: '0000180d-0000-1000-8000-00805f9b34fb' (lowercase for consistent comparison).

Why it's a problem: Requesting Bluetooth permissions after the user taps Connect instead of at app startup

How to avoid: Request Bluetooth permissions at app launch in a Custom Action using the permission_handler package: await Permission.bluetooth.request() on Android, and include NSBluetoothAlwaysUsageDescription in Info.plist for iOS. Check permission status before initiating any scan.

Best practices

  • Request Bluetooth permissions at app startup with an explanation of why the app needs them, before the user reaches the device connection screen
  • Always display connection status prominently — green dot for connected, red for disconnected, yellow for attempting to reconnect
  • Filter BLE scans by service UUID using startScan(withServices: []) to find only relevant health devices and reduce battery drain from scanning all BLE devices
  • Implement auto-reconnect with maximum 3 attempts and exponential backoff, then prompt for manual reconnect to avoid draining the user's battery with endless reconnect attempts
  • Store each reading in Firestore with a timestamp so users can view historical trends and export their data
  • Add threshold alerts: if a heart rate reading exceeds 150 BPM or blood pressure exceeds 140/90, trigger a local notification using flutter_local_notifications
  • Test BLE connectivity on physical devices only — Bluetooth simulation in iOS simulator and Android emulator is unreliable

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

I am building a FlutterFlow health app that needs to connect to a BLE heart rate monitor using flutter_blue_plus. Explain: (1) how to scan for nearby devices filtered by the Heart Rate service UUID, (2) how to connect and subscribe to the Heart Rate Measurement characteristic, (3) how to parse the raw bytes from the characteristic (GATT 0x2A37 format), and (4) how to handle disconnections gracefully with auto-reconnect logic.

FlutterFlow Prompt

Create a Custom Widget for FlutterFlow that connects to a BLE heart rate monitor using flutter_blue_plus. The widget should: scan for devices advertising the Heart Rate service (UUID 0000180D-0000-1000-8000-00805F9B34FB), show a list of found devices, allow the user to tap a device to connect, subscribe to the Heart Rate Measurement characteristic (0x2A37), parse the bytes per GATT specification to extract the BPM value, and fire an action callback with the heart rate integer value whenever a new reading arrives.

Frequently asked questions

How do I connect a Bluetooth health device to my FlutterFlow app?

Use the flutter_blue_plus package in a Custom Widget. Add it to your pubspec dependencies (Custom Code → Pubspec Dependencies). Create a widget that: (1) scans for BLE devices filtering by the health device's GATT service UUID, (2) displays found devices in a list, (3) connects to the selected device with device.connect(), (4) discovers services and finds the measurement characteristic, (5) calls setNotifyValue(true) to subscribe to real-time updates, and (6) parses incoming data bytes according to the GATT specification.

What BLE UUID should I use for a heart rate monitor?

Heart rate monitors use the standardized Bluetooth GATT Heart Rate Profile. The service UUID is 0000180D-0000-1000-8000-00805F9B34FB (short form: 0x180D). The Heart Rate Measurement characteristic UUID is 00002A37-0000-1000-8000-00805F9B34FB (short form: 0x2A37). Filter scans by the service UUID to find only heart rate devices. The characteristic sends notifications — subscribe with setNotifyValue(true) and listen to onValueReceived for real-time BPM updates.

How do I parse BLE heart rate data bytes in Flutter?

The GATT Heart Rate Measurement characteristic sends bytes in a defined format: Byte 0 is the flags byte. Check bit 0 of flags: if 0, the heart rate value is in byte 1 as a uint8 (use bytes[1]). If bit 0 is 1, the value is a uint16 across bytes 1-2 (use (bytes[2] << 8) + bytes[1]). For most consumer heart rate monitors, the value is uint8. Example: bytes [0x04, 0x48, 0x00] has flags=0x04, HR=0x48=72 BPM. Bytes [0x01, 0x60, 0x00] has flags=0x01 (uint16), HR = (0<<8)+0x60 = 96 BPM.

Does the flutter_blue_plus package work on both iOS and Android?

Yes. flutter_blue_plus supports iOS and Android. iOS requires adding NSBluetoothAlwaysUsageDescription to Info.plist in the exported Xcode project (FlutterFlow can handle this via Settings → App Details → Permissions). Android requires BLUETOOTH, BLUETOOTH_ADMIN, and ACCESS_FINE_LOCATION permissions in AndroidManifest.xml. On Android 12+, also add BLUETOOTH_SCAN and BLUETOOTH_CONNECT permissions. Note: flutter_blue_plus does NOT work in the iOS Simulator or Android Emulator — test only on physical devices.

What health devices are supported using GATT profiles?

Bluetooth GATT defines standardized profiles for: Heart Rate monitors (0x180D) — chest straps, fitness trackers. Blood Pressure monitors (0x1810) — arm cuffs. Glucose meters (0x1808) — diabetic glucose monitors. Pulse oximeters (0x1822) — fingertip SpO2 monitors. Body thermometers (0x1809). Weight scales (0x181D). Lung function spirometers (0x1846). Any device that implements the standard GATT profile for its category will work with the service and characteristic UUIDs defined in the Bluetooth SIG specification.

What if I need help building a medical device integration for my FlutterFlow health app?

BLE health device integration involves parsing binary data formats, handling connection state machines, managing permissions across iOS and Android, and implementing reliable reconnect logic. RapidDev has built custom BLE integrations for health monitoring apps including heart rate monitors, blood pressure cuffs, and glucose meters. For medical-grade device integration with FDA or CE compliance considerations, professional development support is particularly important to get right.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.