Platform Boundaries
Flutter keeps UI and business logic shared. Platform behavior still needs explicit boundaries.
Boundary Decision Table
| Need | Start with | Use when |
|---|---|---|
| Existing device API | Pub package/plugin | A maintained plugin supports every target you need |
| Small native API call | MethodChannel | You control app code and need a few async method calls |
| Typed native API surface | Pigeon | Channel calls are growing or need generated host/Dart bindings |
| Native C-compatible library | FFI | You need direct native library calls, especially desktop/mobile |
| Native UI inside Flutter | Platform views | You must embed a native map, web view, camera, or platform component |
| Web browser API | JS interop | Flutter web needs a browser API not exposed by a package |
| Existing native app | Add-to-app | Flutter is one module inside Android/iOS/macOS, not the whole app |
Target Languages
Platform-channel implementation languages follow the target:
| Target | Typical implementation language |
|---|---|
| Android | Kotlin or Java |
| iOS | Swift or Objective-C |
| Windows | C++ |
| macOS | Objective-C or Swift depending on plugin shape |
| Linux | C |
| Web | JavaScript interop, not normal platform channels |
Practical Rules
- Prefer a high-quality plugin before writing platform code.
- Verify platform support in the plugin’s pub.dev metadata and docs.
- Add the platform test before adding more platform APIs.
- Keep channel names, method names, and codecs stable once released.
- Never assume mobile plugin support means desktop or web support.
Gotcha: Adding a plugin with native code often needs a full app restart. Hot reload is not enough to prove the new platform code is loaded.