Getting Started with Flutter Lint and Static Analysis

Static analysis in Flutter (and Dart) is the process of examining code for errors and style issues without running the app. It allows you to catch bugs and enforce best practices early, before executing a single line of code.
Adopting static analysis represents a fundamental "shift left" in the software development lifecycle (SDLC). This philosophy advocates for moving quality assurance and testing activities to the earliest possible stages of development.
Linting is a subset of static analysis that focuses on style and best-practice rules. Flutterβs analysis tool (often called the Dart/Flutter analyzer) uses a set of rules (called lints) to ensure your code follows the Dart style guide and Effective Dart recommendations.
This guide will walk you through setting up and mastering Flutter's linting and static analysis capabilities, from the basics to advanced configurations.
The Anatomy of Flutter Static Analysisβ
Let's start by exploring different parts of static analysis in Flutter.
The Core Engine: The Dart Analyzerβ
Everything starts with the Dart Analyzer, the engine that analyzes your code. Itβs not just a command-line tool; itβs the backbone behind the instant feedback you see in your IDE. When you mistype a variable or mix incompatible types, itβs the analyzer that notices.
It does this by turning your source code into an Abstract Syntax Tree (AST), a structured model of your code. From there, it scans and validates everything against a defined set of rules. This tight connection between the analyzer, your IDE, and your project setup creates an immediate feedback loop where that familiar wavy underline appears the moment something's off.
The Rulebook: analysis_options.yamlβ
The behavior of the analyzer is governed by one file: analysis_options.yaml. Sitting at the root of your Flutter project, it tells the analyzer what to enforce and what to ignore. You can enable or disable rules, adjust their severity, and even exclude certain files or directories.
In teams, this file becomes the single source of truth for maintaining consistent code quality and style across all contributors.
The Official Starting Point: flutter_lintsβ
Beyond language-level checks, Flutter also encourages best practices through linter rules. The easiest way to get started is with the flutter_lints package, Googleβs officially recommended rule set for Flutter apps.
Built on top of the general Dart lints, it gives you a strong foundation that matches the Dart Style Guide and Effective Dart recommendations. Instead of debating style or rule configurations, you can start coding with confidence, knowing your project already follows community standards for readability and maintainability.
Going Beyond Defaults to Build Incredible Applicationsβ
While flutter_lints provides an excellent foundation, production-grade applications and growing teams quickly hit its limits. You need more than just style checking; you need to enforce architecture, manage code complexity, and ensure long-term maintainability. This is where dcm.dev comes in a complete static analysis designed specifically for Dart and Flutter.
DCM is a productivity and efficiency tool. Instead of spending weeks developing rules or searching for rules that probably don't exist, you can plug DCM in and gain access to:
-
450+ Pre-Built Rules in Addition to Flutter Lint Rules: A massive, well-tested library of rules covering performance, style, leaks, and best practices for both Dart and Flutter. Many of the rules comes with auto-fix that can save hours of effort!
-
Advanced Code Metrics: Go beyond simple lints with metrics like Cyclomatic Complexity, Lines of Code, and others to objectively measure code health and identify areas for refactoring.

-
Monitor Your Code Quality Trends: With DCM Dashboard, quickly access the latest state of all open issues across all your projects with your organization and observe changes over time to easily spot unexpected changes and act quickly to resolve them.

