Creating Custom Platform Channels for Bluetooth Low Energy (BLE) Communication in Flutter
Jul 11, 2025



Summary
Summary
Summary
Summary
This tutorial guides you through creating custom Flutter platform channels for BLE communication. You’ll set up Dart MethodChannels, define a BLE protocol, and implement native scanning on iOS (Swift) and Android (Kotlin). Finally, learn strategies for testing and debugging your BLE integration to ensure reliable device discovery and data exchange across both platforms.
This tutorial guides you through creating custom Flutter platform channels for BLE communication. You’ll set up Dart MethodChannels, define a BLE protocol, and implement native scanning on iOS (Swift) and Android (Kotlin). Finally, learn strategies for testing and debugging your BLE integration to ensure reliable device discovery and data exchange across both platforms.
This tutorial guides you through creating custom Flutter platform channels for BLE communication. You’ll set up Dart MethodChannels, define a BLE protocol, and implement native scanning on iOS (Swift) and Android (Kotlin). Finally, learn strategies for testing and debugging your BLE integration to ensure reliable device discovery and data exchange across both platforms.
This tutorial guides you through creating custom Flutter platform channels for BLE communication. You’ll set up Dart MethodChannels, define a BLE protocol, and implement native scanning on iOS (Swift) and Android (Kotlin). Finally, learn strategies for testing and debugging your BLE integration to ensure reliable device discovery and data exchange across both platforms.
Key insights:
Key insights:
Key insights:
Key insights:
Setting up the Flutter Side: Define a MethodChannel in Dart to send BLE commands like startScan and stopScan to native code.
Defining the BLE Protocol: Agree on JSON or byte protocols for method names and argument keys to ensure consistent data exchange.
Implementing the iOS Platform Channel: Use CBCentralManager in Swift, handle state updates, and scan when Flutter invokes startScan.
Implementing the Android Platform Channel: Leverage BluetoothLeScanner with MethodChannel in Kotlin for startScan/stopScan logic.
Testing and Debugging: Use logging, physical devices for BLE tests, and native debuggers in Xcode/Android Studio to validate integration.
Introduction
Building robust Bluetooth Low Energy (BLE) features in Flutter often requires leveraging native APIs through platform channels. Flutter’s MethodChannel lets you invoke platform-specific code (Kotlin/Java on Android, Swift/Objective-C on iOS) while maintaining a unified Dart interface. This tutorial walks you through creating custom platform channels for BLE communication, from defining protocols to implementing native scanning and data exchange.
Setting up the Flutter Side
First, add flutter_blue
(or your preferred BLE plugin) to pubspec.yaml
. Then define a MethodChannel in your Dart code that communicates with native layers.
import 'package:flutter/services.dart';
class BlePlatform {
static const MethodChannel _channel =
MethodChannel('com.example.ble/channel');
Future<void> startScan() async {
await _channel.invokeMethod('startScan');
}
Future<void> stopScan() async {
await _channel.invokeMethod('stopScan');
}
}
This class wraps two native calls: startScan
and stopScan
. You can extend it with methods for connecting, reading and writing BLE characteristics.
Defining the BLE Protocol
Next, establish a protocol for passing data. Use JSON strings or byte arrays. For example, you can send a map to indicate a connection request:
Future<void> connectToDevice(String deviceId) async {
final args = {'deviceId': deviceId};
await _channel.invokeMethod('connect', args);
}
Ensure both Dart and native sides agree on method names (connect
) and argument keys (deviceId
). Define constants for UUIDs of services and characteristics in Dart so they stay in sync with your native implementation.
Implementing the iOS Platform Channel
Open ios/Runner/AppDelegate.swift
and import Flutter:
import UIKit
import Flutter
import CoreBluetooth
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate, CBCentralManagerDelegate {
var centralManager: CBCentralManager?
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
let bleChannel = FlutterMethodChannel(
name: "com.example.ble/channel",
binaryMessenger: controller.binaryMessenger)
bleChannel.setMethodCallHandler { call, result in
switch call.method {
case "startScan":
self.centralManager = CBCentralManager(delegate: self, queue: nil)
result(nil)
case "stopScan":
self.centralManager?.stopScan()
result(nil)
default:
result(FlutterMethodNotImplemented)
}
}
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == .poweredOn {
central.scanForPeripherals(withServices: nil, options: nil)
}
}
}
This Swift code initializes a CBCentralManager
and handles scanning when Flutter calls startScan
.
Implementing the Android Platform Channel
In android/app/src/main/kotlin/.../MainActivity.kt
register the channel and implement BLE scanning with BluetoothLeScanner
:
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothManager
import android.os.Bundle
import io.flutter.embedding.android.FlutterActivity
import io.flutter.plugin.common.MethodChannel
class MainActivity: FlutterActivity() {
private val CHANNEL = "com.example.ble/channel"
private lateinit var bluetoothAdapter: BluetoothAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val bluetoothManager = getSystemService(BLUETOOTH_SERVICE) as BluetoothManager
bluetoothAdapter = bluetoothManager.adapter
MethodChannel(
flutterEngine?.dartExecutor?.binaryMessenger, CHANNEL
).setMethodCallHandler { call, result ->
when (call.method) {
"startScan" -> {
bluetoothAdapter.bluetoothLeScanner.startScan(null, null, scanCallback)
result.success(null)
}
"stopScan" -> {
bluetoothAdapter.bluetoothLeScanner.stopScan(scanCallback)
result.success(null)
}
else -> result.notImplemented()
}
}
}
}
Define scanCallback
to handle discovered devices and forward them via MethodChannel.invokeMethod
using an EventChannel or another MethodChannel call.
Testing and Debugging
To verify your implementation:
• Use the Dart side to call startScan
and listen for device lists.
• On iOS Simulator, BLE is limited; test on a physical device.
• For Android, enable location permissions and ensure Bluetooth is on.
• Add logging on both sides (print
in Dart, NSLog
/Log.d
in native) to trace method invocations and state changes.
• Use breakpoints in Xcode and Android Studio to inspect native callbacks.
Vibe Studio

