many_lints 1.0.0
many_lints: ^1.0.0 copied to clipboard
A comprehensive collection of custom lint rules, quick fixes, and code assists for Flutter and Dart projects.
Changelog #
[Unreleased] #
1.0.0 - 2026-08-18 #
Breaking #
-
The
coreandrecommendedpresets are now bug-focused. Six likely-but-not-certain findings moved fromcoretorecommended; rules that only prefer an equivalent spelling, API shape, or functional style moved fromrecommendedtoopinionated.match_getter_setter_field_names,prefer_correct_test_file_name, anduse_setstate_synchronouslymoved intorecommendedbecause they identify concrete mismatches or stale-state risks. Existingopinionatedpreferences remain enabled. -
avoid_ref_read_inside_buildandavoid_ref_watch_outside_buildnow coverpackage:provideras well as Riverpod. Both rules previously only fired inside a Riverpod consumer, so a project usingpackage:providergot nothing from them. They now also reportcontext.read<T>()insidebuildandcontext.watch<T>()outside it.Listed as breaking because
avoid_ref_read_inside_buildis in therecommendedpreset: a provider-based codebase that upgrades will see diagnostics it did not see before. They are true positives — the mistake is identical in both ecosystems, and the provider half ofavoid_ref_watch_outside_buildcatches an outright crash, sincecontext.watch<T>()throws when called frominitState.The receiver is matched on its resolved type, not its name: Riverpod's
refis aWidgetRef/Ref, and provider's extensions hang offBuildContext. SowidgetContext.read<T>()is caught under any receiver name, while a field of an unrelated class you happened to callrefis not. That last part is a regression this change had to fix rather than a hypothetical — covering provider means admitting every widget class to the rules, and until the receiver was type-checked, a plain widget holding some other package'sRefreported.The quick fix's label changed from
Replace with 'ref.watch'toReplace with 'watch', since aFixKindis constant and the old wording named an API a provider user does not have. -
prefer_immutable_bloc_stateno longer matches on class name. It recognised state classes two ways: through theBloc<E, S>/Cubit<S>type argument, and through a bareRegExp(r'State$')over class names. In a project without theblocpackage only the second ever matched, so the rule degenerated into "every class named...Statemust be@immutable" — reported under a message naming a package the project does not use. One Riverpod-only app saw 27 classes flagged, none of them Bloc state.The name-based half moved to the new
prefer_immutable_state, which says what it checks and carries thename_patternoption. To keep the old behaviour, enable both rules; a project that never used Bloc wants only the new one.This also fixes a latent bug the split exposed:
Cubitis itself aBloc, and the Bloc branch was tested first, so aCubit<State>was matched as a Bloc and then searched for a second type argument it does not have. Cubit state was only ever reported by the name heuristic, and went unreported once that was removed. -
Every rule is now off by default, and rules are selected with a preset. In v0.9.0 all 133 rules were enabled the moment the plugin was installed, which meant adopting the package on an existing codebase produced thousands of warnings before any of them could be judged useful.
Installing the plugin now reports nothing until a preset is chosen:
# many_lints.yaml preset: recommendedFive presets are available, each active tier building on the previous one:
Preset Rules Contents none0 Nothing. The default. core35 Near-certain bugs only — dead conditions, impossible casts, leaked resources. recommended91 coreplus likely defects and concrete runtime risks.opinionated177 recommendedplus this package's own style preferences.pedantic234 opinionatedplus strict naming, structure, complexity, and ordering.coreandrecommendedare deliberately conservative: a rule that imposes an architecture, a naming scheme, or a contested style choice is in neither, and rules that do nothing until configured (thebanned_*family,use_class_prefix/use_class_suffix) are in no preset at all.There is deliberately no preset that enables every rule: some rules contradict one another, and config-only rules have no meaningful built-in policy.
A preset can be tuned in either direction without restating its contents, using
enabled::preset: recommended rules: prefer_type_over_var: enabled: true # add a rule the preset omits avoid_only_rethrow: enabled: false # drop one it includesThe terse
rule_name: true/rule_name: falsespelling works too. Configuring a rule by name — giving it anexclude:, aninclude:, amessage:or an option — also opts it in, so an existingrules:block keeps working without an addedenabled: true.preset:is read from the same two places as the rest of this package's configuration:many_lints.yamlat the package root, or a top-levelmany_lints:section inanalysis_options.yaml. Presets cannot be distributed as includable YAML the waypackage:lintsdoes, because the analyzer replaces a plugin's configuration wholesale acrossinclude:rather than merging it, and thediagnostics:key accepts only severity values. -
Removed
prefer_contains. The Dart SDK rule with the same name covers the same cases plus additionalindexOfcomparisons and provides its own quick fix. A removed-rule tombstone remains registered so existing configurations point users to the SDK rule instead of producing an unknown-rule warning. -
Removed
prefer_named_boolean_parametersin favor of the Dart SDK'savoid_positional_boolean_parameters, which covers the same intent and also diagnoses a single positional boolean parameter by default. -
Removed
avoid_unnecessary_overrides_in_statein favor of the Dart SDK'sunnecessary_overrides. The SDK rule covers the State lifecycle cases, preserves legitimate override exemptions, and provides a fix. A removed-rule tombstone points existing configurations to the SDK rule. -
use_bloc_suffix,use_cubit_suffixanduse_notifier_suffixare replaced by two general rules,use_class_suffixanduse_class_prefix. The old rules enforced naming for exactly three hardcoded base types; the new ones work for any type, including one declared in your own package, so a project using...Store,...Repositoryor...UseCasecan adopt them instead of forking.Both are entirely config-driven and report nothing until configured. To restore the previous behaviour:
# many_lints.yaml rules: use_class_suffix: entries: - type: Bloc package: bloc suffix: Bloc - type: Cubit package: bloc suffix: Cubit - type: Notifier package: riverpod suffix: NotifierAny
// ignore: many_lints/use_bloc_suffixcomment (or the_cubit_/_notifier_variants) must be renamed to the new rule, anddiagnostics:entries likewise.
Added #
This release adds 123 rules. The complete inventory, grouped by documentation category, is below; the following entries call out the rules whose behaviour or design needs additional migration context.
-
Architecture:
avoid_banned_annotations,avoid_banned_exports,avoid_banned_imports,avoid_banned_names,avoid_banned_types,banned_usage -
Async safety:
avoid_catch_error,avoid_future_ignore,avoid_missing_completer_stack_trace,avoid_passing_async_when_sync_expected,avoid_redundant_async,prefer_correct_future_return_type,require_atomic_async_updates,use_setstate_synchronously -
Bloc / Riverpod:
emit_new_bloc_state_instances,handle_bloc_event_subclasses -
Class naming:
avoid_unnecessary_enum_prefix,match_class_name_pattern,prefer_boolean_prefixes,prefer_correct_callback_field_name,prefer_correct_error_name,prefer_correct_handler_name,prefer_correct_identifier_length,prefer_correct_setter_parameter_name,prefer_correct_type_name,prefer_prefixed_global_constants,use_class_prefix,use_class_suffix -
Code organization:
arguments_ordering,avoid_duplicate_mixins,avoid_unnecessary_constructor,avoid_unnecessary_extends,enum_constants_ordering,initializers_ordering,map_keys_ordering,match_lib_folder_structure,member_ordering,parameters_ordering,pattern_fields_ordering,prefer_match_file_name,prefer_single_declaration_per_file,record_fields_ordering -
Code quality:
avoid_accessing_other_classes_private_members,avoid_complex_conditions,avoid_deep_nesting,avoid_high_cyclomatic_complexity,avoid_long_files,avoid_long_functions,avoid_long_parameter_list,avoid_non_null_assertion,avoid_self_compare,avoid_shadowed_extension_methods,avoid_too_many_methods,avoid_unnecessary_call,function_always_returns_null,function_always_returns_same_value,match_getter_setter_field_names,max_imports,max_statements,no_magic_number,no_magic_string,prefer_declaring_const_constructor,prefer_getter_over_method,prefer_moving_to_variable,prefer_named_parameters,prefer_primary_constructors -
Collections and types:
avoid_not_encodable_in_to_json,avoid_unrelated_type_casts,prefer_correct_json_casts -
Control flow:
avoid_negated_conditions,avoid_nested_conditional_expressions,avoid_unmodified_loop_condition,avoid_unnecessary_continue,avoid_unnecessary_return,avoid_unused_after_null_check,no_equal_conditions,no_equal_switch_case,no_equal_then_else,prefer_conditional_expressions,prefer_early_return,prefer_returning_condition -
Formatting:
avoid_inconsistent_digit_separators,double_literal_format,format_comment -
fpdart:
avoid_ad_hoc_left_type,avoid_bare_await_in_do,avoid_dollar_outside_do_frame,avoid_either_of_future,avoid_future_of_either,avoid_future_of_option,avoid_get_or_else_swallowing_failure,avoid_nested_do_notation,avoid_removed_fpdart_api,avoid_throw_in_fp_callback,avoid_unnecessary_option,avoid_unrun_task,avoid_untyped_safe_cast,prefer_chain_either,prefer_chaining_over_intermediate_run,prefer_do_notation,prefer_from_nullable,prefer_from_predicate,prefer_safe_collection_access,prefer_string_parse_extensions,prefer_task_either_over_try_catch,prefer_unit_over_void -
Resource management:
avoid_late_final_reassignment,avoid_unremovable_callbacks_in_listeners -
Shorthand patterns:
avoid_nested_shorthands -
State management:
avoid_late_context,prefer_immutable_state -
Testing:
format_test_name,prefer_correct_test_file_name -
Type annotations:
prefer_explicit_parameter_names,prefer_explicit_type_arguments,prefer_typedefs_for_callbacks -
Widget best practices:
always_pass_global_key,avoid_deep_widget_nesting,avoid_too_many_widgets_per_build,check_for_equals_in_render_object_setters,never_discard_build_context,prefer_extracting_callbacks,prefer_widget_private_members -
Added
prefer_correct_future_return_typeto theopinionatedpreset, with diagnostics and a quick fix for async declarations whose return type hides their non-nullableFutureresult. -
prefer_returning_conditionwarns when anifreturnstruewith a followingreturn false— the condition itself, spelled out in three lines. In theopinionatedpreset. -
avoid_nested_conditional_expressionswarns when a conditional is nested inside another, packing a decision tree onto one line. The outermost carries the diagnostic, so one chain reports once. Configurable throughmax_depth(default1). In theopinionatedpreset. -
avoid_complex_conditionswarns when a condition combines more&&/||operands thanmax_operands(default 3). A hand-writtenoperator ==is never reported: it is one&&per field by construction, and splitting it would scatter a check that reads as a unit. In thepedanticpreset. -
avoid_long_parameter_listwarns when a function takes more parameters than the budget. Positional and named are counted separately (defaults 4 and 10), since named parameters are labelled at the call site and do not depend on order. In thepedanticpreset. -
prefer_getter_over_methodwarns when a no-argument method only reads a value. Three exclusions keep it off members whose parentheses are fixed: a body that calls anything (Clock.now()answers differently each time), a conventional name (toJson,call,copyWith), and aStream/Futurereturn. In thepedanticpreset. -
no_equal_conditionswarns when anif/else ifchain tests the same condition twice, making the later branch unreachable and letting the case it was meant to handle fall through toelse. Two independentifs are not compared, since the first may have changed the state the second reads. In therecommendedpreset. -
function_always_returns_same_valuewarns when everyreturnin a function yields the same constant, so the branching around them decides nothing. Protocol callbacks are excluded both by name (onNotification,shouldRepaint, anyon...) and by shape — a parameter typed...Notificationmarks a listener whoseboolis a "handled" signal rather than an answer. In therecommendedpreset. -
avoid_redundant_asyncwarns only when a function has noawaitorthrowand every explicit return already produces a compatibleFuture. Raw-value, mixed-return, fall-through,async*, and@overridebodies are left alone, so removingasyncnever creates an invalid return. In theopinionatedpreset. -
avoid_unnecessary_callwarns when a function is invoked through an explicit.call(). A null-aware invocation is left alone (callback?()does not parse), as is a class definingcallas a real method — the receiver's type tells the two apart. In theopinionatedpreset. -
avoid_long_functionswarns when a function body exceedsmax_lines(default 50), counted between the braces so the signature and doc comment do not count. In thepedanticpreset: a budget is a house style, and measured against a production Flutter app the default reported 187 functions with a median of 98 lines, all genuinely long and none of them a bug. Test files usually wantexclude: [test/**]rather than a higher budget. -
prefer_correct_callback_field_namewarns when a callback field or parameter is namedsomethingCallbackorsomethingHandlerrather thanonSomething, the spelling Flutter uses throughout its API. A function named for what it computes (builder,comparator) is never reported, nor is a bare framework noun: a parameter named exactlyhandleris the request handler in dart_frog, not a callback for an event. In thepedanticpreset, like the other strict naming rules. -
prefer_boolean_prefixeswarns when a boolean field, getter or method is not named as a yes-or-no question. The verb does not have to lead —localeIsDefaultasks the same question asisDefaultLocale— and a bare third-person verb (involves,matches) is already one. Overrides, setters, and a private field backing an accessor are never reported, since none of them can be renamed independently. In thepedanticpreset: naming is where codebases disagree most, and a predicate likescreen.atLeast(Breakpoint.tablet)reads fine without a question verb. -
member_orderingwarns when a class member is declared before one the configured order puts earlier. The order is declared throughorder:, and the default puts the constructor first, then fields, then behaviour — the shape modern Dart and Flutter code already has.==/hashCode/toString, operators, and a RiverpodNotifier.buildare never reported, because each is a member whose position is fixed by something other than taste. In thepedanticpreset: against a production Flutter app already following a consistent style it still reported 227 members, every one a real deviation and none of them a bug. -
prefer_immutable_statewarns when a class whose name marks it as state lacks@immutable. It owns the name-based half thatprefer_immutable_bloc_stateused to carry, including thename_patternoption, and is deliberately state-management-agnostic: it covers Riverpod notifier state, a hand-rolled store, or any plain...Statevalue object. FlutterState<T>subclasses are excluded by type, since holding mutable fields is their entire job. In theopinionatedpreset. -
avoid_late_final_reassignmentwarns when alate finalfield is assigned twice on one straight-line path.late finalpromises one assignment and Dart enforces it, but at run time by throwingLateInitializationError— so a second write the analyzer can see is a guaranteed crash rather than a possibility. Branches are not followed: two writes in opposite arms of anifare how alate finalis meant to be initialised. In thecorepreset. -
avoid_unnecessary_constructorwarns when a class declares an empty unnamed constructor identical to the one Dart provides when none is written. Aconst, named, documented or annotated constructor each does something the implicit one cannot and is left alone — as is any class with a second constructor, where declaring the unnamed one is what keeps it available. In theopinionatedpreset. -
avoid_unnecessary_extendswarns when a class explicitly extendsObject, which every class does anyway. A user-declaredObjectshadowingdart:core's is a real choice and is left alone. In theopinionatedpreset. -
avoid_unnecessary_returnwarns when a barereturn;is the last statement of a function returningvoidorFuture<void>, where control leaves the function without it. An earlyreturn;that skips later statements is left alone, as is an omitted return type, which meansdynamicrather thanvoid. In theopinionatedpreset. -
avoid_unnecessary_enum_prefixwarns when an enum constant repeats its own enum's name, which every call site already carries —Status.statusActiverather thanStatus.active. The prefix has to end at a word boundary, sostatusableis not a match, and a constant named exactly like its enum is the whole word rather than a prefix. In theopinionatedpreset. -
no_equal_switch_casewarns when two branches of aswitchproduce identical bodies, where sharing the patterns with||would say it once. Three shapes are excluded because none can be merged: a guarded case (eachwhenbelongs to its own pattern), the catch-all (it has to stay last), and an empty body (that is a fallthrough). In thepedanticpreset — whether two independent enum branches that agree today should be merged is a genuine judgement call. -
avoid_duplicate_mixinswarns when awithclause applies the same mixin more than once. Every application after the first contributes nothing, but a reader counting the behaviours mixed in sees one more than exists. Resolved types are compared rather than source text, so an aliased import counts as one mixin while a different generic instantiation does not. Re-applying a mixin a superclass already has is left alone, since that does change the linearization order. In therecommendedpreset. -
avoid_self_comparewarns when a value is passed to its owncompareTo, which always answers0— so a sort built on it leaves the list untouched, and a conditional guarded by it always takes the same branch. Only receivers and arguments that are safe to evaluate twice are compared: a repeated call, and a hand-written getter that can report a moving value, are both left alone. The operator form (a == a) stays withavoid_equal_expressions, so the two never report the same line. In thecorepreset. -
avoid_unnecessary_continuewarns when acontinueis the last statement of a loop body, where control reaches the next iteration without it. It is usually a leftover from a change that moved or deleted the statements it once guarded, and it reads as though something below is being skipped. A labelledcontinue, and one ending athenbranch to skip anelse, are both doing real work and are left alone. Ships with a quick fix. In theopinionatedpreset. -
prefer_moving_to_variablewarns when the same property-access or invocation chain is repeated inside one block, and could be computed once into a variable. Reported at the first occurrence, which is where the variable belongs. In thepedanticpreset.Four options, two of them beyond the usual scope of this rule:
max_extra_occurrences(how many extra repetitions to tolerate, default0; the originalallowed_duplicated_chainsspelling remains as a deprecated alias),min_chain_length(the shortest pure-property chain worth naming, default2, soa.btwice is left alone),ignored_invocationsandignored_targets.A chain containing an invocation ignores
min_chain_length: repeatingTheme.of(context)repeats the work, where repeating a field read only repeats the text. Calls made for their effect (print(x)), anything that allocates or awaits, chains inside a closure, and assignment targets are all left alone, since re-evaluating those is either the point or not a value at all. When a chain and its own prefix repeat equally often, only the longest is reported. -
avoid_catch_errorwarns onFuture.catchError. Its handler is an untypedFunction, so a wrong signature compiles cleanly and throws only on the error path, and atestcallback returningfalseleaves the error unhandled while reading as though it was caught.try/catchgets both checked at compile time. -
never_discard_build_contextwarns when aBuildContextparameter is named with a wildcard (_,__). Discarding it does not remove the need for a context — the body falls back to an outer one, which sits higher in the tree, soTheme.of/MediaQuery.of/Navigator.ofresolve against a different subtree. Ships a quick fix that names the parametercontext, withheld when that name is already in scope and renaming would shadow it. -
use_class_suffixanduse_class_prefix, each taking anentries:list of{type, suffix|prefix, package?, ignore_private?}. A base type matches whether it is reached byextends,implements,with, or an indirect ancestor, andpackage:is optional — omit it to match a type of that name from any library. Both ship a quick fix that renames the class and any same-named unnamed constructor. -
A rule-wide
ignore_privateoption on both rules, overridable per entry. -
prefer_single_declaration_per_filewarns when a file declares more than one top-level declaration. Classes, mixins, enums, extensions and extension types count; private declarations are skipped by default. In thepedanticpreset — it imposes a strict file-organization convention.Configurable along two axes.
kinds:picks which declaration kinds count, andtypes:narrows to subtypes of named base types, which turns the rule into the type-specific convention:# many_lints.yaml rules: prefer_single_declaration_per_file: types: [Notifier, AsyncNotifier]groups:goes further and gives each group its own one-per-file budget, so several conventions coexist without interfering — a file holding one bloc and one notifier satisfies both:rules: prefer_single_declaration_per_file: groups: - types: [Bloc, Cubit] message: 'One bloc per file.' - types: [Notifier] message: 'One notifier per file.'Each group also accepts
kinds,ignore_private,ignore_visible_for_testingand its ownmessage; any written at the top level become the groups' defaults. A declaration matching several groups is counted by the first one only.
Changed #
-
avoid_returning_widgetsmoves fromopinionatedtorecommended, and gains the two exemptions that kept it out. Returning a widget from a helper is a performance defect with a mechanism — the helper denies Flutter the element identity it needs to skip the subtree, so the parent rebuilds wholesale — rather than the style preferenceopinionatedis for, and it is documented by Flutter.Two shapes are no longer reported:
- A declaration passed as a callback rather than called, such as
Builder(builder: _row). The framework invokes it at its own point in the tree, so it does not collapse a subtree into the caller's rebuild, which is the cost the rule exists to prevent. A declaration that is called to build inline is still reported. Getters are excluded from this exemption —=> _bodyreads the getter rather than tearing it off, so a bare reference to one is the inline build the rule targets. - Functions annotated for a functional-widget generator.
ignored_annotationsnow defaults to[FunctionalWidget, swidget, hwidget, hcwidget]instead of an empty list, so afunctional_widgetuser no longer gets a diagnostic on every generated widget. The option keeps its replace semantics; the newadditional_ignored_annotationsextends the defaults instead of restating them.
- A declaration passed as a callback rather than called, such as
Fixed #
-
Switched the example package to the
pedanticpreset so every preset-backed rule is exercised by the example verifier, including pedantic-only rules. -
Replaced obsolete
plugins.many_lints.diagnosticssnippets across rule pages with the supported per-rule configuration and added a documentation check to prevent that syntax from returning. -
The suffix quick fix no longer eats a character when repairing a near-miss. It scanned candidate lengths longest-first and took the first match within two edits, so
CounterBlokbecameCounterBlocby way of strippingrBlok— producingCounteBloc. It now ranks candidates by edit distance and prefers the length closest to the affix. -
cleanup_methods: []now genuinely replaces the built-in cleanup methods with an empty list fordispose_fieldsanddispose_provided_instances. It previously fell back to[dispose, close, cancel], contradicting the documented replacement semantics. -
Added end-to-end
PluginServercoverage for every previously untested rule-specific option, including Bloc wrappers, hook/widget exemptions, constructor class lists, collection strictness and widget thresholds.
Documentation #
- Added a generated, category-grouped index of all rules, repaired README category links, and corrected stale Dart, Flutter, Riverpod, and test API references.
- Documented the shared
state_base_classesoption on every rule that supports it, and corrected the example inventory so each rule page, example and quick-fix badge matches the plugin registry.
0.9.0 - 2026-08-08 #
Added #
- Per-rule configuration, read from a
many_lints.yamlfile at the package root and falling back to a top-levelmany_lints:section inanalysis_options.yaml. The analyzer cannot carry per-rule configuration for a plugin —RuleConfigexposes only name, group and severity, and custom keys underplugins:are reported as unsupported options — so configuration lives in its own file. When both sources exist the dedicated file wins outright rather than merging, so a pattern always has one traceable origin. - An
excludekey on every one of the 133 rules, taking a list of glob patterns relative to the package root. Exclusion is per rule: silencing a noisy rule in generated or legacy code says nothing about the other 132, so a path can be skipped without weakening the rest of the suite. avoid_only_rethrowgains anignore_typed_catchesoption, which stops it reporting a catch clause that narrows the caught type (on FooException catch (e) { rethrow; }).
0.8.0 - 2026-08-07 #
Added #
avoid_duplicate_collection_elementsnow has a quick fix that removes the duplicate, keeping the first occurrence. It covers all three reported shapes: plain values, spreads andifelements.avoid_shrink_wrap_in_listsnow has a quick fix that removes theshrinkWrap: trueargument. The parameter defaults tofalse, so this is behaviour-preserving; a list that was shrink-wrapped because it nests inside another scrollable still needs the documentedCustomScrollViewrestructuring, which the fix deliberately does not attempt.- Quick fixes for the two widened rules that already had one now cover the newly reported shapes.
prefer_add_allcollapses a run of consecutiveaddcalls intoaddAll([...])(it previously only rewrote loops, so the new shape was reported with no fix available), andavoid_unnecessary_negationsrewrites!trueand!a == !b. test/plugin_fix_output_test.dartasserts the text a quick fix actually produces, by drivingedit.getFixesthrough thePluginServerharness and applying the returned edit.analyzer_testingstill has no fix test API, so fix output previously went unverified.
Fixed #
- 26 quick fixes never fired at all. They were registered and offered by name, but the IDE only ever showed the "Ignore …" suppression actions — selecting the fix did nothing. Each one type-tested the correction producer's
nodedirectly (if (node is! ConstructorName) return;,node.parent, …), which stopped matching after the analyzer 13 AST refactor:nodeCoveringresolves the diagnostic range to the deepest node, so an unnamedFoo(...)yieldsNamedTyperather thanConstructorName, and areportAtTokenon a class name yields a name-part wrapper rather than theClassDeclaration. Every affected fix now walks up withthisOrAncestorOfTypeinstead. Affected:avoid_incomplete_copy_with,avoid_incorrect_image_opacity,avoid_unnecessary_consumer_widgets,avoid_unnecessary_gesture_detector,avoid_unnecessary_overrides,avoid_unnecessary_setstate,avoid_unnecessary_stateful_widgets,avoid_wrapping_in_padding,list_all_equatable_fields,prefer_abstract_final_static_class,prefer_align_over_container,prefer_center_over_align,prefer_constrained_box_over_container,prefer_container,prefer_multi_bloc_provider,prefer_overriding_parent_equality,prefer_padding_over_container,prefer_returning_shorthands,prefer_sized_box_square,prefer_switch_expression,prefer_text_rich,prefer_transform_over_container,prefer_type_over_var,use_closest_build_context,use_gap,use_sliver_prefix. - The three suffix fixes (
use_bloc_suffix,use_cubit_suffix,use_notifier_suffix) had the same problem, and additionally renamed only the class — leavingclass FooBloc { Foo(); }, which does not compile. They now rename any same-named constructor along with the class. avoid_generics_shadowingrenamed the type parameter's declaration but left its usages behind (void process<T>(Config c) {}— not compilable). It located the declaring scope with a fixedparent.parenthop, which stopped reaching class and top-level-function declarations once analyzer 13 added intermediate nodes; it now walks up.avoid_incomplete_copy_withemitteddynamicfor any parameter declared as a field formal (required this.name), since those carry no type annotation. It now falls back to the resolved element type, producingString? surname.prefer_switch_expressionhandled only pre-Dart-3SwitchCasemembers. Dart 3 parsescase Status.active:as aSwitchPatternCase, so the fix bailed on essentially every modern switch it was offered for.prefer_expect_lateremitted two edits starting at the same offset (replacingexpectand insertingawaitbefore it). Overlapping edits raiseConflictingEditException, whichFixProcessorcatches and logs — silently discarding the entire fix. It now emits a single edit.use_closest_build_contextignored untyped closure parameters ((_) { … }), because it read only the type annotation while the rule itself falls back to the resolved element type. The two now agree, so the fix can act on every case the rule reports.avoid_commented_out_codeno longer merges unrelated comments into one block. Comments were grouped by character distance (any gap under 150 characters), which reached across blank lines, closing braces, and whole class boundaries — the check for a blank line compared the same distance as the grouping condition, so it could never fire. A block of commented-out code followed by ordinary prose comments elsewhere in the file merged into a single group whose code-line ratio fell below the threshold, silencing the diagnostic for the entire file. Comments are now grouped only when they sit on directly consecutive lines with nothing but whitespace between them, and a comment trailing code (foo(); // note) starts its own group. This makes the rule report in files where it previously found nothing.
Changed #
Four rules were widened so that a rule name means here what it conventionally means. Each now covers everything it did before plus the shapes commonly reported elsewhere, so existing projects will see new diagnostics on code that previously passed.
prefer_add_allnow also reports consecutiveaddcalls on the same collection (values.add(a); values.add(b);), not only an add-onlyfor-inloop. A run is broken by any other statement, and the receiver must be a stable expression of a collection type, soaddon an unrelated class is left alone.avoid_duplicate_collection_elementsnow reports repeated spreads ([...items, ...items]) and repeatedifelements, and no longer stops analysing a literal at the first spread orifelement —[1, ...base, 1]was previously missed. Spreads inside set and map literals are checked too; plain values there remain out of scope, since the analyzer reports those natively.avoid_unnecessary_negationsnow reports a negated boolean literal (!true) and negations on both sides of a comparison (!a == !b,!a != !b). A single negation in a comparison is still left alone, since removing it would change the result.prefer_switch_with_enumsnow counts comparisons joined by||toward its threshold, and reports a membership test over a literal collection of enum constants ({E.a, E.b, E.c}.contains(value)). A named collection is not reported — it is a reusable set rather than an inlined branch.
avoid_misused_hooks and avoid_shrink_wrap_in_lists were reviewed and left unchanged: hooks called outside a hook context are already covered by avoid_hooks_outside_build, and avoid_shrink_wrap_in_lists already reports every shrinkWrap: true rather than only nested ones.
Documentation #
- Example files and rule pages for the four widened rules now show the added shapes.
- Rewrote the code examples for 17 rules that had drifted into reproducing third-party documentation verbatim (shared class names, method names, and literal values). Behaviour is unchanged.
use_notifier_suffixno longer claims to coverAsyncNotifier; it checksNotifieronly, and the two are unrelated hierarchies in Riverpod.prefer_shorthands_with_constructorsdocuments that it does not resolve the destination parameter's declared type, so adynamicparameter is still reported.- Fixed a type error in the
prefer_use_prefixexample (useStatereturnsValueNotifier<T>, notT) and an undeclared class in theavoid_passing_bloc_to_blocexample. - Moved
prefer_compute_over_isolate_runout of the "Testing Rules" category; it is about web platform compatibility.
0.7.1 - 2026-08-03 #
Fixed #
avoid_unnecessary_stateful_widgetsno longer fires when a mixin applied to theStatecarries the state. A mixinon State<T>can hold the mutable fields, the lifecycle overrides or thesetStatecalls on behalf of the class that applies it, which left theStatebody looking empty while the widget was genuinely stateful. Computed getters on such a mixin still do not count as state.avoid_unnecessary_consumer_widgetsnow reportsConsumerStatefulWidget. The rule matched it but then looked for abuildwith arefparameter on the widget itself, which aConsumerStatefulWidgetnever has — itsrefis a getter on the companionConsumerState— so that half of the rule never reported anything. The widget is now correlated with its state class, and anyrefuse in that class counts, not only one insidebuild.avoid_unnecessary_consumer_widgetsno longer fires when a mixin uses therefon the class's behalf. A mixinon ConsumerState<T>is a normal way to share provider access, and it left the state body looking ref-free while the widget genuinely needed the container. A mixin carrying norefuse still does not suppress the diagnostic.
Documentation #
- Documented that suppression comments for plugin lints require the plugin-name prefix (
// ignore: many_lints/<rule>). A bare// ignore: <rule>has no effect, which is easy to mistake for the rule ignoring suppression altogether. Type-based suppression needs thetype=form (// ignore: type=lint).
0.7.0 - 2026-07-15 #
Added #
prefer_private_named_parametersrule with quick fix — suggests Dart 3.12 private named parameters (this._field) over_field = fieldinitializer-list boilerplate; only active in libraries with language version 3.12+prefer_theme_mode_gettersrule with quick fix — suggests theThemeMode.isDark/isLight/isSystemgetters (Flutter 3.44+) over==/!=comparisons againstThemeModeconstants; only active when the getters exist in the project's Flutter version
0.6.0 - 2026-07-15 #
Changed #
- Bump
analyzerconstraint to^14.1.0(support for the latest Dart/Flutter SDKs) - Bump
analyzer_pluginconstraint to^0.14.14 - Bump
analysis_server_pluginconstraint to^0.3.20 - Bump
analyzer_testingconstraint to^0.3.4
0.5.0 - 2026-07-15 #
Changed #
- Bump
analyzerconstraint to^13.3.0(support for the latest Dart/Flutter SDKs) - Bump
analyzer_pluginconstraint to^0.14.12 - Bump
analysis_server_pluginconstraint to^0.3.18 - Bump
analyzer_testingconstraint to^0.3.2 - Migrate all rules and quick fixes to the analyzer 13 AST API (
NamedArgument,Argument,RegularFormalParameter)
0.4.4 - 2026-06-18 #
Fixed #
prefer_shorthands_with_enumsnow infers enum shorthand context correctly for named arguments.
0.4.3 - 2026-05-08 #
Fixed #
- Diagnostics configuration now works correctly when
many_lintsis loaded through the legacy plugin server constructor.
0.4.2 - 2026-05-08 #
Fixed #
- Respect
diagnosticsconfiguration so individualmany_lintsrules can be disabled fromanalysis_options.yaml.
0.4.1 - 2026-05-07 #
Changed #
- Bump
analyzerconstraint to^12.1.0 - Bump
analyzer_pluginconstraint to^0.14.8 - Bump
analysis_server_pluginconstraint to^0.3.14 - Bump
analyzer_testingconstraint to^0.2.5 - Bump
testconstraint to^1.31.1 - Drop the
analyzerdependency_overridesblock (no longer needed oncetest 1.31.1lifted its analyzer upper bound)
0.4.0 - 2026-02-20 #
Added #
Dart & Code Quality Rules
avoid_constant_conditionsrule to warn when both sides of a comparison are constantsavoid_constant_switchesrule to warn when a switch expression is a constantavoid_contradictory_expressionsrule to detect contradictory comparisons in&&chainsavoid_duplicate_cascadesrule to detect duplicate cascade sections with quick fixavoid_generics_shadowingrule to warn when a generic type parameter shadows a top-level declaration with quick fixavoid_incomplete_copy_withrule to detectcopyWithmethods missing constructor parameters with quick fixavoid_map_keys_containsrule to prefercontainsKey()over.keys.contains()with quick fixavoid_misused_test_matchersrule to detect incompatible matcher usageavoid_only_rethrowrule to flag catch clauses that only rethrow with quick fixavoid_single_field_destructuringrule to avoid single-field destructuring with quick fixavoid_throw_in_catch_blockrule to avoidthrowinside catch blocks with quick fixavoid_unassigned_stream_subscriptionsrule to detect unassigned stream subscriptionslist_all_equatable_fieldsrule to detect Equatable subclasses with missing fields inpropswith quick fixprefer_class_destructuringrule to suggest class destructuring for repeated property accesses with quick fixprefer_containsrule to prefer.contains()over.indexOf()compared to-1with quick fixprefer_enums_by_namerule to prefer.byName()over.firstWhere()with quick fixprefer_equatable_mixinrule to preferEquatableMixinover extendingEquatablewith quick fixprefer_expect_laterrule to preferexpectLaterwhen testing Futures with quick fixprefer_overriding_parent_equalityrule to detect missing==/hashCodeoverrides with quick fixprefer_return_awaitrule to detect missingawaitintry-catchwith quick fixprefer_simpler_patterns_null_checkrule to prefer simpler null-check patterns in if-case expressions with quick fixprefer_single_widget_per_filerule to enforce one public widget per fileprefer_test_matchersrule to prefer matchers over literals inexpect()prefer_wildcard_patternrule to prefer_overObject()with quick fixproper_super_callsrule to enforce correct super call placement with quick fixuse_closest_build_contextrule to use the closest availableBuildContextwith quick fixuse_existing_destructuringrule to use existing destructuring instead of direct access with quick fixuse_existing_variablerule to detect duplicate initializer expressions with quick fix
Flutter Widget Rules
always_remove_listenerrule to detect listeners not removed indispose()with quick fixavoid_border_allrule to preferBorder.fromBorderSideoverBorder.allwith quick fixavoid_conditional_hooksrule to detect hooks called inside conditionals or loopsavoid_expanded_as_spacerrule to preferSpaceroverExpandedwith empty child with quick fixavoid_flexible_outside_flexrule to flagFlexible/ExpandedoutsideRow/Column/Flexavoid_incorrect_image_opacityrule to useImage'sopacityparameter with quick fixavoid_mounted_in_setstaterule to detectmountedcheck insidesetStateavoid_returning_widgetsrule to avoid returning widgets from functions/methodsavoid_shrink_wrap_in_listsrule to avoidshrinkWrapinListViewavoid_unnecessary_gesture_detectorrule to flagGestureDetectorwith no handlers with quick fixavoid_unnecessary_overridesrule to detect overrides that only callsuperwith quick fixavoid_unnecessary_overrides_in_staterule to detect State overrides that only callsuperwith quick fixavoid_unnecessary_setstaterule to detect unnecessarysetStatecalls with quick fixavoid_unnecessary_stateful_widgetsrule to detectStatefulWidgetwith no mutable state with quick fixavoid_wrapping_in_paddingrule to avoid wrapping inPaddingwhen widget has padding support with quick fixdispose_fieldsrule to detect undisposed fields with quick fixprefer_async_callbackrule to preferAsyncCallbackoverFuture<void> Function()with quick fixprefer_compute_over_isolate_runrule for web platform compatibility with quick fixprefer_const_border_radiusrule to preferBorderRadius.all(Radius.circular())with quick fixprefer_constrained_box_over_containerrule to preferConstrainedBoxoverContainerwith quick fixprefer_containerrule to merge nested widgets into a singleContainerwith quick fixprefer_correct_edge_insets_constructorrule to use simplerEdgeInsetsconstructors with quick fixprefer_for_loop_in_childrenrule to prefer for-loops over functional list building with quick fixprefer_single_setstaterule to merge multiplesetStatecalls with quick fixprefer_sized_box_squarerule to preferSizedBox.squarewith quick fixprefer_spacingrule to prefer thespacingargument overSizedBoxprefer_text_richrule to preferText.richoverRichTextwith quick fixprefer_transform_over_containerrule to preferTransformoverContainerwith quick fixprefer_use_callbackrule to preferuseCallbackover inline closures with quick fixprefer_use_prefixrule to preferuseprefix for custom hook functions with quick fixprefer_void_callbackrule to preferVoidCallbackovervoid Function()with quick fixuse_sliver_prefixrule to enforceSliverprefix for sliver-returning widgets with quick fix
BLoC Rules
avoid_bloc_public_methodsrule to avoid public members in Bloc classesavoid_passing_bloc_to_blocrule to avoid passing Bloc/Cubit to another Bloc/Cubitavoid_passing_build_context_to_blocsrule to avoid passingBuildContextto Bloc/Cubitprefer_bloc_extensionsrule to prefercontext.read/context.watchwith quick fixprefer_immutable_bloc_staterule to annotate Bloc state with@immutablewith quick fixprefer_multi_bloc_providerrule to preferMultiBlocProviderwith quick fix
Riverpod Rules
avoid_notifier_constructorsrule to avoid constructors with logic in Notifier classes with quick fixavoid_public_notifier_propertiesrule to avoid public non-overridden properties in Notifier classesavoid_ref_inside_state_disposerule to avoid accessingrefinsidedispose()avoid_ref_read_inside_buildrule to avoidref.readinsidebuildwith quick fixavoid_state_constructorsrule to avoid constructors with logic in State classes with quick fixdispose_provided_instancesrule to detect instances not disposed viaref.onDispose()with quick fixuse_ref_and_state_synchronouslyrule to detect async gaps beforeref/stateaccess with quick fixuse_ref_read_synchronouslyrule to detectref.readstored across async gaps with quick fix
Changed #
- Extracted shared utility
lib/src/constant_expression.dartfor constant expression checking - Updated README and example README to document all 100 rules and 78 quick fixes
0.3.0 - 2026-02-14 #
Added #
prefer_shorthands_with_enumsrule to detect enum values replaceable with shorthand constructorsprefer_shorthands_with_constructorsrule to detect constructors replaceable with shorthand syntaxprefer_shorthands_with_static_fieldsrule to detect static fields replaceable with shorthand syntaxprefer_returning_shorthandsrule to detect return statements replaceable with shorthand syntaxprefer_switch_expressionrule to suggest using switch expressions over switch statementsprefer_explicit_function_typerule to prefer explicit function types overFunctionprefer_type_over_varrule to prefer explicit type annotations overvarprefer_abstract_final_static_classrule to flag utility classes that should be abstract finalprefer_iterable_ofrule to preferIterable.ofoverIterable.fromfor same-type conversionsavoid_accessing_collections_by_constant_indexrule to flag hardcoded index access on collectionsavoid_cascade_after_if_nullrule to detect cascades after if-null operatorsavoid_collection_equality_checksrule to flag equality checks on collectionsavoid_collection_methods_with_unrelated_typesrule to flag collection method calls with unrelated typesavoid_commented_out_coderule to detect commented-out code blocks
Changed #
- Extracted shared utilities, renamed helpers, and refactored suffix rules
0.2.1 - 2026-02-05 #
0.2.0 - 2026-02-05 #
Added #
use_gaprule to preferGapwidget overSizedBoxorPaddingfor spacing- Quick fixes for suffix rules (
use_bloc_suffix,use_cubit_suffix,use_notifier_suffix) - Quick fix for
avoid_unnecessary_consumer_widgetsrule - Example project demonstrating all lint rules
- Dartdoc comments to public APIs
Changed #
- Renamed test methods to snake_case for consistency
- Applied recommended lints from
lintspackage
0.1.2 - 2026-02-04 #
Fixed #
- Add
lib/main.dartre-export for properanalysis_server_plugindiscovery - Replace deprecated analyzer API usages with new equivalents
0.1.0 - 2026-02-04 #
Added #
avoid_single_child_in_multi_child_widgets- detect single-child usage in multi-child widgetsavoid_unnecessary_consumer_widgets- flag unnecessary Riverpod consumer widgetsavoid_unnecessary_hook_widgets- flag unnecessary hook widgets with quick fixprefer_align_over_container- preferAlignoverContainerfor alignment onlyprefer_any_or_every- preferany/everyover manual iteration with quick fixprefer_center_over_align- preferCenteroverAlignfor centering with quick fixprefer_padding_over_container- preferPaddingoverContainerfor padding only with quick fixuse_bloc_suffix- enforceBlocsuffix on bloc classesuse_cubit_suffix- enforceCubitsuffix on cubit classesuse_dedicated_media_query_methods- prefer dedicatedMediaQuerymethods with quick fixuse_notifier_suffix- enforceNotifiersuffix on notifier classesconvert_iterable_map_to_collection_forassist