- More Features: That's not all, DCM has loads of other features including assets quality check, health commands including finding unused code and files, integrating with AI assisted tooling to ensure high quality code generation via MCP server and more to explore and more to come!
In short, dcm.dev allows your team to stand on the shoulders of static analysis experts, letting you focus your valuable time and energy on what matters most: building incredible applications.
DCM is a code quality tool that helps your team move faster by reducing the time spent on code reviews, finding tricky bugs, identifying complex code, and unifying code style.
Setting Up Flutter Static Analysis (Flutter lint rules)β
Setting up static analysis (linting) in Flutter is straightforward, especially with the official lint rule sets provided by Dart and Flutter. Recent versions of Flutter come with a default set of lint rules out-of-the-box. If you created your project with Flutter 2.5 (stable) or newer, you likely already have static analysis enabled by default.
You can verify this by checking your pubspec.yaml for flutter_lints
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^6.0.0
and looking for an analysis_options.yaml file in the project root (next to pubspec).
...
βββ android
βββ assets
βββ build
βββ ios
βββ lib
βββ linux
βββ macos
βββ pubspec.lock
βββ pubspec.yaml
βββ analysis_options.yaml
βββ test
βββ web
βββ windows
If your project is older or missing these, follow these steps to enable the latest Flutter lints:
-
In your project root, run the command:
flutter pub add --dev flutter_lints -
Create a file named
analysis_options.yamlat the root of your project (in the same directory as yourpubspec.yaml). In this file, include the recommended Flutter lint rules by adding a single line:include: package:flutter_lints/flutter.yaml -
After adding the file, run
flutter pub get(if you added the dependency manually) to ensure the package is installed. The analyzer will automatically start using these rules in your IDE. You can also run the analyzer manually (see next section) to verify everything is set up correctly.
flutter analyze
Below is an example analysis_options.yaml content for a Flutter project. This is similar to what Flutterβs template provides by default.
# Activate recommended lints for Flutter apps (from flutter_lints package)
include: package:flutter_lints/flutter.yaml
linter:
# You can customize the lint rules below
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
Running the Analyzer and Interpreting Resultsβ
With the setup complete, the analysis can be run in two primary ways, both of which yield the same results.
From the Command Lineβ
To perform a static analysis of the entire project from the terminal, run the following command from the project's root directory:
flutter analyze
This command invokes the Dart analyzer, which will scan all Dart files in the project according to the rules defined in analysis_options.yaml.
For example, if you had a stray semicolon after an if statement, the analyzer might output a warning like this:
Analyzing flutter_test_cases...
info β’ Constructors for public widgets should have a named 'key' parameter β’
lib/widgets/repaint_boundary_to_image.dart:22:7 β’ use_key_in_widget_constructors
info β’ Invalid use of a private type in a public API β’ lib/widgets/repaint_boundary_to_image.dart:24:3
β’ library_private_types_in_public_api
info β’ Don't invoke 'print' in production code β’ lib/widgets/snapshot_widget.dart:112:5 β’ avoid_print
info β’ Don't invoke 'print' in production code β’ lib/widgets/snapshot_widget.dart:132:5 β’ avoid_print
4 issues found. (ran in 1.5s)
In this example, it flags an unnecessary semicolon and cites the empty_statements rule. Similarly, if you use a print statement and the avoid_print lint is enabled, you would see a message warning:
info β’ Don't invoke 'print' in production code β’ lib/widgets/snapshot_widget.dart:132:5 β’ avoid_print
Each issue includes the file, line number, and the name of the lint rule (so you can look up details if needed).
Running flutter analyze regularly (or having your IDEβs analysis on) is highly recommended. It gives you quick feedback on code quality and potential errors. If issues are found, you can fix them as you go. In fact, some lint warnings have automated fixes, you can run
dart fix --dry-run
dart fix --apply
to auto-apply trivial fixes suggested by the analyzer. This can update your code (for example, adding missing const keywords or removing unused imports) according to the linterβs recommendations.
Getting Started with DCMβ
You might want to go beyond defaults and start with DCM as explained earlier. Getting started with DCM is super simple!
-
Get a proper license that works for you and your team. You can start for free or request a trial for your team
-
Setup your IDE whether is VS Code or IntelliJ
-
Activate your DCM license
dcm activate --license-key=YOUR_KEY -
And finally you can simply integrate with DCM by simply starting with our
recommendedset of rules or enable over 450+ rule, run advanced code health commands, enable metrics, integrate with your AI assisted tool and many more.analysis_options.yamldcm:
extends:
- recommended
Now go ahead and run DCM commands.
Available commands:
analyze Analyze Dart code for lint rule violations.
analyze-assets Analyze image assets for incorrect formats, names, exceeding size, and missing high-resolution images.
analyze-structure Analyze Dart project structure.
analyze-widgets Analyze Flutter widgets for quality, usages, and duplication.
calculate-metrics Collect code metrics for Dart code.
check-code-duplication Check for duplicate functions, methods, and test cases.
check-dependencies Check for missing, under-promoted, over-promoted, and unused dependencies.
check-exports-completeness Check for exports completeness in *.dart files.
check-parameters Check for various issues with function, method and constructor parameters.
check-unused-code Check for unused code in *.dart files.
check-unused-files Check for unused *.dart files.
check-unused-l10n Check for unused localization in *.dart files.
fix Apply fixes for fixable analysis issues.
format Format *.dart files.
init Set up DCM.
run Run multiple passed DCM commands at once.
Inside the IDEβ
The true power of the analyzer is its real-time integration with IDEs. As code is written in VS Code or Android Studio, the analyzer runs continuously in the background. Any violations of the rules in analysis_options.yaml will appear almost instantly:
- Inline Highlighting: The specific line of code with the issue will be underlined. Hovering over the underlined code will display a tooltip with the same error message seen on the command line.