Vibe Studio, powered by Steve’s advanced AI agents, is a revolutionary no-code, conversational platform that empowers users to quickly and efficiently create full-stack Flutter applications integrated seamlessly with Firebase backend services. Ideal for solo founders, startups, and agile engineering teams, Vibe Studio allows users to visually manage and deploy Flutter apps, greatly accelerating the development process. The intuitive conversational interface simplifies complex development tasks, making app creation accessible even for non-coders.
Conclusion
By setting up a consistent MethodChannel protocol and implementing native BLE scanning in Swift and Kotlin, you can integrate advanced Bluetooth Low Energy features into Flutter apps. This approach maintains a clean separation between Dart UI code and platform-specific logic, ensuring maintainable and scalable mobile development.
Introduction
Building robust Bluetooth Low Energy (BLE) features in Flutter often requires leveraging native APIs through platform channels. Flutter’s MethodChannel lets you invoke platform-specific code (Kotlin/Java on Android, Swift/Objective-C on iOS) while maintaining a unified Dart interface. This tutorial walks you through creating custom platform channels for BLE communication, from defining protocols to implementing native scanning and data exchange.
Setting up the Flutter Side
First, add flutter_blue
(or your preferred BLE plugin) to pubspec.yaml
. Then define a MethodChannel in your Dart code that communicates with native layers.
import 'package:flutter/services.dart';
class BlePlatform {
static const MethodChannel _channel =
MethodChannel('com.example.ble/channel');
Future<void> startScan() async {
await _channel.invokeMethod('startScan');
}
Future<void> stopScan() async {
await _channel.invokeMethod('stopScan');
}
}
This class wraps two native calls: startScan
and stopScan
. You can extend it with methods for connecting, reading and writing BLE characteristics.
Defining the BLE Protocol
Next, establish a protocol for passing data. Use JSON strings or byte arrays. For example, you can send a map to indicate a connection request:
Future<void> connectToDevice(String deviceId) async {
final args = {'deviceId': deviceId};
await _channel.invokeMethod('connect', args);
}
Ensure both Dart and native sides agree on method names (connect
) and argument keys (deviceId
). Define constants for UUIDs of services and characteristics in Dart so they stay in sync with your native implementation.
Implementing the iOS Platform Channel
Open ios/Runner/AppDelegate.swift
and import Flutter:
import UIKit
import Flutter
import CoreBluetooth
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate, CBCentralManagerDelegate {
var centralManager: CBCentralManager?
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
let bleChannel = FlutterMethodChannel(
name: "com.example.ble/channel",
binaryMessenger: controller.binaryMessenger)
bleChannel.setMethodCallHandler { call, result in
switch call.method {
case "startScan":
self.centralManager = CBCentralManager(delegate: self, queue: nil)
result(nil)
case "stopScan":
self.centralManager?.stopScan()
result(nil)
default:
result(FlutterMethodNotImplemented)
}
}
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == .poweredOn {
central.scanForPeripherals(withServices: nil, options: nil)
}
}
}
This Swift code initializes a CBCentralManager
and handles scanning when Flutter calls startScan
.
Implementing the Android Platform Channel
In android/app/src/main/kotlin/.../MainActivity.kt
register the channel and implement BLE scanning with BluetoothLeScanner
:
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothManager
import android.os.Bundle
import io.flutter.embedding.android.FlutterActivity
import io.flutter.plugin.common.MethodChannel
class MainActivity: FlutterActivity() {
private val CHANNEL = "com.example.ble/channel"
private lateinit var bluetoothAdapter: BluetoothAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val bluetoothManager = getSystemService(BLUETOOTH_SERVICE) as BluetoothManager
bluetoothAdapter = bluetoothManager.adapter
MethodChannel(
flutterEngine?.dartExecutor?.binaryMessenger, CHANNEL
).setMethodCallHandler { call, result ->
when (call.method) {
"startScan" -> {
bluetoothAdapter.bluetoothLeScanner.startScan(null, null, scanCallback)
result.success(null)
}
"stopScan" -> {
bluetoothAdapter.bluetoothLeScanner.stopScan(scanCallback)
result.success(null)
}
else -> result.notImplemented()
}
}
}
}
Define scanCallback
to handle discovered devices and forward them via MethodChannel.invokeMethod
using an EventChannel or another MethodChannel call.
Testing and Debugging
To verify your implementation:
• Use the Dart side to call startScan
and listen for device lists.
• On iOS Simulator, BLE is limited; test on a physical device.
• For Android, enable location permissions and ensure Bluetooth is on.
• Add logging on both sides (print
in Dart, NSLog
/Log.d
in native) to trace method invocations and state changes.
• Use breakpoints in Xcode and Android Studio to inspect native callbacks.
Vibe Studio

