Watch the video (11 min)
On this page26 sections
  1. Prerequisites
  2. Connection With Previous Lectures
  3. Learning Objectives
  4. 1. What Is pubspec.yaml?
  5. 2. YAML: key: value
  6. 3. YAML: Indentation Means "Inside"
  7. 4. YAML: Lists and Comments
  8. 5. The name Field
  9. 6. description and version
  10. 7. environment and the SDK Constraint
  11. 8. dependencies
  12. 9. Semantic Versioning
  13. 10. Version Constraints
  14. 11. dev_dependencies
  15. 12. executables (A Quick Look)
  16. 13. pubspec.lock vs pubspec.yaml
  17. 14. A Real-World Example
  18. Code Examples
  19. Common Beginner Mistakes
  20. Best Practices
  21. Mini Challenge
  22. Practice Questions
  23. Practice Answers
  24. Quick Revision
  25. Key Takeaways
  26. What's Next?

Lecture 104 — pubspec.yaml

Category17 — Libraries & Packages
Lecture104 of 140
Estimated video length11 min
TargetDart 3.12.2
LevelIntermediate

Every project has had a pubspec.yaml, but we never read it. One wrong space there can hide a package without any error. This lecture reads the file line by line.

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 came from running dart pub get or dart run on Dart 3.12.2. Package versions such as path 1.9.1 are the newest ones at the time of writing, so you may see higher numbers.


Prerequisites

Students should already understand, from Lectures 002–103:

  • creating a project with dart create and running it with dart run (Lecture 002)
  • lists and maps (Lectures 039 and 049)
  • import with an as prefix (Lecture 101)
  • what a package is and how package: imports work (Lecture 103)

Connection With Previous Lectures

In Lecture 002, dart create gave us a file called pubspec.yaml and we promised to study it later. In Lecture 103 we learned that packages are shared code. But how does a project say which packages it needs, and which versions?

Sara adds the path package to her project, runs dart pub get, and sees this:

output
Got dependencies!

No error. Yet her program still says Couldn't resolve the package 'path'. The mistake is one missing pair of spaces in pubspec.yaml. To find it, we need to read that file properly.


Learning Objectives

By the end of this lecture you will be able to:

  • Explain what pubspec.yaml is for
  • Write YAML key–value pairs, nested sections and lists
  • Use the name, description, version and environment fields
  • Add packages under dependencies and dev_dependencies
  • Read version constraints such as ^1.9.0, '>=1.8.0 <2.0.0' and any
  • Explain semantic versioning: major, minor and patch
  • Tell pubspec.yaml and pubspec.lock apart
  • Fix the errors dart pub get reports for a broken pubspec

1. What Is pubspec.yaml?

pubspec.yaml is a project's description file. It gives the project its name, says which Dart versions it works with, and lists the packages it needs. Every Dart package has one.

Analogy — think of the label on a recipe card. Before the steps, it shows the dish's name, which oven it needs, and the list of ingredients. Your .dart files are the steps; pubspec.yaml is the label.

This is the file dart create hello_dart writes:

yaml
name: hello_dart
description: A sample command-line application.
version: 1.0.0
# repository: https://github.com/my_org/my_repo

environment:
  sdk: ^3.12.1

# Add regular dependencies here.
dependencies:
  path: ^1.9.0

dev_dependencies:
  lints: ^6.0.0
  test: ^1.25.6

The sdk line matches the Dart version that created the project.


2. YAML: key: value

The file is written in YAML, a simple format for settings. The basic unit is a key, a colon, a space, and a value:

yaml
name: marks_app
version: 1.0.0

Like a Dart Map, each key appears once. The space after the colon is required.


3. YAML: Indentation Means "Inside"

A key with nothing after its colon starts a section. Lines indented under it belong to it. Use two spaces per level:

yaml
environment:
  sdk: ^3.12.0

sdk is inside environment. YAML forbids tab characters for indentation:

output
Error on line 4, column 1 of pubspec.yaml: Tab characters are not allowed as indentation.
  ╷
4 │     sdk: ^3.12.0
  │ ^^^^
  ╵

4. YAML: Lists and Comments

A line starting with - is a list item. A # starts a comment that YAML ignores:

