Flutter Plugin & Package Development
7 questions foundWhat is the difference between a Dart package and a Flutter plugin, and when would you specifically need to build a plugin rather than simply a pure Dart package?
Beginner A pure Dart package contains only Dart code and can genuinely be used across any Dart environment, including server side or command line applications, whereas a Flutter plugin specifically includes platform specific native code, such as Kotlin or Java for Android and Swift or Objective-C for iOS, and is genuinely required whenever your particular functionality needs to actually interact directly with an underlying native platform capability, such as accessing the device's camera or its GPS sensor, that simply cannot be implemented using pure Dart code alone.
flutter create --template=plugin my_plugin
flutter create --template=package my_dart_package
Real-world example A team building a genuinely simple utility library containing only date formatting helper functions creates a pure Dart package, while a separate team needing direct access to a device's specific native Bluetooth hardware capability instead creates a proper full Flutter plugin.
Common follow-ups: What is the difference between a plugin and a federated plugin?;Can a single package genuinely contain both pure Dart code and platform specific native code together?
Platform Channels (Native Android & iOS Integration);Publishing Packages on pub.dev
What is the basic folder structure of a typical Flutter plugin project, and how does it separate the shared Dart interface from each individual platform's own specific native implementation?
Beginner A typical Flutter plugin project's folder structure includes a lib folder containing the shared Dart code that actually exposes your plugin's own public facing API, along with separate dedicated android and ios folders each containing that specific platform's own actual native implementation code, and this clear separation lets consumers of your plugin interact with just one single, consistent Dart interface while your plugin itself internally and transparently delegates the genuine underlying platform specific work to whichever native implementation is actually appropriate for the specific device the app happens to actually be currently running on.
my_plugin/
lib/
my_plugin.dart
android/
src/main/kotlin/
ios/
Classes/
Real-world example A plugin providing access to a device's specific battery level exposes just one single, clean getBatteryLevel method through its shared Dart interface, while internally properly delegating to entirely separate native Kotlin code on Android and separate native Swift code on iOS respectively.
Common follow-ups: What is a federated plugin, and how does it further separate these platform implementations into entirely distinct separate packages?;How do you properly test a plugin's own specific native platform code?
Platform Channels (Native Android & iOS Integration);Dependency Injection in Flutter (GetIt & Service Locator)
How does a federated plugin architecture let several genuinely separate teams or contributors independently maintain the different platform specific implementations of exactly the same overall shared plugin?
Intermediate A federated plugin splits what would otherwise be one single combined plugin package into several genuinely separate packages, typically including one app facing package exposing the actual public Dart API, one separate platform interface package defining the shared abstract contract every platform implementation must properly follow, and then individual separate platform specific implementation packages for Android, iOS, web, and so on, and this particular architecture lets genuinely different teams or contributors independently maintain and separately release updates to just one single specific platform's own implementation without needing to coordinate a combined simultaneous release across every single supported platform together at once.
my_plugin/
my_plugin_platform_interface/
my_plugin_android/
my_plugin_ios/
my_plugin_web/
Real-world example The official url_launcher plugin uses a federated architecture, letting its own web specific implementation be updated and separately released completely independently of its Android and iOS implementations, without requiring every single platform to always be coordinated and released together simultaneously.
Common follow-ups: What is the platform interface package specifically responsible for defining within this overall federated architecture?;How does a specific consuming app actually know which particular platform implementation package it genuinely needs to depend on?
Publishing Packages on pub.dev;Platform Channels (Native Android & iOS Integration)
How should you properly write both unit tests and platform specific integration tests for a Flutter plugin to genuinely verify it correctly works across every single one of its supported platforms?
Intermediate Testing a Flutter plugin thoroughly typically involves writing regular Dart unit tests specifically for the plugin's own shared Dart facing logic, often using a mocked implementation of the underlying platform channel to properly simulate a native response without genuinely needing an actual real physical device, combined with genuinely separate platform specific integration tests that actually run directly on a real device or a properly configured emulator or simulator specifically to verify the real actual native code path genuinely works correctly end to end exactly as intended on each individual supported platform.
test('getBatteryLevel returns a value', () async {
MyPlugin.setMockMethodCallHandler((call) async => 85);
expect(await MyPlugin.getBatteryLevel(), 85);
});
Real-world example A battery monitoring plugin's development team writes Dart unit tests using a mocked platform channel response to verify their shared Dart logic, plus genuinely separate platform specific integration tests that actually run directly on both a real Android device and a real iOS device to properly verify the true native implementation genuinely and correctly works.
Common follow-ups: How do you actually properly mock a platform channel's response specifically within a Dart unit test?;What specific CI infrastructure is genuinely needed to actually run these platform specific integration tests automatically?
Unit Testing in Flutter;Integration Testing in Flutter
What should a well written pubspec.yaml and a genuinely thorough README file for a Flutter plugin actually include, to help other developers properly understand how to correctly use and depend on it?
Intermediate A genuinely well documented plugin's pubspec.yaml should clearly and accurately specify the plugin's own name, an appropriately clear and helpful description, its current version number following proper semantic versioning conventions, and its genuine minimum required Flutter and Dart SDK constraints, while a genuinely thorough accompanying README should clearly explain exactly what specific problem the plugin actually solves, provide clear, working installation instructions, include a genuinely simple, easy to follow basic usage example, and clearly document any specific required platform level setup steps, such as needing to add a particular required permission entry to an Android manifest file.
name: battery_plus
description: A Flutter plugin for accessing detailed device battery information.
version: 4.0.2
environment:
sdk: '>=3.0.0 <4.0.0'
Real-world example A widely used community plugin's genuinely thorough README clearly walks a brand new developer completely through the entire required Android manifest permission setup step by step, meaningfully preventing a genuinely common and easily avoidable initial setup mistake many new users might otherwise make.
Common follow-ups: What does proper semantic versioning actually mean specifically in the context of a published package's own version number?;How do you properly and clearly document any specific required breaking changes between two separate different major plugin versions?
Publishing Packages on pub.dev;Flutter App Architecture with Modular Feature Folders
How do you properly handle a breaking native API change on one specific platform, such as a new required Android permission model, while genuinely still maintaining backward compatibility for consumers of your plugin using an older version of that same platform?
Advanced Handling a genuinely breaking native platform change, such as Android introducing a meaningfully new required runtime permission model in a newer specific API level, typically requires your plugin's own native implementation code to properly check the actual current device's specific API level at runtime and conditionally branch its own internal logic accordingly, continuing to correctly support older devices using the previous established approach while properly adopting the newer required approach specifically on devices that genuinely support it, and clearly documenting this specific compatibility nuance within your plugin's own README helps consuming developers properly understand exactly what genuine behavior to actually expect across these different specific device API levels.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
// Use newer required permission model
} else {
// Use older, previously established permission model
}
Real-world example A media picker plugin's native Android implementation properly checks the actual current device's specific API level, correctly using the considerably newer granular media permission model on genuinely newer Android versions while still properly falling back to the previous established broader storage permission on older devices.
Common follow-ups: How do you properly and clearly communicate a genuine breaking native platform change to consumers of your plugin?;What specific testing strategy genuinely helps catch a platform compatibility issue like this before an actual official plugin release?
Flutter App Security Best Practices;Platform Channels (Native Android & iOS Integration)
How can you design a plugin's own public Dart API to remain genuinely stable and backward compatible across several successive plugin versions, even as its own internal native implementation continues to meaningfully evolve and change over time?
Advanced Designing a genuinely stable, backward compatible public API means deliberately exposing only the specific minimal, carefully considered set of methods and properties consumers genuinely actually need, keeping your plugin's own internal native implementation details properly and fully hidden and private, and when a genuine new capability or feature does eventually need to be added, doing so by properly adding an entirely new optional method or an optional parameter with a genuinely sensible default value, rather than modifying an already existing method's own established signature in a way that would otherwise genuinely break any existing code that already depends on your plugin's own current established API.
// Adding a new optional parameter preserves backward compatibility
Future<int> getBatteryLevel({bool includePercentage = true}) async { ... }
Real-world example A widely used battery plugin adds a genuinely new optional parameter to an already existing method to support an additional new capability, ensuring every single existing app already depending on that plugin's previous established method signature continues working correctly completely without any required code changes whatsoever on their end.
Common follow-ups: What is the recommended, generally accepted process for eventually deprecating an older method that genuinely needs to be properly and fully removed?;How does semantic versioning help clearly communicate to consumers exactly when a genuine breaking change has actually occurred?
Publishing Packages on pub.dev;Flutter App Architecture with Modular Feature Folders