Packages & Dependencies
Version constraints, pubspec.lock, version solving, overrides, publishing, melos, upgrades
- 01
Your pubspec says
intl: ^0.20.2and pub refuses to take 0.21.0, yetdio: ^5.7.0happily moves to 5.9.0 — what is the caret doing?EasyThe caret means "up to the next breaking version", and below 1.0.0 pub moves that boundary down a level: the minor number acts as the major.
name: checkout_app environment: sdk: ^3.9.0 dependencies: # ^5.7.0 == >=5.7.0 <6.0.0 — the usual case. … - 02
Your app keeps
pubspec.lockin.gitignore, and CI just built against a different version of a dependency than your laptop did. Who was wrong?EasyThe app was: an application commits its lock file and a published package does not, because only one of the two is ever the root of a version solve.
# pubspec.lock — generated by pub, never hand-edited. packages: dio: dependency: "direct main" # you asked for it in `dependencies` description: name: dio … - 03
You wanted a newer
dio, ranflutter pub get, and nothing moved. Which pub command is allowed to change the lock file, and how far?Easypub getonly answers questions the lock file has not already answered; moving an already-locked package ispub upgrade, and changing the constraint itself ispub upgrade --major-versions.# Nothing moved? Ask what the lock file is holding, and what is reachable. $ flutter pub outdated # Package Current Upgradable Resolvable Latest # dio 5.7.0 5.9.3 5.9.3 5.9.3 # intl 0.20.2 0.20.2 0.20.2 0.21.0 <- something caps it # acme_analytics 1.9.4 1.9.6 2.3.0 2.3.0 <- major bump waiting … - 04
flutter pub getfails with fifteen lines of "because X depends on Y ^2.0.0..." — how do you read that wall down to the one conflict you have to fix?MediumRead the last sentence first: pub's solver writes a proof, and only the bottom line is about your package — everything above it exists to justify that line.
$ flutter pub get Resolving dependencies... Because every version of flutter_localizations from sdk depends on intl 0.20.2 and acme_analytics >=2.1.0 depends on intl ^0.21.0, flutter_localizations from sdk is incompatible with acme_analytics >=2.1.0. And because no versions of acme_analytics match >2.0.0 <2.1.0, … - 05
One line of
dependency_overridesturned a failingflutter pub getgreen. What did you just switch off, and what were the honest alternatives?MediumYou told the solver to stop enforcing a constraint that a package author wrote deliberately, and to build a combination nobody has tested.
# ---- Wrong: the conflict is silenced, the risk is not ---- dependencies: acme_analytics: ^2.1.0 # needs intl ^0.21.0 flutter_localizations: sdk: flutter # pins intl 0.20.2 exactly … - 06
A reviewer says
build_runnersitting independenciesis bloating your APK. Are they right, and how do you prove what is actually in the binary?MediumNot directly — Dart's AOT compiler only ships code reachable from
main(), so a package you never import adds nothing to the binary; the real cost of the wrong section is what your consumers have to resolve, and, for plugins, native code that ships regardless.# Which bucket is each package in? The lock file already says. $ grep -B3 'dependency: "direct dev"' pubspec.lock | head -20 # dependency: "direct main" -> yours, and inherited by your consumers # dependency: "direct dev" -> dev_dependencies, never inherited # dependency: transitive -> someone else asked for it … - 07
A teammate points a dependency at a GitHub branch to unblock a fix. What does that cost you, and when would you accept it?
MediumA git dependency trades version solving for a single moving pointer: it takes over that package for the entire graph, satisfies nobody else's range, and can never be published.
dependencies: # hosted (the default) — a range the solver can negotiate dio: ^5.9.0 # hosted on a private server — still versioned, still solvable acme_ui: … - 08
You bump the Flutter SDK and
pub getdies with "requires SDK version >=2.12.0 <3.0.0" on a package nobody on the team owns. What happened, and what are your moves?MediumThat package declared an upper bound on the Dart SDK in its own
environment:block, pub treats that bound as hard, and every published version of it is now excluded from the solve — so the failure is about the package's metadata, not about your code.# ---- The package you do not own, as published two years ago ---- # name: legacy_charts # environment: # sdk: '>=2.12.0 <3.0.0' # hard upper bound: no candidate on Dart 3.x # # $ flutter pub get … - 09
Your team is about to publish its first package to pub.dev — what does the dry run actually check, and what can you never take back?
MediumPublishing is permanent: pub.dev never deletes or overwrites a version, so the dry run is the last cheap check you get before the archive is public forever.
# pubspec.yaml — the fields the dry run looks at name: acme_result # claimed forever on the first publish description: A tiny Result type for Dart with pattern-matching helpers and no dependencies. version: 0.1.0 # pre-1.0: a breaking change bumps the minor repository: https://github.com/acme/acme_result issue_tracker: https://github.com/acme/acme_result/issues … - 10
You want to add a required named parameter to a public constructor in your package — is that a patch, a minor, or a major?
MediumMajor — in Dart "breaking" means anything that can stop a consumer's code from compiling, and a new required parameter breaks every existing call site.
// ── 1.2.0, already published ──────────────────────────────────────── class Toast { Toast({required this.message, this.seconds = 3}); final String message; final int seconds; } … - 11
Seven local packages, and every branch switch means running pub get in each directory — what do pub workspaces or Melos actually fix?
MediumA workspace makes the whole repo one resolution: one
pubspec.lock, one.dart_tool/package_config.json, onepub getat the root for every package in it.# ── Before: path deps, each package resolved on its own ───────────── # packages/auth/pubspec.yaml # dependencies: # acme_core: { path: ../core } # packages/auth/pubspec_overrides.yaml <- generated, gitignored, easy to # leave behind and hard to notice … - 12
A transitive dependency you never chose ships a broken patch on Friday and your build starts failing — what do you do first?
MediumFirst establish whether it can even reach you: with
pubspec.lockcommitted and CI runningpub get --enforce-lockfile, a bad publish upstream changes nothing until somebody deliberately upgrades.#!/usr/bin/env bash set -euo pipefail # 1) Who pulls it in, and what actually moved? dart pub deps --style=compact | grep -B3 broken_json git diff HEAD~1 -- pubspec.lock | grep -B2 broken_json … - 13
Why does one camera plugin turn into five packages on pub.dev, and which of them does your app actually depend on?
HardA federated plugin splits the Dart-facing API from each platform's implementation so a platform can be added by somebody who does not own the plugin — and your app depends only on the app-facing package.
# ── 1. app-facing package: the API plus the endorsements ──────────── # bar_scanner/pubspec.yaml name: bar_scanner dependencies: flutter: { sdk: flutter } bar_scanner_platform_interface: ^2.0.0 … - 14
Your team upgrades dependencies twice a year and each round costs two days of firefighting — how do you make upgrades boring?
HardTurn one big bang into a stream of small reviewable bumps, and be explicit about the few things you pin on purpose and never float.
# .github/dependabot.yml version: 2 updates: - package-ecosystem: pub directory: / schedule: { interval: weekly, day: monday } … - 15
Two packages in your monorepo need incompatible major versions of the same dependency — what does pub do, and how do you get out?
HardPub resolves one version of every package for the whole resolution, so there is no "both" — the solve fails, and every real fix ends with one of the two constraints moving.
# What the failure actually looks like $ flutter pub get # Because acme_billing depends on analytics ^3.0.0 and acme_auth depends on # analytics ^2.4.0, version solving failed. # 1) Read the chain to the end, then see who really pulls it in … - 16
A package at the centre of your app has not shipped in two years and now blocks a Dart SDK bump — fork, vendor, or replace?
HardDecide by how much of the package you actually use and how much native code it carries, then keep the change reviewable by not touching your own import sites in the same PR.
# ── The decision, written where the next person will find it ──────── # pubspec.yaml dependencies: # 1) Pin and wait — an expiry date, not an opinion legacy_charts: 2.3.1 # blocks the Dart 3.7 bump; revisit 2026-06-01 (#1204) …