- Problems/Dart Analysis Tab: A dedicated panel in the IDE (e.g., the "Problems" tab in VS Code) will aggregate all issues across the entire project, allowing for easy navigation to each problem area.

From Defaults to Custom Configurationβ
While the flutter_lints package provides an excellent default, professional development often requires a more tailored approach. Customizing the analysis_options.yaml file allows a team to enforce stricter checks, adopt specific coding conventions, and ultimately take full ownership of their code quality standards.
This file becomes more than just a configuration; it evolves into a living document that codifies a team's development philosophy, defining "what good code looks like" for their specific project.
Let's see what we can do!
Enforcing Maximum Type Safetyβ
Dart's type system is powerful, but the analyzer can be configured to be even stricter, catching potential runtime errors during development. This is done using the language key under the analyzer entry in analysis_options.yaml.
analyzer:
language:
strict-casts: true
strict-inference: true
strict-raw-types: true
-
strict-casts: true: This flag ensures that the analyzer reports a potential error when an implicit downcast might fail at runtime. For example, if aList<Object>is implicitly cast to aList<String>, this flag will raise an issue because the cast could fail if the list contains non-string elements. -
strict-inference: true: This forces the type inference engine to be more conservative. It will report an issue if it cannot determine a precise type and would otherwise default to dynamic. This encourages developers to be more explicit with their type annotations, reducing ambiguity. -
strict-raw-types: true: This flag ensures that when a generic class is used, a type argument is always provided. For example, it would flagList myListand encourage the developer to specify the type, such asList<int>orList<dynamic>, making the code's intent maintainable.
Customizing Flutter Linter Rulesβ
Beyond the top-level analyzer settings, individual linter rules can be managed under the linter key. This allows for fine-grained control over the project's coding style and conventions.
include: package:flutter_lints/flutter.yaml
linter:
rules:
# Disable a rule from the included set
avoid_print: false
# Enable an additional rule
require_trailing_commas: true
avoid_positional_boolean_parameters: true
Managing Code Exclusions and Severityβ
Sometimes, a specific rule must be violated in a controlled manner, or a rule's importance needs to be elevated. The analyzer provides mechanisms for both scenarios.
Suppressing Flutter Rulesβ
There are two ways to exclude code from a specific analysis rule:
-
For a single line: Place a comment directly above the line of code.
// ignore: avoid_print
print('Debug log'); -
For an entire file: Add a comment at the top of the Dart file.
// ignore_for_file: prefer_const_constructors
import 'package:flutter/material.dart';
// ... rest of the file
Changing Lint Rule Severityβ
It is also possible to change the severity of a rule. For example, a team might decide that a specific style violation is so important that it should be treated as an error, not just a warning. This is configured under the analyzer.errors key.
analyzer:
errors:
# Treat missing required parameters as an error instead of a warning
missing_required_param: error
# Just warn (not error) if const could be used
prefer_const_constructors: warning # (opt-in if you want const guidance)
# set the 'lines_longer_than_80_chars' warning to just info
lines_longer_than_80_chars: info
Exclude files or directoriesβ
Sometimes you might want to ignore generated code or specific files (say, your .g.dart files or build/ directory) from analysis to avoid false warnings. You can add an exclude list under the analyzer section:
analyzer:
exclude:
- build/**
- lib/generated_plugin_registrant.dart
This level of control allows teams to fine-tune the analyzer's behavior to match their project's specific quality gates and priorities.
DCM Rule and Metric Configurationβ
Going beyond defaults and using an advanced lint tool for Flutter like DCM also comes with huge benefits of configuring lots of rules and metrics to tailor to your specific needs.
Here is just one example from all those 450+ rules which avoid-banned-imports:
dcm:
rules:
- avoid-banned-imports:
entries:
- paths: ['some/folder/.*\.dart', 'another/folder/.*\.dart']
deny: ['package:flutter/material.dart']
message: 'Do not import Flutter Material Design library, we should not depend on it!'
severity: error
- paths: ['core/.*\.dart']
deny: ['package:flutter_bloc/flutter_bloc.dart']
message: 'State management should be not used inside "core" folder.'
Integrating Static Analysis into Your Workflowβ
Once the ruleset is defined in analysis_options.yaml, the next step is to integrate static analysis deeply into the development workflow. This involves providing more context to the analyzer through code annotations and automating checks to ensure that quality standards are consistently upheld by the entire team.
The Power of Annotationsβ
The meta package provides a set of special annotations that allow developers to communicate their intentions directly to the analysis tools. These annotations provide hints that the analyzer cannot deduce on its own, enabling it to provide more accurate and helpful warnings.
To use them, first add the package as a dependency:
flutter pub add meta
Key annotations include:
-
@immutable: When applied to a class, this annotation asserts that all of its fields arefinal. The analyzer will then flag any subclasses that are not also marked as immutable. This is essential for Flutter widgets to ensure their properties cannot change after construction, which is a core principle of the framework's declarative UI. -
@mustCallSuper: When a method in a subclass overrides a method from a superclass that is annotated with@mustCallSuper, the analyzer will issue a warning if the subclass's method does not include a call to the superclass's method (e.g.,super.dispose()). This is critical for preventing resource leaks inStatefulWidgetlifecycle.
Read more about annotations in the Flutter meta package documentation.
Preparing for Publication: The pana Scoreβ
For developers who intend to publish packages to the official Dart package repository, pub.dev, static analysis plays a direct and visible role in how the package is perceived by the community. When a package is published, an automated tool called pana (Package ANAlysis) runs a series of checks to generate a quality score, which is prominently displayed on the package's page
A key category in this evaluation is "Pass static analysis," which executes dart analyze (or flutter analyze) on the package's code. A clean analysis report is essential for achieving a high score. This score acts as a powerful social signal; it tells other developers that the package author is disciplined, adheres to community best practices, and has produced code that is likely to be reliable and well-maintained.
Therefore, mastering static analysis is not just about internal code quality; it is a critical component of building a positive public reputation within the Flutter ecosystem.
Flutter Lint Rules and Automation with Continuous Integration (CI/CD)β
The ultimate safety net for maintaining code quality is to automate the analysis process using a Continuous Integration (CI) pipeline. The goal is to create an automated, impartial gatekeeper that prevents code violating the established analysis rules from ever being merged into the project's main branch.
For teams using platforms like GitHub, this can be achieved with a GitHub Actions workflow. The following is a simple, copy-paste-ready example of a workflow file (.github/workflows/analyze.yml) that runs flutter analyze on every push and pull request to the main branch:
name: Flutter Analyze
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
analyze:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: subosito/flutter-action@v2
with:
channel: 'stable'
- run: flutter pub get
- run: flutter analyze
You can repeat this workflow on your desired and favorite CI tool as well.
DCM and Your Favorite CD/CI Platform Goes Beyond Basicβ
DCM also works seamlessly with major CI/CD platforms like GitHub Actions, GitLab CI/CD, Azure DevOps, Bitbucket Pipelines and Codemagic.
More importantly, DCM provides flexible output formats for CI pipelines: console, JSON, Checkstyle, Code Climate, GitLab, GitHub, so you can integrate into your existing build dashboards or pipelines easily.
Check out more details on our DCM CI/CD Integrations.
Conclusionβ
By properly configuring package:flutter_lints and customizing your analysis_options.yaml file, you create a robust development environment that catches issues early and guides your team toward Flutter best practices.
Remember that linting is not about rigid enforcement but about creating a consistent, readable, and maintainable codebase that your entire team can work with effectively. Choose rules that make sense for your project and team, and don't hesitate to adjust them as your project evolves.
Enjoying this article?
Subscribe to get our latest articles and product updates by email.
