Package layout conventions
- The pubspec
- LICENSE
- README.md
- CHANGELOG.md
- Public directories
- Public assets
- Implementation files
- Web files
- Command-line apps
- Tests and benchmarks
- Documentation
- Examples
- Internal tools and scripts
- Hooks
- Project-specific caching for tools
When you build a pub package, we encourage you to follow the conventions that this page describes. They describe how you organize the files and directories within your package, and how to name things.
Here's what a complete package (named
enchilada
)
that uses every corner of these guidelines
might look like:
enchilada/
.dart_tool/ *
pubspec.yaml
pubspec_overrides.yaml **
pubspec.lock ***
LICENSE
README.md
CHANGELOG.md
benchmark/
make_lunch.dart
bin/
enchilada
doc/
api/ ****
getting_started.md
example/
main.dart
hook/
build.dart
integration_test/
app_test.dart
lib/
enchilada.dart
tortilla.dart
guacamole.css
src/
beans.dart
queso.dart
test/
enchilada_test.dart
tortilla_test.dart
tool/
generate_docs.dart
web/
index.html
main.dart
style.css
* The
.dart_tool/
directory exists after you've run
dart pub get
.
Don't check it into source control.
To learn more, see
Project specific caching for tools.
** The
pubspec_overrides.yaml
file,
if present, overrides certain aspects of
pubspec.yaml
.
Usually you don't want to check it into source control.
*** The
pubspec.lock
file exists after you've run
dart pub get
.
Leave it out of source control unless your package is an
application package.
**** The
doc/api
directory exists locally after you've run
dart doc
.
Don't check the
api
directory into source control.
The pubspec
#enchilada/
pubspec.yaml
pubspec.lock
Every package has a
pubspec, a file named
pubspec.yaml
, in the root directory of the package. That's what
makes
it a
package.
Running
dart pub get
,
dart pub upgrade
, or
dart pub downgrade
on the package
creates a
lockfile, named
pubspec.lock
.
If your package is an
application package,
check the lockfile into source control. Otherwise, don't.
For more information, see the pubspec page.
LICENSE
#enchilada/
LICENSE
If you're publishing your package, include a license file named
LICENSE
.
We recommend using an
OSI-approved license
such as
BSD-3-Clause,
so that others can reuse your work.
README.md
#enchilada/
README.md
One file that's very common in open source is a
README
file that
describes the project. This is especially important in pub. When you upload
to the
pub.dev site,
your
README.md
file
is shown—rendered as
Markdown—on the page for your package.
This is the perfect place to introduce people to your code.
For guidance on how to write a great README, see Writing package pages.
CHANGELOG.md
#enchilada/
CHANGELOG.md
Include a
CHANGELOG.md
file that has a section for
each release of your package,
with notes to help users of your package upgrade.
Users of your package often review the changelog
to discover bug fixes and new features,
or to determine how much effort it will take to upgrade
to the latest version of your package.
To support tools that parse
CHANGELOG.md
,
use the following format:
- Each version has its own section with a heading.
- The version headings are either all level 1 or all level 2.
- The version heading text contains a package version number, optionally prefixed with "v".
When you upload your package to the
pub.dev site,
your package's
CHANGELOG.md
file (if any)
appears in the
Changelog
tab, rendered as
Markdown.
Here's an example of a
CHANGELOG.md
file.
As the example shows, you can add subsections.
# 1.0.1
* Fixed missing exclamation mark in `sayHi()` method.
# 1.0.0
* **Breaking change:** Removed deprecated `sayHello()` method.
* Initial stable release.
## Upgrading from 0.1.x
Change all calls to `sayHello()` to instead be to `sayHi()`.
# 0.1.1
* Deprecated the `sayHello()` method; use `sayHi()` instead.
# 0.1.0
* Initial development release.
Public directories
#
Two directories in your package are public to other packages:
lib
and
bin
. You place
public libraries
in
lib
and
public tools
in
bin
.
Public libraries
#The following directory structure shows the lib
portion of enchilada:
enchilada/
lib/
enchilada.dart
tortilla.dart
Many
packages
define Dart libraries that other packages can import and use.
These public Dart library files go inside a directory called
lib
.
Most packages define a single library that users can import. In that case,
its name should usually be the same as the name of the package, like
enchilada.dart
in the example here. But you can also define other
libraries with whatever names make sense for your package.
When you do, users can import these libraries using the name of the package and the library file, like so:
import 'package:enchilada/enchilada.dart';
import 'package:enchilada/tortilla.dart';
If you want to organize your public libraries, you can also create
subdirectories inside
lib
. If you do that, users will specify that path
when they import it. Say you have the following file hierarchy:
enchilada/
lib/
some/
path/
olives.dart
Users import olives.dart
as follows:
import 'package:enchilada/some/path/olives.dart';
Note that only
libraries
should be in
lib
.
Entrypoints—Dart scripts with a
main()
function—cannot
go in
lib
. If you place a Dart script inside
lib
,
you will discover that any
package:
imports it contains don't
resolve. Instead, your entrypoints should go in the appropriate
entrypoint directory.
For more information on packages, see Creating packages.
Public tools
#
Dart scripts placed inside of the
bin
directory are public. If you're
inside the directory of a package, you can use
dart run
to run scripts from the
bin
directories of any other package the package depends on. From
any
directory, you can
run scripts
from packages that you have activated using
dart pub global activate
.
If you intend for your package to be depended on,
and you want your scripts to be private to your package, place them
in the top-level
tool
directory.
If you don't intend for your package to be depended on, you can leave your
scripts in
bin
.
Public assets
#enchilada/
lib/
guacamole.css
While most packages exist to let you reuse Dart code, you can also reuse other kinds of content. For example, a package for Bootstrap might include a number of CSS files for consumers of the package to use.
These go in the top-level
lib
directory. You can put any kind of file
in there and organize it with subdirectories however you like.
Implementation files
#enchilada/
lib/
src/
beans.dart
queso.dart
The libraries inside
lib
are publicly visible: other packages are free to
import them. But much of a package's code is internal implementation libraries
that should only be imported and used by the package itself. Those go inside a
subdirectory of
lib
called
src
. You can create subdirectories in there if
it helps you organize things.
You are free to import libraries that live in
lib/src
from within other Dart
code in the
same
package (like other libraries in
lib
, scripts in
bin
, and
tests) but you should never import from another package's
lib/src
directory.
Those files are not part of the package's public API, and they might change in
ways that could break your code.
How you import libraries from within your own package depends on the locations of the libraries:
-
When
reaching inside or outside
lib/
(lint: avoid_relative_lib_imports ), usepackage:
. - Otherwise, prefer relative imports .
For example:
// When importing from within lib:
import 'src/beans.dart';
// When importing from outside lib:
import 'package:enchilada/src/beans.dart';
The name you use here (in this case
enchilada
) is the name you specify for
your package in its
pubspec.
Web files
#enchilada/
web/
index.html
main.dart
style.css
For web packages, place entrypoint code—Dart scripts that include
main()
and supporting files, such as CSS or HTML—under
web
.
You can organize the
web
directory into subdirectories if you like.
Put
library code
under
lib
.
If the library isn't imported directly by code under
web
, or by
another package, put it under
lib/src
.
Put
web-based examples
under
example
. See
Public assets
for tips on where to put assets,
such as images.
Command-line apps
#enchilada/
bin/
enchilada
Some packages define programs that can be run directly from the command line. These can be shell scripts or any other scripting language, including Dart.
If your package defines code like this, put it in a directory named
bin
.
You can run that script from anywhere on the command line, if you set it up
using
dart pub global
.
Tests and benchmarks
#enchilada/
test/
enchilada_test.dart
tortilla_test.dart
Every package should have tests. With pub, the convention is
that most of these go in a
test
directory (or some directory inside it if you
like) and have
_test
at the end of their file names.
Typically, these use the test package.
enchilada/
integration_test/
app_test.dart
Flutter app packages may also have special integration tests, which use the
integration_test
package. These tests live in their own
integration_test
directory.
Other packages may choose to follow a similar pattern, to separate their slower
integration tests from their unit tests, but note that by default
dart test
will not run these tests. You will have to explicitly run them with
dart test integration_test
.
enchilada/
benchmark/
make_lunch.dart
Packages that have performance critical code may also include benchmarks. These test the API not for correctness but for speed (or memory use, or maybe other empirical metrics).
Documentation
#enchilada/
doc/
api/
getting_started.md
If you have code and tests, the next piece you might want
is good documentation. That goes inside a directory named
doc
.
When you run the
dart doc
tool, it places the API documentation, by default, under
doc/api
.
Since the API documentation is generated from the source code,
you should not place it under source control.
Other than the generated
api
, we don't
have any guidelines about format or organization of the documentation
that you author. Use whatever markup format that you prefer.
Examples
#enchilada/
example/
main.dart
Code, tests, docs, what else
could your users want? Standalone example programs that use your package, of
course! Those go inside the
example
directory. If the examples are complex
and use multiple files, consider making a directory for each example. Otherwise,
you can place each one right inside
example
.
In your examples, use
package:
to import files from your own package.
That ensures that the example code in your package looks exactly
like code outside of your package would look.
If you might publish your package, consider creating an example file with one of the following names:
example/example[.md]
example[/lib]/main.dart
example[/lib]/package_name.dart
example[/lib]/package_name_example.dart
example[/lib]/example.dart
example/README[.md]
When you publish a package that contains one or more of the above files,
the pub.dev site creates an
Example
tab to display the first file it finds
(searching in the order shown in the list above).
For example, if your package has many files under its
example
directory,
including a file named
README.md
,
then your package's Example tab displays the contents of
example/README.md
(parsed as
Markdown.)
Internal tools and scripts
#enchilada/
tool/
generate_docs.dart
Mature packages often have little helper scripts and programs that people run while developing the package itself. Think things like test runners, documentation generators, or other bits of automation.
Unlike the scripts in
bin
, these are
not
for external users of the package.
If you have any of these, place them in a directory called
tool
.
Hooks
#enchilada/
hook/
build.dart
Packages can define hooks to be invoked by the Dart and Flutter SDK. These hooks have a predefined CLI, and will be invoked by the SDK tools if present.
Because these hooks are invoked by the
dart
and
flutter
tools on runs and builds, the dependencies
of these hooks must be normal dependencies and not
dev_dependencies
.
Project-specific caching for tools
#
The
.dart_tool/
directory is created when you run
dart pub get
and might be deleted at any time. Various tools use this directory
for caching files specific to your project and/or local machine.
The
.dart_tool/
directory should never be checked into
source control, or copied between machines.
It is also generally safe to delete the
.dart_tool/
directory,
though some tools might need recompute the cached information.
Example:
The
dart pub get
tool
will download and extract dependencies to a global
$PUB_CACHE
directory,
and then write a
.dart_tool/package_config.json
file mapping
package names
to directories in the global
$PUB_CACHE
directory.
The
.dart_tool/package_config.json
file is used by other tools,
such as the analyzer and compilers when they need to resolve statements
such as
import 'package:foo/foo.dart'
.
When developing a tool that needs project-specific caching,
you might consider using the
.dart_tool/
directory
because most users already ignore it with
.gitignore
.
When caching files for your tool in a user's
.dart_tool/
directory,
you should use a unique subdirectory. To avoid collisions,
subdirectories of the form
.dart_tool/<tool_package_name>/
are reserved for the package named
<tool_package_name>
.
If your tool isn't distributed through the
pub.dev site,
you might consider publishing a placeholder package in order to
reserve the unique name.
Example:
package:build
provides a
framework for writing code generation steps.
When running these build steps, files are cached in
.dart_tool/build/
.
This helps speed-up future re-runs of the build steps.
Unless stated otherwise, the documentation on this site reflects Dart 3.9.2. Page last updated on 2025-8-19. View source or report an issue.