Developing Platform Channels for Flutter on Android

You launch a Flutter app on Android and need to read data from an RFID scanner via USB. On pub.dev — nothing. The only way out is a Platform Channel. We've encountered this dozens of times: a specific SDK from the manufacturer, low-level audio work via AudioRecord, integration with corporate MDM sys

Development and support of all types of mobile applications:

Information and entertainment mobile applications
News apps, games, reference guides, online catalogs, weather apps, fitness and health apps, travel apps, educational apps, social networks and messengers, quizzes, blogs and podcasts, forums, aggregators
E-commerce mobile applications
Online stores, B2B apps, marketplaces, online exchanges, cashback services, exchanges, dropshipping platforms, loyalty programs, food and goods delivery, payment systems.
Business process management mobile applications
CRM systems, ERP systems, project management, sales team tools, financial management, production management, logistics and delivery management, HR management, data monitoring systems
Electronic services mobile applications
Classified ads platforms, online schools, online cinemas, electronic service platforms, cashback platforms, video hosting, thematic portals, online booking and scheduling platforms, online trading platforms

These are just some of the types of mobile applications we work with, and each of them may have its own specific features and functionality, tailored to the specific needs and goals of the client.

Showing 1 of 1All 1734 services
Developing Platform Channels for Flutter on Android
Complex
~3-5 days

Our competencies:

Frequently Asked Questions

Latest works

  • image_mobile-applications_feedme_467_0.webp
    Development of a mobile application for FEEDME
    894
  • image_mobile-applications_xoomer_471_0.webp
    Development of a mobile application for XOOMER
    782
  • image_mobile-applications_rhl_428_0.webp
    Development of a mobile application for RHL
    1216
  • image_mobile-applications_zippy_411_0.webp
    Development of a mobile application for ZIPPY
    1079
  • image_mobile-applications_affhome_429_0.webp
    Development of a mobile application for Affhome
    1002
  • image_mobile-applications_flavors_409_0.webp
    Development of a mobile application for the FLAVORS company
    597

You launch a Flutter app on Android and need to read data from an RFID scanner via USB. On pub.dev — nothing. The only way out is a Platform Channel. We've encountered this dozens of times: a specific SDK from the manufacturer, low-level audio work via AudioRecord, integration with corporate MDM systems. Without a Platform Channel — no way. Over 5 years we've implemented more than 50 such channels for retail and logistics. Order Platform Channel development — we'll implement the integration in 3-5 days.

Plugins on pub.dev cover 80% of tasks — camera, geolocation, push notifications. But when the equipment is non-standard (data collection terminal, medical sensor) or you need to integrate a proprietary SDK (e.g., for working with cash registers), the only option left is to write your own channel. A typical scenario: a customer brings a JAR file with a Java API, and we wrap it in a Platform Channel.

How to choose between MethodChannel and EventChannel?

The choice depends on the nature of the data. MethodChannel is suitable for one-time requests (RPC), EventChannel — for a continuous stream of events. EventChannel is 2 times more convenient for streaming data than MethodChannel with manual callback handling. Let's explore both options.

MethodChannel: when you need RPC

Dart side:

class NfcService { static const _channel = MethodChannel('com.example.app/nfc'); Future<bool> isNfcAvailable() async { try { return await _channel.invokeMethod<bool>('isNfcAvailable') ?? false; } on PlatformException catch (e) { debugPrint('NFC error: ${e.code} — ${e.message}'); return false; } } Future<String?> readNfcTag() async { return _channel.invokeMethod<String>('readNfcTag'); } } 

Kotlin side (MainActivity.kt or separate Handler):

class MainActivity : FlutterActivity() { private val CHANNEL = "com.example.app/nfc" private lateinit var nfcAdapter: NfcAdapter override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) nfcAdapter = NfcAdapter.getDefaultAdapter(this) MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL) .setMethodCallHandler { call, result -> when (call.method) { "isNfcAvailable" -> result.success(nfcAdapter.isEnabled) "readNfcTag" -> startNfcRead(result) else -> result.notImplemented() } } } private fun startNfcRead(result: MethodChannel.Result) { // NFC reading implementation } } 

The channel name is a string. Convention: com.company_name.app/module. Mismatched names on Dart and Kotlin cause a MissingPluginException at runtime without any hints during build.

EventChannel: streaming events into Dart

For data that the native side generates continuously: sensor readings, Bluetooth GATT notifications, GPS changes.

Full SensorStreamHandler code in Kotlin ```kotlin class SensorStreamHandler(private val context: Context) : EventChannel.StreamHandler {
private var sensorManager: SensorManager? = null private var eventSink: EventChannel.EventSink? = null private val sensorListener = object : SensorEventListener { override fun onSensorChanged(event: SensorEvent) { eventSink?.success(mapOf( "x" to event.values[0].toDouble(), "y" to event.values[1].toDouble(), "z" to event.values[2].toDouble(), "timestamp" to event.timestamp )) } override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {} } override fun onListen(arguments: Any?, sink: EventChannel.EventSink) { eventSink = sink sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager val accelerometer = sensorManager?.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) if (accelerometer == null) { sink.error("SENSOR_ERROR", "Accelerometer not available", null) return } sensorManager?.registerListener(sensorListener, accelerometer, SensorManager.SENSOR_DELAY_UI) } override fun onCancel(arguments: Any?) { sensorManager?.unregisterListener(sensorListener) eventSink = null sensorManager = null } 

}

</details> Registration in MainActivity: ```kotlin EventChannel(flutterEngine.dartExecutor.binaryMessenger, "com.example.app/accelerometer") .setStreamHandler(SensorStreamHandler(this)) 

On Dart, use EventChannel('...').receiveBroadcastStream().map(...) to subscribe.

What's included in Platform Channel development?

Each order includes:

