Your app passes flutter analyze. Zero warnings. You think you’re ready to ship. Production crashed anyway.
That “clean” codebase may have TextEditingControllers leaking memory on every screen transition, API keys sitting in plain text, and touch targets that violate the European Accessibility Act (effective June 2025).
Some developers avoid deeper analysis, worried about what they’ll find. But issues don’t disappear because you didn’t look. They surface as production crashes, user complaints, and emergency fixes at 2am. Static analysis means you find them first — on your terms, on your schedule.
Standard linting ensures your code is idiomatic and consistent. Static analysis ensures it is robust and compliant. One focuses on how the code looks and is read; the other focuses on how the code executes and behaves.
This article explains the difference between linting and static analysis, and why your Flutter app needs both.
Code that fails static analysis should not ship.
The Mental Model: Where Tools Sit
To understand the gap, we must look at the three layers of code validation:
┌──────────────────────────────────────────────────────────────┐
│ YOUR CODE │
└──────────────────────────────────────────────────────────────┘
│
┌───────────────────────┼─────────────────────┐
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ COMPILER │ │ LINTING │ │STATIC ANALYSIS│
├───────────────┤ ├───────────────┤ ├───────────────┤
│ "Does this │ │ "Is this │ │ "Does this │
│ parse?" │ │ consistent?" │ │ work │
│ │ │ │ │ correctly?" │
├───────────────┤ ├───────────────┤ ├───────────────┤
│ Syntax errors │ │ Style issues │ │ Memory leaks │
│ Type errors │ │ Naming │ │ Security gaps │
│ │ │ Formatting │ │ Crash risks │
│ │ │ │ │ Accessibility │
└───────────────┘ └───────────────┘ └───────────────┘
▲ ▲ ▲
│ │ │
dart compile flutter_lints SonarQube
very_good_analysis DCM
Saropa Lints
In mature ecosystems like Java, C#, or C++, tools like SonarQube, Coverity, and Checkmarx are industry standards. They don’t just check for trailing commas; they perform deep data-flow analysis to find memory mismanagement and security vulnerabilities.
In Flutter, we often stop at the “Linting” phase. This leaves the “Analysis” phase to manual code reviews — a process that is expensive, slow, and prone to human oversight.
The Blind Spots: 3 Bugs Your Linter Will Ignore
Here are three common scenarios where flutter analyze passes, but your app is broken.
1. Resource Management: The Memory Leak
Flutter controllers allocate native resources that Dart’s garbage collector cannot automatically reclaim. This is a primary source of “jank” and apps that the OS eventually kills for excessive resource consumption.
The Violation — A linter sees this as perfectly valid, idiomatic Dart:
class _MyState extends State {
// Valid initialization, follows all naming conventions.
late final TextEditingController _controller = TextEditingController();
late final FocusNode _focusNode = FocusNode();
@override
Widget build(BuildContext context) => TextField(controller: _controller);
// Missing dispose(). Both controller and focus node leak native listeners.
}
The Solution — Static analysis understands the contract of Listenable objects. It tracks the object's lifecycle and flags the missing dispose() call as a logical error.
@override
void dispose() {
_controller.dispose();
_focusNode.dispose();
super.dispose();
}
2. Asynchronous Safety: The “Mounted” Trap
The most common runtime error in Flutter occurs when setState() is called after a widget has been removed from the tree—typically following an await point.
The Violation — The linter cannot see the timing risk inherent in asynchronous execution.
void _loadData() async {
final data = await api.fetchData(); // Network delay occurs here
// If the user navigated away during the delay, the next line crashes.
setState(() => _data = data);
}
The Solution — Static analysis tracks control flow through async methods and enforces the mounted check.
void _loadData() async {
final data = await api.fetchData();
if (!mounted) return; // Guard against calling setState on a disposed widget
setState(() => _data = data);
}
3. Regulatory Compliance: Accessibility (a11y)
With the European Accessibility Act taking effect in June 2025, accessibility is becoming a legal requirement for digital services.
The Violation — Standard linters are blind to the physical constraints of the UI or the needs of Screen Readers.
// Passes style lints, but violates WCAG 2.1 touch-target standards
SizedBox(
width: 24,
height: 24,
child: IconButton(icon: Icon(Icons.add), onPressed: _add),
)
The Solution — Static analysis can flag widgets that fall below the 44x44pt minimum touch target or lack Semantics labels required by screen readers.
IconButton(
iconSize: 48, // Meets physical touch-target requirements
tooltip: 'Add new item', // Provides context for Screen Readers
icon: Icon(Icons.add),
onPressed: _add,
)
4. Organizational Rules: Architecture Enforcement
Static analysis allows a team to encode their architectural decisions into the build process, preventing “Tribal Knowledge” from becoming a bottleneck.
// Design system enforcement
ElevatedButton(...) // VIOLATION: "Use CompanyButton to ensure brand consistency"
// Theme enforcement
Container(color: Colors.blue) // VIOLATION: "Hardcoded color. Use Theme.of(context)"
// API layer enforcement
http.get(url) // VIOLATION: "Use ApiClient wrapper for global error handling"
Understanding the Diagnostic Output
While a linter focuses on format, a static analysis violation provides the context needed to understand the risk:
lib/screens/editor.dart:47:5
require_mounted_check: setState called after await without mounted check.
Risk: Calling setState on an unmounted widget causes a runtime exception.
Fix: Add "if (!mounted) return;" immediately before the setState call.

