NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
pub.dev · #1105 most downloaded on pub.dev
Dart Code Linter is a software analytics tool that helps developers analyse and improve software quality. Dart Code Linter is based on a fork of Dart Code Metrics.
Last release today
07 Oct 2026
Ships on a steady schedule
a new release about every 2 weeks
Nearly every release is documented
notes for 40 of 41 stable releases
Nothing withdrawn
no release was ever pulled
3 years old
46 releases · first in 2023
fix: honour --exclude when selecting files (default .g.dart/.freezed.dart exclude never matched) by @Akhrameev in #282
Full Changelog: v4.4.1...v4.4.2
--exclude being ignored when selecting the files to analyze, which made check-unused-files --delete-files delete generated files. Two defects were involved. The default pattern, {/**.g.dart,/**.freezed.dart}, never matched anything: joining it to the root folder put the root in front of the brace group, which left a double slash after the root in every alternative. And files were matched by their path relative to the root folder, while the patterns are joined to it, so a pattern only ever matched when the root folder was the current directory. With the default exclude, check-unused-files, check-unused-code and check-unnecessary-nullable reported files and declarations inside generated files, and --delete-files deleted the generated libraries nothing imported; with --root-folder set to anything but the current directory, every --exclude pattern was ignored by those commands and by analyze and fix as well. A pattern that is a single brace group is now joined to the root folder one alternative at a time, an alternative starting with / staying absolute just as a whole pattern starting with / already did, and files are matched by their own path. analyze and fix skip *.g.dart and *.freezed.dart files on their own, so for them only explicit patterns change.--exclude as users of other code in check-unused-files, check-unused-code, check-unnecessary-nullable and check-unused-l10n: they are left out of the report, but still analyzed for imports, usages and invocations. Fixing the default pattern alone would have traded one false positive for another, since generated code is often the only user of the code it was generated for: a hand written file imported only by a generated library would have been reported as unused, and deleted by --delete-files; the private constructor freezed requires (const Foo._();), called only from the generated part, would have been reported with --analyze-private-members; and a parameter only generated code passes null to would have been reported as unnecessarily nullable. Excluded files are still resolved, so excluding them does not make these commands faster. Files excluded through analyzer: exclude: in analysis_options.yaml are not analyzed at all, as before.check-unused-l10n from reporting members of a localization class declared in a file matched by --exclude. The exclusion only ever decided whose usages counted, so such a class was reported whatever --exclude said.--exclude description in the MCP CLI reference (extension/mcp/resources/cli_commands.md), which called it a comma-separated list: it takes a single glob, a comma outside braces is part of the file name (so lib/a.dart,lib/b.dart excluded nothing), and repeating the flag keeps only the last value. A new "Excluding Files" section shows how to combine patterns in one brace group and that passing the flag replaces the default.One column per quarter.
fix(no-equal-arguments): avoid CLI crash on unmatched arguments by @davidlondono in #270
Full Changelog: v4.4.0...v4.4.1
check-unnecessary-nullable. Analyzer 14.4.0 dropped the special case in ConstructorElement.displayName that returned the bare type name for the unnamed constructor, so that declaration was reported as Type.new there and as Type on every earlier analyzer. The name is now normalized in this package, so the report no longer depends on which analyzer the consumer resolved. Named constructors are unaffected and keep their Type.name form.pub_updater constraint to >=0.3.0 <0.7.0. The previous ceiling excluded pub_updater 0.6.0, which requires Dart 3.12.0; on that SDK the package lost the pub points for supporting up-to-date dependencies.Add primary constructor example to prefer-static-class test by @ngoc-quoc-huynh in #274
Full Changelog: v4.3.0...v4.4.0
check-unused-code, enabled via the --suggest-private-members CLI flag or the unused-code.suggest-private-members analysis-options key and disabled by default. A declaration is reported when it is referenced, but every reference to it sits in the library that declares it, so renaming it to a private name would compile unchanged. It covers type members and top level declarations alike, and is independent of analyze-private-members and analyze-public-members. Since it reports code that is used rather than dead code, it has its own exit gate, --[no-]fatal-could-be-private, and a declaration nothing references at all is never reported as a suggestion: when both checks are on, such a declaration is reported once, as unused. A member of a type the same run reports as dead is left out of the suggestions as well, since deleting the type is the answer for the whole group.name and toString) and the call method that makes its type callable, since obj(...) binds a member spelled exactly call and the rename fails to compile even inside the declaring library (only a method is exempt, a field, a getter or a static named call being reached by an ordinary reference and still suggested); fields bound by a named this.x or super.x formal, which cannot be renamed at all since Dart forbids a named parameter starting with an underscore; instance members redeclared anywhere in the hierarchy of a type declared in another library, where a private name would compile but silently stop dispatch from reaching the override or the interface implementation, while a member no foreign subtype mentions is still suggested because a private member is inherited across libraries unchanged (an implementer that declares the member nowhere in its own hierarchy, an abstract class or a hand written mock answering through noSuchMethod, leaves nothing to match it against and is a known limitation of the check); members of a type that is already private, where the rename changes nothing because no other library can name the type (and, for a mixin, changes something bad, since a public class can mix a private mixin in and republish its members; an unnamed extension does not count as private here, its members applying in every importing library); and everything declared in a library any consumer of the package can import, which covers both a file re-exported by a barrel and one that simply sits under lib/ outside lib/src, since Dart lets a consumer import that directly whether or not anything exports it. That last cut takes the members of the types in such a library along with its top level declarations, a consumer that can name the type reaching the public members of it just as directly, and it drops the suggestion alone: whether the analyzed code references such a declaration is still reported by the unused check, unchanged. A published package that keeps nothing under lib/src therefore sees no suggestions without --monorepo, every declaration it has being reachable by a consumer the analysis cannot see.check-unused-code whenever member analysis is enabled: a statically resolved assignment to a member (target.value = 1, including through a cascade) marked every member named value used across the whole program. A write resolves through the enclosing assignment rather than through the identifier naming the member, so the identifier's own element is null however precisely the target is typed, and that null was read as "reached through a dynamic target". Writes to a genuinely unknown target are still recorded by name, so a member only ever written dynamically is still not reported.analyze-public-members and analyze-private-members verdicts shipped in 4.3.0 as much as the new suggestions: !flag, x!, -x and ~x are compound assignment expressions whose write element is null because there is no write at all, not because one failed to resolve, and reading that null as "reached through a dynamic target" marked every member of that name used across the whole program. An ordinary negation of a member silenced every same-named member everywhere; on this package's own lib the gap hid 16 findings.check-unused-code JSON report to format version 3, for the new issueKind key on every issue (unused or could-be-private). The key alone is additive, but the meaning of a report is not: with suggest-private-members enabled it carries entries that are not unused code, which a consumer pinned to version 2 would count as dead declarations.feat(unused-code): detect unused public members behind a flag ( #176 ) by @Akhrameev in #268
Full Changelog: v4.2.2...v4.3.0
check-unused-code, enabled via the --analyze-public-members CLI flag or the unused-code.analyze-public-members analysis-options key and disabled by default. It is independent of analyze-private-members, so a large project can keep the cheaper private members check on while leaving this one off. Members that cannot be seen to be used through a reference are skipped instead of reported: members overriding or implementing an inherited member (found by walking allSupertypes, which also covers toString/hashCode/noSuchMethod and overrides written without @override), members carrying an annotation that says they are called from elsewhere (@override, @mustBeOverridden, @visibleForOverriding, @redeclare, @protected, @visibleForTesting, @JS, @pragma('vm:entry-point')), members exported to JavaScript with @JSExport (counted from the enclosing class too, but there only for its instance members, since a class level annotation never wraps statics; a class level @pragma('vm:entry-point') deliberately does not work this way at all, as it only permits allocation from native code and leaves members needing their own pragma), members whose name is invoked or read on a dynamic target anywhere in the program, toJson (called by json.encode rather than by reference), enum constants of an enum whose values is referenced, and unnamed constructors (their invocations carry no identifier to record; named constructors are analyzed).a + b, a[b], -a, a++) and implicit call invocations previously marked only the enclosing extension as used, so a class's operators looked unused as soon as member analysis was on. The combiner of a compound assignment (a += b, which reaches operator +) was recorded nowhere at all, so an extension whose operator is used only that way was falsely reported even by the default top-level analysis. Operators and implicit call invocations on a dynamic target resolve to no element and are now recorded by the member name they reach, the same way dynamic method calls and property reads are.tools/analyzer_plugin/pubspec.yaml verbatim and resolves it against pub.dev, so its dart_code_linter range (frozen at <4.2.0 since 4.1.7) resolved to 4.1.9 for anyone on 4.2.0 or later: the IDE ran 4.1.9's rules while the CLI ran the installed version, with no error, because a satisfying version always exists. Restored the exact version pin used up to 4.0.1, so each release's loader loads exactly that release.check-unused-code with analyze-private-members enabled: a used class member no longer marks a dead top-level declaration of the same name as used (a class calling its own dispose used to hide an unused top-level dispose function). The name based fallback that works around dart-lang/sdk#49182 now requires both sides to agree on whether they are members, which cannot introduce false positives because member dispatch never resolves to a library level declaration.--analyze-private-members flag and the unused-code.analyze-private-members analysis-options key in the README, including their precedence and known limitations.// ignore: unused-code suppressions.avoid-non-configurable-callbacks-in-init-state where a named argument whose label reads widget or a known callback method name (e.g. Options(widget: child)) was mistaken for a real reference to the state's widget getter or to that method, suppressing the warning. Reproducible only on analyzer 10-12, where a named argument's label is a Label-wrapped identifier; analyzer 13+ uses a bare token for it instead.avoid-non-exhaustive-switch-on-sealed-classes where a parenthesized wildcard case (case (_):) was not recognized as defeating exhaustiveness the same way a bare _ does.fix: support configured plugin rules on Dart 3.13 by @davidlondono in #263
Full Changelog: v4.2.1...v4.2.2
fix(prefer-dot-shorthands): add support for unnamed constructors by @juan-campuzano in #266
Full Changelog: v4.2.0...v4.2.1
prefer-dot-shorthands to also flag unnamed constructor calls (suggesting .new(...)) and enum/static member access used as a switch statement/expression case pattern matched against the switch's scrutinee type — both were previously left unflagged.feat: Add new rules by @juan-campuzano in #260
dart_code_linter.rules.avoid-non-configurable-callbacks-in-init-state rule, which flags a State.initState that configures a widget-supplied object (e.g. widget.controller.setNavigationDelegate(...)) with a callback object whose named callbacks never reference the widget's own fields — a sign the behavior is fully hardcoded with no way for callers of the widget to customize it.avoid-non-exhaustive-switch-on-sealed-classes rule, which flags a default/wildcard (_) case in a switch statement or expression over a sealed type. Relying on a fallback case defeats the compiler's exhaustiveness checking for sealed hierarchies, so newly added subtypes can silently fall through instead of forcing an explicit decision at each call site.prefer-dot-shorthands rule (with auto-fix), which flags enum/static member access, static method calls, and named constructor calls that repeat a type name already inferable from context (a call argument's declared parameter type, or an explicitly typed variable's initializer) — Dart 3.10's dot-shorthand syntax lets these collapse to .value instead of Type.value. The rule only fires on files whose language version is 3.10 or later, since the shorthand syntax does not compile below that.feat(unused-code): detect unused private class members behind a flag ( #176 ) by @Akhrameev in #259
Full Changelog: v4.1.8...v4.1.9
check-unused-code. Private members cannot be referenced from outside the declaring library, which rules out the reflection and cross-library false positives that make public members unreliable to analyze. Mirroring the SDK's unused_element semantics, a sole private constructor is never reported: it is the intentional prevent-instantiation/extension pattern (an entirely unused class is still reported by the class-level check). Enabled via the --analyze-private-members CLI flag or the unused-code.analyze-private-members analysis-options key; disabled by default.chore: support analyzer 14 (dart_style 3.1.11 unblocked the bump) by @Akhrameev in #257
Full Changelog: v4.1.7...v4.1.8
analyzer ceiling to <15.0.0, enabling analyzer 14.x support now that dart_style 3.1.11 added compatibility with it. Add 14.0.0 and 14.1.0 rows to the scripts/test_analyzer_compat.py matrix (analyzer_plugin 0.14.13/0.14.14, analysis_server_plugin 0.3.19/0.3.20).fix(analyzer-plugin): replace deprecated getChildAssumingFile with getFile ( #250 ) by @Akhrameev in #254
Full Changelog: v4.1.6...v4.1.7
Folder.getChildAssumingFile with ResourceProvider.getFile in the analyzer plugin's UUID bootstrap, resolving the pana static-analysis deprecation warning on analyzer 13.x. The call stays compatible across the full >=10.0.0 <14.0.0 range (Folder.getFile only exists in analyzer 13.3.0+).MethodDeclaration.isAbstract with a structural ast_compat.isAbstractMethod() helper in avoid-unused-parameters, keeping the call non-deprecated across the full >=10.0.0 <14.0.0 range (isComplete only exists in analyzer 13.2+).fix(lint-analyzer): resolve ExtensionTypeDeclaration.primaryConstructor deprecation via ast_compat helper ( #249 ) by @Akhrameev in #252
Full Changelog: v4.1.5...v4.1.6
prefer-enums-by-name rule that converts Enum.values.firstWhere((e) => e.name == x) to Enum.values.byName(x). The fix is offered only when the call is safely convertible: a single-parameter == name arrow closure, no orElse, and a lookup that does not reference the closure parameter.ExtensionTypeDeclaration.primaryConstructor (deprecated on analyzer 13.1+ in favor of namePart, which doesn't exist before 13.1) by reading the extension type's name structurally through a new ast_compat helper instead of the version-specific getter.fix(lint-analyzer): apply fixes back-to-front to avoid RangeError on multiple fixes ( #188 ) by @Akhrameev in #246
Full Changelog: v4.1.4...v4.1.5
avoid-duplicate-exports rule that deletes the duplicate export directive (the earlier export already covers the same URI, so the removal is behavior-preserving).RangeError crash in the fix command when a file had multiple auto-fixable issues; fixes are now applied from the end of the file towards the start so earlier edits no longer invalidate later offsets.feat(no-blank-line-before-single-return): add auto-fix and fix trailing-brace false positive by @Akhrameev in #245
Full Changelog: v4.1.3...v4.1.4
no-blank-line-before-single-return rule that removes the blank line(s) before a single return statement in a block, preserving any comments.no-blank-line-before-single-return where a trailing comment on the block's opening brace (e.g. { // comment) was reported even without a blank line before the return.feat(avoid-unnecessary-type-casts): add auto-fix to remove redundant cast by @Akhrameev in #244
Full Changelog: v4.1.2...v4.1.3
avoid-unnecessary-type-casts rule that removes the redundant as cast (e.g. value as String becomes value).fix(no-magic-number): remove self-reported parameter_assignments analyzer warnings by @Akhrameev in #243
Full Changelog: v4.1.1...v4.1.2
parameter_assignments analyzer warnings in the no-magic-number rule implementation by replacing ++count with count + 1 in the literal-counting callbacks (no behavioral change).Add config option prefer-match-file-name.ignore-enums and prefer-match-file-name.ignore-typedefs to suppress reports for enum and typedef declarations
prefer-match-file-name.ignore-enums and prefer-match-file-name.ignore-typedefs to suppress reports for enum and typedef declarations whose name doesn't match the file name.Add support for analysis_server_plugin (analyzer 13.x).
analysis_server_plugin (analyzer 13.x).analyzer 13.x via a cross-version AST shim (lib/src/utils/ast_compat.dart) that recognises the reshaped named-argument, record-field, default-parameter and label nodes structurally.analyzer constraint to >=10.0.0 <14.0.0.make test-analyzer-compat-full to cover analyzer 10.x, 11.x, 12.x and 13.x; skip versions whose Dart SDK constraint is incompatible with the host SDK instead of failing.Add ignored-invocations and ignored-targets options to prefer-moving-to-variable rule to suppress reports for specific method/getter names or target r
ignored-invocations and ignored-targets options to prefer-moving-to-variable rule to suppress reports for specific method/getter names or target receivers.newline-before-return with comment-aware, whitespace-preserving behavior and edge-case fixture coverage.Honor per-line // ignore: comments for function- and class-level metric violations (both leading and trailing forms). File-level metrics keep their //
// ignore: <metric-id> comments for function- and class-level metric violations (both leading and trailing forms). File-level metrics keep their // ignore_for_file: behavior.Fix prefer-moving-to-variable rule not detecting duplicate invocations in expression function bodies (=> expr).
prefer-moving-to-variable rule not detecting duplicate invocations in expression function bodies (=> expr).Update packageVersion constant to match pubspec version (was hardcoded as 3.2.0).
packageVersion constant to match pubspec version (was hardcoded as 3.2.0).Relax analyzer constraint to >=10.0.0 <13.0.0 to support Flutter stable with meta 1.17.0.
analyzer constraint to >=10.0.0 <13.0.0 to support Flutter stable with meta 1.17.0.analyzer_plugin constraint to >=0.14.0 <0.16.0.analyzer 12.x (replace removed LibraryIdentifier with version-agnostic approach).dependency_overrides from main and example pubspecs.BREAKING: Update minimum Dart SDK to >=3.5.0 (compatible with Flutter 3.24+).
analyzer to >=11.0.0 <12.0.0.analyzer_plugin to ^0.14.5.always-remove-listener rule.extension/mcp/).Update homepage in pubspec.yaml.
pubspec.yaml.Update analyzer constraint to ^8.2.0. Only works with dart >= 3.9.0.
analyzer constraint to ^8.2.0. Only works with dart >= 3.9.0.use-design-system-items.only-barrel-import.Update analyzer constraint to ^8.2.0
analyzer constraint to ^8.2.0Add rule use-design-system-items.
use-design-system-items.only-barrel-import.analyzer to ^8.0.0Changed prefer-media-query-direct-access to FlutterRule.
prefer-media-query-direct-access to FlutterRule.Add rule prefer-media-query-direct-access.
prefer-media-query-direct-access.prefer-named-record-fields.Add rule prefer-media-query-direct-access.
prefer-media-query-direct-access.Fixed DCL version in analyzer_plugin.
Add rule prefer-named-record-fields.
prefer-named-record-fields.Nothing published for this version
Update analyzer to version ^6.0.0
analyzer to version ^6.0.0Added fatal-warnings-threshold fatal-performance-threshold and fatal-style-threshold to set the failure threshold for analyze command
fatal-warnings-threshold fatal-performance-threshold and fatal-style-threshold to set the failure threshold for analyze commandFixed generating report file when find issues in the report
Added prefer single quotes rule
Removed deprecated fields in analysis options and collection method
Reverted analyzer ^6.0.0 to ^5.14.0
analyzer ^6.0.0 to ^5.14.0- Fixed some test
Append new presset analysis_options.1.0.0.yaml
analysis_options.1.0.0.yamlRename common rules to dart rules
Removed Angular framework rules
Fix: report in IDE'S is adjusted
Automated publishing of packages to pub.dev
- Fork: Dart code metrics 5.7.3
Your coding agent can read these notes before it upgrades. Set up the MCP server →