Skip to content

prefer_named_parameters

v1.0.0WarningConfigurableCode Quality

This rule flags a declaration taking more than a few positional parameters.

schedule(3, 4, 5) tells the reader nothing, and swapping two arguments of the same type compiles cleanly and fails at runtime. Named parameters put the meaning at the call site, where it is read.

The threshold matters more than the principle. One or two positional parameters are usually the subject of the call — substring(0, 4), Point(x, y) — and naming them is noise, so the default budget is 2.

This rule is in the pedantic preset: the budget is a house style.

See also: Effective Dart: parameters

Four ints in a row, all interchangeable to the compiler:

class Reminder {
Reminder(this.hour, this.minute, this.repeatDays, this.snoozeMinutes);
final int hour;
final int minute;
final int repeatDays;
final int snoozeMinutes;
}
// Call site: Reminder(7, 30, 5, 9) — which one is which?
class Reminder {
Reminder({
required this.hour,
required this.minute,
required this.repeatDays,
required this.snoozeMinutes,
});
final int hour;
final int minute;
final int repeatDays;
final int snoozeMinutes;
}
// Call site: Reminder(hour: 7, minute: 30, repeatDays: 5, snoozeMinutes: 9)

One or two positional parameters stay positional — they are the subject of the call:

String slice(String value, int start) => value.substring(start);
analysis_options.yaml
many_lints:
rules:
prefer_named_parameters:
max_positional: 3

main, onRequest and middleware are exempt out of the box. Add your own framework entry points rather than turning the rule off — dart_frog passes a route’s path segments in order, so naming them is not the author’s to decide:

many_lints.yaml
rules:
prefer_named_parameters:
additional_ignored_names: [handleRequest, buildRoute]
// Not reported: `onRequest` is in the default ignore list.
void onRequest(Object context, String id, String revision, String locale) {}

Only positional parameters count. Optional positionals count too, so void f(int a, [int b, int c]) is 3.

@override is skipped. The signature belongs to the supertype.

Operators and setters are skipped. Their parameters cannot be named.

A private constructor is skipped by default. It is not an API: it is reached from one place in the same library, usually a factory assembling injected dependencies. Note that this keys on the constructor’s own name — Pipeline._(a, b, c) is exempt, but the unnamed constructor of a private class _Pipeline(a, b, c) is not:

class Pipeline {
// Not reported.
Pipeline._(this._storage, this._adapter, this._policy);
final Object _storage;
final Object _adapter;
final Object _policy;
}

Set ignore_private_constructors: false to include them.

Private constructors and framework entry points together accounted for 22 of 28 reports on a real codebase, which is why both are exempt by default.

analysis_options.yaml
many_lints:
rules:
prefer_named_parameters:
max_positional: 2
additional_ignored_names: [handleRequest]
ignore_private_constructors: true
Option Type Default Description
max_positional int 2 Most positional parameters a declaration may take
ignored_names list of strings [main, onRequest, middleware] Declarations whose signature a framework dictates
additional_ignored_names list of strings [] Names to add to that list
ignore_private_constructors bool true Skip Type._(...), which is not an API

To disable this rule:

many_lints.yaml
rules:
prefer_named_parameters: false

To keep the rule on but skip certain paths, use per-rule exclude.