yaml
# Words that help people find the package
topics:
  - education
  - grades

topics holds a list of two strings, like ['education', 'grades'] in Dart.


5. The name Field

name is the only field every package must have. It must be a valid Dart identifier: lowercase words joined with underscores, such as marks_app. A dash is not allowed:

yaml
name: marks-app
output
Error on line 1, column 7 of pubspec.yaml: "name" field must be a valid Dart identifier.
  ╷
1 │ name: marks-app
  │       ^^^^^^^^^
  ╵

Reserved words are rejected too:

output
Error on line 1, column 7 of pubspec.yaml: "name" field may not be a Dart reserved word.
  ╷
1 │ name: class
  │       ^^^^^
  ╵

The name is what goes after package: in imports, which is why it must look like a Dart name.


6. description and version

description is one sentence about the package. version is the package's own version number, and it must have three parts:

yaml
version: 1.0
output
Error on line 2, column 10 of pubspec.yaml: "version" field must have three numeric components: major, minor, and patch. Instead of "1.0", consider "1.0.0".
  ╷
2 │ version: 1.0
  │          ^^^
  ╵

Section 9 explains those three parts. Optional fields such as homepage and repository hold web links; they matter when you share a package.


7. environment and the SDK Constraint

environment: sdk: says which Dart versions can run the project. Without it, dart pub get refuses to continue:

output
pubspec.yaml has no lower-bound SDK constraint.
You should edit pubspec.yaml to contain an SDK constraint:

environment:
  sdk: '^3.12.0'

See https://dart.dev/go/sdk-constraint

So the smallest working pubspec has two entries:

yaml
name: marks_app

environment:
  sdk: ^3.12.0

8. dependencies

Packages your program uses go under dependencies, one per line, as name: constraint:

yaml
dependencies:
  path: ^1.9.0
bash
dart pub get
output
Resolving dependencies...
Downloading packages...
+ path 1.9.1
Changed 1 dependency!

+ path 1.9.1 means pub chose version 1.9.1 and downloaded it. Now import 'package:path/path.dart' as p; works.


9. Semantic Versioning

Package versions follow semantic versioning: MAJOR.MINOR.PATCH.

ChangeExampleMeaning
PATCH1.9.0 → 1.9.1a bug fix
MINOR1.9.1 → 1.10.0new features; old code still works
MAJOR1.10.0 → 2.0.0breaking changes; old code may fail

Before 1.0.0, a package is still settling, so the minor number counts as breaking: 0.20.x → 0.21.0 may break your code.


10. Version Constraints

A constraint says which versions you accept:

ConstraintAllowsUse it when
^1.9.0>=1.9.0 <2.0.0almost always
^0.20.2>=0.20.2 <0.21.0the package is below 1.0
'>=1.8.0 <2.0.0'exactly that rangeyou need a custom range
1.9.1only 1.9.1rarely; it blocks bug fixes
anyevery versionalmost never

The caret ^ means "this version or newer, up to the next breaking change". It needs all three parts:

output
Error on line 7, column 9 of pubspec.yaml: Invalid version constraint: Expected version number after "^" in "^1.9", got "1.9".

A range contains spaces and >, so it must be in quotes:

output
Error on line 7, column 10 of pubspec.yaml: Expected comment or line break.
  ╷
7 │   path: >=1.8.0 <2.0.0
  │          ^
  ╵

11. dev_dependencies

Packages you need only while developing, never when the program runs, go under dev_dependencies. The lints package, which adds extra checks to dart analyze, is one:

yaml
dev_dependencies:
  lints: ^6.0.0
output
+ lints 6.1.0
Changed 1 dependency!

Packages that use yours do not download your dev dependencies. Testing packages also go here; we will write tests in Lecture 118 — Introduction to Testing.


12. executables (A Quick Look)

executables gives a program in bin/ a command name:

yaml
executables:
  marks: marks_app

This says: the command marks runs bin/marks_app.dart. It matters only when a package is installed as a global tool. We will install tools that way in Lecture 105.


13. pubspec.lock vs pubspec.yaml

dart pub get writes a second file, pubspec.lock, recording the exact version it chose:

