Flutter BLE app development: wat het vraagt

Gepubliceerd op 9 april 2026

14 min lezen
Flutter BLE companion app verbonden met fietshardware
Jonas Ockerman
Jonas OckermanCo-Founder

Je hebt een hardwareproduct en je hebt er een app bij nodig. Aan de oppervlakte simpel genoeg. Maar zodra Bluetooth Low Energy in beeld komt, groeit de scope op manieren die pas zichtbaar worden als je al drie maanden bezig bent.

Wij leerden dat bij het bouwen van de companion app voor Classified Cycling, een Belgisch bedrijf dat draadloze aandrijftechnologie maakt voor fietsers. Hun hardware verbindt over BLE, heeft over-the-air firmware-updates nodig en draait buiten, waar Bluetooth zich onvoorspelbaar gedraagt. De app beheert intussen verbindingen met vier verschillende hardwaretypes, integreert met twee externe partnerecosystemen en verzorgt de firmwaredistributie voor allemaal. Wat begon als “een pairing-scherm en wat instellingen” werd een volledig uitgebouwd connectiviteitsplatform.

Deze post behandelt wat BLE companion apps echt vragen, waarom Flutter er goed mee overweg kan, waar de echte complexiteit zit en welke vragen de moeite waard zijn voor je begint.

Wat BLE companion apps echt doen

Een companion app is de mobiele interface naar een hardwaretoestel. Voor de meeste producten betekent dat vier dingen:

Pairing en device discovery. De app scant naar BLE-randapparatuur in de buurt, herkent het juiste toestel (meestal aan fabrikantspecifieke advertising data), authenticeert ermee en bewaart de bond. Op iOS beheert het systeem de bonding-status. Op Android doe je dat zelf. Die twee gedragen zich verschillend genoeg om in de praktijk twee aparte engineeringproblemen te zijn.

Configuratie. Eenmaal gekoppeld leest en schrijft de app configuratiewaarden via BLE-characteristics. Voor Classified Cycling gaat het om de toewijzing van shifterknoppen, ritmodi en schakeldrempels. Elke instelbare parameter is een GATT-characteristic met een UUID, een datatype en lees- en schrijfrechten.

Live telemetrie. Sommige toestellen zenden continu data uit. De hub van Classified stuurt batterijniveau, verbindingsstatus en schakelmomenten door als BLE-notificaties. De app abonneert zich op die characteristics en werkt de UI in realtime bij. De uitdaging is dat betrouwbaar doen terwijl de telefoon in een truizak zit, de hub op een fietsframe hangt en er een peloton koolstofvezel en metaal tussen zit.

OTA firmware-updates. Dit is het lastigste stuk. Firmware over BLE pushen betekent enkele honderden kilobytes overzetten via een traag, verlieslatend kanaal, op een manier die het toestel niet kan bricken als het onderbroken wordt. De meeste hardware gebruikt het Device Firmware Update-protocol van Nordic Semiconductor (Nordic DFU) of iets vergelijkbaars. De app orchestreert de overdracht, handelt retries af en moet netjes herstellen als de verbinding halverwege wegvalt.

Wie het BLE-landschap bepaalt waarop teams bouwen
Nordic Semiconductor
DFU en chips in ontelbare randapparaten
Apple Core Bluetooth
iOS-bonding, achtergrondmodi en storeregels
Android Bluetooth-stack
Scanlimieten, permissies en OEM-energiebeleid

Waarom Flutter werkt voor BLE

Het BLE-ecosysteem van Flutter is samengekomen rond twee goed onderhouden packages: flutter_reactive_ble en flutter_blue_plus. Beide dekken de volledige levenscyclus van een verbinding. Beide ondersteunen iOS en Android vanuit één codebase.

Dat punt van die ene codebase weegt zwaarder dan het klinkt. BLE loopt op iOS en Android al op OS-niveau uiteen. Bouw je aparte native apps, dan schrijf en onderhoud je twee aparte BLE-stacks. Met Flutter schrijf je de BLE-laag één keer en vang je de platformverschillen op in een afgebakende laag in plaats van verspreid over twee codebases.

Het plugin-ecosysteem is ook volwassen voor specifieke use cases. Nordic DFU heeft een goed ondersteund Flutter-package. iBeacon-detectie, GATT-profielen voor gangbare hardwarecategorieën, beheer van achtergrondscans: dat zijn opgeloste problemen met onderhouden packages, geen dingen die je vanaf nul bouwt.

Voor wie de beslissing neemt: Flutter voor BLE betekent één team, één codebase en een ecosysteem met genoeg diepgang om de meeste eisen aan een companion app meteen af te dekken. Het realistische alternatief, aparte native iOS- en Android-teams, kost meer en duurt langer voor functioneel hetzelfde resultaat.

