Skip to content
Blog

Building Production-Ready Custom Plugins for Flutter

Learn how to structure, develop, test, and publish production-grade Flutter plugins for Android, iOS, Web, and Desktop platforms.

Published on • November 9, 2026

AI Assistant

A Flutter plugin bridges Dart code with native platform code (Kotlin, Swift, C++, JavaScript) to expose operating system capabilities that aren’t available out of the box in the framework.

Whether you’re wrapping a third-party native SDK or authoring internal corporate tooling, understanding plugin scaffolding, lifecycle management, and distribution ensures long-term package maintainability.

Scaffolding a Plugin Project

Use the flutter create CLI with the --template=plugin flag to scaffold a multi-platform plugin structure:

flutter create --template=plugin \
  --platforms=android,ios,web,windows,macos,linux \
  --org=net.redlinesoft \
  my_custom_sensor

This generates a workspace layout:

my_custom_sensor/
├── android/            # Native Kotlin/Java code
├── ios/                # Native Swift/Objective-C code
├── web/                # JS Interop code
├── lib/
│   ├── my_custom_sensor.dart            # User-facing API surface
│   ├── my_custom_sensor_platform_interface.dart # Abstract interface
│   └── my_custom_sensor_method_channel.dart     # MethodChannel default
├── example/            # Sample Flutter test app
└── pubspec.yaml        # Plugin metadata & platform declarations

Configuring Platform Declarations in pubspec.yaml

The plugin’s pubspec.yaml specifies native class entry points for each supported target platform:

name: my_custom_sensor
description: "A production-grade custom sensor plugin for Flutter."
version: 1.0.0
homepage: https://github.com/redlinesoft/my_custom_sensor

environment:
  sdk: ">=3.4.0 <4.0.0"
  flutter: ">=3.22.0"

flutter:
  plugin:
    platforms:
      android:
        package: net.redlinesoft.my_custom_sensor
        pluginClass: MyCustomSensorPlugin
      ios:
        pluginClass: MyCustomSensorPlugin
      web:
        pluginClass: MyCustomSensorWeb
        fileName: my_custom_sensor_web.dart

Writing the Native Kotlin Implementation (Android)

android/src/main/kotlin/net/redlinesoft/my_custom_sensor/MyCustomSensorPlugin.kt:

package net.redlinesoft.my_custom_sensor

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 MyCustomSensorPlugin: FlutterPlugin, MethodCallHandler {
  private lateinit var channel : MethodChannel

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

  override fun onMethodCall(call: MethodCall, result: Result) {
    if (call.method == "readLightSensor") {
      val luxValue = 420.5 // Read from native SensorManager
      result.success(luxValue)
    } else {
      result.notImplemented()
    }
  }

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

Writing the Native Swift Implementation (iOS)

ios/Classes/MyCustomSensorPlugin.swift:

import Flutter
import UIKit

public class MyCustomSensorPlugin: NSObject, FlutterPlugin {
  public static func register(with registrar: FlutterPluginRegistrar) {
    let channel = FlutterMethodChannel(name: "my_custom_sensor", binaryMessenger: registrar.messenger())
    let instance = MyCustomSensorPlugin()
    registrar.addMethodCallDelegate(instance, channel: channel)
  }

  public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
    switch call.method {
    case "readLightSensor":
      let luxValue: Double = 420.5
      result(luxValue)
    default:
      result(FlutterMethodNotImplemented)
    }
  }
}

Best Practices for Custom Plugin Development

  1. Use Federated Architecture: Separate platform implementations (my_custom_sensor_android, my_custom_sensor_ios, my_custom_sensor_web) into sub-packages to allow independent maintenance and smaller app bundle sizes.
  2. Provide Unit Tests: Mock PlatformInterface implementations in test/ to allow consumer apps to test their code without needing live physical devices.
  3. Verify pub.dev Quality Points: Before publishing (flutter pub publish --dry-run), format code (dart format .), ensure 100% documentation coverage (dart doc), and include detailed CHANGELOG.md notes.

Takeaway: Following Flutter’s plugin standards ensures clean platform abstraction, testability, and seamless publication to pub.dev.