yaml
  path:
    dependency: "direct main"
    ...
    version: "1.9.1"
pubspec.yamlpubspec.lock
you write itpub writes it — never edit it
ranges: "^1.9.0"exact versions: "1.9.1"
your packages onlyalso the packages they need

Once a version is locked, dart pub get keeps it. Ali pins path: 1.8.3, then relaxes it to ^1.8.0:

output
< path 1.8.3 (was 1.9.1) (1.9.1 available)
Changed 1 dependency!
output
  path 1.8.3 (1.9.1 available)
Got dependencies!
1 package has newer versions incompatible with dependency constraints.
Try `dart pub outdated` for more information.

^1.8.0 allows 1.9.1, but the lock file still says 1.8.3, so pub keeps it. That is the lock file's job: everyone on the project gets the same versions. Upgrading on purpose, and dart pub outdated, are Lecture 105.


14. A Real-World Example

Ali's marks_app uses every field from this lecture:

yaml
name: marks_app
description: Prints a class average and the report file for it.
version: 1.0.0

environment:
  sdk: ^3.12.0

dependencies:
  path: ^1.9.0

dev_dependencies:
  lints: ^6.0.0

executables:
  marks: marks_app

bin/marks_app.dart:

dart
import 'package:path/path.dart' as p;

void main() {
  var marks = {'Ali': 82, 'Sara': 91, 'Zayan': 67};
  var total = marks.values.reduce((a, b) => a + b);
  var average = total / marks.length;
  print('Average: ${average.toStringAsFixed(1)}');
  print('Report: ${p.join('reports', 'class_7.txt')}');
}
bash
dart pub get
dart run
output
Resolving dependencies...
Downloading packages...
+ lints 6.1.0
+ path 1.9.1
Changed 2 dependencies!
Average: 80.0
Report: reports/class_7.txt

On Windows, p.join prints reports\class_7.txt — the path package uses the right separator for each system.


Code Examples

Each example is a project folder: the pubspec.yaml, then the output of dart pub get. Examples 7–10 also have a program in bin/, run with dart run.

Example 1 — The Smallest pubspec

yaml
name: first_app

environment:
  sdk: ^3.12.0
output
Resolving dependencies...
Downloading packages...
Got dependencies!

Example 2 — Description and Version

yaml
name: grade_book
description: Keeps track of student grades.
version: 0.1.0

environment:
  sdk: ^3.12.0
output
Got dependencies!

Example 3 — A YAML List

yaml
name: grade_book
# Words that help people find the package
topics:
  - education
  - grades

environment:
  sdk: ^3.12.0
output
Got dependencies!

Example 4 — One Dependency

yaml
name: file_namer

environment:
  sdk: ^3.12.0

dependencies:
  path: ^1.9.0
output
+ path 1.9.1
Changed 1 dependency!

Example 5 — A Range in Quotes

yaml
name: file_namer

environment:
  sdk: ^3.12.0

dependencies:
  path: '>=1.8.0 <2.0.0'
output
+ path 1.9.1
Changed 1 dependency!

Same result as ^1.8.0, written out in full.

Example 6 — A Dev Dependency

yaml
name: tidy_app

environment:
  sdk: ^3.12.0

dev_dependencies:
  lints: ^6.0.0
output
+ lints 6.1.0
Changed 1 dependency!

Example 7 — Student Marks

yaml
name: marks_app

environment:
  sdk: ^3.12.0

dependencies:
  path: ^1.9.0
dart
import 'package:path/path.dart' as p;

void main() {
  var marks = {'Ali': 82, 'Sara': 91, 'Zayan': 67};
  marks.forEach((name, mark) {
    var file = p.join('reports', '${name.toLowerCase()}.txt');
    print('$name ($mark) -> $file');
  });
}
output
Ali (82) -> reports/ali.txt
Sara (91) -> reports/sara.txt
Zayan (67) -> reports/zayan.txt

Example 8 — Shopping Total with intl

yaml
name: shop_app

environment:
  sdk: ^3.12.0

dependencies:
  intl: ^0.20.2
dart
import 'package:intl/intl.dart';

