flutter_restore
Static compatibility scanner for restoring and auditing old Flutter projects.
flutter_restore reads project files, builds a structured snapshot, runs
compatibility rules, and prints a plain text or JSON report. It does not run
Flutter, Gradle, Xcode, CMake, package resolution, or any build step.
Table of Contents
- Overview
- Installation
- Usage
- Supported Platforms
- Rules
- Reports
- Migration Plans
- Exit Codes
- Architecture
- Development
- Maintainers
Overview
Old Flutter projects often fail before application code is reached. The usual failure points are stale runner files, obsolete Gradle or CMake setup, old web bootstrap templates, deprecated platform integration, and native build settings that modern tooling no longer expects.
flutter_restore helps answer:
What is old, risky, or incompatible here before I try to run it?
The scanner is designed for:
- restoring abandoned Flutter applications
- auditing projects before migration
- estimating native migration effort
- producing machine-readable compatibility reports
- inspecting fragile repositories without executing old build scripts
Installation
From this repository:
dart pub get
Run directly:
dart run flutter_restore scan path/to/flutter/project
Activate locally during development:
dart pub global activate --source path .
flutter_restore scan path/to/flutter/project
Usage
Run a plain text scan:
dart run flutter_restore scan path/to/project
Run a JSON scan:
dart run flutter_restore scan --json path/to/project
Scan every supported platform:
dart run flutter_restore scan --platform all path/to/project
Scan a single platform:
dart run flutter_restore scan --platform android path/to/project
dart run flutter_restore scan --platform ios path/to/project
dart run flutter_restore scan --platform linux path/to/project
dart run flutter_restore scan --platform windows path/to/project
dart run flutter_restore scan --platform web path/to/project
Print an ordered migration plan instead of a raw findings report:
dart run flutter_restore plan path/to/project
dart run flutter_restore plan --json path/to/project
The plan command accepts the same --json and --platform options as
scan.
Supported Platforms
| Platform | Static Facts | Compatibility Rules |
|---|---|---|
| Android | Gradle, AGP, Kotlin, SDK levels, Flutter Gradle integration, embedding | Java/Gradle, AGP/Gradle, AGP/Java, AGP/SDK, legacy Flutter Android migration |
| iOS | deployment targets, Podfile, SwiftPM, CocoaPods, AppDelegate, Xcode project settings | deployment target, dependency management, lifecycle, AppDelegate, Xcode project consistency |
| Linux | runner files, CMake baseline, GTK pkg-config wiring | target presence, runner completeness, CMake minimum, GTK pkg-config |
| Windows | runner files, CMake baseline, run loop, version metadata, title bar, first-frame redraw | target presence, runner completeness, CMake minimum, run loop, version info, dark title bar, ForceRedraw |
| Web | index template, manifest, favicon, bootstrap, service worker wiring, base href | target presence, runner completeness, bootstrap, loader API, service worker, base href |
| Dependencies | pubspec.yaml constraints, pubspec.lock resolved versions, SDK pins | lock consistency, Dart/Flutter SDK requirements, known package conflicts, deprecated packages |
Rules
Rules emit Finding objects with stable ids, severity, location, detected
value, and recommendation fields when useful.
Project-wide rules:
missing-pubspecmissing-pubspec-lockmissing-metadata
Android rules:
gradle-java-17-unsupportedmissing-gradle-versionmissing-agp-versionunknown-agp-gradle-rangeagp-gradle-mismatchagp-java-requirementagp-compile-sdk-too-newlegacy-flutter-gradle-applyflutter-plugins-fileandroid-v1-embeddingmissing-plugin-dsl
iOS rules:
ios-deployment-target-too-lowios-podfile-platform-too-lowios-deployment-target-mismatchios-plugin-deployment-target-conflictios-plugin-deployment-target-too-lowios-swiftpm-disabledios-legacy-cocoapods-onlyios-mixed-dependency-managementios-legacy-flutter-podfile-integrationios-potential-cocoapods-only-pluginsios-uiscene-incompleteios-custom-appdelegate-only-lifecycleios-manual-generated-plugin-registrantios-custom-platform-integration-in-appdelegateios-implicit-engine-lifecycleios-legacy-flutter-engine-initializationios-suspicious-flutter-xcconfigios-old-flutter-build-scriptios-legacy-framework-embeddingios-legacy-build-setting
Linux rules:
linux-platform-directory-missinglinux-runner-files-incompletelinux-cmake-minimum-too-lowlinux-gtk-pkg-config-missing
Windows rules:
windows-platform-directory-missingwindows-runner-files-incompletewindows-cmake-minimum-too-lowwindows-legacy-run-loopwindows-version-info-not-tool-drivenwindows-dark-title-bar-support-missingwindows-force-redraw-missing
Web rules:
web-platform-directory-missingweb-runner-files-incompleteweb-bootstrap-missingweb-base-href-missingweb-legacy-load-entrypointweb-custom-bootstrap-incompleteweb-deprecated-service-worker-versionweb-manual-service-worker-registration
Desktop rules:
desktop-legacy-target-platform-override
Dependency rules (project-wide, driven by pubspec.yaml and pubspec.lock):
dependency-missing-from-lockdependency-constraint-lock-mismatchdependency-stale-lock-entrydependencies-require-newer-dart-sdkdependencies-require-newer-flutter-sdkdependencies-declared-dart-range-too-widedependencies-declared-flutter-range-too-widepackage-requires-newer-dart-sdkincompatible-packages-<first>-<second>package-note-<name>
The built-in package tables cover common packages and known conflicts (for
example intl vs. flutter_localizations, or dart_code_metrics vs.
analyzer 6). They are heuristics, not a resolver: extend
CompatibilityData with your own entries when auditing a codebase with
internal packages.
Reports
Plain text output is intended for humans and uses colored severity labels.
flutter_restore scan
Project: /projects/legacy_app
Package: legacy_app
Android
Gradle: 5.6.4
AGP: 3.5.4
Kotlin: 1.3.50
SDK: compile=35, min=21, target=28
Legacy flutter.gradle apply: true
.flutter-plugins: true
Android v1 embedding: true
Plugin DSL: false
Findings
[HIGH] Gradle 5.6.4 is too old for Java 17
This Gradle line supports Java up to 16; modern Android builds commonly use Java 17.
android/gradle/wrapper/gradle-wrapper.properties
JSON output is intended for scripts, CI, dashboards, and later automation.
{
"platforms": ["android"],
"snapshot": {
"rootPath": "/projects/legacy_app",
"hasPubspec": true,
"hasPubspecLock": true,
"hasMetadata": true,
"pubspecName": "legacy_app",
"flutterRevision": "0123456789abcdef",
"android": {
"gradleVersion": "5.6.4",
"agpVersion": "3.5.4",
"kotlinVersion": "1.3.50",
"compileSdk": 35,
"minSdk": 21,
"targetSdk": 28,
"usesLegacyFlutterGradleApply": true,
"hasFlutterPluginsFile": true,
"usesAndroidV1Embedding": true,
"usesPluginDsl": false
}
},
"findings": [
{
"id": "legacy-flutter-gradle-apply",
"severity": "HIGH",
"title": "Legacy Flutter Gradle apply detected",
"message": "The Android project applies flutter.gradle with apply from, which is incompatible with newer Flutter Gradle integration.",
"location": "android/app/build.gradle"
}
]
}
Severity levels:
BLOCKER: analysis cannot continue as expected, or migration is blockedHIGH: likely build or migration failureMEDIUM: important migration risk or missing precisionINFO: useful context for restoration planning
Migration Plans
flutter_restore plan turns scan findings into an ordered, advisory
migration plan. It is deliberately not an autofix: old Flutter projects
often contain custom changes in android/ and ios/, and blindly
regenerating platform files can destroy that customization. The plan
describes what to migrate, why, which files are affected, and which steps
must happen first.
Migration plan
[1] Upgrade Android Gradle infrastructure
reason: AGP/Gradle incompatible with current Java
affects:
android/build.gradle
android/gradle/wrapper/gradle-wrapper.properties
[2] Migrate Flutter Gradle plugin application
depends_on: 1
[3] Migrate Android embedding
depends_on: 2
Estimated risk: HIGH
Native customization detected: YES
Step numbering is one-based and stable, so depends_on always refers to
earlier steps. Estimated risk is derived from the most severe finding
(LOW, MEDIUM, or HIGH), and Native customization detected reports
whether custom native code was found in platform runners. The plan
command always exits with code 0; use scan when exit codes matter.
Exit Codes
| Code | Meaning |
|---|---|
0 |
scan completed without blocker findings |
2 |
scan completed and at least one blocker finding was reported |
64 |
command line usage error |
Architecture
The project is split into small layers:
- scanners read files and collect facts
- models represent discovered project state
- compatibility data lives separately from rule logic
- rules analyze a
ProjectSnapshot - the plan builder turns findings into an ordered
MigrationPlan - renderers format output
- the CLI wires the flow together
scan -> ProjectSnapshot -> CompatibilityRule -> Finding -> report
\-> MigrationPlanBuilder -> MigrationPlan -> plan report
The rule runner is split with Dart part files:
rule_runner.project.dartrule_runner.android.dartrule_runner.ios.dartrule_runner.platforms.dart
Development
Install dependencies:
dart pub get
Run tests:
dart test
Run static analysis:
dart analyze
Format code:
dart format .
The test suite includes scanner tests, rule tests, CLI integration tests, and fixtures for legacy and modern Flutter project layouts.
Maintainers
- Vortex-bit256
Libraries
- flutter_restore
- Static compatibility scanner for legacy Flutter projects.