Dart Package Management with pub
last modified October 22, 2026
In this article we explore pub, the package manager that ships with the Dart SDK. It resolves, downloads, and links the libraries your project depends on, and it records exactly which versions were chosen.
Package Management Overview
A Dart package is a reusable unit of code described by a
pubspec.yaml file. The pub tool reads that file, works
out a compatible set of dependency versions, downloads them from
pub.dev, and links them into your project. The chosen versions are
recorded in a lock file so that every machine resolves the same set.
| File or term | Role |
|---|---|
pubspec.yaml |
Package metadata and the dependencies you want |
pubspec.lock |
Exact resolved versions, checked in for applications |
.dart_tool/package_config.json |
Generated map from package names to local paths |
dependencies |
Packages needed at runtime |
dev_dependencies |
Packages needed only for development and tests |
Creating a Package
Every package starts with a pubspec.yaml. The required fields are
name, environment, and a version constraint for the SDK.
The publish_to: none line marks the project as private so it cannot
be published by accident.
name: hello_cli description: A minimal Dart package. version: 0.1.0 publish_to: none environment: sdk: ^3.13.0
Running dart pub get reads the file and prepares the package. With no
dependencies declared, there is nothing to download, but pub still resolves the
environment and writes the metadata files that tooling relies on.
$ dart pub get Resolving dependencies... Downloading packages... Got dependencies!
Runtime and Development Dependencies
Dependencies are listed under two keys. Anything the running program needs goes
in dependencies, while tools used only during development, such as
test runners and linters, go in dev_dependencies. Development
dependencies are not pulled in when your package is used as a dependency by
someone else.
name: cli_demo description: Command-line and pub examples. version: 1.0.0 publish_to: none environment: sdk: ^3.13.0 dependencies: args: ^2.5.0 path: ^1.9.0
With dependencies declared, dart pub get downloads each package and
reports what changed. The plus lines show the versions that were added, and the
final line summarises the change count.
$ dart pub get Resolving dependencies... Downloading packages... + args 2.7.0 + path 1.9.1 Changed 2 dependencies!
Tools that are only needed while developing the package belong under
dev_dependencies instead. A test runner and a linter are typical
entries. They are installed when working on the package itself, but they are not
pulled in when someone else lists your package as a dependency.
dev_dependencies: test: ^1.25.0 lints: ^5.0.0
Version Constraints
A dependency value is a version constraint, not just a version. The caret syntax
^1.2.0 is the most common: it allows any version from
1.2.0 up to, but not including, 2.0.0. Explicit ranges
and the special value any are also available.
dependencies: http: ^1.2.0 # >=1.2.0 <2.0.0 path: '>=1.8.0 <2.0.0' # explicit range collection: any # any version meta: 1.15.0 # exactly this version
The caret is preferred for libraries because it accepts compatible updates. An explicit range gives finer control when a package makes frequent breaking changes. Pinning an exact version is the strictest option and is usually reserved for applications that need fully deterministic builds.
Using a Package
After dart pub get, a dependency is imported with a
package: URI. The convention is to import it under a prefix, which
keeps names from different packages from colliding.
import 'package:path/path.dart' as p;
void main() {
print(p.join('home', 'user', 'file.txt'));
print(p.extension('archive.tar.gz'));
print(p.basenameWithoutExtension('/tmp/report.final.pdf'));
}
The as p prefix means the package's functions are reached through
p.. join builds a path using the current platform's
separator, extension returns the last extension, and
basenameWithoutExtension strips the directories and the final
extension. Using a package this way is no different from importing a library from
the SDK.
$ dart run pub_import.dart home/user/file.txt .gz report.final
The Lock File
dart pub get writes a pubspec.lock file that pins the
exact version of every package, including transitive ones. Applications commit
this file so that collaborators and build servers install exactly the same
versions. Each entry records the source, the resolved version, and a hash used to
verify the download.
packages:
args:
dependency: "direct main"
description:
name: args
url: "https://pub.dev"
source: hosted
version: "2.7.0"
sdks:
dart: ">=3.13.0 <4.0.0"
The direct main value marks args as a dependency listed
in dependencies. Packages pulled in indirectly are marked
transitive, and development-only packages are marked
direct dev. A later dart pub get preserves locked
versions when possible, but can change them if updated constraints or new
dependencies require it. dart pub upgrade deliberately seeks newer
versions within the constraints. For strict CI reproducibility, use
dart pub get --enforce-lockfile, which fails if the lock file does not
describe a valid resolution or a hosted package's content hash has changed.
Common pub Commands
Most day-to-day package work uses a handful of commands. Adding a package updates
the pubspec.yaml and resolves it in one step, while removing it does
the reverse. The outdated command reports whether newer versions are
available without changing anything.
$ dart pub get # resolve and link dependencies $ dart pub add collection # add a dependency to pubspec.yaml $ dart pub remove collection $ dart pub upgrade # upgrade within the constraints $ dart pub outdated # list newer versions
The following session adds a package, removes it again, and then checks for updates. Each command reports what it changed, which makes the flow of versions easy to follow.
$ dart pub add collection Resolving dependencies... Downloading packages... + collection 1.19.1 Changed 1 dependency! $ dart pub remove collection Resolving dependencies... Downloading packages... These packages are no longer being depended on: - collection 1.19.1 Changed 1 dependency! $ dart pub outdated Showing outdated packages. [*] indicates versions that are not the latest available. Found no outdated packages
Workspaces
When several related packages live in one repository, a pub
workspace lets them share a single resolution and a single lock
file. The root pubspec.yaml declares which directories are members,
and each member is resolved together with the others.
name: my_workspace publish_to: none environment: sdk: ^3.13.0 workspace: - packages/core - packages/app
Each member adds resolution: workspace to its own
pubspec.yaml. Using a workspace keeps every package on the same set
of dependency versions, which avoids the conflicts that arise when each package
resolves independently.
The workspace list also accepts a glob pattern, which is useful
when packages are added frequently:
workspace: - packages/*
A workspace member can declare its own nested workspace. The root only lists
packages/server; pub discovers auth and
api through the server package:
name: server resolution: workspace environment: sdk: ^3.13.0 workspace: - auth - api
The sub-packages also set resolution: workspace. All packages in
the root and nested workspaces share one lock file and one
.dart_tool/package_config.json. Use
dart pub workspace list to see the resolved layout:
$ dart pub workspace list Package Path _ ./ server packages/server/ auth packages/server/auth/ api packages/server/api/
Commands that operate on a single package accept -C, so you do
not need to change directories. For example,
dart pub -C packages/app add http adds a dependency to the
packages/app member.
Newer pub Commands
Recent Dart releases added several commands that are useful in day-to-day package maintenance:
| Command | Purpose |
|---|---|
dart pub bump major | Increment the major, minor, or patch version of the current package; breaking applies the pre-1.0 convention. |
dart pub unpack foo | Download and unpack a package into a local directory without adding it as a dependency. |
dart pub get --enforce-lockfile | Fail if pubspec.lock is missing, incomplete, or no longer matches the constraints. |
dart pub workspace list | List the root and member packages of a workspace. |
Since Dart 3.12, Git dependencies with large files are also supported directly:
when git lfs is installed, dart pub get fetches Git LFS
content automatically. No extra configuration is needed in
pubspec.yaml.
Source
Dart: The pubspec file
Dart: Dependencies
Dart: pub commands
Dart: Lock file
pub manages the libraries a Dart project depends on. The pubspec.yaml
declares what the project needs, separating runtime and development dependencies,
while version constraints control how flexible each requirement is. The lock file
pins the resolved versions for reproducible builds, and a small set of commands
covers adding, removing, upgrading, and inspecting dependencies.
Author
List all Dart tutorials.