void main() {
  var money = NumberFormat.currency(symbol: 'Rs ', decimalDigits: 0);
  var prices = [1200, 850, 2450];
  var total = prices.reduce((a, b) => a + b);
  print('Total: ${money.format(total)}');
}
output
+ clock 1.1.3
+ intl 0.20.3
+ meta 1.19.0
+ path 1.9.1
Changed 4 dependencies!
Total: Rs 4,500

^0.20.2 allowed 0.20.3. We asked for one package and got four: intl needs the other three, and pub fetched them too.

Example 9 — Bank Balance with a Command Name

yaml
name: bank_app

environment:
  sdk: ^3.12.0

executables:
  bank: bank_app
dart
void main() {
  var balance = 5000.0;
  balance += 1500;
  balance -= 2000;
  print('Balance: $balance');
}
output
Got dependencies!
Balance: 4500.0

Example 10 — Temperature Log and Its Lock File

yaml
name: weather_log

environment:
  sdk: ^3.12.0

dependencies:
  path: ^1.9.0
dart
import 'package:path/path.dart' as p;

void main() {
  var celsius = [21.5, 24.0, 19.5];
  var file = p.join('logs', 'week_42.txt');
  print('Saving ${celsius.length} temperatures to ${p.basename(file)}');
}
output
+ path 1.9.1
Changed 1 dependency!
Saving 3 temperatures to week_42.txt

pubspec.lock now records version: "1.9.1" for path.


Common Beginner Mistakes

1. Forgetting to indent a dependency

yaml
dependencies:
path: ^1.9.0 # ✗ not indented
output
Got dependencies!

No error! path became a separate top-level key, so dependencies is empty. The program fails later:

output
Error: Couldn't resolve the package 'path' in 'package:path/path.dart'.

Indent it two spaces: path: ^1.9.0.

2. Indenting one line too far

yaml
dependencies:
  path: ^1.9.0
   lints: ^6.0.0 # ✗ three spaces
output
Error on line 8, column 9 of pubspec.yaml: Mapping values are not allowed here. Did you miss a colon earlier?
  ╷
8 │    lints: ^6.0.0
  │         ^
  ╵

Every line in a section needs the same indentation.

3. A colon inside a value

output
Error on line 2, column 23 of pubspec.yaml: Mapping values are not allowed here. Did you miss a colon earlier?
  ╷
2 │ description: Marks app: prints student marks.
  │                       ^
  ╵

Put the value in quotes: description: 'Marks app: prints student marks.'

4. Writing a key twice

yaml
name: marks_app
name: grades # ✗ name already given
output
Error on line 2, column 1 of pubspec.yaml: Duplicate mapping key.
  ╷
2 │ name: grades
  │ ^^^^
  ╵

5. Asking for a version that does not exist

output
Because marks_app depends on path ^2.0.0 which doesn't match any versions, version solving failed.

There is no path 2.x yet. Check the package's page on pub.dev for its real versions.

6. Misspelling a package name

output
Because marks_app depends on pathh any which doesn't exist (could not find package pathh at https://pub.dev), version solving failed.

Best Practices

  • Indent with two spaces; never tabs.
  • Use ^ constraints for dependencies; avoid exact versions and any.
  • Quote any value containing :, #, > or <.
  • Keep tools such as lints and test in dev_dependencies.
  • Run dart pub get after every edit to pubspec.yaml.
  • Never edit pubspec.lock by hand.

Mini Challenge

Write the pubspec for Zayan's quiz_game: description A small quiz game for Zayan., version 1.0.0, SDK ^3.12.0, path ^1.9.0 as a dependency and lints ^6.0.0 as a dev dependency. dart pub get should print:

output
Resolving dependencies...
Downloading packages...
+ lints 6.1.0
+ path 1.9.1
Changed 2 dependencies!

Try it yourself before opening the solution.

Show solution
yaml
name: quiz_game
description: A small quiz game for Zayan.
version: 1.0.0

environment:
  sdk: ^3.12.0

dependencies:
  path: ^1.9.0

dev_dependencies:
  lints: ^6.0.0

Practice Questions

Work through these in order — they run from beginner to application to challenge.