Vibe Studio, powered by Steve’s advanced AI agents, is a revolutionary no-code, conversational platform that empowers users to quickly and efficiently create full-stack Flutter applications integrated seamlessly with Firebase backend services. Ideal for solo founders, startups, and agile engineering teams, Vibe Studio allows users to visually manage and deploy Flutter apps, greatly accelerating the development process. The intuitive conversational interface simplifies complex development tasks, making app creation accessible even for non-coders.
Conclusion
By setting up a consistent MethodChannel protocol and implementing native BLE scanning in Swift and Kotlin, you can integrate advanced Bluetooth Low Energy features into Flutter apps. This approach maintains a clean separation between Dart UI code and platform-specific logic, ensuring maintainable and scalable mobile development.
Build Flutter Apps Faster with Vibe Studio
Build Flutter Apps Faster with Vibe Studio
Build Flutter Apps Faster with Vibe Studio
Build Flutter Apps Faster with Vibe Studio
Vibe Studio is your AI-powered Flutter development companion. Skip boilerplate, build in real-time, and deploy without hassle. Start creating apps at lightning speed with zero setup.
Vibe Studio is your AI-powered Flutter development companion. Skip boilerplate, build in real-time, and deploy without hassle. Start creating apps at lightning speed with zero setup.
Vibe Studio is your AI-powered Flutter development companion. Skip boilerplate, build in real-time, and deploy without hassle. Start creating apps at lightning speed with zero setup.
Vibe Studio is your AI-powered Flutter development companion. Skip boilerplate, build in real-time, and deploy without hassle. Start creating apps at lightning speed with zero setup.
Join a growing community of builders today
Join a growing
community
of builders today
Join a growing
community
of builders today










The Jacx Office: 16-120
2807 Jackson Ave
Queens NY 11101, United States


The Jacx Office: 16-120
2807 Jackson Ave
Queens NY 11101, United States


The Jacx Office: 16-120
2807 Jackson Ave
Queens NY 11101, United States


The Jacx Office: 16-120
2807 Jackson Ave
Queens NY 11101, United States


The Jacx Office: 16-120
2807 Jackson Ave
Queens NY 11101, United States


The Jacx Office: 16-120
2807 Jackson Ave
Queens NY 11101, United States