saropa_lints 4.1.2
saropa_lints: ^4.1.2 copied to clipboard
1500+ custom lint rules with 201 quick fixes for Flutter and Dart. Static analysis for security, accessibility, and performance.

Saropa Lints #
Developed by Saropa. Making the world of Dart & Flutter better, one lint at a time.
Why Saropa Lints? #
Linting vs static analysis #
flutter analyze checks syntax and style. Static analysis checks behavior.
Your linter catches unused variables and formatting issues. It doesn't catch undisposed controllers, hardcoded credentials, or setState after dispose — because these require understanding what the code does, not just how it's written.
In mature ecosystems, tools like SonarQube, Coverity, and Checkmarx fill this gap. Flutter hasn't had an equivalent — until now.
What it catches #
Code that compiles but fails at runtime:
// Memory leak — controller never disposed
final _controller = TextEditingController();
// Crash — setState after widget disposed
await api.fetchData();
setState(() => _data = data); // boom
// State loss — new GlobalKey every build
Widget build(context) {
final key = GlobalKey<FormState>(); // wrong
return Form(key: key, ...);
}
Saropa Lints detects these patterns and hundreds more:
- Security — Hardcoded credentials, sensitive data in logs, unsafe deserialization
- Accessibility — Missing semantics, inadequate touch targets, screen reader issues
- Performance — Unnecessary rebuilds, memory leaks, expensive operations in build
- Lifecycle — setState after dispose, missing mounted checks, undisposed resources
Accuracy focused: Rules use proper AST type checking instead of string matching, reducing false positives on variable names like "upstream" or "spinning".
Essential for popular packages #
If you use GetX, Riverpod, Provider, Bloc, Isar, Hive, or Firebase, these audits are critical. These libraries are powerful but have patterns that fail silently at runtime:
| Library | Common issues caught | Guide |
|---|---|---|
| GetX | Undisposed controllers, memory leaks from workers, missing super calls | Using with GetX |
| Riverpod | Circular provider deps, ref.read() in build, missing ProviderScope | Using with Riverpod |
| Provider | Provider.of in build causing rebuilds, recreated providers losing state | Using with Provider |
| Bloc | Events in constructor, mutable state, unclosed Blocs, BlocListener in build | Using with Bloc |
| Isar | Enum fields causing data corruption on schema changes; caching Isar streams (runtime crash risk) | Using with Isar |
| Hive | Missing init, unclosed boxes, hardcoded encryption keys, type adapter issues | Using with Hive |
| Firebase | Unbounded queries, missing batch writes, invalid Analytics events, FCM token leaks | Using with Firebase |
Standard linters don't understand these libraries. They see valid Dart code. Saropa Lints has 50+ rules specifically for library-specific anti-patterns that cause crashes, memory leaks, cost overruns, and data corruption in production. The new avoid_cached_isar_stream rule (with quick fix) prevents a common Isar runtime error.
Why it matters #
The European Accessibility Act takes effect June 2025, requiring accessible apps in retail, banking, and travel. GitHub detected 39 million leaked secrets in repositories during 2024.
These aren't edge cases. They're compliance requirements and security basics that standard linters miss.
OWASP Compliance Mapping #
Security rules are mapped to OWASP Mobile Top 10 (2024) and OWASP Top 10 (2021) standards. This enables:
- Compliance reporting for security audits
- Risk categorization aligned with industry standards
- Coverage analysis across OWASP categories
| OWASP Mobile | Coverage | OWASP Web | Coverage |
|---|---|---|---|
| M1 Credential Usage | 5+ rules | A01 Broken Access Control | 4+ rules |
| M3 Authentication | 5+ rules | A02 Cryptographic Failures | 10+ rules |
| M4 Input Validation | 6+ rules | A03 Injection | 6+ rules |
| M5 Communication | 2+ rules | A05 Misconfiguration | 4+ rules |
| M6 Privacy Controls | 5+ rules | A07 Authentication | 8+ rules |
| M8 Misconfiguration | 4+ rules | A09 Logging Failures | 2+ rules |
| M9 Data Storage | 7+ rules | ||
| M10 Cryptography | 4+ rules |
Gaps: M2 (Supply Chain) and M7 (Binary Protection) require separate tooling — dependency scanners and build-time protections. A06 (Outdated Components) similarly requires dependency scanning.
Rules expose their OWASP mapping programmatically:
// Query a rule's OWASP categories
final rule = AvoidHardcodedCredentialsRule();
print(rule.owasp); // Mobile: M1 | Web: A07
Free and open #
Good options exist, but many are paid or closed-source. We believe these fundamentals should be free and open. A rising tide lifts all boats.
The tier system lets you adopt gradually — start with ~100 critical rules, work up to 1500+ when you're ready.
Quick Start #
1. Add dependencies #
# pubspec.yaml
dev_dependencies:
custom_lint: ^0.8.0
saropa_lints: ^1.3.0
2. Enable custom_lint #
# analysis_options.yaml
analyzer:
plugins:
- custom_lint
3. Choose your tier #
# analysis_options.yaml
custom_lint:
saropa_lints:
tier: recommended # essential | recommended | professional | comprehensive | insanity
4. Run the linter #
dart run custom_lint
Migrating from other tools? #
- Migrating from very_good_analysis (also covers
lints,lint,pedantic) - Migrating from DCM (Dart Code Metrics)
- Migrating from solid_lints
- Using with flutter_lints (complementary setup)
The 5 Tiers #
Pick the tier that matches your team:
| Tier | Best For |
|---|---|
| Essential | Every project. Prevents crashes, memory leaks, security holes. |
| Recommended | Most teams. Adds performance, accessibility, null safety, collection bounds. |
| Professional | Enterprise. Adds architecture, documentation, comprehensive testing. |
| Comprehensive | Quality obsessed. Best practices everywhere. |
| Insanity | Greenfield projects. Every single rule. |
Plus 200+ optional stylistic rules — team preferences, not in any tier.
Configuration template #
See example/analysis_options_template.yaml for a complete reference with all 1500+ rules organized by category, tier membership, and examples.
Using a tier #
# analysis_options.yaml
custom_lint:
saropa_lints:
tier: recommended # Most teams start here
Available tiers: essential, recommended, professional, comprehensive, insanity
Customizing rules #
After choosing a tier, you can enable or disable specific rules.
IMPORTANT: Rules must use YAML list format (with - prefix), not map format:
custom_lint:
saropa_lints:
tier: recommended
rules:
# Disable a rule from the tier
- avoid_hardcoded_strings_in_ui: false
# Enable a rule from a higher tier
- require_public_api_documentation: true
# Enable stylistic rules (not in any tier by default)
- prefer_single_quotes: true
- prefer_trailing_comma_always: true
Wrong (map format - rules will be silently ignored):
rules:
avoid_hardcoded_strings_in_ui: false # NO DASH = NOT PARSED!
Correct (list format):
rules:
- avoid_hardcoded_strings_in_ui: false # DASH = PARSED!
Severity levels #
Each rule has a fixed severity (ERROR, WARNING, or INFO) defined in the rule itself. Severity cannot be overridden per-project. If a rule's severity doesn't match your needs:
- Use
// ignore: rule_nameto suppress individual occurrences - Disable the rule entirely with
- rule_name: false - Open an issue if you think the default severity should change
Baseline for Brownfield Projects #
The Problem
You want to adopt saropa_lints on an existing project. You run dart run custom_lint and see:
lib/old_widget.dart:42 - avoid_print
lib/old_widget.dart:87 - no_empty_block
lib/legacy/api.dart:15 - avoid_dynamic
... 500 more violations
That's overwhelming. You can't fix 500 issues before your next sprint. But you also can't ignore linting entirely - new code should be clean.
The Solution: Baseline
The baseline feature records all existing violations and hides them, while still catching violations in new code.
- Old code: Violations hidden (baselined)
- New code: Violations reported normally
This lets you adopt linting today without fixing legacy code first.
Quick Start (One Command)
dart run saropa_lints:baseline
This command:
- Runs analysis to find all current violations
- Creates
saropa_baseline.jsonwith those violations - Updates your
analysis_options.yamlautomatically
Result: Old violations are hidden, new code is still checked.
Three Combinable Baseline Types
| Type | Config | Description | Best For |
|---|---|---|---|
| File-based | baseline.file |
JSON listing specific violations | "Fix nothing yet" |
| Path-based | baseline.paths |
Glob patterns for directories | "Ignore legacy folders" |
| Date-based | baseline.date |
Git blame - ignore old code | "Fix gradually by age" |
All three types are combinable - any match suppresses the violation.
Full Configuration
custom_lint:
saropa_lints:
tier: recommended
baseline:
# Option 1: Specific violations (generated by CLI)
file: "saropa_baseline.json"
# Option 2: Ignore code unchanged since this date (uses git blame)
date: "2025-01-15"
# Option 3: Ignore entire directories (supports glob patterns)
paths:
- "lib/legacy/"
- "lib/deprecated/"
- "**/generated/"
- "*.g.dart"
# Only baseline certain severities (keep seeing critical issues)
only_impacts: [low, medium]
Path Pattern Examples
| Pattern | Matches |
|---|---|
lib/legacy/ |
All files under lib/legacy/ |
**/generated/ |
Any generated/ folder at any depth |
*.g.dart |
All files ending in .g.dart |
lib/**/old_*.dart |
Files like lib/foo/old_widget.dart |
Priority Filtering
Use only_impacts to baseline only certain severity levels while still seeing critical issues:
baseline:
file: "saropa_baseline.json"
only_impacts: [low, medium, opinionated] # Still see critical and high
Cleaning Up Over Time
As you fix violations, update the baseline to remove fixed items:
dart run saropa_lints:baseline --update
Output shows what was fixed:
Baseline Update Summary:
Previous: 150 violations
Current: 120 violations
Fixed: 30 violations removed!
CLI Reference
dart run saropa_lints:baseline # Generate new baseline
dart run saropa_lints:baseline --update # Refresh, remove fixed violations
dart run saropa_lints:baseline --dry-run # Preview without changes
dart run saropa_lints:baseline --skip-config # Don't update analysis_options.yaml
dart run saropa_lints:baseline -o custom.json # Custom output path
dart run saropa_lints:baseline ./my_project # Run on specific directory
dart run saropa_lints:baseline --help # See all options
Rule Categories #
| Category | Description |
|---|---|
| Flutter Widgets | Lifecycle, setState, keys, performance |
| Modern Dart 3.0+ | Class modifiers, patterns, records, when guards |
| Modern Flutter | TapRegion, OverlayPortal, SearchAnchor, CarouselView |
| State Management | Provider, Riverpod, Bloc patterns |
| Performance | Build optimization, memory, caching |
| Security | Credentials, encryption, input validation — OWASP mapped |
| Accessibility | Screen readers, touch targets, semantics |
| Testing | Assertions, mocking, flaky test prevention |
| Architecture | Clean architecture, DI, SOLID principles |
| Error Handling | Exceptions, logging, recovery |
| Async | Futures, Streams, cancellation |
| API & Network | Timeouts, retries, caching |
| Internationalization | Localization, RTL, plurals |
| Documentation | Public API, examples, deprecation |
Stylistic Rules #
20 rules for team preferences — not included in any tier. Enable individually based on your conventions.
Examples: prefer_relative_imports, prefer_single_quotes, prefer_arrow_functions, prefer_trailing_comma_always
See README_STYLISTIC.md for the full list with examples, pros/cons, and quick fixes.
Performance #
Running all 1500+ rules uses significant memory. The tier system helps:
- Rules set to
falseare not loaded - Start with
essentialorrecommended - Upgrade tiers as you fix warnings
# GOOD: Start with recommended tier
custom_lint:
saropa_lints:
tier: recommended
# BAD: Enabling everything at once on a legacy codebase
custom_lint:
saropa_lints:
tier: insanity # May show thousands of warnings
Performance Tip: Use Lower Tiers During Development #
custom_lint is notoriously slow with large rule sets. For faster iteration during development:
- Use
essentialtier locally (~400 rules) - catches critical bugs, runs 3-5x faster - Use
professionalor higher in CI - thorough checking where speed matters less - Upgrade tiers gradually - fix warnings before enabling more rules
# Fast local development
custom_lint:
saropa_lints:
tier: essential # ~400 rules, fastest
# Thorough CI checking
custom_lint:
saropa_lints:
tier: professional # ~1400 rules, comprehensive
The tier you choose has a direct impact on analysis speed:
essential: ~400 rules → fastest (memory leaks, security, crashes)recommended: ~900 rules → moderate (+ accessibility, performance)professional: ~1400 rules → slower (+ architecture, documentation)comprehensive/insanity: 1500+ rules → slowest (everything)
Adoption Strategy #
Static analysis doesn't create problems — it reveals ones that already exist. The tiered system lets you start at any level and progress at your own pace. Findings are for your workflow. You control what you address and when.
New Projects #
Start with professional or comprehensive. Fix issues as you write code.
Existing Projects #
- Enable
essential. Fix critical issues first. - Move to
recommended. Fix warnings as you touch files. - Enable higher tiers when the noise is manageable.
Suppressing Warnings #
When a rule doesn't apply to specific code:
// ignore: avoid_hardcoded_strings_in_ui
const debugText = 'DEBUG MODE';
// Hyphenated format also works:
// ignore: avoid-hardcoded-strings-in-ui
const debugText = 'DEBUG MODE';
// For entire files:
// ignore_for_file: avoid_print_in_production
Always add a comment explaining why you're suppressing.
Automatic File Skipping #
Rules automatically skip files that can't be manually fixed:
| File Pattern | Skipped By Default |
|---|---|
*.g.dart, *.freezed.dart, *.gen.dart |
Yes (generated code) |
*_fixture.dart, fixture/**, fixtures/** |
Yes (test fixtures) |
*_test.dart, test/** |
No (override with skipTestFiles) |
example/** |
No (override with skipExampleFiles) |
This reduces noise from generated code and intentionally "bad" fixture files.
Limitations #
- Scope: custom_lint (and therefore saropa_lints) only runs rules inside the package where you invoke it.
dependency_overridespointing to local packages are not linted automatically—add saropa_lints to the overridden package and rundart run custom_lintin that package (or wire a workspace task) if you want coverage there. - File types: Only Dart source files (
.dart) are analyzed. Non-Dart assets (JSON, XML, YAML, scripts, etc.) are out of scope for custom_lint.
Running the Linter #
Command line (recommended - always works):
dart run custom_lint
Impact Report #
Run lints with results grouped by business impact:
dart run saropa_lints:impact_report
Output shows critical issues first, with actionable guidance:
--- CRITICAL (2) ---
lib/main.dart:45 - avoid_hardcoded_credentials
lib/auth.dart:23 - require_dispose
--- HIGH (5) ---
lib/widget.dart:89 - avoid_icon_buttons_without_tooltip
...
Impact Summary
==============
CRITICAL: 2 (fix immediately!)
HIGH: 5 (address soon)
MEDIUM: 12 (tech debt)
LOW: 34 (style)
Total: 53 issues
Impact levels:
critical: Each occurrence is serious — even 1-2 is unacceptable (memory leaks, security)high: 10+ requires action (accessibility, performance anti-patterns)medium: 100+ indicates tech debt (error handling, complexity)low: Large counts acceptable (style, naming conventions)
Exit code equals the number of critical issues (capped at 125), making it CI-friendly.
IDE Integration (unreliable):
custom_lint uses the Dart analyzer plugin system, which has known reliability issues. IDE integration may or may not work depending on your setup. If you don't see warnings in your IDE:
- Run
flutter pub get - Restart VS Code completely (not just the analysis server)
- Check View → Output → Dart Analysis Server for errors
- If still not working, use the CLI - it's reliable
For reliable workflows, use:
- Pre-commit hooks
- CI/CD checks
- VS Code tasks (see below)
VS Code Task Setup (Recommended) #
Create .vscode/tasks.json in your project root:
{
"version": "2.0.0",
"tasks": [
{
"label": "custom_lint",
"type": "shell",
"command": "dart run custom_lint",
"group": {
"kind": "build",
"isDefault": true
},
"presentation": {
"reveal": "always",
"panel": "dedicated"
},
"problemMatcher": {
"owner": "custom_lint",
"fileLocation": ["relative", "${workspaceFolder}"],
"pattern": {
"regexp": "^\\s*(.+):(\\d+):(\\d+)\\s+•\\s+(.+)\\s+•\\s+(\\w+)\\s+•\\s+(ERROR|WARNING|INFO)$",
"file": 1,
"line": 2,
"column": 3,
"message": 4,
"code": 5,
"severity": 6
}
}
}
]
}
Usage:
- Press Ctrl+Shift+B (or Cmd+Shift+B on Mac) to run custom_lint
- Warnings appear in the Problems panel (Ctrl+Shift+M)
- Click any warning to jump to that line in your code
This is more reliable than IDE integration because it runs the actual CLI tool rather than depending on the analyzer plugin system.
VS Code Status Bar Button (Optional) #
Want a clickable button instead of remembering the keyboard shortcut? Install the included extension:
python scripts/install_vscode_extension.py
Then restart VS Code.
What you get:
- A "Lints" button in the status bar (bottom of VS Code)
- A search icon in the editor title bar when viewing Dart files
- Both trigger
dart run custom_lintand open the Problems panel
Troubleshooting #
IDE doesn't show lint warnings #
If your IDE isn't automatically detecting lint issues:
- Use the keyboard shortcut: Press Ctrl+Shift+B (or Cmd+Shift+B on Mac) to run custom_lint manually via the VS Code task
- Use the bug button: If you installed the status bar extension, click the "Lints" button in the status bar or the search icon in the editor title bar
- Restart VS Code completely (not just the analysis server)
- Check View → Output → Dart Analysis Server for errors
- If IDE integration remains unreliable, use the CLI directly:
dart run custom_lint
Out of Memory errors #
If you see errors like:
../../runtime/vm/zone.cc: 96: error: Out of memory.
Crash occurred when compiling package:analyzer/... in optimizing JIT mode
Solution 1: Clear the pub cache (most effective)
dart pub cache clean
dart pub get
dart run custom_lint
Solution 2: Increase Dart heap size (PowerShell)
$env:DART_VM_OPTIONS="--old_gen_heap_size=4096"
dart run custom_lint
Solution 3: Delete local build artifacts
# Windows
rmdir /s /q .dart_tool && dart pub get
# macOS/Linux
rm -rf .dart_tool && dart pub get
Native crashes (Windows) #
If you see native crashes with error codes like ExceptionCode=-1073741819:
# Windows
rmdir /s /q .dart_tool && flutter pub get
# macOS/Linux
rm -rf .dart_tool && flutter pub get
Then run dart run custom_lint again.
Contributing #
We believe great tools are built by communities, not companies. Contributions and feedback are always welcome.
If you think a rule is:
- Wrong - tell us why, we'll fix it or remove it
- Too strict - maybe it belongs in a higher tier
- Too lenient - maybe it should be stricter or have options
- Missing - propose it, or better yet, implement it
We don't have all the answers. If you've shipped production Flutter apps and have opinions, we want to hear them.
How to contribute #
See CONTRIBUTING.md for detailed guidelines.
Adding a new rule:
- Create rule in appropriate
lib/src/rules/*.dartfile - Add to the appropriate tier(s) in
lib/tiers/*.yaml - Add tests in
test/rules/*_test.dart - Update CHANGELOG.md
Reporting issues:
- Include a minimal code sample that triggers (or should trigger) the rule
- Explain what you expected vs what happened
- If you disagree with a rule's premise, say so directly
Discussing rules #
Not sure if something is a bug or a design decision? Open a discussion issue. We're happy to explain our reasoning and change our minds when presented with good arguments.
Professional Services #
Optional paid services for teams that want hands-on help. See PROFESSIONAL_SERVICES.md for details.
| Service | Description |
|---|---|
| New Projects | Development scoped to your stage — MVP, Production, or Enterprise |
| Upgrade | Move existing projects to higher tiers as they grow |
| Audit | Assess codebases you inherited; remediation quoted separately |
| Custom Rules | Rules specific to your architecture and compliance requirements |
Contact: saropa.com | [email protected]
Documentation #
| Document | Description |
|---|---|
| README_STYLISTIC.md | 200+ optional stylistic rules with examples |
| PERFORMANCE.md | Performance optimization guide and profiling |
| ROADMAP.md | Planned rules and project direction |
| CONTRIBUTING.md | How to contribute rules and report issues |
| CHANGELOG.md | Version history and release notes |
| SECURITY.md | Security policy and reporting vulnerabilities |
| PROFESSIONAL_SERVICES.md | Professional services and custom rules |
Package Integration Guides #
We provide specialized lint rules for popular Flutter packages. These catch library-specific anti-patterns that standard linters miss.
| Category | Package | Guide |
|---|---|---|
| State Management | Riverpod | Using with Riverpod |
| Bloc | Using with Bloc | |
| Provider | Using with Provider | |
| GetX | Using with GetX | |
| Databases | Isar | Using with Isar |
| Hive | Using with Hive | |
| Backend Services | Firebase | Using with Firebase |
| Platform | iOS/macOS | Apple Platform Rules |
For Package Authors
Want lint rules for your package? We'd love to collaborate with package maintainers to add rules that catch common gotchas and enforce best practices for your library.
Benefits:
- Help users avoid common mistakes with your package
- Reduce support burden from preventable issues
- Improve developer experience with your library
Contact us via CONTRIBUTING.md or open an issue to discuss adding rules for your package.
Badge #
To indicate your project is using saropa_lints:
[](https://pub.dev/packages/saropa_lints)
License #
MIT - see LICENSE. Use it however you like.
Built with care by the Flutter community. Questions? Ideas? We'd love to hear from you.
pub.dev | GitHub | Issues | Saropa
About This Project #
"The bitterness of poor quality remains long after the sweetness of meeting the schedule has been forgotten." — Karl Wiegers
"Quality is not an act, it is a habit." — Aristotle
saropa_lints is a comprehensive static analysis package for Flutter and Dart applications. With 1500+ lint rules organized into 5 progressive tiers (and more planned), it catches memory leaks, security vulnerabilities, accessibility violations, and runtime crashes that standard linters miss. Whether you're building a startup MVP or enterprise software, saropa_lints helps you ship more stable, secure, and accessible apps.
Keywords: Flutter linter, Dart static analysis, custom_lint rules, Flutter code quality, memory leak detection, security scanning, accessibility testing, WCAG compliance, European Accessibility Act, Flutter best practices, Dart analyzer plugin, code review automation, CI/CD linting, Flutter enterprise tools
Hashtags: #Flutter #Dart #StaticAnalysis #CodeQuality #FlutterDev #DartLang #Linting #DevTools #OpenSource #Accessibility #Security #BestPractices
Sources #
-
custom_lint — Plugin framework for custom Dart analysis rules https://pub.dev/packages/custom_lint
-
Dart Analyzer — Dart's static analysis engine https://dart.dev/tools/analysis
-
Flutter Accessibility — Flutter accessibility documentation https://docs.flutter.dev/ui/accessibility-and-internationalization/accessibility
-
WCAG 2.1 Guidelines — Web Content Accessibility Guidelines https://www.w3.org/WAI/standards-guidelines/wcag/
-
European Accessibility Act — EU accessibility legislation effective June 2025 https://accessible-eu-centre.ec.europa.eu/content-corner/news/eaa-comes-effect-june-2025-are-you-ready-2025-01-31_en
-
GitHub Secret Scanning — Leaked credentials detection report https://github.blog/security/application-security/next-evolution-github-advanced-security/
-
OWASP Top 10 — Application security vulnerabilities https://owasp.org/www-project-top-ten/
-
SonarQube — Static analysis platform https://www.sonarsource.com/products/sonarqube/
-
Effective Dart — Official Dart style guide https://dart.dev/effective-dart
-
Flutter Performance — Performance best practices https://docs.flutter.dev/perf