Beginner

  1. Write the smallest valid pubspec for a project named weather_app.
  2. Which names are valid: bank_app, bank-app, 2bank, class?
  3. Which versions does ^2.3.1 allow? And ^0.4.0?
  4. A package goes from 1.4.2 to a new version. What is the new number for a bug fix, a new feature, and a breaking change?

Application

  1. Add intl with constraint ^0.20.2 to your answer from Question 1.
  2. version: 2.0 fails. Fix it.
  3. Sara's file has dependencies: and, on the next line, path: ^1.9.0 with no indentation. Why does dart pub get succeed while her import fails?
  4. Should lints go under dependencies or dev_dependencies? Why?

Challenge

  1. Write a pubspec for bank_app: a description, version 1.2.0, SDK ^3.12.0, path written as a quoted range equal to ^1.9.0, lints ^6.0.0 for development, and an executable bank for bin/bank_app.dart.
  2. Ali's lock file has path 1.8.3, his pubspec says ^1.8.0, and 1.9.1 exists. Which version does dart pub get use, and why?

Practice Answers

Question 1 — Solution

yaml
name: weather_app

environment:
  sdk: ^3.12.0
output
Got dependencies!

Question 2 — Solution

Only bank_app. bank-app and 2bank give "name" field must be a valid Dart identifier.; class gives "name" field may not be a Dart reserved word.

Question 3 — Solution

^2.3.1 allows >=2.3.1 <3.0.0. ^0.4.0 allows >=0.4.0 <0.5.0, because below 1.0 the minor number marks breaking changes.

Question 4 — Solution

Bug fix: 1.4.3. New feature: 1.5.0. Breaking change: 2.0.0.

Question 5 — Solution

yaml
name: weather_app

environment:
  sdk: ^3.12.0

dependencies:
  intl: ^0.20.2
output
+ clock 1.1.3
+ intl 0.20.3
+ meta 1.19.0
+ path 1.9.1
Changed 4 dependencies!

Question 6 — Solution

yaml
version: 2.0.0

A version needs three parts: major, minor and patch.

Question 7 — Solution

Without indentation, path is a top-level key, not inside dependencies. The YAML is valid, so pub reports no error, but it never downloads path. Indent it two spaces.

Question 8 — Solution

dev_dependencies. lints only checks code while you develop; the running program never uses it.

Question 9 — Solution

yaml
name: bank_app
description: Tracks a simple bank balance.
version: 1.2.0

environment:
  sdk: ^3.12.0

dependencies:
  path: '>=1.9.0 <2.0.0'

dev_dependencies:
  lints: ^6.0.0

executables:
  bank: bank_app
output
+ lints 6.1.0
+ path 1.9.1
Changed 2 dependencies!

Question 10 — Solution

1.8.3. It still fits ^1.8.0, and dart pub get keeps locked versions so that everyone gets the same ones. Getting 1.9.1 on purpose is covered in Lecture 105.


Quick Revision

  1. What three things does pubspec.yaml describe?
  2. How many spaces per indentation level? Are tabs allowed?
  3. How do you write a list item in YAML?
  4. Which field is required in every pubspec?
  5. What does ^1.9.0 mean?
  6. What do MAJOR, MINOR and PATCH changes mean?
  7. Who writes pubspec.lock, and what does it record?

Key Takeaways

  • pubspec.yaml names a project, sets its Dart versions and lists its packages.
  • YAML uses key: value, two-space indentation for nesting, - for lists and # for comments.
  • name must be a lowercase Dart identifier; version needs three parts.
  • environment: sdk: is required; dart pub get stops without it.
  • dependencies are used by the program; dev_dependencies only while developing.
  • ^1.9.0 means >=1.9.0 <2.0.0; below 1.0, ^0.20.2 means <0.21.0.
  • pubspec.lock records exact versions, and dart pub get keeps them.
  • A missing indent can drop a package silently, so read dart pub get's output.

What's Next?

Lecture 105 — Dart Pub & Dependency Management. We have been editing pubspec.yaml by hand. Next we will let dart pub do it: adding and removing packages from the command line, upgrading locked versions on purpose, and checking for outdated packages.


Video lecture

Subscribe
Placeholder video — the real recording isn't linked yet.