Effective Dart: Style

A surprisingly important part of good code is good style. Consistent naming,ordering, and formatting helps code that is the same look the same. It takesadvantage of the powerful pattern-matching hardware most of us have in ourocular systems. If we use a consistent style across the entire Dart ecosystem,it makes it easier for all of us to learn from and contribute to each others’code.

Identifiers

Identifiers come in three flavors in Dart.

  • UpperCamelCase names capitalize the first letter of each word, includingthe first.

  • lowerCamelCase names capitalize the first letter of each word, _except_the first which is always lowercase, even if it’s an acronym.

  • lowercasewith_underscores use only lowercase letters, even for acronyms,and separate words with .

DO name types using UpperCamelCase.

Linter rule: camel_case_types

Classes, enums, typedefs, and type parameters should capitalize the first letterof each word (including the first word), and use no separators.

  1. class SliderMenu { ... }
  2.  
  3. class HttpRequest { ... }
  4.  
  5. typedef Predicate<T> = bool Function(T value);

This even includes classes intended to be used in metadata annotations.

  1. class Foo {
  2. const Foo([arg]);
  3. }
  4.  
  5. @Foo(anArg)
  6. class A { ... }
  7.  
  8. @Foo()
  9. class B { ... }

If the annotation class’s constructor takes no parameters, you might want tocreate a separate lowerCamelCase constant for it.

  1. const foo = Foo();
  2.  
  3. @foo
  4. class C { ... }

DO name extensions using UpperCamelCase.

Like types, extensions should capitalize the first letter of each word(including the first word), and use no separators.

  1. extension MyFancyList<T> on List<T> { ... }
  2.  
  3. extension SmartIterable<T> on Iterable<T> { ... }

Version note: Extensions are a Dart 2.7 language feature. For details, see the extensions design document.

DO name libraries, packages, directories, and source files using lowercase_with_underscores.

Linter rules: library_names,file_namesSome file systems are not case-sensitive, so many projects require filenames tobe all lowercase. Using a separating character allows names to still be readablein that form. Using underscores as the separator ensures that the name is stilla valid Dart identifier, which may be helpful if the language later supportssymbolic imports.

  1. library peg_parser.source_scanner;
  2.  
  3. import 'file_system.dart';
  4. import 'slider_menu.dart';
  1. library pegparser.SourceScanner;
  2.  
  3. import 'file-system.dart';
  4. import 'SliderMenu.dart';

Note: This guideline specifies how to name a library if you choose to name it. It is fine to omit the library directive in a file if you want.

DO name import prefixes using lowercase_with_underscores.

Linter rule: library_prefixes

  1. import 'dart:math' as math;
  2. import 'package:angular_components/angular_components'
  3. as angular_components;
  4. import 'package:js/js.dart' as js;
  1. import 'dart:math' as Math;
  2. import 'package:angular_components/angular_components'
  3. as angularComponents;
  4. import 'package:js/js.dart' as JS;

DO name other identifiers using lowerCamelCase.

Linter rule: non_constant_identifier_names

Class members, top-level definitions, variables, parameters, and namedparameters should capitalize the first letter of each word except the firstword, and use no separators.

  1. var item;
  2.  
  3. HttpRequest httpRequest;
  4.  
  5. void align(bool clearItems) {
  6. // ...
  7. }

PREFER using lowerCamelCase for constant names.

Linter rule: constant_identifier_names

In new code, use lowerCamelCase for constant variables, including enum values.

  1. const pi = 3.14;
  2. const defaultTimeout = 1000;
  3. final urlScheme = RegExp('^([a-z]+):');
  4.  
  5. class Dice {
  6. static final numberGenerator = Random();
  7. }
  1. const PI = 3.14;
  2. const DefaultTimeout = 1000;
  3. final URL_SCHEME = RegExp('^([a-z]+):');
  4.  
  5. class Dice {
  6. static final NUMBER_GENERATOR = Random();
  7. }

You may use SCREAMING_CAPS for consistency with existing code,as in the following cases:

  • When adding code to a file or library that already uses SCREAMING_CAPS.
  • When generating Dart code that’s parallel to Java code —for example, in enumerated types generated from protobufs.

Note: We initially used Java’s SCREAMING_CAPS style for constants. We changed for a few reasons:

  • SCREAMING_CAPS looks bad for many cases, particularly enum values forthings like CSS colors.
  • Constants are often changed to final non-const variables, which wouldnecessitate a name change.
  • The values property automatically defined on an enum type is const andlowercase.

DO capitalize acronyms and abbreviations longer than two letters like words.

Capitalized acronyms can be hard to read, andmultiple adjacent acronyms can lead to ambiguous names.For example, given a name that starts with HTTPSFTP, there’s no wayto tell if it’s referring to HTTPS FTP or HTTP SFTP.

To avoid this, acronyms and abbreviations are capitalized like regular words,except for two-letter acronyms. (Two-letter abbreviations likeID and Mr. are still capitalized like words.)

  1. HttpConnectionInfo
  2. uiHandler
  3. IOStream
  4. HttpRequest
  5. Id
  6. DB
  1. HTTPConnection
  2. UiHandler
  3. IoStream
  4. HTTPRequest
  5. ID
  6. Db

DON’T use a leading underscore for identifiers that aren’t private.

