docs.flutter.dev uses cookies from Google to deliver and enhance the quality of its services and to analyze traffic.
How to write packages and plugins for Flutter.
Packages enable the creation of modular code that can be shared easily. A minimal package consists of the following:
pubspec.yamlA metadata file that declares the package name, version, author, and so on.
libThe lib directory contains the public code in the package, minimally a single <package-name>.dart file.
For a list of dos and don'ts when writing an effective plugin, see the Medium article by Mehmet Fidanboylu, Writing a good plugin.
Packages can contain more than one kind of content:
Dart packagesGeneral packages written in Dart, for example the path package. Some of these might contain Flutter specific functionality and thus have a dependency on the Flutter framework, restricting their use to Flutter only, for example the fluro package.
Plugin packagesA specialized Dart package that contains an API written in Dart code combined with one or more platform-specific implementations.
Plugin packages can be written for Android (using Kotlin or Java), iOS (using Swift or Objective-C), web, macOS, Windows, or Linux, or any combination thereof.
A concrete example is the url_launcher plugin package. To see how to use the url_launcher package, and how it was extended to implement support for web, see the Medium article by Harry Terkelsen, How to Write a Flutter Web Plugin, Part 1.
FFI packagesA specialized Dart package that enables calling native code using dart:ffi. These packages work in Dart standalone and don't require OS-specific build files. They are created with the flutter create --template=package_ffi command (see Create an FFI package). This is the recommended approach to build and bundle native code since Flutter 3.38.
The following instructions explain how to write a Flutter package.
To create a starter Flutter package, use the --template=package flag with flutter create:
This creates a package project in the hello folder with the following content:
LICENSEA (mostly) empty license text file.
test/hello_test.dartThe unit tests for the package.
hello.imlA configuration file used by the IntelliJ IDEs.
.gitignoreA hidden file that tells Git which files or folders to ignore in a project.
.metadataA hidden file used by IDEs to track the properties of the Flutter project.
pubspec.yamlA yaml file containing metadata that specifies the package's dependencies. Used by the pub tool.
README.mdA starter markdown file that briefly describes the package's purpose.
lib/hello.dartA starter app containing Dart code for the package.
.idea/modules.xml, .idea/workspace.xmlA hidden folder containing configuration files for the IntelliJ IDEs.
CHANGELOG.mdA (mostly) empty markdown file for tracking version changes to the package.
For pure Dart packages, simply add the functionality inside the main lib/<package name>.dart file, or in several files in the lib directory.
To test the package, add unit tests in a test directory.
For additional details on how to organize the package contents, see the Dart library package documentation.
If you want to develop a package that calls into platform-specific APIs, you need to develop a plugin package.
The API is connected to the platform-specific implementation(s) using a platform channel.
Federated plugins are a way of splitting the API of a plugin into a platform interface, independent platform implementations of that interface, and an app-facing interface that uses the registered implementation of the running platform.
Package-separated federated plugins are federated plugins where the platform interface, platform implementations, and the app-facing interface are all separated into their own Dart packages.
So, a package-separated federated plugin can use one package for iOS, another for Android, another for web, and yet another for a car (as an example of an IoT device). Among other benefits, this approach allows a domain expert to extend an existing plugin to work for the platform they know best.
A federated plugin requires the following:
app-facing interfaceThe interface that plugin users interact with when using the plugin. This interface specifies the API used by the Flutter app. In a package-separated federated plugin, this is the package that plugin users depend on to use the plugin.
platform implementation(s)One or more implementations that contain the platform-specific implementation code. The app-facing interface calls into these implementations—they aren't directly used, or depended on when package-separated, in an app unless they contain platform-specific functionality accessible to the end user.
platform interfaceThe interface that glues the app-facing interface to the platform implementations(s). This declares an interface that any platform implementation must implement to support the app-facing interface. Having a separate package that defines this interface ensures that all platform packages implement the same functionality in a uniform way.
Ideally, when adding a platform implementation to a packaged-separated federated plugin, you will coordinate with the package author to include your implementation. In this way, the original author endorses your implementation.
For example, say you write a foobar_windows implementation for the (imaginary) foobar plugin. In an endorsed plugin, the original foobar author adds your Windows implementation as a dependency in the pubspec for the app-facing package. Then, when a developer includes the foobar plugin in their Flutter app, the Windows implementation, as well as the other endorsed implementations, are automatically available to the app.
If you can't, for whatever reason, get your implementation added by the original plugin author, then your plugin is not endorsed. A developer can still use your implementation, but must manually add the plugin to the app's pubspec.yaml file:
This approach also works for overriding an already endorsed plugin implementation of foobar.
For more information on federated plugins, why they are useful, and how they are implemented, see the Medium article by Harry Terkelsen, How To Write a Flutter Web Plugin, Part 2.
Plugins can specify the platforms they support by adding keys to the platforms map in the pubspec.yaml file. For example, the following pubspec file shows the flutter: map for the hello plugin, which supports only iOS and Android:
When adding plugin implementations for more platforms, the platforms map should be updated accordingly. For example, here's the map in the pubspec file for the hello plugin, when updated to add support for macOS and web:
A platform package uses the same format, but includes an implements entry indicating which app-facing package it implements. For example, a hello_windows plugin containing the Windows implementation for hello would have the following flutter: map:
An app facing package can endorse a platform package by adding a dependency on it, and including it as a default_package in the platforms: map. If the hello plugin above endorsed hello_windows, it would look as follows:
Note that, as shown here, an app-facing package can have some platforms implemented within the package, and others in endorsed federated implementations.
Many frameworks support both iOS and macOS with identical or mostly identical APIs, making it possible to implement some plugins for both iOS and macOS with the same codebase. Normally each platform's implementation is in its own folder, but the sharedDarwinSource option allows iOS and macOS to use the same folder instead:
When sharedDarwinSource is enabled, instead of an ios directory for iOS and a macos directory for macOS, both platforms use a shared darwin directory for all code and resources. When enabling this option, you need to move any existing files from ios and macos to the shared directory. You also need to update the podspec file to set the dependencies and deployment targets for both platforms, for example:
To create a plugin package, use the --template=plugin flag with flutter create.
Use the --platforms= option followed by a comma-separated list to specify the platforms that the plugin supports. Available platforms are: android, ios, web, linux, macos, and windows. If no platforms are specified, the resulting project doesn't support any platforms.
Use the --org option to specify your organization, using reverse domain name notation. This value is used in various package and bundle identifiers in the generated plugin code.
By default, the plugin project uses Swift for iOS code and Kotlin for Android code. If you prefer Objective-C or Java, you can specify the iOS language using -i and the Android language using -a. Please choose one of the following:
This creates a plugin project in the hello folder with the following specialized content:
lib/hello.dartThe Dart API for the plugin.
android/src/main/java/com/example/hello/HelloPlugin.ktThe Android platform-specific implementation of the plugin API in Kotlin.
ios/Classes/HelloPlugin.mThe iOS-platform specific implementation of the plugin API in Objective-C.
example/A Flutter app that depends on the plugin, and illustrates how to use it.
As a plugin package contains code for several platforms written in several programming languages, some specific steps are needed to ensure a smooth experience.
The API of the plugin package is defined in Dart code. Open the main hello/ folder in your favorite Flutter editor. Locate the file lib/hello.dart.
We recommend you edit the Android code using Android Studio.
Before editing the Android platform code in Android Studio, first make sure that the code has been built at least once (in other words, run the example app from your IDE/editor, or in a terminal execute:
Then use the following steps:
The Android platform code of your plugin is located in hello/java/com.example.hello/HelloPlugin.
You can run the example app from Android Studio by pressing the run (▶) button.
We recommend you edit the iOS code using Xcode.
Before editing the iOS platform code in Xcode, first make sure that the code has been built at least once (in other words, run the example app from your IDE/editor, or in a terminal execute cd hello/example; flutter build ios --no-codesign --config-only).
Then use the following steps:
The iOS platform code for your plugin is located in Pods/Development Pods/hello/../../example/ios/.symlinks/plugins/hello/ios/Classes in the Project Navigator. (If you are using sharedDarwinSource, the path will end with Flutter/hello/Sources/hello instead.)
You can run the example app by pressing the run (▶) button.
Flutter uses Swift Package Manager as the primary strategy to manage native iOS and macOS dependencies.
To add a dependency to your plugin using Swift Package Manager:
For complete details and instructions on structuring native folders, resource bundling, or handling hybrid setups, visit the Swift Package Manager for plugin authors guide.
Flutter continues to support CocoaPods for backward compatibility. If your plugin needs to support developers who haven't migrated to Swift Package Manager yet, specify your CocoaPods dependency at the end of ios/hello.podspec:
For private pods, refer to Private CocoaPods to ensure repo access:
To fetch and link the plugin's dependencies, add the plugin to your app project's pubspec.yaml dependencies, and run flutter pub get. Flutter automatically resolves and wires up the Swift Package Manager descriptors or CocoaPods pod files during the native app build step.
If your plugin requires a privacy manifest, for example, if it uses any required reason APIs, update the PrivacyInfo.xcprivacy file to describe your plugin's privacy impact, and add the following to the bottom of your podspec file:
For more information, check out Privacy manifest files on the Apple developer site.
We recommend you edit the Linux code using an IDE with C++ integration. The instructions below are for Visual Studio Code with the "C/C++" and "CMake" extensions installed, but can be adjusted for other IDEs.
Before editing the Linux platform code in an IDE, first make sure that the code has been built at least once (in other words, run the example app from your Flutter IDE/editor, or in a terminal execute cd hello/example; flutter build linux).
Then use the following steps:
The Linux platform code for your plugin is located in flutter/ephemeral/.plugin_symlinks/hello/linux/.
You can run the example app using flutter run. Note: Creating a runnable Flutter application on Linux requires steps that are part of the flutter tool, so even if your editor provides CMake integration building and running that way won't work correctly.
We recommend you edit the macOS code using Xcode.
Before editing the macOS platform code in Xcode, first make sure that the code has been built at least once (in other words, run the example app from your IDE/editor, or in a terminal execute cd hello/example; flutter build macos --config-only).
Then use the following steps:
The macOS platform code for your plugin is located in Pods/Development Pods/hello/../../example/macos/Flutter/ephemeral/.symlinks/plugins/hello/macos/Classes in the Project Navigator. (If you are using sharedDarwinSource, the path will end with hello/darwin/Classes instead.)
You can run the example app by pressing the run (▶) button.
We recommend you edit the Windows code using Visual Studio.
Before editing the Windows platform code in Visual Studio, first make sure that the code has been built at least once (in other words, run the example app from your IDE/editor, or in a terminal execute cd hello/example; flutter build windows).
Then use the following steps:
The Windows platform code for your plugin is located in hello_plugin/Source Files and hello_plugin/Header Files in the Solution Explorer.
You can run the example app by right-clicking hello_example in the Solution Explorer and selecting Set as Startup Project, then pressing the run (▶) button. Important: After making changes to plugin code, you must select Build > Build Solution before running again, otherwise an outdated copy of the built plugin will be run instead of the latest version containing your changes.
Finally, you need to connect the API written in Dart code with the platform-specific implementations. This is done using a platform channel, or through the interfaces defined in a platform interface package.
To add support for specific platforms to an existing plugin project, run flutter create with the --template=plugin flag again in the project directory. For example, to add web support in an existing plugin, run:
If this command displays a message about updating the pubspec.yaml file, follow the provided instructions.
In many cases, non-web platform implementations only use the platform-specific implementation language, as shown above. However, platform implementations can also use platform-specific Dart as well.
The examples below only apply to non-web platforms. Web plugin implementations are always written in Dart, and use pluginClass and fileName for their Dart implementations as shown above.
In some cases, some platforms can be implemented entirely in Dart (for example, using FFI). For a Dart-only platform implementation on a platform other than web, replace the pluginClass in pubspec.yaml with a dartPluginClass. Here is the hello_windows example above modified for a Dart-only implementation:
In this version you would have no C++ Windows code, and would instead subclass the hello plugin's Dart platform interface class with a HelloPluginWindows class that includes a static registerWith() method. This method is called during startup, and can be used to register the Dart implementation:
Platform implementations can also use both Dart and a platform-specific language. For example, a plugin could use a different platform channel for each platform so that the channels can be customized per platform.
A hybrid implementation uses both of the registration systems described above. Here is the hello_windows example above modified for a hybrid implementation:
The Dart HelloPluginWindows class would use the registerWith() shown above for Dart-only implementations, while the C++ HelloPlugin class would be the same as in a C++-only implementation.
We encourage you test your plugin with automated tests to ensure that functionality doesn't regress as you make changes to your code.
To learn more about testing your plugins, check out Testing plugins. If you are writing tests for your Flutter app and plugins are causing crashes, check out Flutter in plugin tests.
If you want to develop a package that calls into native APIs using Dart's FFI, you need to develop an FFI package.
If you need to access the Flutter Plugin API or configure a Google Play services runtime on Android, use the flutter create --template=plugin command instead.
To create a starter FFI package, use the --template=package_ffi flag with flutter create:
This creates an FFI package project in the hello folder with the following specialized content:
lib: The Dart code that defines the API of the package, and which calls into the native code using dart:ffi.
src: The native source code.
hook/build.dart: The build hook script that compiles the native code.
To use the native code, bindings in Dart are needed.
To avoid writing these by hand, they are generated from the header file (src/hello.h) by package:ffigen. Reference the ffigen docs for information on how to install this package.
To regenerate the bindings, run the following command:
Very short-running native functions can be directly invoked from any isolate. For an example, see sum in lib/hello.dart.
Longer-running functions should be invoked on a helper isolate to avoid dropping frames in Flutter applications. For an example, see sumAsync in lib/hello.dart.
It is recommended practice to add the following documentation to all packages:
When you publish a package, API documentation is automatically generated and published to pub.dev/documentation. For example, see the docs for device_info_plus.
If you wish to generate API documentation locally on your development machine, use the following commands:
Change directory to the location of your package:
Tell the documentation tool where the Flutter SDK is located (change the following commands to reflect where you placed it):
For tips on how to write API documentation, see Effective Dart Documentation.
Individual licenses inside each LICENSE file should be separated by 80 hyphens on their own on a line.
If a LICENSE file contains more than one component license, then each component license must start with the names of the packages to which the component license applies, with each package name on its own line, and the list of package names separated from the actual license text by a blank line. (The packages need not match the names of the pub package. For example, a package might itself contain code from multiple third-party sources, and might need to include a license for each one.)
The following example shows a well-organized license file:
Here is another example of a well-organized license file:
Here is an example of a poorly-organized license file:
Another example of a poorly-organized license file:
Have you noticed that some of the packages and plugins on pub.dev are designated as Flutter Favorites? These are the packages published by verified developers and are identified as the packages and plugins you should first consider using when writing your app. To learn more, see the Flutter Favorites program.
Once you have implemented a package, you can publish it on pub.dev, so that other developers can easily use it.
Prior to publishing, make sure to review the pubspec.yaml, README.md, and CHANGELOG.md files to make sure their content is complete and correct. Also, to improve the quality and usability of your package (and to make it more likely to achieve the status of a Flutter Favorite), consider including the following items:
Next, run the publish command in dry-run mode to see if everything passes analysis:
The next step is publishing to pub.dev, but be sure that you are ready because publishing is forever:
For more details on publishing, see the publishing docs on dart.dev.
If you are developing a package hello that depends on the Dart API exposed by another package, you need to add that package to the dependencies section of your pubspec.yaml file. The code below makes the Dart API of the url_launcher plugin available to hello:
You can now import 'package:url_launcher/url_launcher.dart' and launch(someUrl) in the Dart code of hello.
This is no different from how you include packages in Flutter apps or any other Dart project.
But if hello happens to be a plugin package whose platform-specific code needs access to the platform-specific APIs exposed by url_launcher, you also need to add suitable dependency declarations to your platform-specific build files, as shown below.
The following example sets a dependency for url_launcher in hello/android/build.gradle:
You can now import io.flutter.plugins.urllauncher.UrlLauncherPlugin and access the UrlLauncherPlugin class in the source code at hello/android/src.
For more information on build.gradle files, see the Gradle Documentation on build scripts.
The following example sets a dependency for url_launcher in hello/ios/hello.podspec:
You can now #import "UrlLauncherPlugin.h" and access the UrlLauncherPlugin class in the source code at hello/ios/Classes.
For additional details on .podspec files, see the CocoaPods Documentation.
All web dependencies are handled by the pubspec.yaml file, like any other Dart package.
Unless stated otherwise, the documentation on this site reflects Flutter 3.44.0. Page last updated on 2026-06-08. View source or report an issue.