skip to content

Federated Plugins

A plugin package declares per-platform code under flutter: plugin: platforms:, and federated plugins split app-facing, interface and platform packages. Interviewers ask how endorsement works.

part ofFlutteroverview, primer and where to startread it →
on this pageshow

explore

questions

6

In Flutter, what is the difference between a plain Dart package and a plugin package, and how does pubspec.yaml mark a package as a plugin?

level: juniorimportance: must knowfreq 58%

answer

  1. Dart-only code vs host-platform code
  2. flutter: plugin: platforms: map
  3. pluginClass per platform, package on Android
  4. tool generates the plugin registrants
  5. web implementation is Dart plus fileName

basics

~20 s

A plain Dart package ships only Dart code. A plugin package also ships host-platform code (Kotlin or Java, Swift or Objective-C, C++, or web Dart) and declares it per platform under flutter: plugin: platforms: in pubspec.yaml, so the Flutter tool registers it at build time.

solid answer

~40 s

A **Dart package** (which may contain Flutter widgets) is pure Dart and works on every platform Dart and Flutter run on. A **plugin package** adds platform-specific code and says so in `pubspec.yaml` under `flutter:` → `plugin:` → `platforms:`: one entry per supported platform, with `pluginClass` naming the native class to register and, on Android, `package` naming its Java/Kotlin package. A web entry names a Dart `pluginClass` plus the `fileName` that holds it. When the app builds, the Flutter tool reads those maps from every dependency and generates the registrants (`GeneratedPluginRegistrant` on Android and iOS, a Dart plugin registrant for Dart-side classes) that wire each plugin into the engine. A platform with no entry simply has no implementation, so calls into it fail at runtime.

code

yaml · 25 lines
yaml
name: thermo
version: 0.1.0

environment:
  sdk: ^3.13.0
  flutter: ">=3.47.0"

dependencies:
  flutter:
    sdk: flutter
  flutter_web_plugins:
    sdk: flutter
  plugin_platform_interface: ^2.0.2

flutter:
  plugin:
    platforms:
      android:
        package: com.example.thermo
        pluginClass: ThermoPlugin
      ios:
        pluginClass: ThermoPlugin
      web:
        pluginClass: ThermoWeb
        fileName: thermo_web.dart

go deeper

for a junior

Recall the one-line split: Dart-only code versus Dart plus host-platform code, and that the flutter: plugin: platforms: map in pubspec.yaml is what marks a plugin.

for a middle

Explain each platforms key (pluginClass, package, fileName, dartPluginClass) and how the tool turns them into generated registrants, so no manual registration is needed.

for a senior

Show you can diagnose a plugin that works on one platform and silently fails on another by reading its platforms map and the generated registrant.

for a principal

Frame when a capability justifies owning host-platform code at all, since every plugin adds native build, review and upgrade surface per platform.

