-
Notifications
You must be signed in to change notification settings - Fork 125
Configuration
The formatter supports very few tweakable settings, by design.
For the most part, you should simply run the formatter and let it do what it does, so that you can focus on what your code does. The formatter has reasonable defaults which are used for all Dart code inside Google, the Dart SDK and the entire Flutter project. If the defaults are good enough for those millions of lines of code, they are probably good enough for your.
That being said, the behavior of the formatter can be configured slightly in a few ways.
When you run dart format
, it looks for an analysis_options.yaml
file in the directory where each formatted file is found. If not found there, it walks parent directories until it finds one. This way, you can configure an entire collection of files with a single options file. Typically, you have an analysis_options.yaml
file in the root directory of your pub package and it applies to everything in that package.
You can add a formatter
section to this file to configure two settings:
Line wrapping is one of the most important and most challenging things the formatter does. By default, it tries to keep every line of code 80 characters long or shorter. We've found this to be a good width that uses screen space well, plays nice with side-by-side diff tools, and avoids long and difficult to read lines.
However, some users strongly prefer different page widths. You can change this by adding a page_width
key to the options:
formatter:
page_width: 123
Before Dart 3.7, the older "short" style treated a trailing comma in an argument list specially. If you added one, it had two effects:
-
It opted in to a different formatting style:
function( without, trailing, comma); function( having, trailing, comma, );
-
It forced the argument list to split even if it otherwise would fit on one line.
In Dart 3.7, the new "tall" style eliminates this distinction. You always get the second formatting style. The new style also eliminates the behavior where a trailing comma forces the surrounding construct to split. This is a deliberate choice because it ensures that formatting is reversible. Since the tall style formatter will add a trailing comma when it splits a construct, it must be able to remove them too. Otherwise, it will inadvertently leaves detritus from its own previous runs in the code.
However, some users are very attached to the old behavior where a trailing comma let them decide on a bespoke basis which constructs to force to split. In order to continue to provide that, users can opt in to preserving trailing commas like so:
formatter:
trailing_commas: preserve
Note that this does not fully restore the old short formatting style. It only means that a trailing comma in an argument list, parameter list, or collection literal will force the surrounding construct to split.
Within formatted code, there are a couple of special marker comments you can use to control formatting.
You can configure the page within a file using a comment like:
// dart format width=123
This comment must appear before any code in the file and must match that format exactly. The width applies to the entire file and overrides the width set by any surrounding analysis_options.yaml
file.
This feature is mainly for code generators that generate and immediately format code but don't know about any surrounding analysis_options.yaml
that might be configuring the page width. By inserting this comment in the generated code before formatting, it ensures that the code generator's behavior matches the behavior of dart format.
End users should mostly use analysis_options.yaml
for configuring their preferred page width, or do nothing and use the default page width of 80.
You can opt a region of code out of automated formatting by surrounding it in a pair of special marker comments:
main() {
this.isFormatted();
// dart format off
no + formatting
+
here;
// dart format on
formatting.isBackOnHere();
}
The comments must be exactly // dart format off
and // dart format on
. A file may have multiple regions, but they can't overlap or nest. If you want to opt the rest of a file out, you can use a single // dart format off
comment without a closing // dart format on
at the end.
Disabling formatting can be useful for highly structured data where a custom layout may help a reader see that structure. But, in general, we recommend you use this feature sparingly. When you opt code out of automated formatting, you are opting back in to all of the headaches that manual formatting entails: arguing with teammates about style, needing to manually reformat it after applying automated refactoring, reduced readability for users who expect a different style, etc.
Note that these marker comments only work in Dart files at language version 3.7 or later.
Dart supports versioning at the language level. This lets us evolve the language in ways that would otherwise be breaking without breaking users.
The formatter also uses language versioning in order to make formatting style changes less disruptive. When formatting a file, the formatter uses the language version of the file to determine which style is applied.
This means that when you change the SDK constraint of your package to update to a new language version, you may also be opting in to new style changes. Conversely, it means that if you don't change the language version of your code, then the formatter should format it the same way, even if you update to a newer Dart or Flutter SDK.
The main way that language version affects the style is that Dart code before 3.7 is formatted using the older "short" style and Dart code at 3.7 or later gets the newer "tall" style.
For over a decade, the formatter supported one single opinionated style. That style was older than Flutter, older than many Dart language features, and older than almost all Dart code in the world today. The way users write Dart code has changed significantly in those years. In particular, Flutter uses Dart as sort of a UI markup language, leading to large deeply nested declarative expressions. The formatter's original style optimized for the kind of imperative code that was more common in Dart's early history, but doesn't scale as well to deep tree-like expressions.
When support for trailing commas was later added to Dart, it wasn't clear how they should be formatted. Up to that point, the formatter always placed the closing )
of an argument list right after the last argument. It's silly to do that if there's a trailing comma stuck in there, so the hacky compromise we added at the time was that if you write a trailing comma, you get a different formatting style:
// Without trailing comma:
someFunction(longArgument,
anotherArgument);
// With trailing comma:
someFunction(
longArgument,
anotherArgument,
);
This hack undermined the formatter's main goal of getting users out of the business of making tiny meaningless formatting choices. But it did let us observe which style users preferred in the wild. Over time (and with surveys and analysis of huge corpora of Dart to back it up), it became clear that most users prefer the latter style.
So we rewrote the formatter internally to support two styles, the original "short" style, and a new "tall" style that always formats like the latter argument list and adds and removes trailing commas on your behalf.
As of 2025, we are in the process of migrating the ecosystem over to that new style. We use the Dart language version of the code being formatted to determine which style you get. Code at Dart 3.6 or earlier continues to be formatted using the old style so that users don't see their code formatting spontaneously change under them. Code at Dart 3.7 or later gets the new style. When you update your pubspec's SDK constraint to >=3.7 or later, you are also opting in to the new style.
The goal is not to support both styles indefinitely. We still want a single ecosystem with a single consistent style. We are supporting both for some amount of time to ease the transition and let users migrate when it's a good time for them to do so. Tying the style to language version means that eventually when support for older language versions is dropped, we can also remove support for that older style.
Users have asked for more configurability as long as the formatter has existed. The answer is generally "no". There are three main reasons for this:
First, a key goal of the formatter is to reduce the amount of time engineers spending thinking about, arguing about, implementing, and applying formatting choices. We want to free up brainpower so that you can think about what your code does instead of worrying about how it looks.
If the formatter is highly configurable, then you'll spend time deciding what configuration you want. That's time not spent writing your app and providing value to your users. If the style can be configured, then every team has to sit down and argue amongst themselves about what their team's house style will be. When new people join from other teams, they may carry their style preferences with them and cause conflict on the team.
Anyone who has worked on multiple large C++ teams has likely experienced this perennial arguing and strife.
Like every programming language, Dart's success rests on having a thriving ecosystem of open source code that application developers can build on top of. If every package in the ecosystem has its own coding style, then it's harder for users to read that code and contibute to it. These little bits of friction can add up and lead to users not fixing bugs or otherwise helping the ecosystem grow.
When every package is not just formatted but formatted the same way, then all Dart code in the world becomes easier to read. It all looks familiar and is easier for users to contribute to. A non-configurable formatter helps us have one ecosystem instead of a thousand little ones.
The Dart team supports a large number of complex tools with a relatively small headcount. Because we are open source and users don't directly pay for the Dart tools, we have to be efficient with our time.
The Dart language grammar is very large and every style rule must be thoroughly tested. The formatter is run by millions of users thousands of times a day. It is often invoked immediately before their code is saved to disk. If the formatter has bugs that discard or break code, it can be highly disruptive.
Because of this, we have very thorough tests, over 8,000 as of this writing and always increasing. Many style rules are contextual, which means they can't easily be tested in isolation and we often have to test not just every language construct, but every construct in every context where it can appear.
Every configurable style rule multiplies the volume of the manifold that needs to be tested. Every style configuration increases the cost to support new language syntax. We simply don't have the resources to maintain a fully configurable collection of style rules to the level of reliability that our users expect.