skip to content

In Dart, how do required positional, optional positional [ ] and named { } parameters differ, and when do you choose named ones?

level: juniorimportance: must knowfreq 65%

answer

  1. positional first, then one optional group
  2. square brackets or braces, not both
  3. constant default or nullable type
  4. readable call sites, skip any option
  5. Flutter constructors are all named

basics

~20 s

Dart functions take required positional parameters first, then either optional positional ones in [ ] or named ones in { }, never both; optional ones need a constant default or a nullable type. Named parameters read clearly and can be skipped individually.

solid answer

~40 s

Required positional parameters are passed in order and cannot be omitted. After them a function may declare **either** optional positional parameters in `[ ]` **or** named parameters in `{ }`, but not both groups. Optional parameters need a compile-time-constant default or a nullable type whose implicit default is `null`; a named one can be marked `required` instead. I choose named parameters when there are several options or any booleans, because `formatPrice(99, decimals: 0, showSign: true)` explains itself and callers can skip any subset; optional positional only fits a short natural progression like `DateTime(year, [month, day])`. Since Dart 2.17 named arguments may appear anywhere in a call, and Flutter widget constructors rely on named parameters throughout.

code

dart · 19 lines
dart
String formatPrice(
  num amount, {
  String currency = 'EUR',
  int decimals = 2,
  bool showSign = false,
}) {
  final sign = showSign && amount > 0 ? '+' : '';
  return '$sign${amount.toStringAsFixed(decimals)} $currency';
}

String pad(String text, [int width = 10, String fill = ' ']) =>
    text.padLeft(width, fill);

void main() {
  print(formatPrice(1299.99)); // 1299.99 EUR
  print(formatPrice(1299.99, decimals: 0, showSign: true)); // +1300 EUR
  print(pad('42')); // '        42'
  print(pad('42', 5, '0')); // 00042
}

go deeper

for a junior

Recall the three kinds, the square-bracket and brace syntax, and that optional parameters need a constant default or a nullable type.

for a middle

Explain why a function has at most one optional group, how defaults and nullable types interact, and why Flutter constructors use named parameters.

for a senior

Choose parameter kinds for API readability and evolution: named for options and booleans, optional positional only for a natural progression.

for a principal

Set API conventions for shared packages, including named parameters for anything optional and the Dart 3.13 parameter-modifier rule during upgrades.

## Three kinds of parameters A Dart function declares its parameters in up to two groups: 1. **Required positional parameters** come first, in order: `String formatPrice(num amount)`. Every call must pass them, by position. 2. Then **either** a group of **optional positional** parameters in square brackets, `[String currency = 'EUR']`, **or** a group of **named** parameters in braces, `{int decimals = 2, bool showSign = false}`. A single function cannot have both groups. | | Declared as | Passed as | Optional? | |---|---|---|---| | Required positional | `f(int a)` | `f(1)` | no | | Optional positional | `f([int a = 0])` | `f()` or `f(1)` | yes | | Named | `f({int a = 0})` | `f()` or `f(a: 1)` | yes, unless marked `required` | ## Defaults and nullability An optional parameter, positional or named, needs a way to have a value when the caller leaves it out: - give it a **default value** with `=`; the default must be a **compile-time constant**; - or make its type **nullable**, in which case the implicit default is `null`: `[String? device]`, `{bool? bold}`; - a named parameter can instead be marked **`required`**, so the analyzer rejects calls that omit it. The rules around `required` are part of null safety and covered there. A non-nullable optional parameter with no default is a compile error, because `null` would be its only possible default. ## Why named parameters dominate Dart APIs Named arguments make call sites readable, especially for booleans and numbers whose meaning is not obvious from the value: ```dart formatPrice(1299.5, decimals: 0, showSign: true); // versus a positional mystery: formatPriceOld(1299.5, 'EUR', 0, true); ``` They also let callers skip any subset of options, which optional positional parameters cannot do without passing every earlier one. That is why Flutter widget constructors use named parameters almost exclusively, even for mandatory ones such as a `child`, marked `required`. Since Dart 2.17, **named arguments can appear anywhere in the argument list**, not only after the positional ones. That is what makes `repeat(times: 2, () { ... })` legal, with a trailing callback after a named argument. ## When optional positional still fits Optional positional parameters suit a short, natural progression where earlier arguments are passed more often than later ones. The SDK's own examples are `String.fromCharCodes(charCodes, [start = 0, end])` and the `DateTime(year, [month = 1, day = 1, ...])` constructor. If a caller might want to pass a later one while omitting an earlier one, switch to named. ## Parameter modifiers in Dart 3.13 Since Dart 3.13, `final` and `var` on an ordinary function, method, closure or in-body constructor parameter are a **compile-time error** (`extraneous_modifier`); those modifiers are reserved for primary constructors, where they declare fields. To keep parameters from being reassigned as a style rule, enable the `parameter_assignments` lint. ## Common mistakes - **Mixing both optional groups**: `f(int a, [int b = 0], {int c = 0})` is rejected; pick one. - **A non-constant default**: `{DateTime at = DateTime.now()}` does not compile. Use a nullable parameter and resolve it in the body. - **Booleans passed by position**: `formatPrice(9.99, 'EUR', 0, true)` compiles, but nobody can read it in review, and swapping two same-typed arguments goes unnoticed. - **Treating named as required**: a named parameter without `required` is optional, so a caller can silently omit it and get the default. - **Explicit `= null` defaults**: a nullable optional parameter already defaults to `null`; the `avoid_init_to_null` lint, in the `recommended` and `flutter` sets, flags the redundant form. ## What an interviewer listens for - The three kinds and the "positional first, then `[]` or `{}` but not both" rule. - Constant defaults and the nullable alternative. - A reason for choosing named parameters: readability and skipping. - Awareness that Flutter APIs are built on named parameters.

  • Why does void log(final String message) stop compiling when a package moves to Dart 3.13?
    Dart 3.13 reserves `final` and `var` on parameters for primary constructors, where they declare fields. On an ordinary function, method, closure or in-body constructor parameter they are now an `extraneous_modifier` compile error. Remove the modifier, or run `dart fix`; if the team wants non-reassignable parameters as a style rule, enable the `parameter_assignments` lint.
  • Can a single Dart function declare both optional positional and named parameters?
    No. After the required positional parameters, a function has at most one optional group: either `[ ]` positional or `{ }` named. If you need both kinds of flexibility, make the optional values named, which also keeps call sites readable.
  • In a Dart call, must named arguments come after positional ones?
    Not since Dart 2.17. Named arguments can appear anywhere in the argument list, so `repeat(times: 2, () { ... })` is valid and lets a trailing callback sit last for readability. The parameters are still declared positional-first in the function signature.

saying these in an interview costs you the question

  • Thinks a function can mix [ ] and { } optional parameters
  • Believes named parameters are always required
  • Uses a non-constant expression as a default value
  • Declares a non-nullable optional parameter without a default
  • Says named arguments must come last in a call