Claude
Skills
Sign in
Back

custom-plugin-flutter-skill-plugins

Included with Lifetime
$97 forever

Production-grade Flutter plugin development mastery - Platform channels, federated architecture, MethodChannel/EventChannel, iOS Swift/Android Kotlin integration, pub.dev publishing with comprehensive code examples

Generalscriptsassets

What this skill does


# custom-plugin-flutter: Plugins Skill

## Quick Start - Create Production Plugin

```bash
# Create federated plugin
flutter create --template=plugin --platforms=android,ios,web my_plugin

# Plugin structure
my_plugin/
├── lib/
│   ├── my_plugin.dart              # Public API
│   └── my_plugin_method_channel.dart
├── android/
│   └── src/main/kotlin/.../MyPlugin.kt
├── ios/
│   └── Classes/MyPlugin.swift
├── example/                         # Example app
├── test/                           # Unit tests
├── pubspec.yaml
└── README.md
```

```dart
// lib/my_plugin.dart - Public API
import 'my_plugin_platform_interface.dart';

class MyPlugin {
  Future<String?> getPlatformVersion() {
    return MyPluginPlatform.instance.getPlatformVersion();
  }

  Future<void> initialize({required String apiKey}) {
    return MyPluginPlatform.instance.initialize(apiKey: apiKey);
  }

  Stream<SensorData> get sensorStream {
    return MyPluginPlatform.instance.sensorStream;
  }
}
```

## 1. MethodChannel (Request/Response)

### Dart Implementation

```dart
class MethodChannelMyPlugin extends MyPluginPlatform {
  static const _channel = MethodChannel('com.example/my_plugin');

  @override
  Future<String?> getPlatformVersion() async {
    try {
      return await _channel.invokeMethod<String>('getPlatformVersion');
    } on PlatformException catch (e) {
      throw PluginException('Failed to get version: ${e.message}');
    }
  }

  @override
  Future<Map<String, dynamic>> getDeviceInfo() async {
    try {
      final result = await _channel.invokeMethod<Map>('getDeviceInfo');
      return Map<String, dynamic>.from(result ?? {});
    } on PlatformException catch (e) {
      throw PluginException('Failed to get device info: ${e.message}');
    }
  }

  @override
  Future<void> performAction(ActionParams params) async {
    try {
      await _channel.invokeMethod('performAction', params.toMap());
    } on PlatformException catch (e) {
      throw PluginException('Action failed: ${e.message}');
    }
  }
}
```

### iOS Swift Implementation

```swift
// ios/Classes/MyPlugin.swift
import Flutter
import UIKit

public class MyPlugin: NSObject, FlutterPlugin {
  public static func register(with registrar: FlutterPluginRegistrar) {
    let channel = FlutterMethodChannel(
      name: "com.example/my_plugin",
      binaryMessenger: registrar.messenger()
    )
    let instance = MyPlugin()
    registrar.addMethodCallDelegate(instance, channel: channel)
  }

  public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
    switch call.method {
    case "getPlatformVersion":
      result("iOS " + UIDevice.current.systemVersion)

    case "getDeviceInfo":
      result([
        "model": UIDevice.current.model,
        "name": UIDevice.current.name,
        "systemVersion": UIDevice.current.systemVersion
      ])

    case "performAction":
      guard let args = call.arguments as? [String: Any] else {
        result(FlutterError(code: "INVALID_ARGS", message: "Invalid arguments", details: nil))
        return
      }
      // Perform action with args
      result(nil)

    default:
      result(FlutterMethodNotImplemented)
    }
  }
}
```

### Android Kotlin Implementation

