Read this in other languages: English, TΓΌrkΓ§e
Educational Project β This project was developed as a practical reference to teach Flutter's Federated Plugin architecture, type-safe platform communication with Pigeon, and the proper use of the
plugin_platform_interfacepackage.
- About the Project
- Architecture Overview
- Package Structure
- Technologies Used
- Installation
- Usage
- Pigeon Integration
- Platform Interface Structure
- Testing
- Project Structure
- Development
- License
device_info_plugin is a simple Flutter plugin that queries device information (model name and operating system version). The main goal of this project is to provide an end-to-end example of the following concepts used in the real world:
| Concept | In This Project |
|---|---|
| Federated Plugin Architecture | 4 separate packages in a monorepo (app-facing, platform_interface, ios, android) |
| Pigeon | Type-safe host API bindings for iOS (Swift) and Android (Kotlin) |
plugin_platform_interface |
Secure platform interface with token-based validation |
| Melos | Workspace management, version control, and automatic code generation |
| Dart 3 Pattern Matching | Use of switch expressions in the example app |
| Mockito + build_runner | Unit tests across all layers |
This project strictly follows the Federated Plugin structure recommended by the Flutter team. It uses the exact same architecture as official plugins like url_launcher.
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Flutter App β
β (example/lib/main.dart) β
ββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββββββ
β depends on
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β device_info_plugin (App-Facing Package) β
β β
β DeviceInfoPlugin.getDeviceInfo() β
β βββ DeviceInfoPluginPlatform.instance.getDeviceInfo() β
ββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββββββ
β depends on
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β device_info_plugin_platform_interface (Common Contract) β
β β
β abstract DeviceInfoPluginPlatform extends PlatformInterface β
β class DeviceInfo { deviceModel, osVersion } β
β class DeviceInfoPluginMethodChannel (fallback) β
ββββββββββββββββββ¬ββββββββββββββββββββββββββ¬ββββββββββββββββββββββββ
implements β β implements
βΌ βΌ
ββββββββββββββββββββββββββββββ βββββββββββββββββββββββββββββββββββββ
β device_info_plugin_ios β β device_info_plugin_android β
β β β β
β Pigeon β Swift (UIKit) β β Pigeon β Kotlin (android.os) β
β UIDevice.current.model β β Build.MODEL β
β UIDevice.current.version β β Build.VERSION.RELEASE β
ββββββββββββββββββββββββββββββ βββββββββββββββββββββββββββββββββββββ
App β DeviceInfoPlugin β DeviceInfoPluginPlatform.instance
β
βββββββββββ΄ββββββββββ
β (at runtime) β
βΌ βΌ
iOS Plugin (Swift) Android Plugin (Kotlin)
βββββββββββββββ ββββββββββββββββββββ
β Pigeon Host β β Pigeon Host β
β API call β β API call β
ββββββββ¬βββββββ ββββββββββ¬βββββββββββ
β β
βΌ βΌ
UIDevice API android.os.Build
| Package | Role | Version |
|---|---|---|
device_info_plugin |
App-Facing β The public API directly used by the developer | 0.0.6 |
device_info_plugin_platform_interface |
Platform Interface β The contract all platforms must obey | 0.0.4 |
device_info_plugin_ios |
iOS Implementation β Native access via Pigeon + Swift | 0.0.6 |
device_info_plugin_android |
Android Implementation β Native access via Pigeon + Kotlin | 0.0.4 |
| Technology | Purpose |
|---|---|
Pigeon v27.1.0 |
Type-safe, code-generated message passing between Dart β Native (Swift/Kotlin) |
plugin_platform_interface v2.1.8 |
Token-based protection of the platform interface (PlatformInterface.verifyToken) |
Melos v7.8.2 |
Monorepo workspace management, automatic versioning, and changelog |
| Mockito | Mock-based unit testing |
| build_runner | Code generation for Mockito mock classes |
very_good_analysis v10.2.0 |
Strict lint rules |
| FVM | Flutter version management |
# Clone the repo
git clone https://github.com/Thixq/device_info_plugin.git
cd device_info_plugin
# Setup Flutter SDK with FVM (optional)
fvm install
fvm use
# Install dependencies
dart pub get
# Melos bootstrap (all packages + code generation)
dart run melos bootstrapThe melos bootstrap command automatically:
- Runs
pub getin all packages - Generates Mockito mocks using
build_runner - Generates Dart/Swift/Kotlin binding files using
pigeon
import 'package:device_info_plugin/device_info_plugin.dart';
final plugin = DeviceInfoPlugin();
final info = await plugin.getDeviceInfo();
print(info?.deviceModel); // "iPhone" or "Pixel 8"
print(info?.osVersion); // "17.4" or "14"The project contains a fully working example app:
cd device_info_plugin/example
flutter runThe example app manages loading/error/success states using Dart 3's pattern matching (switch expressions):
child: switch ((_isLoading, _error, _deviceInfo)) {
(true, _, _) => const CircularProgressIndicator(),
(_, final String error, _) => _ErrorContent(error: error, onRetry: _fetchDeviceInfo),
(_, _, final DeviceInfo? info) => _DeviceInfoContent(deviceInfo: info, onRefresh: _fetchDeviceInfo),
},Pigeon provides type-safe communication between Dart and native platform code. Unlike the string-based approach of MethodChannel, it offers compile-time safety.
// device_info_plugin_android/pigeons/messages.dart
@ConfigurePigeon(
PigeonOptions(
dartOut: 'lib/src/messages.g.dart',
kotlinOut: 'android/src/main/kotlin/.../Messages.g.kt',
kotlinOptions: KotlinOptions(
package: 'com.thixq.deviceinfo.device_info_plugin_android',
),
),
)
class DeviceInfoAndroid {
String? deviceModel;
String? osVersion;
}
@HostApi()
abstract class DeviceInfoHostApi {
DeviceInfoAndroid getDeviceInfo();
}// device_info_plugin_ios/pigeons/messages.dart
@ConfigurePigeon(
PigeonOptions(
dartOut: 'lib/src/messages.g.dart',
swiftOut: 'ios/Classes/Messages.g.swift',
),
)
class DeviceInfoIOS {
String? deviceModel;
String? osVersion;
}
@HostApi()
abstract class DeviceInfoHostApi {
DeviceInfoIOS getDeviceInfo();
}π€ Android (Kotlin)
class DeviceInfoPluginAndroidPlugin : FlutterPlugin, DeviceInfoHostApi {
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
DeviceInfoHostApi.setUp(flutterPluginBinding.binaryMessenger, this)
}
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
DeviceInfoHostApi.setUp(binding.binaryMessenger, null)
}
override fun getDeviceInfo(): DeviceInfoAndroid {
return DeviceInfoAndroid(
deviceModel = Build.MODEL,
osVersion = Build.VERSION.RELEASE
)
}
}π iOS (Swift)
public class DeviceInfoPluginIosPlugin: NSObject, FlutterPlugin, DeviceInfoHostApi {
public static func register(with registrar: FlutterPluginRegistrar) {
let instance = DeviceInfoPluginIosPlugin()
DeviceInfoHostApiSetup.setUp(binaryMessenger: registrar.messenger(), api: instance)
}
func getDeviceInfo() throws -> DeviceInfoIOS {
let deviceModel = UIDevice.current.model
let osVersion = UIDevice.current.systemVersion
return DeviceInfoIOS(deviceModel: deviceModel, osVersion: osVersion)
}
}# For a single package
cd device_info_plugin_ios
dart run pigeon --input pigeons/messages.dart
# or for all packages with Melos (bootstrap hook)
dart run melos bootstrapThe PlatformInterface class provided by the plugin_platform_interface package ensures that platform implementations can be swapped securely.
- Token validation: Prevents fake implementations made using
implementsinstead ofextends. - Default fallback: Provides a
MethodChannelbased default viaDeviceInfoPluginMethodChannel. - Single instance management: Manages the platform implementation globally with the Singleton pattern.
abstract class DeviceInfoPluginPlatform extends PlatformInterface {
DeviceInfoPluginPlatform() : super(token: _token);
static final Object _token = Object();
// Default: MethodChannel implementation
static DeviceInfoPluginPlatform _instance = DeviceInfoPluginMethodChannel();
static DeviceInfoPluginPlatform get instance => _instance;
static set instance(DeviceInfoPluginPlatform instance) {
// Token validation β security layer
PlatformInterface.verifyToken(instance, _token);
_instance = instance;
}
Future<DeviceInfo?> getDeviceInfo() {
throw UnimplementedError('getDeviceInfo() is not implemented.');
}
}Every platform implementation registers itself with the static registerWith() method. Flutter's default_package / dartPluginClass mechanism calls this method automatically:
# device_info_plugin/pubspec.yaml
flutter:
plugin:
platforms:
ios:
default_package: device_info_plugin_ios
android:
default_package: device_info_plugin_android// device_info_plugin_ios/lib/device_info_plugin_ios.dart
static void registerWith() {
DeviceInfoPluginPlatform.instance = DeviceInfoPluginIosPlugin();
}Each package contains its own unit tests. Tests use mock objects with Mockito and platform interface validation with MockPlatformInterfaceMixin.
| Package | What is Tested | Tools |
|---|---|---|
device_info_plugin |
App-facing API delegating to the platform interface | Mockito + MockPlatformInterfaceMixin |
device_info_plugin_platform_interface |
Default instance, MethodChannel fallback, token validation | Mockito + setMockMethodCallHandler |
device_info_plugin_ios |
Pigeon HostApi call and response mapping | Mockito (DeviceInfoHostApi mock) |
device_info_plugin_android |
Pigeon HostApi call and response mapping | Mockito (DeviceInfoHostApi mock) |
# Tests for all packages
dart run melos exec -- flutter test
# A specific package
cd device_info_plugin
flutter test
# A specific test file
flutter test test/device_info_plugin_test.dartdevice_info_plugin/ # π Monorepo root
βββ pubspec.yaml # Workspace definition + Melos configuration
βββ CHANGELOG.md # Auto-generated changelog (Melos)
β
βββ device_info_plugin/ # π¦ App-Facing Package
β βββ lib/
β β βββ device_info_plugin.dart # Public barrel export
β β βββ src/
β β βββ device_info_plugin_base.dart # DeviceInfoPlugin class
β βββ test/
β β βββ device_info_plugin_test.dart
β βββ example/ # π± Example Flutter application
β βββ lib/main.dart
β
βββ device_info_plugin_platform_interface/ # π¦ Platform Interface
β βββ lib/
β β βββ device_info_plugin_platform_interface.dart # Barrel export
β β βββ device_info_plugin_method_channel.dart # MethodChannel fallback
β β βββ src/
β β βββ device_info_model.dart # DeviceInfo data class
β β βββ device_info_plugin_platform.dart # Abstract platform class
β βββ test/
β βββ device_info_plugin_platform_interface_test.dart
β
βββ device_info_plugin_ios/ # π¦ iOS Implementation
β βββ lib/
β β βββ device_info_plugin_ios.dart # Dart side (Pigeon API call)
β β βββ src/
β β βββ messages.g.dart # π€ Pigeon-generated Dart bindings
β βββ pigeons/
β β βββ messages.dart # Pigeon definition file
β βββ ios/Classes/
β β βββ DeviceInfoPluginIosPlugin.swift # Native Swift implementation
β β βββ Messages.g.swift # π€ Pigeon-generated Swift bindings
β βββ test/
β βββ device_info_plugin_ios_test.dart
β
βββ device_info_plugin_android/ # π¦ Android Implementation
βββ lib/
β βββ device_info_plugin_android.dart # Dart side (Pigeon API call)
β βββ src/
β βββ messages.g.dart # π€ Pigeon-generated Dart bindings
βββ pigeons/
β βββ messages.dart # Pigeon definition file
βββ android/src/main/kotlin/.../
β βββ DeviceInfoPluginAndroidPlugin.kt # Native Kotlin implementation
β βββ Messages.g.kt # π€ Pigeon-generated Kotlin bindings
βββ test/
βββ device_info_plugin_android_test.dart
# Workspace bootstrap (pub get + code generation)
dart run melos bootstrap
# Analysis across all packages
dart run melos exec -- dart analyze
# Testing across all packages
dart run melos exec -- flutter test
# Version bumping and changelog generation
dart run melos version- Create a
device_info_plugin_<platform>/directory - Write the Pigeon definition in
pigeons/messages.dart - Create a class extending
DeviceInfoPluginPlatform - Add the
registerWith()static method - Add as
default_packagein the maindevice_info_plugin/pubspec.yaml - Add to the
workspace:list in the rootpubspec.yaml
You can utilize the following resources while examining this project:
- Flutter Federated Plugins β Official documentation
- Pigeon Package β Type-safe platform channels
- plugin_platform_interface β Platform interface standards
- Melos β Dart/Flutter monorepo management
- url_launcher β The official federated plugin used as a reference
This project was developed for educational purposes.
Developed by Thixq as an educational reference for Flutter Federated Plugin Architecture.