The Professional’s Choice
Linting is for your team — it makes the code readable and consistent. But Static Analysis is for your users — it ensures the app is reliable, secure, and accessible.
Shipping with only flutter analyze is like checking if a car has paint but never looking under the hood. And in regulated markets, that car won’t pass inspection. It might look like a "clean" codebase, but if it leaks memory or blocks users with disabilities, the clean syntax won't save you.
The payoff compounds.
Static analysis is part of development, not a post-delivery audit. You catch issues while building, before they become someone else’s problem. The tiered approach means critical gaps are fixed first. The rest becomes part of your ongoing workflow — invisible improvements that compound over time.
“Quality is not an act, it is a habit.” — Aristotle
Following In This Series
Part 2: Flutter anti-patterns reference — Code that compiles but crashes (Coming Soon)
Part 3: saropa_lints setup guide — Installation, configuration, CI/CD (Coming Soon)
Sources and Further Reading
- 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://commission.europa.eu/strategy-and-policy/policies/justice-and-fundamental-rights/disability/union-equality-strategy-rights-persons-disabilities-2021-2030/european-accessibility-act_en
- SonarQube — Static analysis for 30+ languages https://www.sonarsource.com/products/sonarqube/
- Coverity — Enterprise static analysis and SAST https://scan.coverity.com/
- Checkmarx — Application security testing https://checkmarx.com/
- Dart Memory Management — Garbage collection in Dart https://dart.dev/resources/faq#how-does-dart-manage-memory
- State.dispose() — Lifecycle and disposal requirements https://api.flutter.dev/flutter/widgets/State/dispose.html
- OWASP: Hardcoded Passwords — Security vulnerability classification https://owasp.org/www-community/vulnerabilities/Use_of_hard-coded_password
- GitHub Secret Scanning Report — 39M leaked secrets detected in 2024 https://github.blog/security/application-security/next-evolution-github-advanced-security/
- JWT Introduction — JSON Web Token structure and format https://jwt.io/introduction
- WCAG 2.1 Quick Reference — Accessibility success criteria https://www.w3.org/WAI/WCAG21/quickref/
- WCAG 2.5.5 Target Size (Level AAA) — Minimum 44x44 CSS pixels https://www.w3.org/WAI/WCAG21/Understanding/target-size.html
- Flutter Accessibility — Accessibility features and guidelines https://docs.flutter.dev/ui/accessibility-and-internationalization/accessibility
- State.setState() — Mounted checks and async safety https://api.flutter.dev/flutter/widgets/State/setState.html
- Dart Async/Await — Asynchronous programming in Dart https://dart.dev/libraries/async/async-await
- State.mounted — Checking if widget is still in tree https://api.flutter.dev/flutter/widgets/State/mounted.html
- Flutter Formatting Tools — flutter analyze documentation https://docs.flutter.dev/tools/formatting
- flutter_lints — Flutter’s default lint rules https://pub.dev/packages/flutter_lints
- very_good_analysis — VGV’s strict analysis rules https://pub.dev/packages/very_good_analysis
- saropa_lints —Making the world of Dart & Flutter better, one lint at a time https://pub.dev/packages/saropa_lints
Final Word 🪅