```kotlin
// android/src/main/kotlin/.../MyPlugin.kt
package com.example.my_plugin

import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result

class MyPlugin: FlutterPlugin, MethodCallHandler {
  private lateinit var channel: MethodChannel

  override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
    channel = MethodChannel(binding.binaryMessenger, "com.example/my_plugin")
    channel.setMethodCallHandler(this)
  }

  override fun onMethodCall(call: MethodCall, result: Result) {
    when (call.method) {
      "getPlatformVersion" -> {
        result.success("Android ${android.os.Build.VERSION.RELEASE}")
      }
      "getDeviceInfo" -> {
        result.success(mapOf(
          "model" to android.os.Build.MODEL,
          "manufacturer" to android.os.Build.MANUFACTURER,
          "version" to android.os.Build.VERSION.RELEASE
        ))
      }
      "performAction" -> {
        val args = call.arguments as? Map<*, *>
        // Perform action
        result.success(null)
      }
      else -> result.notImplemented()
    }
  }

  override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
    channel.setMethodCallHandler(null)
  }
}
```

## 2. EventChannel (Streams)

### Dart Implementation

```dart
class MethodChannelMyPlugin extends MyPluginPlatform {
  static const _eventChannel = EventChannel('com.example/my_plugin/events');

  Stream<SensorData>? _sensorStream;

  @override
  Stream<SensorData> get sensorStream {
    _sensorStream ??= _eventChannel
        .receiveBroadcastStream()
        .map((event) => SensorData.fromMap(event as Map));
    return _sensorStream!;
  }
}

class SensorData {
  final double x, y, z;
  final DateTime timestamp;

  SensorData({required this.x, required this.y, required this.z, required this.timestamp});

  factory SensorData.fromMap(Map map) {
    return SensorData(
      x: map['x'] as double,
      y: map['y'] as double,
      z: map['z'] as double,
      timestamp: DateTime.fromMillisecondsSinceEpoch(map['timestamp'] as int),
    );
  }
}
```

### iOS EventChannel

```swift
class SensorStreamHandler: NSObject, FlutterStreamHandler {
  private var eventSink: FlutterEventSink?
  private var timer: Timer?

  func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
    self.eventSink = events
    startSensorUpdates()
    return nil
  }

  func onCancel(withArguments arguments: Any?) -> FlutterError? {
    stopSensorUpdates()
    eventSink = nil
    return nil
  }

  private func startSensorUpdates() {
    timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { [weak self] _ in
      self?.eventSink?([
        "x": 1.0,
        "y": 2.0,
        "z": 3.0,
        "timestamp": Int(Date().timeIntervalSince1970 * 1000)
      ])
    }
  }

  private func stopSensorUpdates() {
    timer?.invalidate()
    timer = nil
  }
}

// Register in plugin
public static func register(with registrar: FlutterPluginRegistrar) {
  let eventChannel = FlutterEventChannel(
    name: "com.example/my_plugin/events",
    binaryMessenger: registrar.messenger()
  )
  eventChannel.setStreamHandler(SensorStreamHandler())
}
```

### Android EventChannel

```kotlin
class SensorStreamHandler : EventChannel.StreamHandler {
  private var eventSink: EventChannel.EventSink? = null
  private var handler: Handler? = null
  private var runnable: Runnable? = null

  override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
    eventSink = events
    startSensorUpdates()
  }

  override fun onCancel(arguments: Any?) {
    stopSensorUpdates()
    eventSink = null
  }

  private fun startSensorUpdates() {
    handler = Handler(Looper.getMainLooper())
    runnable = object : Runnable {
      override fun run() {
        eventSink?.success(mapOf(
          "x" to 1.0,
          "y" to 2.0,
          "z" to 3.0,
          "timestamp" to System.currentTimeMillis()
        ))
        handler?.postDelayed(this, 100)
      }
    }
    handler?.post(runnable!!)
  }

  private fun stopSensorUpdates() {
    runnable?.let { handler?.removeCallbacks(it) }
  }
}
```

## 3. Federated Plugin Architecture

```
my_plugin/                          # App-facing package
├── lib/my_plugin.dart
├── pubspec.yaml
│   dependencies:
│     my_plugin_platform_interface: ^1.0.0
│     my_plugin_android: ^1.0.0     # Endorsed
│     my_plugin_ios: ^1.0.0         # Endorsed
│     my_plugin_web: ^1.0.0         # Endorsed

my_plugin_platform_interface/       # Platform interface
├── lib/
│   ├── my_plugin_platform_

Related in General