On this page26 sections
- Prerequisites
- Connection With Previous Lectures
- Learning Objectives
- 1. What Is pubspec.yaml?
- 2. YAML: key: value
- 3. YAML: Indentation Means "Inside"
- 4. YAML: Lists and Comments
- 5. The name Field
- 6. description and version
- 7. environment and the SDK Constraint
- 8. dependencies
- 9. Semantic Versioning
- 10. Version Constraints
- 11. dev_dependencies
- 12. executables (A Quick Look)
- 13. pubspec.lock vs pubspec.yaml
- 14. 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 104 — pubspec.yaml
| Category | 17 — Libraries & Packages |
| Lecture | 104 of 140 |
| Estimated video length | 11 min |
| Target | Dart 3.12.2 |
| Level | Intermediate |
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 getordart runon Dart 3.12.2. Package versions such aspath 1.9.1are 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 createand running it withdart run(Lecture 002) - lists and maps (Lectures 039 and 049)
importwith anasprefix (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:
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.yamlis for - Write YAML key–value pairs, nested sections and lists
- Use the
name,description,versionandenvironmentfields - Add packages under
dependenciesanddev_dependencies - Read version constraints such as
^1.9.0,'>=1.8.0 <2.0.0'andany - Explain semantic versioning: major, minor and patch
- Tell
pubspec.yamlandpubspec.lockapart - Fix the errors
dart pub getreports 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
.dartfiles are the steps;pubspec.yamlis the label.
This is the file dart create hello_dart writes:
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.6The 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:
name: marks_app
version: 1.0.0Like 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:
environment:
sdk: ^3.12.0sdk is inside environment. YAML forbids tab characters for indentation:
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:
# Words that help people find the package
topics:
- education
- gradestopics 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:
name: marks-appError 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:
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:
version: 1.0Error 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:
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-constraintSo the smallest working pubspec has two entries:
name: marks_app
environment:
sdk: ^3.12.08. dependencies
Packages your program uses go under dependencies, one per line, as name: constraint:
dependencies:
path: ^1.9.0dart pub getResolving 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.
| Change | Example | Meaning |
|---|---|---|
| PATCH | 1.9.0 → 1.9.1 | a bug fix |
| MINOR | 1.9.1 → 1.10.0 | new features; old code still works |
| MAJOR | 1.10.0 → 2.0.0 | breaking 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:
| Constraint | Allows | Use it when |
|---|---|---|
^1.9.0 | >=1.9.0 <2.0.0 | almost always |
^0.20.2 | >=0.20.2 <0.21.0 | the package is below 1.0 |
'>=1.8.0 <2.0.0' | exactly that range | you need a custom range |
1.9.1 | only 1.9.1 | rarely; it blocks bug fixes |
any | every version | almost never |
The caret ^ means "this version or newer, up to the next breaking change". It needs all
three parts:
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:
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:
dev_dependencies:
lints: ^6.0.0+ 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:
executables:
marks: marks_appThis 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:
path:
dependency: "direct main"
...
version: "1.9.1"pubspec.yaml | pubspec.lock |
|---|---|
| you write it | pub writes it — never edit it |
ranges: "^1.9.0" | exact versions: "1.9.1" |
| your packages only | also 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:
< path 1.8.3 (was 1.9.1) (1.9.1 available)
Changed 1 dependency! 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:
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_appbin/marks_app.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')}');
}dart pub get
dart runResolving dependencies...
Downloading packages...
+ lints 6.1.0
+ path 1.9.1
Changed 2 dependencies!
Average: 80.0
Report: reports/class_7.txtOn 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
name: first_app
environment:
sdk: ^3.12.0Resolving dependencies...
Downloading packages...
Got dependencies!Example 2 — Description and Version
name: grade_book
description: Keeps track of student grades.
version: 0.1.0
environment:
sdk: ^3.12.0Got dependencies!Example 3 — A YAML List
name: grade_book
# Words that help people find the package
topics:
- education
- grades
environment:
sdk: ^3.12.0Got dependencies!Example 4 — One Dependency
name: file_namer
environment:
sdk: ^3.12.0
dependencies:
path: ^1.9.0+ path 1.9.1
Changed 1 dependency!Example 5 — A Range in Quotes
name: file_namer
environment:
sdk: ^3.12.0
dependencies:
path: '>=1.8.0 <2.0.0'+ path 1.9.1
Changed 1 dependency!Same result as ^1.8.0, written out in full.
Example 6 — A Dev Dependency
name: tidy_app
environment:
sdk: ^3.12.0
dev_dependencies:
lints: ^6.0.0+ lints 6.1.0
Changed 1 dependency!Example 7 — Student Marks
name: marks_app
environment:
sdk: ^3.12.0
dependencies:
path: ^1.9.0import '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');
});
}Ali (82) -> reports/ali.txt
Sara (91) -> reports/sara.txt
Zayan (67) -> reports/zayan.txtExample 8 — Shopping Total with intl
name: shop_app
environment:
sdk: ^3.12.0
dependencies:
intl: ^0.20.2import '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)}');
}+ 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
name: bank_app
environment:
sdk: ^3.12.0
executables:
bank: bank_appvoid main() {
var balance = 5000.0;
balance += 1500;
balance -= 2000;
print('Balance: $balance');
}Got dependencies!
Balance: 4500.0Example 10 — Temperature Log and Its Lock File
name: weather_log
environment:
sdk: ^3.12.0
dependencies:
path: ^1.9.0import '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)}');
}+ path 1.9.1
Changed 1 dependency!
Saving 3 temperatures to week_42.txtpubspec.lock now records version: "1.9.1" for path.
Common Beginner Mistakes
1. Forgetting to indent a dependency
dependencies:
path: ^1.9.0 # ✗ not indentedGot dependencies!No error! path became a separate top-level key, so dependencies is empty. The program fails
later:
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
dependencies:
path: ^1.9.0
lints: ^6.0.0 # ✗ three spacesError 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
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
name: marks_app
name: grades # ✗ name already givenError on line 2, column 1 of pubspec.yaml: Duplicate mapping key.
╷
2 │ name: grades
│ ^^^^
╵5. Asking for a version that does not exist
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
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 andany. - Quote any value containing
:,#,>or<. - Keep tools such as
lintsandtestindev_dependencies. - Run
dart pub getafter every edit topubspec.yaml. - Never edit
pubspec.lockby 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:
Resolving dependencies...
Downloading packages...
+ lints 6.1.0
+ path 1.9.1
Changed 2 dependencies!Try it yourself before opening the solution.
Show solution
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.0Practice Questions
Work through these in order — they run from beginner to application to challenge.
Beginner
- Write the smallest valid pubspec for a project named
weather_app. - Which names are valid:
bank_app,bank-app,2bank,class? - Which versions does
^2.3.1allow? And^0.4.0? - A package goes from
1.4.2to a new version. What is the new number for a bug fix, a new feature, and a breaking change?
Application
- Add
intlwith constraint^0.20.2to your answer from Question 1. version: 2.0fails. Fix it.- Sara's file has
dependencies:and, on the next line,path: ^1.9.0with no indentation. Why doesdart pub getsucceed while her import fails? - Should
lintsgo underdependenciesordev_dependencies? Why?
Challenge
- Write a pubspec for
bank_app: a description, version1.2.0, SDK^3.12.0,pathwritten as a quoted range equal to^1.9.0,lints^6.0.0for development, and an executablebankforbin/bank_app.dart. - Ali's lock file has
path 1.8.3, his pubspec says^1.8.0, and1.9.1exists. Which version doesdart pub getuse, and why?
Practice Answers
Question 1 — Solution
name: weather_app
environment:
sdk: ^3.12.0Got 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
name: weather_app
environment:
sdk: ^3.12.0
dependencies:
intl: ^0.20.2+ clock 1.1.3
+ intl 0.20.3
+ meta 1.19.0
+ path 1.9.1
Changed 4 dependencies!Question 6 — Solution
version: 2.0.0A 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
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+ 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
- What three things does
pubspec.yamldescribe? - How many spaces per indentation level? Are tabs allowed?
- How do you write a list item in YAML?
- Which field is required in every pubspec?
- What does
^1.9.0mean? - What do MAJOR, MINOR and PATCH changes mean?
- Who writes
pubspec.lock, and what does it record?
Key Takeaways
pubspec.yamlnames a project, sets its Dart versions and lists its packages.- YAML uses
key: value, two-space indentation for nesting,-for lists and#for comments. namemust be a lowercase Dart identifier;versionneeds three parts.environment: sdk:is required;dart pub getstops without it.dependenciesare used by the program;dev_dependenciesonly while developing.^1.9.0means>=1.9.0 <2.0.0; below 1.0,^0.20.2means<0.21.0.pubspec.lockrecords exact versions, anddart pub getkeeps 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.