Skip to content
Blog

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.