skip to content

Pubspec Constraints

The pubspec.yaml file names a package, bounds the SDK and declares dependencies with caret or range constraints from hosted, git or path sources. Interviewers ask what ^0.x allows.

part ofDartoverview, primer and where to startread it →
on this pageshow

explore

questions

5

In a Flutter app's pubspec.yaml, which versions do `http: ^1.2.0` and `http: ^0.13.6` allow, and how is each written without a caret?

level: juniorimportance: must knowfreq 55%

answer

  1. caret means compatible range
  2. 1.x: up to next major
  3. 0.x: up to next minor
  4. quote anything containing >
  5. no constraint means any

basics

~20 s

In pub, ^1.2.0 allows >=1.2.0 <2.0.0, up to the next major, while ^0.13.6 allows >=0.13.6 <0.14.0, up to the next minor, because below 1.0 a minor bump may break. Written longhand, the range must be quoted.

solid answer

~40 s

Pub reads `^version` as "every version guaranteed compatible with this one": for **1.0 and later** that is up to the next **major** (`^1.2.0` = `'>=1.2.0 <2.0.0'`), and **below 1.0** up to the next **minor** (`^0.13.6` = `'>=0.13.6 <0.14.0'`), because a 0.x minor release may break. So an app on `http: ^1.2.0` picks up 1.x fixes and features but never 2.0 without a pubspec edit. The traditional syntax uses comparison operators and must be **quoted** whenever it contains `>`, or YAML misreads it. An exact pin like `1.2.3` or `any` is discouraged; dart.dev recommends caret syntax, and a missing constraint on a hosted dependency means `any`.

code

yaml · 9 lines
yaml
dependencies:
  # 1.2.0 <= v < 2.0.0 : any 1.x from 1.2.0
  http: ^1.2.0
  # 5.4.0 <= v < 6.0.0
  dio: ^5.4.0
  # 0.3.1 <= v < 0.4.0 : pre-1.0 stops at next minor
  some_beta_sdk: ^0.3.1
  # Traditional syntax: quoted because it contains '>'
  intl: '>=0.19.0 <0.21.0'

go deeper

for a junior

Know both caret rules: 1.x and later runs to the next major, 0.x runs to the next minor, and quote ranges containing >.

for a middle

Explain why the 0.x rule differs, and how constraints and the lockfile split the roles of allowing and pinning versions.

for a senior

Choose lower bounds from the APIs you actually use, keep package constraints wide, and treat each major bump as a reviewed change.

for a principal

Set constraint policy across many apps and packages, balancing automatic fixes against the risk of wide ranges.

