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
- 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. - Provide Unit Tests: Mock
PlatformInterfaceimplementations intest/to allow consumer apps to test their code without needing live physical devices. - Verify pub.dev Quality Points: Before publishing (
flutter pub publish --dry-run), format code (dart format .), ensure 100% documentation coverage (dart doc), and include detailedCHANGELOG.mdnotes.
Takeaway: Following Flutter’s plugin standards ensures clean platform abstraction, testability, and seamless publication to pub.dev.