Flutter BLE-stacks tegenover twee native codebases
flutter_reactive_ble
Stream-gerichte API
Eén Dart-laag
Codebase
iOS + Android
Platformen
Reactive pipelines bij je app passen
Best wanneer
flutter_blue_plus
Breed gebruikt alternatief
Eén Dart-laag
Codebase
iOS + Android
Platformen
Callback-stijl bij het team past
Best wanneer
Native iOS + Android
Aparte implementaties
Swift + Kotlin BLE
Codebase
Gedupliceerde logica
Platformen
Legacy-beperkingen buiten Flutter
Best wanneer

Waar de echte complexiteit zit

Het pairing-scherm is het makkelijke deel. Dit is wat echt tijd kost.

iOS en Android bonden anders. iOS beheert BLE-bonding op OS-niveau. Android legt het bloot op app-niveau. Herinstalleert een gebruiker op iOS de app, dan onthoudt het OS de bond nog wel, maar is het opgeslagen device-ID van de app weg, waardoor de app zijn eigen gekoppelde toestellen niet terugvindt zonder opnieuw te scannen. Op Android beheer je bonding zelf. Die randgevallen vragen expliciete afhandeling.

Bluetooth-omstandigheden buiten. De Classified-app draait op fietsen. De telefoon zit in een truizak, de hub hangt onder het zadel en de renner beweegt aan 40 km/u door een veld van koolstof en metaal. Een BLE-signaal plant zich in die omstandigheden niet betrouwbaar voort. De app heeft automatische herverbindingslogica nodig, monitoring van de verbindingskwaliteit en een state machine die het verschil kent tussen “tijdelijk losgekoppeld” en “gebruiker is weggewandeld”.

De levenscyclus van een BLE-verbinding. Een BLE-verbinding is geen socket. Ze heeft toestanden: scannen, verbinden, services ontdekken, authenticeren, characteristics lezen, notificaties ontvangen en loskoppelen. Elke toestand kan falen, en het herstel verschilt per fout. Dit correct beheren vraagt een expliciete state machine, geen reeks callbacks. Zo ziet de levenscyclus eruit voor Classified:

01Scan naar randapparatuur die matcht met de fabrikantprefix van het toestel
02Verbind en ontdek GATT-services en -characteristics
03Authenticeer met een challenge-response handshake over een versleutelde characteristic
04Abonneer op notificatie-characteristics voor telemetrie en status
05Lees en schrijf configuratie-characteristics terwijl de gebruiker de app bedient
06Bepaal bij het verbreken of de gebruiker dat deed of dat het een drop was, en handel navenant

Verwerking in de achtergrond. iOS en Android beperken allebei wat apps in de achtergrond mogen doen. Moet de app een BLE-verbinding aanhouden terwijl het scherm uit staat, gebruikelijk bij fitness- en fietsapps, dan moet je de achtergrondmodi expliciet afhandelen. De Core Bluetooth-achtergrondmodi van iOS vragen om declaraties van capabilities en leggen beperkingen op aan wat je mag lezen. De Android-beperkingen op achtergrondscans werden strenger in Android 8 en opnieuw in Android 12.

Dart-isolates voor scanloops. Continu scannen naar BLE vanuit de hoofdisolate van Dart gaat ten koste van de UI-performantie. Het juiste patroon is de scanloop in een aparte Dart-isolate draaien en de scanresultaten via message passing terugsturen naar de UI-isolate. Dat is hetzelfde patroon dat we gebruiken voor blokkerende native operaties in ons Flutter FFI-werk.

Het venster van de firmware-update. Een OTA-update duurt 2 tot 5 minuten over een gangbare BLE-link. In die tijd mag de app de verbinding niet verliezen, mag de batterij van de telefoon niet leeglopen en mag het OS de update niet onderbreken. Dat is geen UI-probleem, dat is reliability engineering.

Geen van deze dingen is onoverkomelijk. Maar elk ervan vraagt bewuste engineering, en de tijd stapelt op. Heeft je team nog nooit een BLE companion app uitgeleverd, dan ligt de schatting voor “de BLE-laag” waarschijnlijk 40 tot 60% lager dan ze zou moeten zijn.

BLE-app tegenover een webinterface

Niet elk verbonden toestel heeft een native companion app nodig. Web Bluetooth bestaat en werkt in browsers op Chromium. Een webdashboard kan voor heel wat use cases configuratie en statusweergave dekken. Zo kies je:

VereisteNative appWeb Bluetooth / webdashboard
OTA firmware-updatesJaBeperkt (enkel bestandsoverdracht)
BLE-verbinding in de achtergrondJaNee
iOS-ondersteuningJaNee (Apple blokkeert Web BLE op iOS)
Offline gebruikJaDeels
Distributie via de app storeVerplichtNiet nodig
PushnotificatiesJaBeperkt
Complexe GATT-profielenJaJa
Snel een eerste versieTragerSneller

Web Bluetooth draait alleen in Chrome en Edge. Op iOS werkt het helemaal niet. Zitten je gebruikers op iPhones, dan is een webinterface geen volwaardige vervanging voor een app.

Voor use cases die alleen over configuratie gaan, waar Android je enige platform is en gebruikers technisch genoeg zijn om een browser te gebruiken, is Web Bluetooth redelijk. Zodra iOS, verbinding in de achtergrond, OTA-updates of distributie naar consumenten meespelen, heb je een native app nodig.

