Flutter × BLE:跟藍牙硬體對話的各種路線
Published on
·9 min read

Flutter × BLE:跟藍牙硬體對話的各種路線

Authors
  • avatar
    Name
    Bert / DOTUNE
    Developer

Flutter 寫 UI 很強,但跟硬體溝通——藍牙、USB、嵌入式板子——就不是簡單的事了。不過反過來看,正因為 Flutter 一套 code 跨 iOS、Android、Desktop,它反而是嵌入式廠商最理想的夥伴。想像一個智慧家電 App,手機設定、電腦監控、平板操作——除了 Flutter 沒幾個方案能同時覆蓋這些場景。

但 Flutter 怎麼跟硬體對話?不是找到一個 BLE package、scan()connect() 就完事了——你要根據場景,在兩個關鍵環節各做選擇,然後拼成一條路線。這篇整理我實際走過的三種組合。


先搞清楚有兩層

在講路線之前,先拆開來看。Flutter 跟 BLE 硬體溝通,涉及兩個獨立的決策:

橋接層——Flutter 怎麼接到 native 世界

  • Platform Channel(走原生 Kotlin/Swift)
  • FFI(直接 call C/Rust library)

通訊層——藍牙由誰來管

  • flutter_blue_plus(Dart package,內部走 Platform Channel)
  • 廠商 SDK(封裝好的 AAR / framework,黑箱處理藍牙)
  • Rust btleplug(Desktop 上直接用 Rust 管藍牙)

橋接層 2 選 × 通訊層 3 選,可以排出好幾種組合。但實務上大部分場景會落在下面這三條路線裡面——這篇整理的是最常見的三種,不是全部。


方案一:廠商給 SDK + Platform Channel

最常見的情況——廠商不公開通訊協定,只給你封裝好的 SDK。Android 一個 AAR、iOS 一個 framework,裡面黑箱處理了藍牙、配對、加密、韌體更新。你只能透過 SDK 提供的 API 跟裝置對話。

這時候走 Platform Channel:原生端(Kotlin/Swift)接 SDK 的 API,結果透過 Method Channel 傳回 Flutter。

理論上廠商也可以直接寫一套 Flutter SDK 給你——省掉 Platform Channel,import 就能用。但現實是大部分廠商不會這麼做,你還是得自己封裝。

好處是省事——連線穩定性和安全性廠商都幫你處理完了。但代價也有:

  • 兩套原生 code。 Kotlin 一套、Swift 一套,即使邏輯一樣,API 形狀和語言不同,沒辦法複製貼上。AI 可以幫你寫,但維護期兩邊各改各的,還是會多一份心力
  • Platform Channel 的序列化成本。 資料量小的時候無感,但如果 SDK 每秒噴幾百筆 event,Method Channel 來回序列化會變成瓶頸。這種情況可以考慮用 FFI 走 binary channel,但那是更進階的事了
  • 自由度低。 SDK 是黑箱——裡面的編解碼邏輯你改不了、優化不了。廠商的實作品質不一定差,但你沒有自己寫的空間,效能瓶頸在哪也看不到

什麼時候用這條路:

  • 廠商鎖定 SDK,沒有公開協定文件
  • 裝置功能複雜(OTA 韌體更新、安全認證),自己逆向不值得

什麼時候不適合:

  • 你有選擇權——如果廠商同時給了協定文件和 SDK,走方案二自己解碼,自由度大很多

方案二:廠商給標準 + flutter_blue_plus + 自己解碼

跟方案一相反——廠商給你編解碼的標準,但不給你封裝好的 code。

很多硬體廠商會公開他們的通訊協定文件——例如「byte 0-3 是溫度(float32)、byte 4-7 是濕度、byte 8 是電量」。你知道 BLE 的 GATT service UUID 和 characteristic UUID,知道資料格式,但怎麼把 raw bytes 轉成可讀的數值——你要自己寫。

