flutter_prunekit 2.0.0
flutter_prunekit: ^2.0.0 copied to clipboard
Blazing-fast static analyzer for detecting unused classes, enums, mixins & extensions in Dart/Flutter. 100% precision, zero false positives.
flutter_prunekit #
π― Find and remove dead (unused) code in Dart & Flutter projects β classes, enums, mixins, extensions, methods and more.
Short, fast, zero-config static analysis to detect dead code and help keep your codebase small and maintainable.
Highlights β’ Installation β’ Quick Start β’ Documentation
π Why flutter_prunekit? #
Dead code bloats your app, confuses developers, and slows down builds. flutter_prunekit uses advanced static analysis to find unused classes, enums, mixins, and extensionsβso you can ship faster, cleaner code.
β¨ Highlights #
- π― High precision results backed by 370+ automated tests and production pilots (last validated Oct 2024).
- β‘ Analysis finishes in seconds for medium Flutter apps thanks to parallel AST traversal.
- π§ Understands modern Dart features: extensions, mixins, part files, generics, override chains.
- π οΈ Zero-config defaults with flexible ignore annotations, config, and glob patterns.
- π Offline CLI that runs on macOS, Linux, and Windows with no external services.
π What it Detects #
Classes & Types:
- β Classes - Regular and abstract classes
- β Enums - All enum declarations
- β Mixins - Mixin declarations
- β
Extensions - Named and unnamed extensions with full semantic analysis
- Extension methods, getters, operators
- Cross-file extension usage tracking
- Generic type-parameterized extensions
Functions & Methods: π
- β Top-level Functions - Global function declarations
- β Instance Methods - Class and enum instance methods with override detection
- β Static Methods - Class and enum static methods and factory constructors
- β Extension Methods - Methods on extension types
- β Getters & Setters - Property accessors (both top-level, class-level, and enum-level)
- β
Operators - Overloaded operators (
+,==,[], etc.) - β Private Methods - Unused private methods detection
- β
Lifecycle Methods - Automatic exclusion of Flutter lifecycle methods (
initState,dispose, etc.)
π Coming Soon (Roadmap) #
We're actively working on detecting unused:
- π Fields & Properties - Unused class fields
- π Variables - Unused top-level and local variables
- π Type aliases - Unused typedef declarations
- π Constructor parameters - Unused named parameters
Want a feature? Open an issue!
π¦ Installation #
Option 1: Add to Your Project (Recommended) #
dart pub add --dev flutter_prunekit
Or manually add to pubspec.yaml:
dev_dependencies:
flutter_prunekit: ^2.0.0
Then run:
dart pub get
Option 2: Global Installation #
dart pub global activate flutter_prunekit
π Quick Start #
Basic Usage #
# Analyze your project (scans lib/ by default)
dart run flutter_prunekit
# Or if globally installed
flutter_prunekit
That's it! The tool will scan your code and show you any unused classes, enums, mixins, or extensions.
Example Output #
βββ Flutter Dead Code Analysis βββ
β Found 5 unused declaration(s):
Classes: 2
Enums: 1
lib/models/old_user.dart:12
OldUser
lib/widgets/legacy_button.dart:8
LegacyButton
lib/utils/deprecated_helper.dart:5
DeprecatedStatus
Top-Level Functions: 1
lib/helpers/formatter.dart:45
formatLegacyData
Instance Methods: 1
UserService (lib/services/user_service.dart:23)
processLegacyUser [instance]
βββ Summary βββ
Files analyzed: 156
Total declarations: 89
Total methods: 234
Unused: 5
Class usage rate: 96.6%
Method usage rate: 99.1%
Analysis time: 2.3s
Programmatic Usage #
Prefer to drive the analyzer from Dart code? Check out example/basic_usage.dart
for a minimal script that wires together the public APIs to scan any project
directory. Running it without arguments analyzes the bundled sample project:
dart run example/basic_usage.dart
This points the analyzer at example/sample_project, which deliberately contains
unused classes, mixins, enums, and extensions so you can see the tool in action.
π οΈ CLI Reference #
| Flag | Description | Default / Notes |
|---|---|---|
--path <dir> |
Analyze specific directories instead of auto-detecting lib/. |
Repeatable; accepts globs. |
--exclude <pattern> |
Ignore paths that match a glob (e.g. lib/legacy/**). |
Evaluated after --path. |
--json |
Emit the full analysis report in JSON (matches formatter schema). | Returns both class & method findings unless --only-methods. |
--only-methods |
Skip class detection and report methods/functions only. | Useful when classes are already clean. |
--include-tests |
Analyze test/ alongside lib/. |
Default is disabled. |
--include-generated |
Opt-in to scanning generated files (e.g. .g.dart). |
Works with flutter_prunekit.yaml excludes. |
--ignore-analysis-options |
Ignore excludes from analysis_options.yaml. |
Handy for temporary deep scans. |
--quiet |
Suppress banners and summaries; outputs only findings. | Helpful for CI logs. |
--verbose |
Print per-file progress and timing. | Pair with CI to debug slow runs. |
--help / -h |
Show the full help text with all options. | Does not run analysis. |
--version |
Print the current package version. | Exits immediately. |
π Usage Guide #
Common Scenarios #
# Scope the scan
dart run flutter_prunekit --path packages/core/lib
# Exclude legacy modules
dart run flutter_prunekit --exclude 'lib/legacy/**'
# Include tests and generated code for a deep audit
dart run flutter_prunekit --include-tests --include-generated
# Debug a slow analysis
dart run flutter_prunekit --verbose
βοΈ Configuration #
Method 1: Config File (Recommended) #
Create flutter_prunekit.yaml in your project root:
# Exclude entire directories or specific files
exclude:
- 'lib/legacy/**' # All files in legacy folder
- 'lib/generated/**' # Generated code folder
- '**/old_*.dart' # Files starting with 'old_'
- 'lib/deprecated.dart' # Specific file
# Custom annotations to treat as "keep" markers
ignore_annotations:
- 'deprecated' # Classes with @deprecated won't be flagged
- 'experimental' # Your custom @experimental annotation
Method 2: Use Existing analysis_options.yaml #
The tool automatically respects your analyzer excludes:
analyzer:
exclude:
- 'lib/generated/**'
- '**/*.g.dart'
- '**/*.freezed.dart'
No additional configuration needed!
π― Ignoring False Positives #
Sometimes you need to keep code that appears unused (reflection, dynamic loading, etc.). Here's how:
Ignore Priority Order #
When multiple ignore methods conflict:
@keepUnusedannotation (highest)flutter_prunekit.yamlpatterns--excludeCLI flag (lowest)
Option 1: Annotate Specific Classes & Methods β Recommended #
Perfect for individual classes or methods that should never be removed:
@keepUnused // β Add this annotation
class LegacyWidget extends StatelessWidget {
// Won't be flagged as unused
}
@keepUnused
mixin ReflectionMixin {
// Used via reflection - keep it!
}
@keepUnused
enum PlatformStatus { active, inactive }
@keepUnused
extension StringHelpers on String {
// Extension used in other packages
}
class Calculator {
@keepUnused // Method-level annotation
int complexCalculation() {
// Used via reflection or dynamic invocation
return 42;
}
int simpleAdd(int a, int b) => a + b; // Normal method
}
Option 2: Pattern-Based Exclusion #
Use config file for excluding multiple files or specific methods:
# flutter_prunekit.yaml
exclude:
- 'lib/legacy/**' # Entire folder
- '**/experimental_*.dart' # Name pattern
- 'lib/platform_specific.dart' # Single file
# Ignore specific methods by pattern
ignore_methods:
- 'test*' # Ignore all test helper methods
- '_internal*' # Ignore internal methods
- 'TestHelper.*' # Ignore all TestHelper methods
- '*.cleanup' # Ignore cleanup in any class
- 'debugPrint' # Ignore specific method
Option 3: Runtime Exclusion (Temporary) #
Use CLI flags for one-off analyses:
# Test excluding certain code
dart run flutter_prunekit --exclude 'lib/legacy/**' --exclude '**/old_*.dart'
β οΈ Known Limitations (Edge Cases) #
These are rare but worth knowing:
1. Dynamic Type Usage
dynamic obj = getObject();
obj.method(); // β οΈ Cannot statically determine class type
Solution: Avoid dynamic where possible, or use @keepUnused
2. Reflection & Mirrors
Type type = reflectClass(MyClass); // β οΈ Runtime-only reference
Solution: Add @keepUnused to reflected classes
3. Conditional Imports (Platform-Specific Code)
import 'stub.dart'
if (dart.library.io) 'io_impl.dart'
if (dart.library.html) 'web_impl.dart';
Solution: Annotate platform-specific classes with @keepUnused
4. Unnamed Extensions (Analyzer API Limitation)
// If ANY unnamed extension is used, ALL get marked as used
extension on String { ... } // Shares identifier with below
extension on int { ... } // Shares identifier with above
Solution: Use named extensions for better tracking:
extension StringHelpers on String { ... }
extension IntHelpers on int { ... }
ποΈ Code Generation Support #
β Fully Supported #
- Freezed -
*.freezed.dartpart files fully analyzed - Realm -
*.realm.dartwith$Model/_Modelpattern - json_serializable -
*.g.dartfiles - built_value - Generated builders and serializers
How It Works #
By default, generated files are excluded (recommended). Use --include-generated to analyze them:
# Include generated code in analysis
dart run flutter_prunekit --include-generated
β οΈ Note: Generated classes may appear unused if only referenced in other generated code. This is usually safe to ignore.
Best Practice: Run analysis both with and without --include-generated to understand your codebase.
Development Setup #
# Clone the repository
git clone https://github.com/furkanvatandas/flutter_prunekit.git
cd flutter_prunekit
# Install dependencies
dart pub get
# Run the tool locally
dart run bin/flutter_prunekit.dart --path lib
π License #
MIT License - see LICENSE file for details.
π¬ Support & Community #
- π Issues: GitHub Issues
- π¬ Discussions: GitHub Discussions
β Show Your Support #
If flutter_prunekit helped clean up your codebase, consider:
- β Starring the repo
- π¦ Sharing on social media
- π Writing a blog post about it