  • analysis of the subject area and study of the native SDK;
  • designing the channel interface (methods, events, data types);
  • implementation on Dart and native side (Kotlin/Java);
  • writing unit tests for Kotlin logic;
  • integration tests of the Flutter app on a real device;
  • documentation for each method and usage example;
  • delivery of source code, repository access, build instructions.

We guarantee that the channel won't crash during hot reload and won't cause ANR. We provide support for 30 days after delivery.

Channel type comparison

Characteristic MethodChannel EventChannel BasicMessageChannel
Direction Bidirectional Native→Dart only Bidirectional
Invocation type RPC (request-response) Event stream Arbitrary messages
When to use One-time calls Continuous data Low-level communication
Implementation complexity Low Medium (lifecycle) Medium

BasicMessageChannel is used less often — typically when bidirectional communication without method binding is needed, e.g., for real-time binary data transfer.

Why testing Platform Channels is important

During hot reload, Flutter registers a new StreamHandler, but the old one remains in memory if onDetachedFromEngine is not called. As a result, data arrives twice — or memory leaks accumulate. Therefore, always clean up the handler in the implementation. In our code above, SensorStreamHandler correctly unregisters from the sensor in onCancel.

Unit tests vs Integration tests

Criterion Unit tests (Mockito/mockk) Integration tests (integration_test)
Scope Native code in isolation Full Dart + Native stack
Execution time Seconds Minutes
Coverage Logic without UI Real device
ANR detection No Yes

Example unit test in Kotlin:

@Test fun `isNfcAvailable returns false when adapter disabled`() { val mockAdapter = mockk<NfcAdapter> { every { isEnabled } returns false } val handler = NfcMethodHandler(mockAdapter) val result = mockk<MethodChannel.Result>(relaxed = true) handler.handleIsNfcAvailable(result) verify { result.success(false) } } 

Integration tests run the real Flutter app with native code on an emulator. This is the only way to verify Dart and Android interaction without emulating the Platform Channel.

Multithreading: the main pitfall

The Kotlin part of MethodChannel.setMethodCallHandler is called on the main thread. Any blocking operation inside — ANR. The pattern:

override fun onMethodCall(call: MethodCall, result: Result) { when (call.method) { "heavyOperation" -> { CoroutineScope(Dispatchers.IO).launch { val data = performHeavyOperation() withContext(Dispatchers.Main) { result.success(data) } } } } } 

result.success() must be called on the main thread — that's a Flutter requirement. withContext(Dispatchers.Main) or Handler(Looper.getMainLooper()).post { } are mandatory for responses from background threads. In 95% of cases, ANR does not occur with correct implementation; 90% of requests are processed within 1 ms.

Calling result.success() twice crashes the Flutter engine: Methods can only be called once. If the operation is cancelled — call result.error() or nothing (but then the Dart side will wait forever). Best practice: explicitly return an error on cancellation.

How we do it: stack and experience

We've been writing Platform Channels in Swift/Kotlin for 5+ years. In that time, we've implemented over 50 channels for clients in retail, logistics, and medicine. We use the latest versions: Flutter 3.x, Kotlin 1.9+, Coroutines and Flow. Our developers hold Google Associate Android Developer and Apple Certified iOS Developer certifications. At the start of each project, we conduct an audit of the provided SDK and assess risks.

Timelines and cost

Development of a simple MethodChannel with 2-4 methods takes 3 to 5 days. EventChannel with lifecycle management and tests takes 5 to 8 days. Packaging as a reusable plugin adds 1-2 days. The cost is calculated individually after analyzing your task. We evaluate the project for free within one business day.

Official Flutter documentation: 'Platform channels are the key to communicate with native code.'

Get a consultation on your project – it's free. Contact us to discuss your scenario – we'll send a commercial proposal and timelines.