Effective Dart
Over the past several years, we’ve written a ton of Dart code and learned a lotabout what works well and what doesn’t. We’re sharing this with you so you canwrite consistent, robust, fast code too. There are two overarching themes:
- Be consistent. When it comes to things like formatting, and casing,arguments about which is better are subjective and impossible to resolve.What we do know is that being consistent is objectively helpful.
If two pieces of code look different it should be because they _are_different in some meaningful way. When a bit of code stands out and catchesyour eye, it should do so for a useful reason.
- Be brief. Dart was designed to be familiar, so it inherits many of thesame statements and expressions as C, Java, JavaScript and other languages.But we created Dart because there is a lot of room to improve on what thoselanguages offer. We added a bunch of features, from string interpolation toinitializing formals, to help you express your intent more simply andeasily.
If there are multiple ways to say something, you should generally pick themost concise one. This is not to say you should code golf yourself intocramming a whole program into a single line. The goal is code that iseconomical, not dense.
The Dart analyzer has a linter to help you write good, consistent code.If a linter rule exists that can help you follow a guideline,then the guideline links to that rule. Here’s an example:
Linter rule: prefer_collection_literals
For help onenabling linter rules,see the documentation forcustomizing static analysis.
The guides
We split the guidelines into a few separate pages for easy digestion:
Style Guide – This defines the rules for laying out andorganizing code, or at least the parts that dartfmt doesn’t handle foryou. The style guide also specifies how identifiers are formatted:
camelCase
,using_underscores
, etc.Documentation Guide – This tells you everything you need toknow about what goes inside comments. Both doc comments and regular,run-of-the-mill code comments.
Usage Guide – This teaches you how to make the best use oflanguage features to implement behavior. If it’s in a statement orexpression, it’s covered here.
Design Guide – This is the softest guide, but the onewith the widest scope. It covers what we’ve learned about designingconsistent, usable APIs for libraries. If it’s in a type signature ordeclaration, this goes over it.
For links to all the guidelines, see thesummary.
How to read the guides
Each guide is broken into a few sections. Sections contain a list of guidelines.Each guideline starts with one of these words:
DO guidelines describe practices that should always be followed. Therewill almost never be a valid reason to stray from them.
DON’T guidelines are the converse: things that are almost never a goodidea. Hopefully, we don’t have as many of these as other languages do becausewe have less historical baggage.
PREFER guidelines are practices that you should follow. However, theremay be circumstances where it makes sense to do otherwise. Just make sure youunderstand the full implications of ignoring the guideline when you do.
AVOID guidelines are the dual to “prefer”: stuff you shouldn’t do butwhere there may be good reasons to on rare occasions.
CONSIDER guidelines are practices that you might or might not want tofollow, depending on circumstances, precedents, and your own preference.
Some guidelines describe an exception where the rule does not apply. Whenlisted, the exceptions may not be exhaustive—you might still need to useyour judgement on other cases.
This sounds like the police are going to beat down your door if you don’t haveyour laces tied correctly. Things aren’t that bad. Most of the guidelines hereare common sense and we’re all reasonable people. The goal, as always, is nice,readable and maintainable code.
Glossary
To keep the guidelines brief, we use a few shorthand terms to refer to differentDart constructs.
A library member is a top-level field, getter, setter, or function.Basically, anything at the top level that isn’t a type.
A class member is a constructor, field, getter, setter, function, oroperator declared inside a class. Class members can be instance or static,abstract or concrete.
A member is either a library member or a class member.
A variable, when used generally, refers to top-level variables,parameters, and local variables. It doesn’t include static or instance fields.
A type is any named type declaration: a class, typedef, or enum.
A property is a top-level variable, getter (inside a class or at the toplevel, instance or static), setter (same), or field (instance or static).Roughly any “field-like” named construct.
Summary of all rules
Style
Identifiers
- DO name types using
UpperCamelCase
. - DO name extensions using
UpperCamelCase
. - DO name libraries, packages, directories, and source files using
lowercase_with_underscores
. - DO name import prefixes using
lowercase_with_underscores
. - DO name other identifiers using
lowerCamelCase
. - PREFER using
lowerCamelCase
for constant names. - DO capitalize acronyms and abbreviations longer than two letters like words.
- DON’T use a leading underscore for identifiers that aren’t private.
- DON’T use prefix letters.
Ordering
- DO place “dart:” imports before other imports.
- DO place “package:” imports before relative imports.
- PREFER placing external “package:” imports before other imports.
- DO specify exports in a separate section after all imports.
- DO sort sections alphabetically.
Formatting
- DO format your code using
dartfmt
. - CONSIDER changing your code to make it more formatter-friendly.
- AVOID lines longer than 80 characters.
- DO use curly braces for all flow control statements.
Documentation
Comments
Doc comments
- DO use
///
doc comments to document members and types. - PREFER writing doc comments for public APIs.
- CONSIDER writing a library-level doc comment.
- CONSIDER writing doc comments for private APIs.
- DO start doc comments with a single-sentence summary.
- DO separate the first sentence of a doc comment into its own paragraph.
- AVOID redundancy with the surrounding context.
- PREFER starting function or method comments with third-person verbs.
- PREFER starting variable, getter, or setter comments with noun phrases.
- PREFER starting library or type comments with noun phrases.
- CONSIDER including code samples in doc comments.
- DO use square brackets in doc comments to refer to in-scope identifiers.
- DO use prose to explain parameters, return values, and exceptions.
- DO put doc comments before metadata annotations.
Markdown
- AVOID using markdown excessively.
- AVOID using HTML for formatting.
- PREFER backtick fences for code blocks.
Writing
- PREFER brevity.
- AVOID abbreviations and acronyms unless they are obvious.
- PREFER using “this” instead of “the” to refer to a member’s instance.
Usage
Libraries
- DO use strings in
part of
directives. - DON’T import libraries that are inside the
src
directory of another package. - PREFER relative paths when importing libraries within your own package’s
lib
directory.
Booleans
Strings
- DO use adjacent strings to concatenate string literals.
- PREFER using interpolation to compose strings and values.
- AVOID using curly braces in interpolation when not needed.
Collections
- DO use collection literals when possible.
- DON’T use
.length
to see if a collection is empty. - CONSIDER using higher-order methods to transform a sequence.
- AVOID using
Iterable.forEach()
with a function literal. - DON’T use
List.from()
unless you intend to change the type of the result. - DO use
whereType()
to filter a collection by type. - DON’T use
cast()
when a nearby operation will do. - AVOID using
cast()
.
Functions
- DO use a function declaration to bind a function to a name.
- DON’T create a lambda when a tear-off will do.
Parameters
- DO use
=
to separate a named parameter from its default value. - DON’T use an explicit default value of
null
.
Variables
Members
- DON’T wrap a field in a getter and setter unnecessarily.
- PREFER using a
final
field to make a read-only property. - CONSIDER using
=>
for simple members. - DON’T use
this.
except to redirect to a named constructor or to avoid shadowing. - DO initialize fields at their declaration when possible.
Constructors
- DO use initializing formals when possible.
- DON’T type annotate initializing formals.
- DO use
;
instead of{}
for empty constructor bodies. - DON’T use
new
. - DON’T use
const
redundantly.
Error handling
- AVOID catches without
on
clauses. - DON’T discard errors from catches without
on
clauses. - DO throw objects that implement
Error
only for programmatic errors. - DON’T explicitly catch
Error
or types that implement it. - DO use
rethrow
to rethrow a caught exception.
Asynchrony
- PREFER async/await over using raw futures.
- DON’T use
async
when it has no useful effect. - CONSIDER using higher-order methods to transform a stream.
- AVOID using Completer directly.
- DO test for
Future<T>
when disambiguating aFutureOr<T>
whose type argument could beObject
.
Design
Names
- DO use terms consistently.
- AVOID abbreviations.
- PREFER putting the most descriptive noun last.
- CONSIDER making the code read like a sentence.
- PREFER a noun phrase for a non-boolean property or variable.
- PREFER a non-imperative verb phrase for a boolean property or variable.
- CONSIDER omitting the verb for a named boolean parameter.
- PREFER the “positive” name for a boolean property or variable.
- PREFER an imperative verb phrase for a function or method whose main purpose is a side effect.
- PREFER a noun phrase or non-imperative verb phrase for a function or method if returning a value is its primary purpose.
- CONSIDER an imperative verb phrase for a function or method if you want to draw attention to the work it performs.
- AVOID starting a method name with
get
. - PREFER naming a method
to_()
if it copies the object’s state to a new object. - PREFER naming a method
as_()
if it returns a different representation backed by the original object. - AVOID describing the parameters in the function’s or method’s name.
- DO follow existing mnemonic conventions when naming type parameters.
Libraries
Classes and mixins
- AVOID defining a one-member abstract class when a simple function will do.
- AVOID defining a class that contains only static members.
- AVOID extending a class that isn’t intended to be subclassed.
- DO document if your class supports being extended.
- AVOID implementing a class that isn’t intended to be an interface.
- DO document if your class supports being used as an interface.
- DO use
mixin
to define a mixin type. - AVOID mixing in a type that isn’t intended to be a mixin.
Constructors
Members
- PREFER making fields and top-level variables
final
. - DO use getters for operations that conceptually access properties.
- DO use setters for operations that conceptually change properties.
- DON’T define a setter without a corresponding getter.
- AVOID returning
null
from members whose return type isbool
,double
,int
, ornum
. - AVOID returning
this
from methods just to enable a fluent interface.
Types
- PREFER type annotating public fields and top-level variables if the type isn’t obvious.
- CONSIDER type annotating private fields and top-level variables if the type isn’t obvious.
- AVOID type annotating initialized local variables.
- AVOID annotating inferred parameter types on function expressions.
- AVOID redundant type arguments on generic invocations.
- DO annotate when Dart infers the wrong type.
- PREFER annotating with
dynamic
instead of letting inference fail. - PREFER signatures in function type annotations.
- DON’T specify a return type for a setter.
- DON’T use the legacy typedef syntax.
- PREFER inline function types over typedefs.
- CONSIDER using function type syntax for parameters.
- DO annotate with
Object
instead ofdynamic
to indicate any object is allowed. - DO use
Future<void>
as the return type of asynchronous members that do not produce values. - AVOID using
FutureOr<T>
as a return type.
Parameters
- AVOID positional boolean parameters.
- AVOID optional positional parameters if the user may want to omit earlier parameters.
- AVOID mandatory parameters that accept a special “no argument” value.
- DO use inclusive start and exclusive end parameters to accept a range.
Equality