## Two kinds of package Flutter code is distributed as Dart packages, but not every package is the same kind: - **Dart package** — only Dart code. It can depend on Flutter and ship widgets, but it contains nothing that needs Android Studio, Xcode or a C++ toolchain. It works wherever the framework runs. - **Plugin package** — a Dart package that *also* contains code written for a host platform: Kotlin or Java for Android, Swift or Objective-C for iOS and macOS, C++ for Windows and Linux, or browser-facing Dart for the web. The Dart side exposes a normal API; underneath, it talks to the host code (typically over a platform channel, which is its own topic). The rule of thumb interviewers look for: if you need a device capability the framework does not already expose — a Bluetooth radio, a vendor SDK, a system settings screen — you need a plugin, either one you adopt or one you write. ## How pubspec.yaml marks a plugin The marker is the `plugin:` key inside the `flutter:` section. Its `platforms:` map lists every platform the package supports: ```yaml flutter: plugin: platforms: android: package: com.example.thermo pluginClass: ThermoPlugin ios: pluginClass: ThermoPlugin web: pluginClass: ThermoWeb fileName: thermo_web.dart ``` | Key | Meaning | |---|---| | `pluginClass` | The native class the tool registers with the engine (on the web, the Dart class) | | `package` | Android only: the Java/Kotlin package that holds `pluginClass` | | `fileName` | Web: the Dart file inside `lib/` that defines the web `pluginClass` | | `dartPluginClass` | A Dart class whose static `registerWith()` runs at startup (Dart-only or hybrid implementations) | | `sharedDarwinSource` | iOS and macOS share one `darwin/` source folder | | `implements`, `default_package` | Federated-plugin wiring between separate packages | A platform that is **absent** from the map is unsupported. The package still resolves, but nothing is registered for that platform, so a call from Dart finds no handler and fails at runtime. ## What the tool does with it On `flutter pub get` and on every build, the Flutter tool walks all dependencies, reads each `flutter: plugin:` block, and: 1. records the resolved plugins in the app's `.flutter-plugins-dependencies` file (since Flutter 3.32 it replaced the older `.flutter-plugins` file); 2. generates `GeneratedPluginRegistrant` for Android (Java) and iOS/macOS, which registers each native `pluginClass` with the engine when it starts; 3. generates a Dart plugin registrant (`.dart_tool/flutter_build/dart_plugin_registrant.dart`) that calls each `dartPluginClass.registerWith()` during startup; 4. on the web, generates a registrant that calls the web class's `registerWith(Registrar)`. You never edit these generated files; you change the pubspec and the tool regenerates them. ## Creating one `flutter create --template=plugin --platforms=android,ios,web my_plugin` scaffolds the pubspec entries above, a Dart API file, a platform-interface file with a default method-channel implementation, and a native class per requested platform. Without `--platforms`, the plugin template adds no platform at all and leaves a `some_platform` placeholder to replace. ## Common misconceptions - A package that imports `package:flutter` is not a plugin by that fact alone — only native or web code declared under `plugin:` makes it one. - Plugins are not compiled into Flutter's engine; they are ordinary host-platform code linked into the app and registered at engine start. - A standard app has no manual registration step: the generated registrant does it whenever the tool builds the app. - Supporting a new platform is not automatic: someone must add both the code and the `platforms:` entry.

  • What happens if an app on Windows calls a plugin whose pubspec has no windows entry?
    The package resolves and the Dart API compiles, but the tool registers nothing for Windows. The first call that crosses into host code finds no handler on the other side and fails at runtime, so apps usually guard such calls by platform or pick a plugin that declares every platform they ship.
  • Why does the Android entry need package as well as pluginClass?
    The generated Android registrant is Java that must reference the plugin class by its fully qualified name. `package` supplies the Java/Kotlin package and `pluginClass` the class name inside it; the tool validates both identifiers before writing the registrant.
  • Where does a web plugin's implementation live if the web has no native language?
    In Dart. The web entry's `pluginClass` is a Dart class and `fileName` names its file under `lib/`. The class exposes a static `registerWith(Registrar)` that installs itself, and the tool's generated web registrant calls it at startup.

saying these in an interview costs you the question

  • Any package that imports Flutter widgets counts as a plugin.
  • Plugins are compiled into the Flutter engine itself.
  • Each plugin must be registered by hand in MainActivity or AppDelegate.
  • A plugin automatically works on platforms missing from its platforms map.
  • The web cannot have plugins because it has no native code.
open as a page

In a Flutter federated plugin, how does endorsing a platform package with default_package work, and how does an app use a non-endorsed implementation instead?

level: middleimportance: should knowfreq 34%

basics

~20 s

The app-facing package endorses a platform package by depending on it and naming it as default_package for that platform, so apps get it automatically. An app picks a non-endorsed implementation by adding it as a direct dependency, which the Flutter tool prefers.

open as a page

In a Flutter federated plugin, what do the app-facing, platform-interface and platform packages each contain, and why split a plugin that way?

level: middleimportance: should knowfreq 40%

basics

~20 s

The app-facing package holds the public API apps call; the platform-interface package holds an abstract class every implementation extends; each platform package implements it for one platform and registers itself. The split lets platforms ship and version independently.

open as a page

In a Flutter federated plugin, why must platform implementations extend the PlatformInterface subclass rather than implement it, and how do tests still mock it?

level: seniorimportance: should knowfreq 24%

basics

~20 s

Extending lets the interface package add new methods with default bodies without breaking any implementation; implementing would force every platform package to update. The instance setter enforces this with a token check, and tests opt out with MockPlatformInterfaceMixin.

open as a page

Since Flutter 3.44 turned Swift Package Manager on by default, what must a Flutter plugin ship for iOS and macOS, and what happens to plugins with only a podspec?

level: seniorimportance: should knowfreq 30%

basics

~20 s

A plugin should ship a Swift package (Package.swift with a Sources folder under ios, macos or darwin) and keep its podspec. Apps fall back to CocoaPods for plugins lacking Swift Package Manager support, which keeps them working but mixes two dependency managers.

open as a page

Your Flutter app must read a Bluetooth thermometer on Android, iOS and web: how do you decide between adopting an existing plugin, extending it, or writing your own federated plugin?

level: principalimportance: nice to knowfreq 18%

basics

~20 s

Adopt when an existing plugin covers every target platform with active maintenance; fill a single missing platform with your own implementing package rather than a fork; write your own federated plugin only when a vendor SDK, protocol or roadmap no public plugin can serve forces it.

open as a page