Skip to content

Formatting rules

Bob Nystrom edited this page May 8, 2023 · 1 revision

The canonical source of truth for the logic dartfmt applies is in the code. The rules can be complex and interact in subtle ways that makes them difficult or just tedious to capture in prose. (Do you really want to know the three different ways a show clause may be line-split? No, you probably don't.)

But if you want an approximate list of the main high-level whitespace rules the formatter applies, these are them:

Spaces, not tabs.

Using spaces for formatting ensures the code looks the same in everyone's editor. It also makes sure it looks the same when posted to blogs, or on code sites like GitHub.

Modern editors emulate the behavior of tabs while inserting spaces, giving you the easy editing of tabs and the consistency of spaces.

One or two newlines after each statement or declaration.

main() {
  first(statement);
  second(statement);

  third(statement);
}

anotherDeclaration() { ... }

No space between the declared name of a method, operator, or setter and its parameter list.

log(arg) { ... }
bool operator ==(other) => ...
set contents(value) { ... }

Space after the operator keyword.

bool operator ==(other) => ...

Spaces around binary and ternary operators.

Note that < and > are considered binary operators when used as expressions, but not for specifying generic types. Both is and is! are considered single binary operators. However, the . used to access members is not and should not have spaces around it.

average = (a + b) / 2;
largest = a > b ? a : b;
if (obj is! SomeType) print('not SomeType');

Spaces after , and : when used in a map or named parameter.

function(a, b, named: c)
[some, list, literal]
{map: literal}

No spaces around unary operators.

!condition
index++

Spaces around in, and after each ; in a loop.

for (var i = 0; i < 100; i++) { ... }

for (final item in collection) { ... }

Space after flow-control keywords.

This is unlike function and method calls, which do not have a space between the name and the opening parenthesis.

while (foo) { ... }

try {
  // ...
} catch (e) {
  // ...
}

No space after (, [, and {, or before ), ], and }.

Also, do not use a space when using < and > for generic types.

var numbers = <int>[1, 2, (3 + 4)];

Space before { in function and method bodies.

When a { is used after a parameter list in a function or method, there should be a space between it and the ) ending the parameters.

getEmptyFn(a) {
  return () {};
}

Place the opening curly brace ({) on the same line as what it follows.

class Foo {
  method() {
    if (someCondition) {
      // ...
    } else {
      // ...
    }
  }
}

Place binary operators on the preceding line in a multi-line expression.

There are valid arguments for both styles but most of our code seems to go this way, and consistency matters most.

var bobLikesIt = isDeepFried ||
    (hasPieCrust && !vegan) ||
    containsBacon;

bobLikes() =>
    isDeepFried || (hasPieCrust && !vegan) || containsBacon;

Place ternary operators on the next line in a multi-line expression.

Also, if you break the line before one of the operators, break around both.

return someCondition
    ? whenTrue
    : whenFalse;

Place the . on the next line in a multi-line expression.

someVeryLongVariableName.withAVeryLongPropertyName
    .aReallyLongMethodName(args);

Format constructor initialization lists with each field on its own line.

MyClass()
    : firstField = 'some value',
      secondField = 'another',
      thirdField = 'last' {
  // ...
}

Note that the : should be wrapped to the next line and indented four spaces. Fields should all line up (so all but the first field end up indented six spaces).

Split every element in a collection if it does not fit on one line.

This means after the opening bracket, before the closing one, and after the , for each element.

mapInsideList([
  {
    'a': 'b',
    'c': 'd'
  },
  {
    'a': 'b',
    'c': 'd'
  },
]);

Indent block and collection bodies two spaces.

if (condition) {
  print('hi');
}

var compoundsWithLongNames = [
  buckminsterfullerene,
  dodecahedrane,
  olympiadane
];

Indent switch cases two spaces and case bodies four spaces.

switch (fruit) {
  case 'apple':
    print('delish');
    break;

  case 'durian':
    print('stinky');
    break;
}

Indent multi-line method cascades two spaces.

buffer
  ..write('Hello, ')
  ..write(name)
  ..write('!');

Indent continued lines with at least four spaces.

someVeryLongVariableName.aReallyLongMethodName(
    arg, anotherArg, wrappedToNextLine);

This includes => as well:

bobLikes() =>
    isDeepFried || (hasPieCrust && !vegan) || containsBacon;

There are exceptions to this when the expression contains multi-line function or collection literals.

new Future.delayed(const Duration(seconds: 1), () {
  print('I am a callback');
});

args.addAll([
  '--mode',
  'release',
  '--checked'
]);

Your goal is to balance using indentation to show expression structure while not wanting to indent large swathes of code unnecessarily.