Your dependencies are dying. Know first. Fix fast.
upkeep is an open source CLI for Flutter and Dart. It finds the packages that are discontinued, incompatible or abandoned, checks that your Android build matrix actually builds, and fixes what can be fixed safely. Every verdict shows its reasons.
dart pub global activate upkeep
Then run upkeep scan. If your shell says command not found, add pub's bin folder to
your PATH: export PATH="$PATH:$HOME/.pub-cache/bin"
$ upkeep scan upkeep 0.2.0 my_app Dart 3.11.0 DEPENDENCIES 23 direct DISCONTINUED telephony 0.2.0 marked discontinued by its publisher on pub.dev AT RISK hive 2.2.3 declares support only up to Dart >=2.12.0 <3.0.0; it resolves today through pub's Dart 3 allowance, not because it was updated no release in 50 months AT RISK hive_flutter 1.1.0 declares support only up to Dart >=2.12.0 <3.0.0; it resolves today through pub's Dart 3 allowance, not because it was updated no release in 62 months SDK BLOCKED 6 packages have a newer release your Dart is too old for flutter_riverpod, go_router, lottie, pdf, printing, shimmer upgrade Flutter to pick these up: flutter upgrade 13 healthy . 1 stale . 2 skipped (sdk, git or path) ANDROID BUILD FAIL Gradle 8.4 is too old for AGP 8.11.1 AGP 8.11.1 requires Gradle 8.13 or newer. This combination does not build. fix: ./gradlew wrapper --gradle-version=8.13 (run inside android/) PASS JDK 17 satisfies AGP 8.11.1 1 blocking, 2 at risk, 1 build failure exit 1
$ upkeep scan upkeep 0.2.0 my_app Dart 3.11.0 DEPENDENCIES 14 direct Nothing to worry about. 12 healthy . 2 stale ANDROID BUILD PASS Gradle 8.14 satisfies AGP 8.11.1 Requires 8.13 or newer. PASS JDK 17 satisfies AGP 8.11.1 Detected via java -version on PATH. PASS compileSdk is managed by Flutter Set from flutter.compileSdkVersion, so it tracks your Flutter SDK. Clean. exit 0
Real output from a real Flutter app, renamed. The Android failure is from a copy with its Gradle wrapper set back to 8.4.
Demo
See it find what is dying, and fix it.
46 seconds. Real output, real projects.
The web app shown in the demo is coming soon. The CLI and the GitHub Action are ready today.
How it works
Three commands. Zero guesswork.
Diagnose, plan, then apply only what is safe. Nothing on disk changes until you say so.
upkeep scan
Reads your pubspec, lockfile and Gradle files, checks every direct dependency against pub.dev, and prints a verdict with its reasons.
upkeep fix
Splits what is wrong into changes upkeep can make on its own and a to-do list for the rest, with the file and line to start from.
upkeep fix --apply
Makes the automatic changes, then confirms pub get still resolves. If it does not, every file is restored.
The problem, measured by Google
44% of Flutter developer frustration is version guesswork.
Not a pitch. Google's own Q2 2026 Flutter survey found platform and ecosystem maturity drives 44% of developer dissatisfaction, and named the cause outright: hours lost juggling Flutter, Dart, Gradle, Kotlin and JVM versions. Meanwhile packages die quietly, and nothing tells you.
pedantic, a package its own publisher discontinued years agoSources: Flutter Q2 2026 user survey, flutter.dev · pub.dev API for pedantic, 13 September 2026.
upkeep fix
From diagnosis to treatment.
Knowing a package is dead is half the job. upkeep fix makes the changes that
are safe to make, proves they work, and turns everything else into a checklist that points at the exact line.
- 01
Plan
upkeep fixShows what it can change on its own and what needs you. Touches nothing.
- 02
Apply
upkeep fix --applyMakes only the safe changes, and refuses to edit files with uncommitted work.
- 03
Verify
pub getRuns the resolver on the result. If it fails, every file is put back and it exits 2.
$ upkeep fix upkeep 0.2.0 my_app fix plan AUTOMATIC 2 changes upkeep can make, each one reversible 1 Raise the Gradle wrapper to 8.13 AGP 8.11.1 requires Gradle 8.13 or newer; the wrapper is on 8.4 android/gradle/wrapper/gradle-wrapper.properties 2 Remove pedantic, which nothing imports marked discontinued by its publisher on pub.dev the publisher nominates lints as the replacement no Dart file imports package:pedantic, and no config file names it pubspec.yaml TO DO 3 changes need a person [ ] Replace telephony marked discontinued by its publisher on pub.dev no maintained replacement is known yet; choose one on pub.dev used in 2 files: lib/features/shield/sms_listener.dart:4 lib/features/shield/services/sms_parser_service.dart:13 [ ] Plan a move off hive not blocking declares support only up to Dart >=2.12.0 <3.0.0; it resolves today through pub's Dart 3 allowance, not because it was updated no release in 50 months used in 4 files: lib/features/stock/models/stock_model.dart:7 lib/features/shield/models/sms_receipt_model.dart:9 lib/features/sales/models/sale_model.dart:6 lib/features/sales/services/sync_service.dart:15 [ ] Plan a move off hive_flutter not blocking declares support only up to Dart >=2.12.0 <3.0.0; it resolves today through pub's Dart 3 allowance, not because it was updated no release in 62 months used in 1 file: lib/core/storage/local_db.dart:1 Run upkeep fix --apply to make the 2 automatic changes. exit 1
$ upkeep fix --apply upkeep 0.2.0 my_app applying 2 changes done Raise the Gradle wrapper to 8.13 done Remove pedantic, which nothing imports verified flutter pub get resolves with the new pubspec unbuilt the Gradle change has not been built yet; flutter build apk --debug confirms it TO DO 3 changes need a person [ ] Replace telephony marked discontinued by its publisher on pub.dev no maintained replacement is known yet; choose one on pub.dev used in 2 files: lib/features/shield/sms_listener.dart:4 lib/features/shield/services/sms_parser_service.dart:13 [ ] Plan a move off hive not blocking declares support only up to Dart >=2.12.0 <3.0.0; it resolves today through pub's Dart 3 allowance, not because it was updated no release in 50 months used in 4 files: lib/features/stock/models/stock_model.dart:7 lib/features/shield/models/sms_receipt_model.dart:9 lib/features/sales/models/sale_model.dart:6 lib/features/sales/services/sync_service.dart:15 [ ] Plan a move off hive_flutter not blocking declares support only up to Dart >=2.12.0 <3.0.0; it resolves today through pub's Dart 3 allowance, not because it was updated no release in 62 months used in 1 file: lib/core/storage/local_db.dart:1 2 applied. 3 to-dos remain. exit 1
- distributionUrl=…/distributions/gradle-8.4-all.zip + distributionUrl=…/distributions/gradle-8.13-all.zip
dependencies: - pedantic: ^1.11.1 telephony: ^0.2.0
Nothing else in the project was touched. flutter pub get resolved the result.
It will not guess
- Only edits files that git can restore
- Never removes a dev dependency or a Flutter plugin
- Keeps anything a Dart file or config file mentions
- Puts every file back if
pub getfails - Everything else becomes a to-do, with file and line
Real output on a real Flutter app with an outdated Gradle wrapper and an unused discontinued package.
upkeep explain
Disagree with a verdict? Check the evidence.
upkeep explain <package> shows every fact a verdict rests on: release dates, the SDK
constraint, pub points, downloads, the Dart 3 tag, whether pub.dev's own analysis succeeded, and every
file and line where your code uses it.
Before you add a package
Works outside a project, and exits 1 on a blocking verdict: upkeep explain x && dart pub add x
The replacement, when one is named
When a publisher retires a package, explain names its successor. For packages that died without being retired, a curated list names one too, links to the source that says so, and only when that successor is healthy today.
$ upkeep explain pedantic upkeep 0.2.0 pedantic explain, against Dart 3.11.0 VERDICT DISCONTINUED fails a CI build marked discontinued by its publisher on pub.dev the publisher nominates lints as the replacement move to lints named by its publisher FACTS FROM PUB.DEV latest release 1.11.1, published 2021-06-17 (62 months ago) sdk constraint >=2.12.0-0 <3.0.0 discontinued yes, replaced by lints publisher google.dev pub points 160 of 160 downloads 869k in the last 30 days dart 3 compatible, per pub.dev exit 1
Real output, 12 September 2026. A perfect score and 869k monthly downloads do not outrank the publisher's word.
What it checks
Two engines. Built on published facts, not opinion.
Your dependencies
Every direct dependency, scored against pub.dev's own data.
- Release recency and pub points
- Dart 3 compatibility, as the resolver sees it
- SDK constraints that block an upgrade
- The official discontinued flag, and the named replacement
Your Android build matrix
Validated against Google's published requirements for AGP 8.0 through 9.4.
- Gradle wrapper against the AGP in use
- JDK against the AGP minimum
compileSdkagainst the documented ceiling- The exact command that fixes each failure
Five verdicts, each one explained
DISCONTINUEDThe publisher said stop. Popularity does not override it.
INCOMPATIBLEIts own ceiling excludes your Dart. No release is coming.
AT RISKMore than one warning sign, each one named.
SDK BLOCKEDYour Flutter is behind. One line, never fails your build.
HEALTHYChecked, and nothing to say.
Principles
A tool that judges your dependencies has one asset. Trust.
Each rule exists because an earlier version of the engine got a verdict wrong.
Missing data is never evidence
When pub.dev cannot be reached, the verdict is unknown, not dead. An early build accused a perfectly maintained package of neglect over one truncated HTTP response.
Popularity never overrides a discontinued flag
pedantic is discontinued, scores 160 of 160, and is still downloaded hundreds of thousands of times a month. The publisher's word wins.
The resolver gets the last word
isar and hive cap themselves below Dart 3 yet still resolve. Where pub.dev's tag contradicts the arithmetic, the tag wins.
Stable is not abandoned
A small, finished, widely used package that has not needed a release in two years is not dying. It is done.
No guessing
Where Google does not publish a number, upkeep says it cannot check. Inferred values soften a verdict, never harden it.
Every verdict prints its reasons
Never a bare label. If you disagree, you can see exactly which fact it acted on, and check it yourself.
GitHub Action
It opens the fix pull request for you.
One workflow file. Every pull request gets a health check in its job summary, and once a week upkeep applies the safe fixes, verifies them, and opens one pull request with the rest as a checklist.
name: upkeep on: pull_request: schedule: - cron: "0 6 * * 1" # Mondays jobs: scan: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: Minas-27/upkeep@main fix: if: github.event_name == 'schedule' runs-on: ubuntu-latest permissions: contents: write pull-requests: write steps: - uses: actions/checkout@v4 - uses: Minas-27/upkeep@main with: command: fix-pr
Clean.
Blocking: a discontinued, incompatible or dead dependency, or an Android matrix that does not build.
The command could not run, or fix --apply refused or rolled back.
The pull request only ever contains the files the automatic fixes touched, and its description is the
full plan. Flutter or plain Dart is picked from your pubspec.yaml.
Not on GitHub? upkeep scan works in any pipeline, and
--format json gives tools a versioned report while --format markdown
suits any job summary.
Roadmap
Today it keeps one project healthy. Next, the whole ecosystem.
Everything in 0.2.0 is live on pub.dev, tested on real Flutter apps. Everything after it is a direction, not a promise, and is labelled that way.
scanLiveVerdicts with reasons, the Android build matrix, CI exit codes. On pub.dev since 0.1.
fixLiveAutomatic, reversible fixes, pubspec changes verified by
pub get, and a to-do list with file and line.explainLiveEvery fact behind a verdict, where your code uses the package, and a successor with its source.
jsonLiveA versioned report for tools, and Markdown for job summaries.
actionLiveA GitHub Action that scans every pull request and opens the fix pull request itself.
On the horizon
Upgrade forecast
Resolve your whole dependency tree against the next Flutter release before you install it, and know on day one whether your project survives the upgrade.
Verified migrations
Move from a dead package to its successor automatically: imports and renamed APIs rewritten, then proven with
dart analyze and your own tests, or rolled back.
The iOS matrix
Xcode, CocoaPods, Swift and deployment targets checked against every plugin's minimums, the way Android is today.
A living compatibility atlas
An open dataset of known-bad version combinations, built from opt-in, anonymous CI results, so one team's broken build warns everyone else before theirs.
Every repository at once
One view across an organisation: which of your forty apps still depend on a package that died this morning, and the pull requests already open to fix them.
Inside your editor
Verdicts inline on every line of pubspec.yaml in VS Code and Android Studio, with the fix one
click away.
The CLI is free and MIT licensed, and stays that way. The compounding asset is the knowledge base behind it: the compatibility matrix, the successor map, the known-bad combinations.
Know before it breaks.
One command. No configuration. The truth about your project's health.
dart pub global activate upkeep