React Native vs Flutter vs native for BLE fitness apps in 2026

Contents
- TL;DR
- What a fitness app actually asks the phone to do
- Three 2026 deadlines that land whatever you choose
- How we compared the options
- React Native vs Flutter vs native at a glance
- Native iOS and Android
- Flutter
- React Native
- Kotlin Multiplatform
- Native BLE core with a cross-platform UI
- Which option fits your fitness product
- What the two BLE plugins actually document
- The watch is the product and neither framework targets it
- Building on BLE or a wearable? Get the stack decision in writing
- Frequently asked questions
- Sources
TL;DR
React Native vs Flutter vs native is the wrong first question for a fitness product. The right one is what your app does with Bluetooth Low Energy, and whether a watch app is in the plan, because the Bluetooth code ends up native under all three answers. What you pick is where that code lives and who maintains it.
- Neither framework ships an official watchOS app target, so the Apple Watch app is a separate Swift build whichever framework runs the phone.
- flutter_blue_plus documents 20 device-level APIs and 7 are Android only, because the two platforms do not expose the same surface.
- flutter_blue_plus has required a paid commercial licence for for-profit use since October 2025, with free use kept for personal, nonprofit and educational projects.
- Google Fit APIs are supported only until the end of 2026, so any Android fitness app reading Fit data needs a migration plan.
What a fitness app actually asks the phone to do
Statista projects worldwide fitness app revenue of $9.12 billion for 2026, at 26% user penetration and $4.45 per user. Its separate fitness tracker forecast, covering the watches and bands those apps talk to, sits at $51.33 billion for the same year. The two count different things, hardware sales against consumer app spend, so do not read a ratio into them. Read the ordering. People buy the device first, then judge your app by how well it keeps up.
Which is why screens are not the hard part of a heart rate app. The hard part is a connection that survives a pocket, a phone that wakes when a workout ends, a firmware update that does not brick a strap the customer paid for, and a number on the wrist when the phone is in a locker. Pick for that list.
Three 2026 deadlines that land whatever you choose
Android's fitness plumbing is being replaced this year. Google supports the Fit APIs only until the end of 2026 and closed new developer signups on May 1, 2024. Health Connect replaces them on device, shipping inside the Android framework from Android 14 and reaching older versions through the Play Store. For the Fit REST API, Google's migration FAQ says there is no alternative, so cloud sync gets redesigned against the Google Health API rather than ported.
The stores set the other two. Google Play requires new apps and updates to target Android 16, API level 36, from August 31, 2026. Apple has required since April 28, 2026 that anything uploaded to App Store Connect is built with Xcode 26 and an iOS 26 generation SDK.
None of the three cares which framework you picked. All three arrive as work on a product whose users notice the day sync stops.
How we compared the options
Five tests, applied the same way in every card, each answerable from public documentation rather than a vendor's opinion.
- Where the Bluetooth code ends up, and who has to write it.
- Whether a watch app can be built in the stack at all.
- Health store access: HealthKit on iOS, Health Connect on Android.
- The licence and the maintenance state of the BLE layer you would depend on.
- Who is on the hook when an OS release breaks it.
One disclosure before the cards. Mercury Development ships in all of these, so every option carries an honest minus, including the ones we reach for most.
React Native vs Flutter vs native at a glance
| Option | Where the BLE code lives | Background BLE | Watch app | BLE licence |
|---|---|---|---|---|
| Native iOS and Android | CoreBluetooth and the Android BLE API | Whatever the platform allows | Same language | Platform SDK |
| Flutter | flutter_blue_plus plus native code | iOS in the plugin, Android outside it | Separate Swift target | Paid for for-profit use |
| React Native | react-native-ble-plx plus native modules | iOS in the plugin, Android outside it | Separate Swift target | Apache 2.0 |
| Kotlin Multiplatform | Shared Kotlin over a per-platform layer | Whatever the platform allows | Same language | Platform SDK or community library |
| Native core, shared UI | A Swift and Kotlin module you own | Whatever the platform allows | Separate Swift target | Platform SDK |
Read the licence column twice. It changed most recently, and most comparisons skip it.
Native iOS and Android
Native means CoreBluetooth on one side and the Android Bluetooth LE API on the other, with nothing between your code and the radio. The bluetooth-central background mode wakes the app for read, write and subscription events, and Core Bluetooth state preservation and restoration lets the system hand a relaunched app its connection state back, though not after a user force-quits it.
None of that is exclusive to native, and this is where most comparisons mislead. A Flutter or React Native app calling the same APIs through a native module behaves the same way. Going native removes the layer between you and the bug, not a capability wall.
Best for: continuous wearables, over-the-air firmware, and regulated device work where a dropped connection is a support ticket.
Facts: two codebases, two release trains, every platform BLE feature on the day it ships.
The honest minus: you pay twice forever, and the bill lands after launch rather than at it. Two teams also drift, and the Bluetooth layer is where they drift first, because iOS identifies a peripheral by a UUID scoped to your app while Android works from the device address. Each side solves that its own way unless somebody writes the rule down.
Flutter
Flutter's BLE story is flutter_blue_plus: 1,760 commits, 619 forks, 5 open issues, and no dependencies beyond Flutter and the platform SDKs. It is central role only. Bluetooth Classic is unsupported, so headphones and older HC-05 style modules are out, and iBeacons do not work on iOS because Apple routes those through CoreLocation.
Best for: foreground device interactions with a demanding interface, such as a scale, a cuff or a studio machine.
Facts: central role only, zero dependencies, per-device operation queueing for multi-sensor apps.
The honest minus: two of them. Since October 2025 and version 2.0.0, for-profit use needs a paid licence priced in tiers by company size, and it bites during development and evaluation rather than only at ship. Versions before 2.0.0 stay under BSD. And the README opens its background section by calling that an advanced case the plugin does not fully support, sending Android teams to a foreground task or workmanager package and adding you may have to fork it.
React Native
react-native-ble-plx is Apache 2.0, carries 3.4k stars and 556 forks, and shipped v3.5.1 in February 2026 with 32 issues open. It covers scanning, connection, service discovery, notifications, RSSI reads, MTU negotiation and iOS background mode. Its Expo config plugin sets isBackgroundEnabled, the iOS background modes and the permission strings without hand-editing Info.plist.
Best for: teams with a React web codebase, where the phone app is mostly screens over a simple device protocol.
Facts: Apache 2.0, Expo config plugin, Android minSdk 23, no bonding, no peripheral role, no Bluetooth Classic, no beacons.
The honest minus: the maintenance signal is poor. The README's compatibility table still tops out at React Native 0.74.1 while React Native shipped 0.87 in August 2026. The neverForLocation flag is documented as experimental, with a warning that BLE might not work and that some beacons are filtered from scan results. That flag is a permissions declaration rather than a Bluetooth feature, and it is the kind of detail a plugin should not be shaky about.
Kotlin Multiplatform
Kotlin Multiplatform shares business logic and leaves the interface native on each side. JetBrains describes it as producing fully native Android apps and compiling to native binaries on iOS with direct access to platform APIs. Compose Multiplatform can share the screens too, so the gap with Flutter is narrower than the usual framing suggests.
Best for: Android-first fitness products with an existing Kotlin backend, and teams modernising an app they will not rewrite.
Facts: shared logic, optional shared interface, incremental adoption alongside existing native code.
The honest minus: it moves the Bluetooth problem rather than removing it. Under your shared code sits a per-platform layer, either a community multiplatform library or an expect and actual pair you write over CoreBluetooth and the Android API. You share the parsing and the retry policy. You do not share the radio. The hiring pool is the narrowest of the four options here.
Native BLE core with a cross-platform UI
The pattern most hardware teams end up at. Write the Bluetooth layer once per platform in Swift and Kotlin, then expose a small API to Flutter through platform channels or to React Native through a Turbo Native Module. Scanning, connection, reads and firmware transfer stay native. The shared layer subscribes to an event stream and draws.
Best for: products where the device protocol is complex and the app around it is ordinary, which describes most connected fitness hardware.
Facts: one BLE implementation per platform, one interface codebase, a bridge you own and can instrument.
The honest minus: you maintain three surfaces instead of two, and the saving shrinks to whatever the interface was worth. Novel Bits puts the uncomfortable question well: if you are writing native Bluetooth code anyway, ask what the cross-platform layer still buys you.
Which option fits your fitness product
Fit splits by what the device does while the app is closed.
If the user opens the app, takes a reading and closes it, cross-platform is fine and the choice comes down to your team. Smart scales, cuffs, body composition devices, most gym equipment. Pick the language your engineers already write.
If the device streams while the phone sits in a pocket, put the Bluetooth layer in native code whichever framework draws the screens. Continuous heart rate, sleep, glucose, any session that outlives the lock screen. Same answer for over-the-air firmware: vendor update libraries land on iOS and Android first, their Flutter and React Native wrappers are community-maintained, and a failed transfer is a returned device.
If a watch app is in the plan, budget it as separate native work from the first sprint. That is not a preference. It is what the platforms currently allow.
On our side, four products in our portfolio talk to hardware over Bluetooth: Kensington Proximo, where the first working Android app shipped in 3 months, the Bladepad controller, Tonal, where the work covers watchOS alongside Bluetooth and ANT plus, and Fitbit, running since 2011 across 7 platforms.
What the two BLE plugins actually document
We read both plugin READMEs against each other instead of repeating the usual claim that one of them is more stable.
Parity is not symmetrical, and the platforms cause that rather than the plugins. flutter_blue_plus documents 20 device-level APIs and 7 are Android only: requestMtu, requestConnectionPriority, bondState, createBond, removeBond, setPreferredPhy and clearGattCache. Most have no iOS counterpart by design, since iOS negotiates MTU itself, handles pairing implicitly and exposes no public control over connection priority. That is not a missing feature list. It means a shared Bluetooth abstraction leaks exactly where you tune a wearable, so your code forks above it anyway.
The details that cost a sprint sit in the same two files. MTU is requested at 512 by default on Android but negotiated on iOS, typically landing between 135 and 255, so packet framing cannot assume a size. Android allows 5 startScan calls per 30 second window, a rule no plugin can lift. An iOS app woken in the background has roughly 10 seconds to work. And flutter_blue_plus warns that the device id it hands you is not comparable across platforms, an app-scoped UUID on iOS against an address on Android, so remember this device is two features rather than one.
None of that is hidden. It is spread across two READMEs that framework comparisons do not read.
The watch is the product and neither framework targets it
Opinion, stated plainly: in fitness the phone app is the settings screen, and the framework debate is being held about the settings screen.
The workout happens on the wrist. That is where the heart rate sample is taken and where the user actually looks. Neither Flutter nor React Native ships an official watchOS target, so in practice the watch app is a separate Swift build inside your Xcode project. It usually links to the phone over WatchConnectivity, though an independent watch app can sync through HealthKit or your own backend. A community project runs Flutter on watchOS in closed beta, and it needs plugins ported to FFI because method-channel plugins are unsupported there.
The single codebase saving therefore stops short of the surface your users touch during the activity your app is named after. That does not make cross-platform wrong. It changes the arithmetic: one codebase plus a watch app against two codebases plus a watch app, and the second column costs well under twice the first.
Ask any vendor quoting a cross-platform build to price the watch app separately and name who writes it. A pause there tells you more than any benchmark.
This article was researched and written by the Mercury Development engineering team, drawing on delivery experience across native iOS, Android and cross-platform builds involving Bluetooth Low Energy, HealthKit and Health Connect.
Building on BLE or a wearable? Get the stack decision in writing
You have the trade-offs. What you probably do not have is the decision written down in a form your board and your engineers both accept. Tell us what the device does, what it does while the app is closed, and whether a watch is coming. We will send back the stack, the surfaces and who staffs each one.