## What a constraint says Every entry in `dependencies` pairs a package with a **version constraint**: the set of versions your code is willing to use. Pub's solver then picks one version per package that satisfies every constraint in the graph. Pub follows semantic versioning, so the constraint is really a statement about which releases you expect to be compatible. Pub supports two syntaxes that describe the same kind of range: **caret syntax** (the recommended one, allowed in SDK constraints since Dart 2.19) and the **traditional syntax** of comparison operators. ## Caret syntax in pub `^version` means the range of all versions guaranteed to be **backwards compatible** with the given version, up to the next one allowed to break: | Constraint | Allows | Traditional form | |---|---|---| | `^1.2.0` | 1.2.0 up to, not including, 2.0.0 | `'>=1.2.0 <2.0.0'` | | `^1.3.0` | 1.3.0 up to, not including, 2.0.0 | `'>=1.3.0 <2.0.0'` | | `^0.13.6` | 0.13.6 up to, not including, 0.14.0 | `'>=0.13.6 <0.14.0'` | | `^0.1.2` | 0.1.2 up to, not including, 0.2.0 | `'>=0.1.2 <0.2.0'` | The split at 1.0 is the thing interviewers probe. For a **1.0-or-later** package, only a major bump may break, so the caret runs to the next major. For a **pre-1.0** package, a minor bump is allowed to break, so the caret stops at the next minor. Moving from `http: ^0.13.6` to the 1.x line was therefore always a pubspec edit, never something `dart pub upgrade` did on its own. ## Traditional syntax and YAML quoting The traditional syntax combines `>=`, `>`, `<=` and `<` bounds, plus an exact version or `any`. dart.dev's guidance on each: - `>=1.2.3` as a lower bound: yes; - `<2.0.0` as an upper bound where you know the first breaking version: yes; - an exact version such as `1.2.3`: no, because it limits every app that uses your package; - `any`: no, it is an empty constraint, and a hosted dependency written without a constraint is treated as `any`. One mechanical rule causes real bugs: if a constraint contains `>`, **quote the whole string**, as in `'>=1.2.3 <2.0.0'`. Unquoted, YAML can read the `>` as syntax rather than as part of the value. ## Constraining an HTTP client to a safe major A typical team rule is "accept fixes and features automatically, never a breaking release". With a 1.x or later package that is simply a caret on the lowest version whose features you use: 1. Check which version introduced the APIs you call. 2. Write `http: ^1.x.0` (or `dio: ^5.x.0`) with that version as the lower bound. 3. Let `dart pub upgrade` move within the major; the lockfile records what was picked. 4. Treat the next major as a deliberate change: edit the constraint, read the changelog, re-test. For a **0.x** dependency the same rule means a caret that stops at the next minor, so expect more frequent pubspec edits. ## Why not pin exact versions In an app, exact pins mostly duplicate what `pubspec.lock` already guarantees, and they block security fixes. In a package, they are worse: every consumer inherits the pin and is far more likely to hit a solving conflict. Caret constraints plus a committed lockfile for apps give both reproducibility and upgrades.

  • In pub, why does `^0.13.6` stop at 0.14.0 instead of 1.0.0?
    Pub's caret covers versions guaranteed compatible with the given one. Below 1.0, semantic versioning lets a minor release break the API, so the next minor is the first possibly breaking version and the caret ends just before it.
  • In a Dart pubspec, what goes wrong with an unquoted `>=1.2.3 <2.0.0` constraint?
    The `>` character is significant in YAML, so an unquoted value may not be read as the intended string, and pub gets something other than the range you meant. dart.dev says to quote the whole constraint, or use the equivalent caret `^1.2.3`.

saying these in an interview costs you the question

  • ^0.13.6 allows everything up to 1.0.0
  • ^1.2.0 lets pub upgrade jump to 2.0.0
  • Pinning exact versions is the safe default for packages
  • Traditional ranges never need quoting in pubspec.yaml
  • Omitting the version pins to the latest release
open as a page

In a Flutter or Dart pubspec.yaml, what decides whether a package belongs under `dependencies` or `dev_dependencies`, and what changes for your package's consumers?

level: juniorimportance: must knowfreq 60%

basics

~20 s

Anything imported from lib/ or bin/ must be a regular dependency; packages used only by tests, examples or tooling go in dev_dependencies. Pub installs your own dev dependencies but ignores the dev dependencies of every package you depend on.

open as a page

In a Dart pubspec.yaml, when would you use a hosted, git, path or sdk dependency source, and which ones block publishing to pub.dev?

level: middleimportance: should knowfreq 35%

basics

~20 s

Hosted, the default, fetches versions from pub.dev or another pub server; git pins a repository, branch, tag or commit; path uses a live local directory; sdk: flutter takes a package bundled with Flutter. Git and path dependencies cannot be published to pub.dev.

open as a page

In a Dart pubspec.yaml, how does the `environment: sdk:` lower bound select the language version, and what does `// @dart = x.y` change?

level: middleimportance: should knowfreq 35%

basics

~20 s

A package's default language version is the major.minor of its SDK constraint's lower bound, not the SDK you have installed. A // @dart = x.y comment at the top of one library overrides that for that file only.

open as a page

In a Dart or Flutter pubspec.yaml, what does `dependency_overrides` do, why do overrides in a published package have no effect on its users, and what are the risks?

level: seniorimportance: should knowfreq 30%

basics

~20 s

dependency_overrides forces one version or source of a package for every reference in the graph, ignoring other packages' constraints. Only the root package's overrides are honoured, so a published package's overrides do nothing for users, and forcing an untested version can break at run time.

open as a page