Designing Federated Plugins using plugin_platform_interface in Flutter
Learn how to use plugin_platform_interface in Flutter to build federated plugins with type-safe platform abstraction and easy unit testing.
Published on • November 9, 2026
AI Assistant

As Flutter expanded from mobile-only to web, desktop (Windows, macOS, Linux), and embedded targets, monolithic plugin implementations—where all platform code resided in a single repository package—became unmaintainable.
To solve this, Flutter introduced Federated Plugins powered by plugin_platform_interface. This pattern decouples the app-facing Dart API from specific platform implementations.
Architecture of a Federated Plugin
A federated plugin is split into distinct packages:
+-------------------------------------------------------+
| my_plugin (App-facing Package) |
| Exposes clean Dart API to Flutter developers |
+--------------------------+----------------------------+
|
v
+-------------------------------------------------------+
| my_plugin_platform_interface (Interface Package) |
| Extends PlatformInterface & defines abstract methods |
+----+---------------------+----------------------+-----+
| | |
v v v
+------------+ +---------------+ +---------------+
| _android | | _ios | | _web |
| package | | package | | package |
+------------+ +---------------+ +---------------+
Step 1: Defining the Platform Interface
Add plugin_platform_interface to your interface package (my_plugin_platform_interface/pubspec.yaml):
dependencies:
plugin_platform_interface: ^2.1.8
Create abstract class extending PlatformInterface:
lib/my_plugin_platform_interface.dart:
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
import 'my_plugin_method_channel.dart';
abstract class MyPluginPlatform extends PlatformInterface {
MyPluginPlatform() : super(token: _token);
static final Object _token = Object();
static MyPluginPlatform _instance = MethodChannelMyPlugin();
static MyPluginPlatform get instance => _instance;
static set instance(MyPluginPlatform instance) {
PlatformInterface.verifyToken(instance, _token);
_instance = instance;
}
Future<String?> getPlatformVersion() {
throw UnimplementedError('getPlatformVersion() has not been implemented.');
}
}
The verifyToken token check prevents third-party packages from accidentally overriding instance with an object that doesn’t extend PlatformInterface.
Step 2: Implementing Default MethodChannel Fallback
lib/my_plugin_method_channel.dart:
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'my_plugin_platform_interface.dart';
class MethodChannelMyPlugin extends MyPluginPlatform {
@visibleForTesting
final methodChannel = const MethodChannel('my_plugin');
@override
Future<String?> getPlatformVersion() async {
final version = await methodChannel.invokeMethod<String>('getPlatformVersion');
return version;
}
}
Step 3: Implementing Platform Packages (e.g., Web)
my_plugin_web/lib/my_plugin_web.dart:
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
import 'package:my_plugin_platform_interface/my_plugin_platform_interface.dart';
import 'package:web/web.dart' as web;
class MyPluginWeb extends MyPluginPlatform {
static void registerWith(Registrar registrar) {
MyPluginPlatform.instance = MyPluginWeb();
}
@override
Future<String?> getPlatformVersion() async {
final userAgent = web.window.navigator.userAgent;
return 'Web Browser ($userAgent)';
}
}
Step 4: Unit Testing Without Live Native Devices
Because the app-facing package routes all calls through MyPluginPlatform.instance, developer consumer apps can easily mock the platform interface in unit tests:
import 'package:flutter_test/flutter_test.dart';
import 'package:my_plugin/my_plugin.dart';
import 'package:my_plugin_platform_interface/my_plugin_platform_interface.dart';
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
class MockMyPluginPlatform extends MyPluginPlatform with MockPlatformInterfaceMixin {
@override
Future<String?> getPlatformVersion() async => 'Mock OS 1.0';
}
void main() {
test('getPlatformVersion returns mock string', () async {
MyPluginPlatform.instance = MockMyPluginPlatform();
final plugin = MyPlugin();
expect(await plugin.getPlatformVersion(), 'Mock OS 1.0');
});
}
Takeaway: plugin_platform_interface is the architectural backbone of federated Flutter plugins, guaranteeing type safety, platform isolation, and painless mock testing.