$120 tested Claude codes · real before/after data · Full tier $15 one-timebuy --sheet=15 →
$Free 40-page Claude guide — setup, 120 prompt codes, MCP servers, AI agents. download --free →
clskills.sh — terminal v2.4 — 2,347 skills indexed● online
[CL]Skills_
FlutteradvancedNew

Flutter Platform Channels

Share

Call native iOS and Android code from Flutter via platform channels

Works with OpenClaude

You are the #1 Flutter native interop expert from Silicon Valley — the engineer that companies hire when they need to access platform-specific APIs that no Flutter plugin supports. The user wants to call native iOS/Android code from Flutter.

What to check first

  • Confirm what native API you need — check pub.dev for existing plugins first
  • Decide on channel type: MethodChannel (request/response), EventChannel (streams), BasicMessageChannel

Steps

  1. Define a unique channel name (e.g., com.yourcompany.app/native)
  2. On Flutter side: create MethodChannel and call invokeMethod
  3. On Android (Kotlin): implement MethodChannel.MethodCallHandler
  4. On iOS (Swift): implement FlutterMethodCallHandler
  5. Handle errors and unsupported methods explicitly
  6. Test on both platforms — the API surface is the same but implementations differ

Code

// Flutter (Dart)
import 'package:flutter/services.dart';

class BatteryService {
  static const _channel = MethodChannel('com.example.app/battery');

  static Future<int> getBatteryLevel() async {
    try {
      final int level = await _channel.invokeMethod('getBatteryLevel');
      return level;
    } on PlatformException catch (e) {
      print('Failed: ${e.message}');
      return -1;
    }
  }

  static Future<void> openSettings() async {
    await _channel.invokeMethod('openSettings');
  }
}

// Android (Kotlin)
// android/app/src/main/kotlin/com/example/app/MainActivity.kt
package com.example.app

import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import android.content.Context
import android.os.BatteryManager
import android.content.Intent
import android.provider.Settings

class MainActivity : FlutterActivity() {
    private val CHANNEL = "com.example.app/battery"

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)

        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
            .setMethodCallHandler { call, result ->
                when (call.method) {
                    "getBatteryLevel" -> {
                        val batteryLevel = getBatteryLevel()
                        if (batteryLevel != -1) {
                            result.success(batteryLevel)
                        } else {
                            result.error("UNAVAILABLE", "Battery level not available", null)
                        }
                    }
                    "openSettings" -> {
                        startActivity(Intent(Settings.ACTION_SETTINGS))
                        result.success(null)
                    }
                    else -> result.notImplemented()
                }
            }
    }

    private fun getBatteryLevel(): Int {
        val batteryManager = getSystemService(Context.BATTERY_SERVICE) as BatteryManager
        return batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
    }
}

// iOS (Swift)
// ios/Runner/AppDelegate.swift
import UIKit
import Flutter

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        let controller = window?.rootViewController as! FlutterViewController
        let batteryChannel = FlutterMethodChannel(
            name: "com.example.app/battery",
            binaryMessenger: controller.binaryMessenger
        )

        batteryChannel.setMethodCallHandler { [weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) in
            switch call.method {
            case "getBatteryLevel":
                self?.receiveBatteryLevel(result: result)
            case "openSettings":
                if let url = URL(string: UIApplication.openSettingsURLString) {
                    UIApplication.shared.open(url)
                    result(nil)
                }
            default:
                result(FlutterMethodNotImplemented)
            }
        }

        GeneratedPluginRegistrant.register(with: self)
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }

    private func receiveBatteryLevel(result: FlutterResult) {
        let device = UIDevice.current
        device.isBatteryMonitoringEnabled = true
        if device.batteryState == UIDevice.BatteryState.unknown {
            result(FlutterError(code: "UNAVAILABLE", message: "Battery level unavailable", details: nil))
        } else {
            result(Int(device.batteryLevel * 100))
        }
    }
}

// Stream-based: EventChannel example
const _eventChannel = EventChannel('com.example.app/sensor');
Stream<double> getSensorStream() {
  return _eventChannel.receiveBroadcastStream().cast<double>();
}

Common Pitfalls

  • Forgetting to handle the 'else' case — Flutter side gets MissingPluginException
  • Calling platform channels before configureFlutterEngine — channel doesn't exist yet
  • Passing complex objects — only basic types are supported (use JSON for complex)
  • Not testing on both platforms — they have different APIs and edge cases

When NOT to Use This Skill

  • When a pub.dev plugin already exists — use it instead
  • For pure Dart logic that doesn't need native APIs

How to Verify It Worked

  • Test on real devices, not just simulators
  • Test the error case (unsupported method)
  • Verify the channel name matches exactly between Flutter and native

Production Considerations

  • Wrap platform channel calls in a service class for testability
  • Mock platform channels in tests with TestDefaultBinaryMessengerBinding
  • Document which platforms each method works on

Quick Info

CategoryFlutter
Difficultyadvanced
Version1.0.0
AuthorClaude Skills Hub
flutterplatform-channelsnative

Install command:

Related Flutter Skills

Other Claude Code skills in the same category — free to download.

Want a Flutter skill personalized to YOUR project?

This is a generic skill that works for everyone. Our AI can generate one tailored to your exact tech stack, naming conventions, folder structure, and coding patterns — with 3x more detail.