Publishing a Flutter package to pub.dev
By Charlin Joe · 9 June 2025
Publishing a Flutter package to pub.dev is straightforward once you understand what the automated scoring system checks and what makes a package trustworthy for other developers. A 130/140 pub points score and a well-structured package will get noticed; a rushed publish with a bare README will sit at the bottom of search results.
Package structure
my_package/
├── lib/
│ ├── my_package.dart # Public API barrel file
│ └── src/ # Implementation (not exported directly)
│ ├── feature_a.dart
│ └── feature_b.dart
├── test/
│ └── my_package_test.dart
├── example/
│ └── lib/
│ └── main.dart # Working Flutter example app
├── CHANGELOG.md
├── LICENSE
├── README.md
└── pubspec.yaml
The src/ directory is convention — it signals that these files are internal implementation details, not part of the public API.
pubspec.yaml
name: my_flutter_package
description: >
A clear, one-sentence description of what the package does.
Avoid words like "simple", "easy", or "best".
version: 0.1.0
homepage: https://github.com/yourorg/my_flutter_package
repository: https://github.com/yourorg/my_flutter_package
issue_tracker: https://github.com/yourorg/my_flutter_package/issues
environment:
sdk: '>=3.0.0 <4.0.0'
flutter: '>=3.10.0'
dependencies:
flutter:
sdk: flutter
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^4.0.0
Key points:
- Version follows semantic versioning:
MAJOR.MINOR.PATCH - Include
repositoryandissue_tracker— these earn pub points - Prefer a narrow SDK lower bound (
>=3.0.0) and open upper bound (<4.0.0)
The barrel file
Export your public API explicitly:
// lib/my_flutter_package.dart
export 'src/feature_a.dart' show PublicClassA, PublicEnum;
export 'src/feature_b.dart';
// Don't export internal helpers
Using show lets you expose only what's intentional and hide internal classes even within the same file.
dart doc comments
Pub.dev's scoring checks for documentation coverage. Document all public APIs:
/// A widget that animates its child into view from below.
///
/// The [duration] controls how long the animation takes. Defaults to 300ms.
/// The [curve] controls the easing. Defaults to [Curves.easeOut].
///
/// Example:
/// ```dart
/// SlideInWidget(
/// child: const Text('Hello'),
/// )
/// ```
class SlideInWidget extends StatefulWidget {
/// Creates a [SlideInWidget].
const SlideInWidget({
super.key,
required this.child,
this.duration = const Duration(milliseconds: 300),
this.curve = Curves.easeOut,
});
/// The widget to animate.
final Widget child;
/// How long the slide animation takes.
final Duration duration;
/// The animation curve.
final Curve curve;
Writing a good README
The README is your package's landing page on pub.dev. Include:
- What it does — one paragraph, no fluff
- Installation — copy-paste
pubspec.yamlsnippet - Quick start — working code example, 10-30 lines
- API overview — key classes and their purpose
- Platform support table (for multi-platform packages)
- License
CHANGELOG.md format
Pub.dev parses your CHANGELOG. Use this format:
## 0.2.0
- Added `SlideInWidget` for animating children into view
- **Breaking:** Renamed `MyWidget.color` to `MyWidget.backgroundColor`
## 0.1.1
- Fixed null safety issue in `FeatureA`
## 0.1.0
- Initial release
Pre-publish checks
# Analyze code
flutter analyze
# Run tests
flutter test
# Check what will be published
dar pub publish --dry-run
# Check pub points locally
dart pub publish --dry-run 2>&1 | grep -A5 'Grant'
Dry run shows exactly what files will be included and flags issues with formatting, license, and documentation.
Publishing
dart pub publish
This opens a browser for authentication, then uploads. The package is publicly available immediately.
Automated publishing with GitHub Actions
name: Publish
on:
push:
tags: ['v*']
jobs:
publish:
permissions:
id-token: write # Required for OIDC auth with pub.dev
contents: read
uses: dart-lang/setup-dart/.github/workflows/publish.yml@v1
This uses pub.dev's OIDC-based automated publishing — no stored token needed. Configure it in pub.dev under package settings → Automated publishing.
Semantic versioning guide
| Change | Version bump |
|---|---|
| Bug fix, no API change | Patch: 0.1.0 → 0.1.1 |
| New feature, backwards compatible | Minor: 0.1.0 → 0.2.0 |
| Breaking change | Major: 0.1.0 → 1.0.0 (or 0.1.0 → 0.2.0 while pre-1.0) |
Pre-1.0, breaking changes are allowed in minor versions. Once you publish 1.0.0, breaking changes require a major bump.
Common pitfalls
Publishing before testing on a fresh project. Create a new Flutter project and add your package as a path dependency. If it doesn't work out of the box, it won't for your users either.
Not specifying platform support. If your package uses platform channels, declare it:
flutter:
plugin:
platforms:
android:
package: com.example.my_package
pluginClass: MyPackagePlugin
ios:
pluginClass: MyPackagePlugin
Uploading with sensitive files. Files in .pubignore are excluded (similar to .gitignore). Make sure .env, config files with keys, and test fixtures with real data are excluded.
Yanking instead of deprecating. Yanking a version breaks existing users who have it pinned. Only yank if the version has a critical security issue. For everything else, publish a new version and mark the old one as discontinued.
Sign in to like, dislike, or report.