skip to content

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%

answer

  1. three roles, often three-plus packages
  2. app depends only on the app-facing one
  3. abstract Platform class with static instance
  4. each platform package registers itself
  5. domain experts add a platform independently

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.

solid answer

~30 s

A federated plugin separates three roles. The **app-facing package** (for example `url_launcher`) is the only one app code imports; it forwards calls to `SomethingPlatform.instance`. The **platform-interface package** (`url_launcher_platform_interface`) declares that abstract class, extending `PlatformInterface` from `plugin_platform_interface`, usually with a default method-channel implementation. Each **platform package** (`url_launcher_android`, `url_launcher_web`, …) declares `implements: url_launcher` in its pubspec, subclasses the interface and sets `instance` in a static `registerWith()`. The payoff: someone with Windows or web expertise can add or fix one platform without touching the others, each package gets its own version and release cadence, and a single uniform interface keeps behaviour consistent across platforms.

go deeper

for a junior

Know the three package roles by name and that an app imports only the app-facing package.

for a middle

Explain how a platform package declares implements, subclasses the abstract Platform class and sets its static instance from registerWith() at startup.

for a senior

Weigh when federation earns its overhead and treat the platform interface as the most change-sensitive package in the set.

for a principal

Decide package boundaries for a plugin that several teams or outside contributors will extend, including who owns the interface and its release policy.

## What "federated" means A **federated plugin** splits a plugin's API into a platform interface, independent platform implementations of that interface, and an app-facing API that uses whichever implementation is registered for the running platform. When each role lives in its own Dart package, the docs call it a **package-separated federated plugin**; that is the shape every large first-party plugin in `flutter/packages` uses. ## The three roles | Role | Example | Contains | Who depends on it | |---|---|---|---| | **App-facing** | `url_launcher` | The public Dart API; delegates every call to the platform instance | The app | | **Platform interface** | `url_launcher_platform_interface` | An abstract `UrlLauncherPlatform` class plus a default implementation | App-facing and every platform package | | **Platform implementation** | `url_launcher_android`, `url_launcher_ios`, `url_launcher_web`, … | Host code plus a Dart subclass of the interface | Normally only the app-facing package (endorsed) | Key points about each: - The **app-facing package** has no platform logic of its own in the pure form. Its public functions validate and convert their arguments, then delegate to `UrlLauncherPlatform.instance`. Apps import only this. - The **platform-interface package** defines the contract: an abstract class that extends `PlatformInterface`, a static `instance` getter and setter, and methods whose default bodies throw `UnimplementedError`. It usually ships a `MethodChannel…` default so a platform that just uses a channel needs little Dart code. - A **platform package** declares, in its own pubspec, which app-facing package it serves: ```yaml flutter: plugin: implements: thermo platforms: web: pluginClass: ThermoWeb fileName: thermo_web.dart ``` and its Dart class sets `ThermoPlatform.instance = ThermoWeb()` inside a static `registerWith()`, which the tool's generated registrant calls at startup. ## Why split it 1. **Independent expertise.** A developer who knows one platform well can write `thermo_windows` without owning the Android or iOS code. The docs name this as the main benefit. 2. **Independent versions.** A web bug fix releases `thermo_web` alone; apps pick it up through normal version solving without a new release of the app-facing package. 3. **A uniform contract.** Because every implementation subclasses the same interface, all platforms expose the same methods and types. 4. **Smaller blast radius.** A change to one platform's native build cannot break another platform's package. ## The costs - More packages to publish, test and keep in step. The platform interface becomes the most sensitive package: a breaking change there forces every platform package to update. - More indirection when debugging — a call passes through the app-facing package, the static `instance`, and then a platform subclass. - For a small plugin with two platforms and one maintainer, a single package (inline implementations in one pubspec) is often simpler. Federation pays off as platforms, contributors or release cadences multiply. ## How it looks at runtime At build time the tool resolves, for each platform, which package implements `thermo` and generates registrants for it. When the app starts, the chosen platform package's `registerWith()` replaces the interface's default `instance`. From then on, the app-facing API talks only to that instance; app code never names a platform package. ## Misconceptions to avoid - Apps do not import `thermo_android` or `thermo_web` directly for normal use; they import `thermo`. - The platform interface is not optional glue: without it, implementations could drift apart. - Federation is about package structure, not a different transport — the Android package may still talk to its Kotlin code over a method channel or generated Pigeon code.

  • Where does the default implementation of a federated plugin usually live, and what is it?
    In the platform-interface package: its abstract class initialises the static `instance` to a method-channel implementation. Platform packages that only need a channel can rely on it, while others replace `instance` with their own subclass in `registerWith()`.
  • Can a federated plugin keep some platforms inside the app-facing package?
    Yes. An app-facing pubspec may give some platforms an inline `pluginClass` and point others at separate packages with `default_package`. The docs show exactly that mix, for example Android and iOS inline and Windows in a separate package.

Like a power adapter standard: the app-facing package is the plug the appliance uses, the platform interface is the published socket specification, and each platform package is a national adapter built by someone who knows that country's wiring.

saying these in an interview costs you the question

  • Apps must import each platform package alongside the app-facing one.
  • The platform interface package contains the native Kotlin and Swift code.
  • Federated plugins use a different transport than method channels.
  • Every plugin should be federated regardless of size.
  • Platform packages can change the interface's methods on their own.