On this page28 sections
- Prerequisites
- Connection With Previous Lectures
- Learning Objectives
- 1. What Pub Does
- 2. dart pub get
- 3. dart pub add
- 4. Transitive Dependencies
- 5. Seeing the Tree with dart pub deps
- 6. dart pub remove
- 7. dart pub outdated
- 8. dart pub upgrade
- 9. Path Dependencies
- 10. Version Solving and Conflicts
- 11. dependency_overrides (Use Sparingly)
- 12. Git Dependencies
- 13. The Pub Cache
- 14. Choosing Quality Packages on pub.dev
- 15. Keeping Dependencies Minimal
- 16. A Real-World Example
- Code Examples
- Common Beginner Mistakes
- Best Practices
- Mini Challenge
- Practice Questions
- Practice Answers
- Quick Revision
- Key Takeaways
- What's Next?
Lecture 105 — Dart Pub & Dependency Management
| Category | 17 — Libraries & Packages |
| Lecture | 105 of 140 |
| Estimated video length | 12 min |
| Target | Dart 3.12.2 |
| Level | Intermediate |
Adding one package can quietly bring ten more, and packages keep releasing new versions. Pub is the tool that fetches, checks, upgrades and removes all of them for you.
Contents
Prerequisites · Connection · Objectives · Lesson · Code Examples · Common Mistakes · Best Practices · Mini Challenge · Practice Questions · Answers · Takeaways
Every output and error message in this lecture is real. Each one was produced by running the commands and code shown. Two things will differ on your machine: package versions (packages keep releasing new ones), and the
Flutter SDKline indart pub deps, which only appears when Dart was installed together with Flutter.
Prerequisites
Students should already understand, from Lectures 101–104:
- importing with
import,as,showandhide(Lecture 101) - splitting code into your own library files (Lecture 102)
- what a package is, pub.dev, and
import 'package:...'(Lecture 103) pubspec.yaml, version constraints such as^1.19.1, anddev_dependencies(Lecture 104)
Connection With Previous Lectures
In Lecture 104 we wrote pubspec.yaml by hand. But a pubspec is only a wish list. Sara's
program still fails until something reads that list and downloads the code:
Error: Couldn't resolve the package 'intl' in 'package:intl/intl.dart'.And the questions keep coming. Which versions did we actually get? What if two packages want different versions of the same third package? How do we know when an update is out? That "something" is pub, and this lecture is about using it well.
Learning Objectives
By the end of this lecture you will be able to:
- Run
dart pub get,add,remove,deps,outdatedandupgrade - Explain direct and transitive dependencies
- Read a version-solving failure and fix it
- Add path and git dependencies
- Use
dependency_overridesas a last resort - Explain what the pub cache is
- Judge a package on pub.dev by likes, pub points and verified publisher
- Keep a project's dependency list small
1. What Pub Does
Pub is Dart's package manager. You run it as dart pub <command> inside a folder that has a
pubspec.yaml. It reads your constraints, picks versions that fit together, downloads them, and
writes the exact versions into pubspec.lock.
Analogy — pub is a school librarian. You hand over a list ("a maths book, edition 1 or newer"). The librarian finds copies that work together, keeps them on a shared shelf, and writes down exactly which copies you borrowed.
2. dart pub get
Ali starts a project called class_report with no packages yet:
name: class_report
description: Prints a class result summary.
version: 1.0.0
environment:
sdk: ^3.12.0$ dart pub get
Resolving dependencies...
Downloading packages...
Got dependencies!dart pub get fetches whatever the pubspec lists. If pubspec.lock already exists, it reuses
the versions written there, so everyone on the team gets the same ones.
3. dart pub add
Instead of editing the pubspec by hand, let pub do it:
$ dart pub add collection
Resolving dependencies...
Downloading packages...
+ collection 1.19.1
Changed 1 dependency!Pub chose the newest version, added a caret constraint to the pubspec, and ran get for you:
dependencies:
collection: ^1.19.1Use dart pub add --dev test for a development-only package (Lecture 104).
4. Transitive Dependencies
Ali adds one more package, http:
$ dart pub add http
Resolving dependencies...
Downloading packages...
+ async 2.13.1
+ http 1.6.0
+ http_parser 4.1.2
+ meta 1.19.0
+ path 1.9.1
+ source_span 1.10.2
+ string_scanner 1.4.1
+ term_glyph 1.2.2
+ typed_data 1.4.0
+ web 1.1.1
Changed 10 dependencies!One request, ten packages. http needs async, async needs meta, and so on. Packages you
list yourself are direct dependencies; packages that arrive because your packages need them
are transitive dependencies.
5. Seeing the Tree with dart pub deps
dart pub deps shows who needs what. ... means "already shown above":
$ dart pub deps
Dart SDK 3.12.1
Flutter SDK 3.44.1
class_report 1.0.0
├── collection 1.19.1
└── http 1.6.0
├── async 2.13.1
│ ├── collection...
│ └── meta...
├── http_parser 4.1.2
│ ├── collection...
│ ├── source_span 1.10.2
│ │ ├── collection...
│ │ ├── path 1.9.1
│ │ └── term_glyph 1.2.2
│ ├── string_scanner 1.4.1
│ │ └── source_span...
│ └── typed_data 1.4.0
│ └── collection...
├── meta 1.19.0
└── web 1.1.1--style=compact groups them, which is easier to read:
$ dart pub deps --style=compact
...
dependencies:
- collection 1.19.1
- http 1.6.0 [async http_parser meta web]
transitive dependencies:
- async 2.13.1 [collection meta]
- http_parser 4.1.2 [collection source_span string_scanner typed_data]
- meta 1.19.0
...Notice that collection is needed by four packages but downloaded once.
6. dart pub remove
Ali doesn't need http after all:
$ dart pub remove http
Resolving dependencies...
Downloading packages...
These packages are no longer being depended on:
- async 2.13.1
- http 1.6.0
- http_parser 4.1.2
- meta 1.19.0
- path 1.9.1
- source_span 1.10.2
- string_scanner 1.4.1
- term_glyph 1.2.2
- typed_data 1.4.0
- web 1.1.1
Changed 10 dependencies!Removing a direct dependency also drops every transitive one that nothing else needs.
7. dart pub outdated
Ali's pubspec now has two packages behind the times — path locked at an old 1.8.0, and
intl: ^0.19.0. dart pub outdated reports it:
$ dart pub outdated
Showing outdated packages.
[*] indicates versions that are not the latest available.
Package Name Current Upgradable Resolvable Latest
direct dependencies:
intl *0.19.0 *0.19.0 0.20.3 0.20.3
path *1.8.0 1.9.1 1.9.1 1.9.1
1 upgradable dependency is locked (in pubspec.lock) to an older version.
To update it, use `dart pub upgrade`.
1 dependency is constrained to a version that is older than a resolvable version.
To update it, edit pubspec.yaml, or run `dart pub upgrade --major-versions`.| Column | Meaning |
|---|---|
| Current | the version in pubspec.lock now |
| Upgradable | the newest version your pubspec constraint allows |
| Resolvable | the newest version that would work if you changed the constraint |
| Latest | the newest version published |
8. dart pub upgrade
upgrade ignores the lock file and takes the newest versions inside your constraints:
$ dart pub upgrade
Resolving dependencies...
Downloading packages...
intl 0.19.0 (0.20.3 available)
> path 1.9.1 (was 1.8.0)
Changed 1 dependency!
1 package has newer versions incompatible with dependency constraints.
Try `dart pub outdated` for more information.path moved; intl didn't, because ^0.19.0 forbids 0.20. A jump like that may contain
breaking changes, so pub only makes it when you ask:
$ dart pub upgrade --major-versions
Changed 1 constraint in pubspec.yaml:
intl: ^0.19.0 -> ^0.20.3
Resolving dependencies...
Downloading packages...
> intl 0.20.3 (was 0.19.0)
Changed 1 dependency!After a major upgrade, run your program and tests before moving on.
9. Path Dependencies
A package doesn't have to live on pub.dev. Ali keeps his grading code in a local package,
marks_tools, in a folder next to class_report:
// marks_tools/lib/marks_tools.dart
String grade(int marks) {
if (marks >= 80) return 'A';
if (marks >= 60) return 'B';
return 'C';
}He points to it with path::
publish_to: none
dependencies:
collection: ^1.19.1
marks_tools:
path: ../marks_tools$ dart pub get
Resolving dependencies...
Downloading packages...
+ marks_tools 0.1.0 from path ../marks_tools
Changed 1 dependency!Changes inside marks_tools show up the next time class_report runs — no pub get needed.
publish_to: none marks the project as private; without it dart analyze warns:
Publishable packages can't have 'path' dependencies.
10. Version Solving and Conflicts
Every package states which versions of its own dependencies it accepts. Pub's version
solver must find one version of each package that satisfies everybody. Suppose
marks_tools declares an old range:
# marks_tools/pubspec.yaml
dependencies:
collection: ">=1.15.0 <1.18.0"class_report asks for ^1.19.1. No version is both below 1.18.0 and at least 1.19.1:
$ dart pub get
Resolving dependencies...
Because every version of marks_tools from path depends on collection >=1.15.0 <1.18.0 and class_report depends on collection ^1.19.1, marks_tools from path is forbidden.
So, because class_report depends on marks_tools from path, version solving failed.
You can try the following suggestion to make the pubspec resolve:
* Consider downgrading your constraint on collection: dart pub add collection:^1.17.2Read it like a sentence: because A needs X and B needs Y, solving failed. The real fix is to
make the ranges overlap — here, widen marks_tools to collection: ^1.17.0. For a pub.dev
package, try dart pub upgrade first; a newer release usually accepts newer dependencies.
11. dependency_overrides (Use Sparingly)
When you cannot change the other package, dependency_overrides forces one version for the
whole project:
dependency_overrides:
collection: ^1.19.1$ dart pub get
Resolving dependencies...
Downloading packages...
! collection 1.19.1 (overridden)
Got dependencies!The ! is a warning: pub stopped checking. marks_tools said it doesn't support
collection 1.19, and you overruled it — if it breaks, that's on you. Use an override only as
a short-term fix and delete it once the real one is in.
12. Git Dependencies
A package can also come straight from a git repository — useful for a fix not yet released.
path: picks a folder inside the repository, and ref: a branch, tag or commit:
dependencies:
term_glyph:
git:
url: https://github.com/dart-lang/tools.git
path: pkgs/term_glyph
ref: main$ dart pub get
Resolving dependencies...
Downloading packages...
+ term_glyph 1.2.3-wip from git https://github.com/dart-lang/tools.git at a73703 in pkgs/term_glyph
Changed 1 dependency!Pub records the exact commit (a73703...) in pubspec.lock, so a teammate gets the same
code even after the branch moves on.
13. The Pub Cache
Downloaded packages don't go into your project. They go into one shared pub cache —
~/.pub-cache on macOS and Linux, %LOCALAPPDATA%\Pub\Cache on Windows — with one folder per
version:
$ ls ~/.pub-cache/hosted/pub.dev | grep path-
path-1.8.0
path-1.8.3
path-1.9.1Every project on your computer shares these copies, which is why the second project to use a
package needs no download, and why dart pub get --offline works. Never edit files in the cache.
dart pub cache repair re-downloads damaged packages; dart pub cache clean empties it.
14. Choosing Quality Packages on pub.dev
pub.dev often has several packages for the same job. Before adding one, check its page:
- Likes — how many developers bookmarked it.
- Pub points — an automatic score (out of 160) for documentation, formatting, static analysis and up-to-date dependencies.
- Downloads — how widely it is used.
- Verified publisher — a check mark showing the publisher proved who they are, such as
dart.devorgoogle.dev. - Recent release and Dart 3 support — a package untouched for years may not work.
collection, path, intl and http are all published by dart.dev — safe defaults.
15. Keeping Dependencies Minimal
Every package is code you didn't write but must trust and keep updated. And each one can bring its own crowd — in a fresh project, a single dev package pulls in dozens:
$ dart pub add --dev test
Resolving dependencies...
Downloading packages...
+ _fe_analyzer_shared 109.0.0
+ analyzer 14.5.0
+ args 2.7.0
+ async 2.13.1
...
+ webkit_inspection_protocol 1.2.1
+ yaml 3.1.4
Changed 49 dependencies!(43 lines left out.) That's fine for test, which you really need. But if the core
libraries from Lecture 101 already do the job, skip the package — and dart pub remove
anything you stop using.
16. A Real-World Example
Ali's finished class_report combines a pub.dev package and his path package:
name: class_report
description: Prints a class result summary.
version: 1.0.0
publish_to: none
environment:
sdk: ^3.12.0
dependencies:
collection: ^1.19.1
marks_tools:
path: ../marks_tools// bin/class_report.dart
import 'package:collection/collection.dart';
import 'package:marks_tools/marks_tools.dart';
void main() {
var marks = {'Ali': 72, 'Sara': 95, 'Zayan': 58, 'Hina': 88};
var byGrade = groupBy(marks.keys, (name) => grade(marks[name]!));
print(byGrade);
print('Highest: ${marks.values.max}');
print('Average: ${marks.values.average}');
}$ dart pub get
Resolving dependencies...
Downloading packages...
Got dependencies!
$ dart pub deps --style=compact
Dart SDK 3.12.1
Flutter SDK 3.44.1
class_report 1.0.0
dependencies:
- collection 1.19.1
- marks_tools 0.1.0
$ dart run
{B: [Ali], A: [Sara, Hina], C: [Zayan]}
Highest: 95
Average: 78.25groupBy, max and average come from collection; grade comes from marks_tools. Two
direct dependencies, no transitive ones, no overrides — a clean project.
Code Examples
Examples 1–6 run inside a project called shop; Examples 7–10 run in the same project after
collection and intl are added.
Example 1 — A Fresh pub get
$ dart pub get
Resolving dependencies...
Downloading packages...
Got dependencies!Example 2 — Adding with a Constraint
$ dart pub add 'path:^1.9.0'
Resolving dependencies...
Downloading packages...
+ path 1.9.1
Changed 1 dependency!import 'package:path/path.dart' as p;
void main() {
print(p.join('reports', '2026', 'marks.txt'));
print(p.extension('marks.txt'));
print(p.basenameWithoutExtension('reports/ali.txt'));
}reports/2026/marks.txt
.txt
aliThe quotes keep your terminal from treating ^ as a special character.
Example 3 — Two Packages at Once
$ dart pub add collection intl
Resolving dependencies...
Downloading packages...
+ clock 1.1.3
collection 1.19.1 (from transitive dependency to direct dependency)
+ intl 0.20.3
Changed 3 dependencies!shop already had test as a dev dependency, which had brought collection in as a
transitive dependency; now it is direct.
Example 4 — Deps Without Dev Packages
$ dart pub deps --style=compact --no-dev
Dart SDK 3.12.1
Flutter SDK 3.44.1
shop 0.0.0
dependencies:
- collection 1.19.1
- intl 0.20.3 [clock meta path]
- path 1.9.1
transitive dependencies:
- clock 1.1.3
- meta 1.19.0Example 5 — Working Offline
$ dart pub get --offline
Resolving dependencies...
Downloading packages...
Got dependencies!Everything came from the pub cache.
Example 6 — Upgrading One Package
$ dart pub upgrade collection
Resolving dependencies...
Downloading packages...
No dependencies changed.Already the newest allowed version — nothing to do.
Example 7 — Student Marks
import 'package:collection/collection.dart';
void main() {
var marks = {'Ali': 72, 'Sara': 95, 'Zayan': 58, 'Hina': 88};
print('Highest: ${marks.values.max}');
print('Average: ${marks.values.average}');
var ranked = marks.keys.sortedBy((name) => -marks[name]!);
print('Ranking: $ranked');
}Highest: 95
Average: 78.25
Ranking: [Sara, Hina, Ali, Zayan].max comes from collection — no reduce needed. Sorting by the negative mark puts the
highest first.
Example 8 — Shopping Total
import 'package:collection/collection.dart';
import 'package:intl/intl.dart';
void main() {
var cart = {'Notebook': 500, 'Pen': 50, 'Bag': 1500, 'Laptop': 85000};
var total = cart.values.sum;
var format = NumberFormat('#,###');
print('Items: ${cart.length}');
print('Total: Rs ${format.format(total)}');
}Items: 4
Total: Rs 87,050Example 9 — Bank Balance
import 'package:intl/intl.dart';
void main() {
var balance = 1250000.5;
var money = NumberFormat.currency(symbol: 'Rs ', decimalDigits: 2);
print("Sara's balance: ${money.format(balance)}");
}Sara's balance: Rs 1,250,000.50Example 10 — A Week of Temperatures
import 'package:collection/collection.dart';
void main() {
var week = [29.5, 31.0, 30.5, 32.0, 28.0, 30.0, 33.5];
print('Hottest: ${week.max}');
print('Coldest: ${week.min}');
print('Average: ${week.average.toStringAsFixed(1)}');
var firstHot = week.firstWhereOrNull((t) => t > 35);
print('First day above 35: ${firstHot ?? 'none'}');
}Hottest: 33.5
Coldest: 28.0
Average: 30.6
First day above 35: nonefirstWhereOrNull returns null instead of throwing, so ?? can supply a default.
Common Beginner Mistakes
1. Importing a package you never added
// import 'package:intl/intl.dart'; // ✗ intl is not in pubspec.yamlError: Couldn't resolve the package 'intl' in 'package:intl/intl.dart'.Run dart pub add intl first.
2. Running pub outside the project folder
Pub answers: Found no `pubspec.yaml` file in `<your folder>` or parent directories.
cd into the folder that holds pubspec.yaml.
3. Misspelling a package name
$ dart pub add colection
Because class_report depends on colection any which doesn't exist (could not find package colection at https://pub.dev), version solving failed.Copy the name from the package's pub.dev page.
4. Expecting dart pub upgrade to cross a major version
upgrade stays inside your constraints, so ^0.19.0 never becomes 0.20. Use
dart pub upgrade --major-versions or edit the constraint.
5. Leaving an override in place
A forgotten dependency_overrides entry hides future conflicts. Each dart pub get prints
(overridden) — treat it as a to-do item.
6. Editing files in the pub cache
A change in ~/.pub-cache affects every project and is lost on dart pub cache repair. If you
need to change a package, copy it and use a path dependency.
Best Practices
- Use
dart pub addanddart pub removerather than hand-editing dependency lists. - Commit
pubspec.lockfor apps, so everyone runs the same versions. - Run
dart pub outdatedregularly; upgrade in small steps and test after each. - Fix conflicts by making ranges overlap; use
dependency_overridesonly briefly. - Add
publish_to: noneto projects with path dependencies. - Prefer packages with a verified publisher, high pub points and a recent release.
- Add a package only when the core libraries can't do the job.
Mini Challenge
Zayan's shop project needs sorted prices and a formatted total. Add both packages with one command, then print the prices cheapest first and their total.
Your program should print exactly:
Cheapest first: [50, 500, 1500, 85000]
Total: Rs 87,050Try it yourself before opening the solution.
Show solution
$ dart pub add collection intlimport 'package:collection/collection.dart';
import 'package:intl/intl.dart';
void main() {
var prices = [1500, 50, 85000, 500];
var format = NumberFormat('#,###');
print('Cheapest first: ${prices.sorted((a, b) => a.compareTo(b))}');
print('Total: Rs ${format.format(prices.sum)}');
}Cheapest first: [50, 500, 1500, 85000]
Total: Rs 87,050How it works
sortedfromcollectionreturns a new sorted list and leavespricesunchanged.sumadds the list;NumberFormatadds the thousands separator.
Practice Questions
Work through these in order — they run from beginner to application to challenge.
Beginner
-
Which command downloads the packages listed in
pubspec.yaml? -
Add the
pathpackage and printp.join('marks', 'ali.txt'). -
In this output, which packages are transitive dependencies?
dependencies: - intl 0.20.3 [clock meta path] - path 1.9.1 transitive dependencies: - clock 1.1.3 - meta 1.19.0
Application
-
Write the pubspec lines for a path dependency on a folder
../bank_tools. -
Using the
dart pub outdatedtable in Step 7, which command updatespath, and which updatesintl? -
With
collection, print the sum and average of[40, 75, 90, 65]. -
This program fails with
Couldn't resolve the package 'intl'. Fix it so it prints2,050.import 'package:intl/intl.dart'; void main() { print(NumberFormat('#,###').format(2050)); } -
Write a git dependency on
term_glyph, found in thepkgs/term_glyphfolder ofhttps://github.com/dart-lang/tools.git, on themainbranch.
Challenge
-
Explain this message, then give the proper fix and the last-resort fix:
Because every version of marks_tools from path depends on collection >=1.15.0 <1.18.0 and class_report depends on collection ^1.19.1, marks_tools from path is forbidden. -
Using
groupByfromcollection, group[29.5, 31.0, 30.5, 28.0, 33.5]into'hot'(30 or more) and'mild', and print the map.
Practice Answers
Question 1 — Solution
$ dart pub get
Resolving dependencies...
Downloading packages...
Got dependencies!Question 2 — Solution
$ dart pub add pathimport 'package:path/path.dart' as p;
void main() {
print(p.join('marks', 'ali.txt'));
}marks/ali.txtOn Windows p.join uses a backslash: marks\ali.txt.
Question 3 — Solution
clock and meta. They arrived because intl needs them. (path is direct here, even though
intl needs it too.)
Question 4 — Solution
publish_to: none
dependencies:
bank_tools:
path: ../bank_toolsQuestion 5 — Solution
path is upgradable inside its constraint, so dart pub upgrade updates it. intl needs a
new constraint, so use dart pub upgrade --major-versions (or change it to ^0.20.3).
Question 6 — Solution
import 'package:collection/collection.dart';
void main() {
var marks = [40, 75, 90, 65];
print('Sum: ${marks.sum}');
print('Average: ${marks.average}');
}Sum: 270
Average: 67.5Question 7 — Solution
The code is fine; the package is missing. Run:
$ dart pub add intlThen dart run prints:
2,050Question 8 — Solution
dependencies:
term_glyph:
git:
url: https://github.com/dart-lang/tools.git
path: pkgs/term_glyph
ref: mainQuestion 9 — Solution
marks_tools accepts only collection below 1.18.0; class_report needs 1.19.1 or newer.
No version satisfies both, so version solving fails. The proper fix is to widen
marks_tools to collection: ^1.17.0. The last resort is an override in class_report:
dependency_overrides:
collection: ^1.19.1Question 10 — Solution
import 'package:collection/collection.dart';
void main() {
var temps = [29.5, 31.0, 30.5, 28.0, 33.5];
var groups = groupBy(temps, (t) => t >= 30 ? 'hot' : 'mild');
print(groups);
}{mild: [29.5, 28.0], hot: [31.0, 30.5, 33.5]}Quick Revision
- What does
dart pub getdo whenpubspec.lockalready exists? - What is a transitive dependency?
- Which command shows the dependency tree?
- What do the Upgradable and Resolvable columns of
dart pub outdatedmean? - Why doesn't
dart pub upgrademove^0.19.0to0.20.3? - When does version solving fail?
- What does the
!before(overridden)warn you about? - Where are downloaded packages stored?
- Name three signs of a quality package on pub.dev.
Key Takeaways
- Pub fetches packages, solves versions and records them in
pubspec.lock. getfetches,add/removeedit the pubspec,depsshows the tree,outdatedreports updates,upgradeapplies them.- Packages you list are direct; packages they need are transitive.
upgradestays inside your constraints;--major-versionschanges them.- Version solving fails when no single version satisfies every constraint — make the ranges overlap.
path:andgit:dependencies load packages from a folder or a repository.dependency_overridesswitches off a safety check; use it briefly, if at all.- All projects share one pub cache; never edit it.
- Choose packages by pub points, likes, downloads and a verified publisher — and add as few as you can.
What's Next?
Lecture 106 — File Handling. That closes Category 17: you can now import libraries, build
your own, and manage packages. Next we start Category 18 and let programs save data — reading
and writing files with dart:io, so Ali's class report can live on disk instead of vanishing
when the program ends.