Dart uses a leading underscore in an identifier to mark members and top-leveldeclarations as private. This trains users to associate a leading underscorewith one of those kinds of declarations. They see “_” and think “private”.

There is no concept of “private” for local variables, parameters, or libraryprefixes. When one of those has a name that starts with an underscore, it sendsa confusing signal to the reader. To avoid that, don’t use leading underscoresin those names.

Exception: An unused parameter can be named , , , etc. Thishappens in things like callbacks where you are passed a value but you don’t needto use it. Giving it a name that consists solely of underscores is theidiomatic way to indicate the value isn’t used.

DON’T use prefix letters.

Hungarian notation andother schemes arose in the time of BCPL, when the compiler didn’t do much tohelp you understand your code. Because Dart can tell you the type, scope,mutability, and other properties of your declarations, there’s no reason toencode those properties in identifier names.

  1. defaultTimeout
  1. kDefaultTimeout

Ordering

To keep the preamble of your file tidy, we have a prescribed order thatdirectives should appear in. Each “section” should be separated by a blank line.

A single linter rule handles all the ordering guidelines:directives_ordering.

DO place “dart:” imports before other imports.

Linter rule: directives_ordering

  1. import 'dart:async';
  2. import 'dart:html';
  3.  
  4. import 'package:bar/bar.dart';
  5. import 'package:foo/foo.dart';

DO place “package:” imports before relative imports.

Linter rule: directives_ordering

  1. import 'package:bar/bar.dart';
  2. import 'package:foo/foo.dart';
  3.  
  4. import 'util.dart';

PREFER placing external “package:” imports before other imports.

Linter rule: directives_ordering

If you have a number of “package:” imports for your own package along with otherexternal packages, place yours in a separate section after the external ones.

  1. import 'package:bar/bar.dart';
  2. import 'package:foo/foo.dart';
  3.  
  4. import 'package:my_package/util.dart';

DO specify exports in a separate section after all imports.

Linter rule: directives_ordering

  1. import 'src/error.dart';
  2. import 'src/foo_bar.dart';
  3.  
  4. export 'src/error.dart';
  1. import 'src/error.dart';
  2. export 'src/error.dart';
  3. import 'src/foo_bar.dart';

DO sort sections alphabetically.

Linter rule: directives_ordering

  1. import 'package:bar/bar.dart';
  2. import 'package:foo/foo.dart';
  3.  
  4. import 'foo.dart';
  5. import 'foo/foo.dart';
  1. import 'package:foo/foo.dart';
  2. import 'package:bar/bar.dart';
  3.  
  4. import 'foo/foo.dart';
  5. import 'foo.dart';

Formatting

Like many languages, Dart ignores whitespace. However, humans don’t. Having aconsistent whitespace style helps ensure that human readers see code the sameway the compiler does.

DO format your code using dartfmt.

Formatting is tedious work and is particularly time-consuming duringrefactoring. Fortunately, you don’t have to worry about it. We provide asophisticated automated code formatter called dartfmt that does it foryou. We have some documentation on the rules it applies, but theofficial whitespace-handling rules for Dart are whatever dartfmt produces.

The remaining formatting guidelines are for the few things dartfmt cannot fixfor you.

CONSIDER changing your code to make it more formatter-friendly.

The formatter does the best it can with whatever code you throw at it, but itcan’t work miracles. If your code has particularly long identifiers, deeplynested expressions, a mixture of different kinds of operators, etc. theformatted output may still be hard to read.

When that happens, reorganize or simplify your code. Consider shortening a localvariable name or hoisting out an expression into a new local variable. In otherwords, make the same kinds of modifications that you’d make if you wereformatting the code by hand and trying to make it more readable. Think ofdartfmt as a partnership where you work together, sometimes iteratively, toproduce beautiful code.

AVOID lines longer than 80 characters.

Linter rule: lines_longer_than_80_chars

Readability studies show that long lines of text are harder to read because youreye has to travel farther when moving to the beginning of the next line. This iswhy newspapers and magazines use multiple columns of text.

If you really find yourself wanting lines longer than 80 characters, ourexperience is that your code is likely too verbose and could be a little morecompact. The main offender is usually VeryLongCamelCaseClassNames. Askyourself, “Does each word in that type name tell me something critical orprevent a name collision?” If not, consider omitting it.

Note that dartfmt does 99% of this for you, but the last 1% is you. It does notsplit long string literals to fit in 80 columns, so you have to do thatmanually.

Exception: When a URI or file path occurs in a comment or string (usually inan import or export), it may remain whole even if it causes the line to go over80 characters. This makes it easier to search source files for a path.

Exception: Multi-line strings can contain lines longer than 80 charactersbecause newlines are significant inside the string and splitting the lines intoshorter ones can alter the program.

DO use curly braces for all flow control statements.

Linter rule: curly_braces_in_flow_control_structures

Doing so avoids the dangling else problem.

  1. if (isWeekDay) {
  2. print('Bike to work!');
  3. } else {
  4. print('Go dancing or read a book!');
  5. }

Exception: When you have an if statement with no else clause and thewhole if statement fits on one line, you can omit the braces if you prefer:

  1. if (arg == null) return defaultValue;

If the body wraps to the next line, though, use braces:

  1. if (overflowChars != other.overflowChars) {
  2. return overflowChars < other.overflowChars;
  3. }
  1. if (overflowChars != other.overflowChars)
  2. return overflowChars < other.overflowChars;