通訊層用 flutter_blue_plus。它內部已經走 Platform Channel 接好了 iOS 的 CoreBluetooth 和 Android 的 BluetoothAdapter,掃描、連線、GATT 讀寫、Notify 全部封裝好。你寫 Dart 就能從 BLE characteristic 讀到 raw bytes,不用自己處理藍牙底層。

解碼的部分看資料量。一般感測器幾秒一筆的溫濕度資料,Dart 直接解就行。但碰到高頻資料——例如心電圖波形、音訊串流、慣性感測器的 raw data——每秒幾百甚至幾千筆,這時候可以把 raw bytes 透過 FFI 丟給 Rust 或 C++ 處理,不用擔心 Dart GC 在關鍵時刻卡一下。

跟方案三的共通點是:解碼層都可能用 Rust/C++ 來寫。 差別在於藍牙誰管——方案二的藍牙通訊是 flutter_blue_plus 處理的(它內部走 Platform Channel),方案三則是 Rust 直接管藍牙。換句話說,方案二的 Rust 只做「解碼」,方案三的 Rust 做「藍牙通訊 + 解碼」。

什麼時候用這條路:

  • 廠商有公開協定文件,但沒有封裝好的 SDK(或 SDK 只有 C/C++ 版本)
  • 裝置是標準 BLE,手機端為主
  • 你想自己控制解碼邏輯,不依賴廠商 code

什麼時候不適合:

  • 桌面端(Windows/Linux/macOS)——flutter_blue_plus 只支援手機,桌面要改用 universal_ble_forklayrz_ble
  • 廠商沒公開協定——你根本不知道那些 bytes 是什麼

方案三:btleplug + FFI(實驗性方案)

這條路的概念是:不用 flutter_blue_plus,也不用平台原生的藍牙 API——直接用 Rust 的 btleplug 跟藍牙晶片對話,然後透過 FFI 接給 Flutter。Rust 同時管藍牙通訊和解碼,Flutter 只處理 UI。

btleplug 2026 年已經支援 Windows、macOS、Linux,核心功能(掃描、連線、GATT 讀寫、Notify)在所有平台都是齊的。pub.dev 上也已經有 bluetooth_flutter 這個 package 嘗試封裝這條路(2026 年 6 月發布 v0.0.1)。

但目前還在非常早期的階段。如果你是做 Desktop App 而且有 Rust 經驗,可以關注這條路的發展——未來一兩年可能會成熟到生產可用。但如果你現在就要上線,這條路還需要大量的自行測試和補坑。手機端更是如此——iOS/Android 不讓你繞過系統藍牙 API,btleplug 在手機上底層還是走 CoreBluetooth/BluetoothAdapter,沒有真正「繞過」,走方案一或方案二更實際。

方向是對的,但目前可能還不是時候。


總結——三條路線對照

方案一:廠商給 SDK方案二:廠商給標準 + 自己解碼方案三:btleplug + FFI
廠商給你什麼封裝好的 SDK(黑箱)協定文件(開放標準)不需要(你用 Rust stack)
平台iOS / AndroidiOS / AndroidDesktop(Win/Mac/Linux)
藍牙誰管廠商 SDKflutter_blue_plusRust btleplug
橋接層手寫 Platform Channel不需要(package 內建)FFI + flutter_rust_bridge
解碼層廠商 SDK 處理你自己寫(Dart 或 Rust/C++)Rust
成熟度看廠商 SDK 品質高,生產可用實驗性,發展中
原生 code 量兩套(Kotlin + Swift)幾乎沒有沒有(都 Rust)
適合廠商封閉 SDK、沒公開協定有協定文件、想自己控制解碼未來可期,現在先觀望

三條路線沒有誰是絕對最好的。關鍵是你手上的硬體是什麼、廠商給了你什麼、目標平台是什麼——然後照這個表,走對的那條路。


BLE 處理的是近距離低功耗通訊。如果你的 IoT 裝置走 WiFi——或 BLE 配對完就切 WiFi——那又是另一條路,門檻比 BLE 低很多。細節寫在 WiFi 篇