Kies een native app wanneer
Je OTA firmware-updates nodig hebt met stevige retries en herstel
Het product verbonden moet blijven in de achtergrond met het scherm uit
Je gebruikers op iPhones zitten of je distributie via de App Store nodig hebt
Pushnotificaties of een installatieflow voor consumenten meetellen voor adoptie
Web Bluetooth kan volstaan wanneer
Het alleen om configuratie gaat op Android, met technische gebruikers in Chrome of Edge
Sessies kort zijn en het tabblad open kan blijven, zonder verbinding in de achtergrond
Je optimaliseert voor de snelste eerste versie en bereik binnen de browser volstaat

Wat je vraagt voor je begint

Dit zijn de vragen die we in elk discovery-gesprek over een BLE companion app stellen. Geef je zo’n project uit handen, dan bespaart heldere antwoorden hierop vooraf tijd en voorkomt het verrassingen in de scope.

Hoeveel toesteltypes? Elk toesteltype is een aparte BLE-implementatie. Een hub, een sensor en een afstandsbediening die allemaal vanuit één app beheerd moeten worden, zijn drie aparte verbindingsstacks, geen enkele.

Welke BLE-profielen gebruik je? Standaard GATT-profielen (Heart Rate, Battery, Device Information) hebben bestaande Flutter-packages. Custom profielen vragen werk aan de protocolimplementatie. Voor het D-Fly-protocol van Shimano moesten we bijvoorbeeld hun eigen pairing-sequentie vanaf nul implementeren. Dat beschreven we in detail in de post over de Shimano DI2-integratie.

Zijn OTA firmware-updates vereist? Dat verdubbelt de complexiteit van de BLE-laag. Niet figuurlijk. De retrylogica, de orchestratie van de overdracht en de herstelafhandeling voor OTA vragen ruwweg evenveel engineering als al de rest samen.

Moet de app een BLE-verbinding aanhouden in de achtergrond? Zo ja, reken dan de afhandeling van achtergrondmodi op beide platformen mee, plus testwerk op de volle waaier aan Android-fabrikanten (Samsung, Xiaomi en Huawei doen batterijoptimalisatie elk anders).

Hoe ziet gebruik buiten eruit? Scenario’s met consumentenelektronica (installatie thuis, af en toe iets instellen) stellen heel andere eisen aan betrouwbaarheid dan sport- en fitnessscenario’s (constante beweging, wisselend signaal, soms veiligheidskritisch).

Zijn er regelgevende eisen? Medische toestellen, toestellen die inhaken op veiligheidskritische systemen en toestellen in gereguleerde categorieën (automotive, luchtvaart, medisch) hebben certificeringseisen die de BLE-implementatie beïnvloeden.

Hoe ligt de verdeling over platformen? Zit 80% van je gebruikers op iOS, dan verandert dat de prioritering. Heb je ondersteuning voor Android 8 nodig, dan bepaalt dat welke API’s beschikbaar zijn. Moet je binnen drie maanden op beide platformen live, dan moeten teamgrootte en scope dat weerspiegelen.

Belangrijkste inzichten

De Classified-app begon als een pairing-scherm. Vier jaar en meerdere hardwaregeneraties later beheert ze verbindingen met vier toesteltypes, integreert ze met twee partnerecosystemen en draait ze in omstandigheden waar de meeste connectiviteitscode nooit voor bedoeld was. Die evolutie is typisch.

BLE companion apps zijn geen apps die toevallig Bluetooth gebruiken. De connectiviteitslaag is het product. Ontwerp en bouw ze vanaf het begin zo.

Het voordeel van één Flutter-codebase is reëel bij BLE-projecten. De consolidatie van het library-ecosysteem en het kunnen delen van verbindingslogica tussen iOS en Android drukken de doorlopende onderhoudskost merkbaar.

OTA firmware-updates zijn geen feature voor later. Zijn ze nodig, scope ze dan vanaf het begin. De architectuurbeslissingen die OTA veilig maken, laten zich moeilijk achteraf inbouwen.

De vragen in de vorige sectie zijn niet uitputtend, maar elk team dat ze vooraf niet helder kan beantwoorden, ontdekt zijn scope halverwege het project. Discovery-gesprekken bestaan niet voor niets.

Bouw je een team dat van React Native is gemigreerd, dan is het BLE-verhaal in Flutter een van de duidelijkste winsten. We gaan dieper in op die migratiebeslissing in onze post over migratie van React Native naar Flutter. Is BLE je belangrijkste drijfveer, dan is Flutter de juiste keuze en is de migratie de investering waard.

Het ecosysteem van Flutter reikt verder dan BLE. Evalueer je Flutter ook voor e-commerce, zoals scan-to-order flows met Bluetooth-barcodescanners, dan legt onze post over Flutter-Magento-integratie uit hoe die twee patronen elkaar raken.

Hulp nodig bij de ontwikkeling van een BLE-app?

Veelgestelde vragen