A blazing-fast dead code scanner for Flutter & Dart projects
Find unused files, classes, functions, widgets, and assets in your Flutter project.
Zero configuration. No Dart SDK required. CI-ready.
Flutter projects accumulate dead code over time β orphaned screens, unused utility classes, forgotten assets eating up your bundle size. Manual cleanup is tedious and error-prone.
fscan statically analyzes your entire Flutter project in seconds and reports exactly what's unused, with zero false positives on private symbols and smart heuristics for public APIs.
| fscan | Manual Review | dart analyze | |
|---|---|---|---|
| Unused files | Yes | Painful | No |
| Unused assets | Yes | Very painful | No |
| Unused private symbols | Yes | Impossible at scale | Partial |
| Unused public APIs | Yes (warnings) | Impossible at scale | No |
| Unused widgets | Yes | Manual | No |
| Unused routes (auto_route/go_router/Navigator) | Yes | Manual | No |
| flutter_gen support | Yes | N/A | N/A |
| Speed (500+ files) | ~2 seconds | Hours | Minutes |
| Requires Dart SDK | No | N/A | Yes |
# Homebrew (macOS/Linux)
brew install beobeodev/tap/fscan
# npm (any platform with Node.js)
npm install -g @beobeodev/fscan
# Go
go install github.com/beobeodev/fscan@latest
# Scan your Flutter project
fscan scan ./my_flutter_app
# That's it. No config files, no setup.fscan results for: ./my_flutter_app
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
UNUSED FILE (4)
lib/unused_public_function.dart
lib/unused_widget.dart
lib/unused_file.dart
lib/unused_public_class.dart
UNUSED PRIVATE CLASS (1)
lib/unused_file.dart:9 Private class "_UnusedPrivateHelper" is never referenced
UNUSED ASSET (1)
assets/images/unused_icon.png
MAYBE UNUSED PUBLIC API (4)
lib/unused_public_class.dart:4 Public class "UnusedPublicClass" has no references
lib/unused_public_function.dart:4 Public function "formatCurrency" has no references
MAYBE UNUSED WIDGET (1)
lib/unused_widget.dart:6 Widget "UnusedWidget" has no direct instantiation
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Total: 11 issues (6 errors, 5 warnings)
| Rule | Description |
|---|---|
unused-file |
Dart files in lib/ not reachable from any entry point |
unused-asset |
Assets declared in pubspec.yaml but never referenced in code |
unused-private-class |
Private classes (_Foo) with zero references anywhere |
unused-private-function |
Private functions (_foo) with zero references anywhere |
| Rule | Description |
|---|---|
maybe-unused-public-api |
Public classes and functions with no references in the project |
maybe-unused-widget |
Widget subclasses never instantiated or route-registered |
maybe-unused-method |
Public methods with no cross-file or same-file callers (owner-class-aware) |
unused-route |
Routes declared in auto_route / go_router / Navigator but never navigated to |
Warnings are for symbols that could be part of a public API or used via dynamic routing. Review them β don't blindly delete.
βββββββββββββββββββββββββββββββββββ
β fscan CLI β
ββββββββββββ¬ββββββββββββββββββββββ-β
β
ββββββββββββββββ΄βββββββββββββββ
βΌ βΌ
βββββββββββββββββββββββ ββββββββββββββββββββββββββ
β Go Deep Scanner β β Dart Semantic Worker β
β (always runs) β β (optional, higher β
β β β accuracy) β
β β’ Regex-based β β β’ package:analyzer β
β β’ No dependencies β β β’ Full AST resolution β
β β’ ~2s for 500 filesβ β β’ Requires Dart SDK β
βββββββββββββββββββββββ ββββββββββββββββββββββββββ
β β
ββββββββββββ¬βββββββββββββββββββ
βΌ
βββββββββββββββββββββββ
β MergeSymbols β
β (Dart overrides Go β
β when available) β
βββββββββββββββββββββββ
- Go deep scanner runs by default with zero external dependencies. It extracts symbol declarations via regex and performs concurrent cross-file reference counting.
- Dart semantic worker (opt-in) uses
package:analyzerfor full AST resolution, constructor tracking, and class hierarchy analysis. When available, its results override the Go scanner for higher accuracy.
- App mode vs Library mode β auto-detected based on
main.dartpresence- App mode: BFS reachability from entry points
- Library mode: orphan detection (files not imported by anything)
- Framework-aware β skips lifecycle methods (
build,createState,dispose,initState, ...),@overrideannotations, and_FooStateclasses paired withStatefulWidget - Generated file handling β detects
.g.dart,.gen.dart,.freezed.dart,.gr.dart,.chopper.dartand more. Generated files are excluded from unused-file detection but included as reference sources. - flutter_gen support β understands typed asset accessors (
BookAssets.icons.arrowLeft.svg()) for accurate asset usage detection - Inline suppression β skip files with
// fscan:ignore-fileor lines with// fscan:ignore
- Exit code 1 when issues are found β use as a CI quality gate
- SARIF output for GitHub Code Scanning integration
- JSON output for custom tooling and dashboards
fscan scan [project-path] [flags]| Flag | Short | Default | Description |
|---|---|---|---|
--entry |
-e |
lib/main.dart |
Entry point files for reachability analysis |
--format |
-f |
text |
Output format: text, json, sarif |
--output |
-o |
stdout | Write report to file |
--rules |
-r |
all | Comma-separated list of rules to enable |
--verbose |
-v |
false | Print scan statistics to stderr |
--skip-dart |
false | Skip Dart semantic worker | |
--strict-assets |
false | Require field-level references for generated assets | |
--dart-worker |
auto | Path to dart_worker/bin/worker.dart |
# Scan with verbose output
fscan scan ./my_app -v
# Output JSON for CI parsing
fscan scan ./my_app -f json -o report.json
# SARIF for GitHub Code Scanning
fscan scan ./my_app -f sarif -o results.sarif
# Only check for unused files and assets
fscan scan ./my_app -r unused-file,unused-asset
# Scan a library package (auto-detects, no main.dart needed)
fscan scan ./my_library_package
# Strict asset checking (detects flutter_gen accessor usage)
fscan scan ./my_app --strict-assets
# Multiple entry points
fscan scan ./my_app -e lib/main.dart -e lib/admin.dart
# With Dart semantic analysis (requires Dart SDK)
fscan scan ./my_app --dart-worker dart_worker/bin/worker.dartname: Dead Code Check
on: [pull_request]
jobs:
fscan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.21'
- name: Install fscan
run: go install github.com/beobeodev/fscan@latest
- name: Scan for dead code
run: fscan scan . -f sarif -o results.sarif
- name: Upload SARIF
if: always()
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarifHuman-readable, grouped by rule with issue counts and a summary line. Best for local development.
{
"project": "./my_flutter_app",
"scanned_at": "2026-04-16T08:00:00Z",
"total": 11,
"issues": [
{
"rule": "unused-file",
"file": "lib/unused_file.dart",
"line": 0,
"message": "lib/unused_file.dart is not reachable from any entry point",
"severity": "error"
}
]
}Full SARIF compliance for integration with GitHub Code Scanning, VS Code SARIF Viewer, and other static analysis tools.
By default, fscan considers an asset "used" if its generated file (e.g., assets.gen.dart) is imported anywhere in the project. This is fast but permissive.
With --strict-assets, fscan performs field-level reference scanning:
// assets.gen.dart (generated by flutter_gen)
class $AssetsIconsGen {
SvgGenImage get arrowLeft => const SvgGenImage('assets/icons/arrow_left.svg');
SvgGenImage get unusedIcon => const SvgGenImage('assets/icons/unused_icon.svg');
}
// user_code.dart
BookAssets.icons.arrowLeft.svg() // arrowLeft is used
// unusedIcon is never referenced β flaggedfscan parses the flutter_gen class hierarchy to build category.fieldName patterns (e.g., icons.arrowLeft) and searches user code with word-boundary checking to eliminate false positives from common names like .add, .close, .active.
Skip entire files or specific lines:
// fscan:ignore-file
// Place in the first 10 lines to skip the entire file
import 'package:legacy/old_api.dart'; // fscan:ignorefscan/
βββ cmd/ # CLI commands (Cobra)
β βββ root.go # Root command, rule descriptions
β βββ scan.go # Scan pipeline orchestration
βββ internal/
β βββ config/ # ScanConfig, entry point detection
β βββ graph/ # Thread-safe directed graph (file β file, file β asset)
β βββ walker/ # Concurrent file walker, import/export/part parser
β βββ scanner/ # Symbol extraction, cross-file reference counting
β βββ asset/ # pubspec.yaml parsing, asset scanning, flutter_gen mapping
β βββ dart/ # Optional Dart semantic worker (subprocess protocol)
β βββ rules/ # Rule engine + 8 detection rules
β βββ report/ # Text, JSON, SARIF reporters
βββ dart_worker/ # Dart subprocess (package:analyzer)
βββ testdata/sample_app/ # Integration test fixtures
βββ Makefile # Build, test, lint, run targets
βββ main.go # Entry point
Parse CLI flags
β
βΌ
Detect entry points (app vs library mode)
β
βΌ
Build import graph (concurrent file walk)
β
βΌ
Scan assets (pubspec.yaml + string refs + flutter_gen)
β
βΌ
Deep scan symbols (Go regex, concurrent)
β
βΌ
Dart semantic analysis (optional, overrides deep scan)
β
βΌ
Apply rules β issues
β
βΌ
Report (text / json / sarif)
β
βΌ
Exit code 0 (clean) or 1 (issues found)
git clone https://github.com/beobeodev/fscan.git
cd fscan
make build # β ./build/fscan
make test # Run all tests
make install # Copy to $GOPATH/binmake dart-setup # Install Dart dependencies
make run-sample-full # Run with both scannersRequires Dart SDK 3.0+.
fscan has been validated against real-world Flutter projects:
| Project | Files | Issues Found | Scan Time |
|---|---|---|---|
| App (774 files) | 774 Dart files | 164 issues | ~3s |
| Library (409 files) | 409 Dart files | 69 issues | ~2s |
| Library + strict assets | 409 Dart files | 139 issues | ~3s |
Contributions are welcome! Please open an issue or submit a PR.
# Run tests
make test
# Run linter
make lint
# Test against sample project
make run-sampleMIT
Built with Go. Made for Flutter developers who care about clean codebases.