blob: 30af4ccb2f61de53919f030162e4056f8d070c74 [file] [log] [blame]
:toc: macro
:toc-title:
:toclevels: 99
⚠️✏️ Changes to the style guide should be announced to the team in the https://discord.com/channels/608014603317936148/608116355836805126[#announcements] channel on https://github.com/flutter/flutter/wiki/Chat[Discord].
Summary
-------
Optimize for readability. Write detailed documentation.
Make error messages useful.
Never use timeouts or timers.
Avoid `is`, `print`, `part of`, `extension` and `_`.
Introduction
------------
This document contains some high-level philosophy and policy decisions for the Flutter
project, and a description of specific style issues for some parts of the codebase.
The style portion describes the preferred style for code written as part of the Flutter
project (the framework itself and all our sample code). Flutter application developers
are welcome to follow this style as well, but this is by no means required. Flutter
will work regardless of what style is used to author applications that use it.
The engine repository uses https://github.com/flutter/engine/blob/master/CONTRIBUTING.md#style[other style guides for non-Dart code]. The language-neutral sections in this document still apply to engine code, however.
Table of Contents
-----------------
toc::[]
Overview
--------
This document describes our approach to designing and programming Flutter,
from high-level architectural principles all the way to indentation rules.
These are our norms, written down so that we can easily convey our shared
understanding with new team members.
The primary goal of these style guidelines is to improve code readability so
that everyone, whether reading the code for the first time or
maintaining it for years, can quickly determine what the code does.
Secondary goals are to design systems that are simple, to increase the
likelihood of catching bugs quickly, and avoiding arguments when there are
disagreements over subjective matters.
For anything not covered by this document, check the
https://www.dartlang.org/guides/language/effective-dart/[Dart style guide]
for more advice. That document is focused primarily on Dart-specific
conventions, while this document is more about Flutter conventions.
In some cases (for example, line wrapping around `if` statements) the
Dart style guide differs from the Flutter guide. For Flutter project code,
the Flutter guide governs. The differences are a result of slightly different
priorities. The Flutter guide is designed for making code highly readable
even to people who have never seen the code before and are new to Dart, as
the Flutter framework code will be read millions of times more than it is written.
The Dart guide, on the other hand, is designed to provide a more balanced approach
that assumes that the writing of the code will be a bigger proportion of the
interactions with the code, and that the reader is more experienced with Dart.
(The `dart format` tool uses the Dart guide, so we do not use it in the
flutter/flutter and flutter/engine repositories. However, we do recommend its
use in general.)
### A word on designing APIs
Designing an API is an art. Like all forms of art, one learns by practicing. The best way to get good at designing APIs is to spend a decade or more designing them, while working closely with people who are using your APIs. Ideally, one would first do this in very controlled situations, with small numbers of developers using one's APIs, before graduating to writing APIs that will be used by hundreds of thousands or even millions of developers.
In the absence of one's own experience, one can attempt to rely on the experience of others. The biggest problem with this is that sometimes explaining why an API isn't optimal is a very difficult and subtle task, and sometimes the reasoning doesn't sound convincing unless you already have a lot of experience designing them.
Because of this, and contrary to almost any other situation in engineering, when you are receiving feedback about API design from an experienced API designer, they will sometimes seem unhappy without quite being able to articulate why. When this happens, seriously consider that your API should be scrapped and a new solution found.
This requires a different and equally important skill when designing APIs: not getting attached to one's creations. One should try many wildly different APIs, and then attempt to write code that uses those APIs, to see how they work. Throw away APIs that feel frustrating, that lead to buggy code, or that other people don't like. If it isn't elegant, it's usually better to try again than to forge ahead.
An API is for life, not just for the one PR you are working on.
Philosophy
----------
### Lazy programming
Write what you need and no more, but when you write it, do it right.
Avoid implementing features you don't need. You can't design a feature
without knowing what the constraints are. Implementing features "for
completeness" results in unused code that is expensive to maintain,
learn about, document, test, etc.
When you do implement a feature, implement it the right way. Avoid
workarounds. Workarounds merely kick the problem further down the
road, but at a higher cost: someone will have to relearn the problem,
figure out the workaround and how to dismantle it (and all the places
that now use it), _and_ implement the feature. It's much better to
take longer to fix a problem properly, than to be the one who fixes
everything quickly but in a way that will require cleaning up later.
You may hear team members say "embrace the http://www.catb.org/jargon/html/Y/yak-shaving.html[yak
shave]!". This is
an encouragement to https://www.youtube.com/watch?v=AbSehcT19u0[take on the larger effort necessary] to perform a
proper fix for a problem rather than just applying a band-aid.
### Write Test, Find Bug
When you fix a bug, first write a test that fails, then fix the bug
and verify the test passes.
When you implement a new feature, write tests for it. (See also: https://github.com/flutter/flutter/wiki/Running-and-writing-tests[Running and writing tests], and the section on writing tests below.)
Check the code coverage
to make sure every line of your new code is tested. See also: https://github.com/flutter/flutter/wiki/Test-coverage-for-package%3Aflutter[Test coverage for package:flutter].
If something isn't tested, it is very likely to regress or to get "optimized away".
If you want your code to remain in the codebase, you should make sure to test it.
Don't submit code with the promise to "write tests later". Just take the
time to write the tests properly and completely in the first place.
### Avoid duplicating state
There should be no objects that represent live state that reflect
some state from another source, since they are expensive to maintain.
(The Web's `HTMLCollection` object is an example of such an object.)
In other words, **keep only one source of truth**, and **don't replicate
live state**.
### Getters feel faster than methods
Property getters should be efficient (e.g. just returning a cached
value, or an O(1) table lookup). If an operation is inefficient, it
should be a method instead. (Looking at the Web again: we would have
`document.getForms()`, not `document.forms`, since it walks the entire tree).
Similarly, a getter that returns a Future should not kick-off the work
represented by the future, since getters appear idempotent and side-effect free.
Instead, the work should be started from a method or constructor, and the
getter should just return the preexisting Future.
### No synchronous slow work
There should be no APIs that require synchronously completing an
expensive operation (e.g. computing a full app layout outside of the
layout phase). Expensive work should be asynchronous.
### Layers
We use a layered framework design, where each layer addresses a
narrowly scoped problem and is then used by the next layer to solve
a bigger problem. This is true both at a high level (widgets relies
on rendering relies on painting) and at the level of individual
classes and methods (e.g. `Text` uses `RichText` and `DefaultTextStyle`).
Convenience APIs belong at the layer above the one they are simplifying.
### Avoid interleaving multiple concepts together
Each API should be self-contained and should not know about other features.
Interleaving concepts leads to _complexity_.
For example:
- Many Widgets take a `child`. Widgets should be entirely agnostic about the type
of that child. Don't use `is` or similar checks to act differently based on the
type of the child.
- Render objects each solve a single problem. Rather than having a render object
handle both clipping and opacity, we have one render object for clipping, and one
for opacity.
- In general, prefer immutable objects over mutable data. Immutable objects can
be passed around safely without any risk that a downstream consumer will change
the data. (Sometimes, in Flutter, we pretend that some objects are immutable even
when they technically are not: for example, widget child lists are often technically
implemented by mutable `List` instances, but the framework will never modify them
and in fact cannot handle the user modifying them.) Immutable data also turns out
to make animations much simpler through _lerping_.
### Avoid secret (or global) state
A function should operate only on its arguments and, if it is an instance
method, data stored on its object. This makes the code significantly easier
to understand.
For example, when reading this code:
```dart
// ... imports something that defines foo and bar ...
void main() {
foo(1);
bar(2);
}
```
...the reader should be confident that nothing in the call to `foo` could affect anything in the
call to `bar`.
This usually means structuring APIs so that they either take all relevant inputs as arguments, or so
that they are based on objects that are created with the relevant input, and can then be called to
operate on those inputs.
This significantly aids in making code testable and in making code understandable and debuggable.
When code operates on secret global state, it's much harder to reason about.
### Prefer general APIs, but use dedicated APIs where there is a reason
For example, having dedicated APIs for performance reasons is fine. If one
specific operation, say clipping a rounded rectangle, is expensive
using the general API but could be implemented more efficiently
using a dedicated API, then that is where we would create a dedicated API.
### Avoid the lowest common denominator
It is common for SDKs that target multiple platforms (or meta-platforms that
themselves run on multiple platforms, like the Web) to provide APIs that
work on all their target platforms. Unfortunately, this usually means that
features that are unique to one platform or another are unavailable.
For Flutter, we want to avoid this by explicitly aiming to be the best way
to develop for each platform individually. Our ability to be used cross-
platform is secondary to our ability to be used on each platform. For example,
https://master-api.flutter.dev/flutter/services/TextInputAction-class.html[TextInputAction]
has values that only make sense on some platforms. Similarly, our platform
channel mechanism is designed to allow separate extensions to be created on
each platform.
### Avoid APIs that encourage bad practices
For example, don't provide APIs that walk entire trees, or that encourage
O(N^2) algorithms, or that encourage sequential long-lived operations where
the operations could be run concurrently.
In particular:
- String manipulation to generate data or code that will subsequently
be interpreted or parsed is a bad practice as it leads to code
injection vulnerabilities.
- If an operation is expensive, that expense should be represented
in the API (e.g. by returning a `Future` or a `Stream`). Avoid
providing APIs that hide the expense of tasks.
### Avoid exposing API cliffs
Convenience APIs that wrap some aspect of a service from one environment
for exposure in another environment (for example, exposing an Android API
in Dart), should expose/wrap the complete API, so that there's no cognitive cliff
when interacting with that service (where you are fine using the exposed
API up to a point, but beyond that have to learn all about the underlying
service).
### Avoid exposing API oceans
APIs that wrap underlying services but prevent the underlying API from
being directly accessed (e.g. how `dart:ui` exposes Skia) should carefully
expose only the best parts of the underlying API. This may require refactoring
features so that they are more usable. It may mean avoiding exposing
convenience features that abstract over expensive operations unless there's a
distinct performance gain from doing so. A smaller API surface is easier
to understand.
For example, this is why `dart:ui` doesn't expose `Path.fromSVG()`: we checked,
and it is just as fast to do that work directly in Dart, so there is no benefit
to exposing it. That way, we avoid the costs (bigger API surfaces are more
expensive to maintain, document, and test, and put a compatibility burden on
the underlying API).
### Avoid heuristics and magic
Predictable APIs that the developer feels gives them control are generally preferred
over APIs that mostly do the right thing but don't give the developer any way to adjust
the results.
Predictability is reassuring.
### Solve real problems by literally solving a real problem
Where possible, especially for new features, you should partner with a real
customer who wants that feature and is willing to help you test it. Only by
actually using a feature in the real world can we truly be confident that a
feature is ready for prime time.
Listen to their feedback, too. If your first customer is saying that your
feature doesn't actually solve their use case completely, don't dismiss their
concerns as esoteric. Often, what seems like the problem when you start a
project turns out to be a trivial concern compared to the real issues faced
by real developers.
### Get early feedback when designing new APIs
If you're designing a new API or a new feature, consider https://github.com/flutter/flutter/wiki/Chat#design-documents[writing a design doc].
Then, get feedback from the relevant people, e.g. send it to `flutter-dev` or
post it on the https://github.com/flutter/flutter/wiki/Chat#existing-channels[relevant chat channel].
### Start designing APIs from the closest point to the developer
When we create a new feature that requires a change to the entire stack, it's tempting to design the lowest-level API first, since that's the closest to the "interesting" code (the "business end" of the feature, where we actually do the work). However, that then forces the higher level APIs to be designed against the lower-level API, which may or may not be a good fit, and eventually the top-level API, which developers will primarily be using, may be forced to be a tortured and twisted mess (either in implementation or in terms of the exposed API). It may even be that the final API doesn't fit how people think about the problem or solve their actual issues, but instead merely exposes the lowest-level feature almost verbatim.
Instead, always design the top-level API first. Consider what the most ergonomic API would be at the level that most developers will be interacting with it. Then, once that API is cleanly designed and usability-tested, build the lower levels so that the higher level can be layered atop.
Concretely, this means designing the API at the `material` or `widgets` layer first, then the API in the `rendering`, `scheduler`, or `services` layer, then the relevant binding, then the `dart:ui` API or the message channel protocol, then the internal engine API or the plugin API. (The details may vary from case to case.)
### Only log actionable messages to the console
If the logs contain messages that the user can safely ignore, then they will do so, and eventually their logs
will be so chatty and verbose that they will miss the critical messages. Therefore, only log actual errors and
actionable warnings (warnings that can always be dealt with and fixed).
Never log "informational" messages by default. It is possible that it may be useful to have messages on certain topics while debugging those topics. To deal with that, have debug flags you can enable that enable extra logging for particular topics. For example, setting `debugPrintLayouts` to true enables logging of layouts.
This also applies to our unopt builds. It's annoying for other people on the team to have to wade through messages that aren't directly relevant to their work. Rely on feature flags, not verbosity levels, when deciding
to output messages. The one exception to this is reporting useful milestones; for example, the `flutter` tool in
verbose mode (`-v`) reports meaningful steps that it is executing because those are almost always useful.
### Error messages should be useful
Every time you find the need to report an error (e.g. throwing an exception in the framework, handling some bad state in the engine, reporting a syntax error in the Dart compiler, etc), consider how you can make this the most useful and helpful error message ever.
Put yourself in the shoes of whoever sees that error message. Why did they see it? What can we do to help them? They are at a crossroads, having seen your error message: they can either get frustrated and hate Flutter, or they can feel thankful that the error helped them resolve an actual issue. **Every error message is an opportunity to make someone love our product.**
### Template values should set developers up for success
Template defaults should focus on providing the best developer experience. Templates should help developers understand the code, be easy to run now and support in the future. Help developers by picking dependencies that are broadly used and/or broadly supported and by leaving https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#leave-breadcrumbs-in-the-comments[comments that are helpful].
See flutter create's templates for an example.
Policies
--------
This section defines some policies that we have decided to honor. In the absence of a very specific policy in this section, the general philosophies in the section above are controlling.
### Plugin compatibility
We guarantee that a plugin published with a version equal to or greater than 1.0.0 will require no more recent a version of Flutter than the latest stable release at the time that the plugin was released. (Plugins may support older versions too, but that is not guaranteed.)
### Workarounds
We are willing to implement temporary (one week or less) workarounds (e.g. `//ignore` hacks) if it helps a high profile developer or prolific contributor with a painful transition. Please contact @Hixie (ian@hixie.ch) if you need to make use of this option.
### Avoid abandonware
Code that is no longer maintained should be deleted or archived in some way that clearly indicates
that it is no longer maintained.
For example, we delete rather than commenting out code. Commented-out code will bitrot too fast to be
useful, and will confuse people maintaining the code.
Similarly, all our repositories should have an owner that does regular triage of incoming issues and PRs,
and fixes known issues. Repositories where nobody is doing triage at least monthly, preferably more often,
should be deleted, hidden, or otherwise archived.
### Widget libraries follow the latest OEM behavior
For our material and cupertino libraries, we generally implement the latest behavior unless doing so
would be a seriously disruptive breaking change. For example, we use the latest stylings for iOS
switch controls, but when Material Design introduced a whole new type of button, we created a new
widget for that rather than updating the existing buttons to have the new style.
### Code that is not copyrighted "The Flutter Authors"
All code in all Flutter repositories must be contributed by developers who have signed https://cla.developers.google.com/[the Google CLA], and must be licensed using our normal BSD license with a copyright referencing "The Flutter Authors", except if it is "third party code".
"Third party code" that is not part of a Dart package must be in a subdirectory of a `third_party` directory at the root of the relevant repository, and the subdirectory in question must contain a `LICENSE` file that details the license covering that code and a `README` describing the provenance of that code.
"Third party code" that is part of a Dart package and is not Dart code must be in a subdirectory of a `third_party` directory at the root of the package, and the subdirectory in question must contain a `LICENSE` file that details the license covering that code and a `README` describing the provenance of that code. The license must then also be duplicated into the package's `LICENSE` file using the syntax described in the https://master-api.flutter.dev/flutter/foundation/LicenseRegistry-class.html[LicenseRegistry] API docs.
"Third party code" that is part of a Dart package and is Dart code must be in a subdirectory of the package's `lib/src/third_party` directory, and the subdirectory in question must contain a `LICENSE` file that details the license covering that code and a `README` describing the provenance of that code. The license must then also be duplicated into the package's `LICENSE` file using the syntax described in the https://master-api.flutter.dev/flutter/foundation/LicenseRegistry-class.html[LicenseRegistry] API docs.
All licenses included in this manner must have been reviewed and determined to be legally acceptable licenses.
All such "third party code" must either be a fork for which we take full responsibility, or there must be an automated rolling mechanism that keeps the code up to date when the upstream source changes.
In general it is _strongly_ recommended that we avoid any such code unless strictly necessary. In particular, we aim for all code in the flutter/flutter repository to be https://github.com/flutter/flutter/wiki/Why-we-have-a-separate-engine-repo#licensing?[single-licensed], which is why it does not contain any "third party code" at all.
Documentation (dartdocs, javadocs, etc)
---------------------------------------
We use "dartdoc" for our Dart documentation, and similar technologies for the documentation
of our APIs in other languages, such as ObjectiveC and Java. All public members in Flutter
libraries should have a documentation.
In general, follow the
https://www.dartlang.org/effective-dart/documentation/#doc-comments[Dart documentation guide]
except where that would contradict this page.
### Answer your own questions straight away
When working on Flutter, if you find yourself asking a question about
our systems, please place whatever answer you subsequently discover
into the documentation in the same place where you first looked for
the answer. That way, the documentation will consist of answers to real
questions, where people would look to find them. Do this right away;
it's fine if your otherwise-unrelated PR has a bunch of documentation
fixes in it to answer questions you had while you were working on your PR.
We try to avoid reliance on "oral tradition". It should be possible
for anyone to begin contributing without having had to learn all the
secrets from existing team members. To that end, all processes should
be documented (typically on the wiki), code should be self-explanatory
or commented, and conventions should be written down, e.g. in our style
guide.
There is one exception: it's better to _not_ document something in our API
docs than to document it poorly. This is because if you don't document it,
it still appears on our list of things to document. Feel free to remove
documentation that violates our rules below (especially the next one),
so as to make it reappear on the list.
### Avoid useless documentation
If someone could have written the same documentation without knowing
anything about the class other than its name, then it's useless.
Avoid checking in such documentation, because it is no better than no
documentation but will prevent us from noticing that the identifier is
not actually documented.
Example (from http://docs.flutter.io/flutter/material/CircleAvatar-class.html[`CircleAvatar`]):
```dart
// BAD:
/// The background color.
final Color backgroundColor;
/// Half the diameter of the circle.
final double radius;
// GOOD:
/// The color with which to fill the circle.
///
/// Changing the background color will cause the avatar to animate to the new color.
final Color backgroundColor;
/// The size of the avatar.
///
/// Changing the radius will cause the avatar to animate to the new size.
final double radius;
```
### Writing prompts for good documentation
If you are having trouble coming up with useful documentation, here are some prompts that might help you write more detailed prose:
* If someone is looking at this documentation, it means that they have a question which they couldn't answer by guesswork or by looking at the code. What could that question be? Try to answer all questions you can come up with.
* If you were telling someone about this property, what might they want to know that they couldn't guess? For example, are there edge cases that aren't intuitive?
* Consider the type of the property or arguments. Are there cases that are outside the normal range that should be discussed? e.g. negative numbers, non-integer values, transparent colors, empty arrays, infinities, NaN, null? Discuss any that are non-trivial.
* Does this member interact with any others? For example, can it only be non-null if another is null? Will this member only have any effect if another has a particular range of values? Will this member affect whether another member has any effect, or what effect another member has?
* Does this member have a similar name or purpose to another, such that we should point to that one, and from that one to this one? Use the `See also:` pattern.
* Are there timing considerations? Any potential race conditions?
* Are there lifecycle considerations? For example, who owns the object that this property is set to? Who should `dispose()` it, if that's relevant?
* What is the contract for this property/method? Can it be called at any time? Are there limits on what values are valid? If it's a `final` property set from a constructor, does the constructor have any limits on what the property can be set to? If this is a constructor, are any of the arguments not nullable?
* If there are `Future` values involved, what are the guarantees around those? Consider whether they can complete with an error, whether they can never complete at all, what happens if the underlying operation is canceled, and so forth.
### Introduce terms as if every piece of documentation is the first the reader has ever seen
It's easy to assume that the reader has some basic knowledge of Dart or Flutter when writing API documentation.
Unfortunately, the reality is that everyone starts knowing nothing, and we do not control where they will begin their journey.
For this reason, avoid using terms without first defining them, unless you are linking to more fundamental documentation that defines that term without reference to the API you are documenting.
For example, a fancy widget in the Material library can refer to the `StatefulWidget` documentation and assume that the reader either knows about the `StatefulWidget` class, or can learn about it by following the link and then later returning to the documentation for the fancy widget. However, the documentation for the `StatefulWidget` class should avoid assuming that the reader knows what a `State` class is, and should avoid defering to it for its definition, because `State` could is likely to defer back to `StatefulWidget` and the reader would be stuck in a loop unable to grasp the basic principles. This is the documentation equivalent of a bootstrapping problem.
Another way to express this is that API documentation should follow a similar layering philosophy as code. The goal of documentation is not just to act as a refresher for experts, but to act as a tutorial for new developers.
### Avoid empty prose
It's easy to use more words than necessary. Avoid doing so
where possible, even if the result is somewhat terse.
```dart
// BAD:
/// Note: It is important to be aware of the fact that in the
/// absence of an explicit value, this property defaults to 2.
// GOOD:
/// Defaults to 2.
```
In particular, avoid saying "Note:", or starting a sentence with "Note that". It adds nothing.
### Leave breadcrumbs in the comments
This is especially important for documentation at the level of classes.
If a class is constructed using a builder of some sort, or can be
obtained via some mechanism other than merely calling the constructor,
then include this information in the documentation for the class.
If a class is typically used by passing it to a particular API, then
include that information in the class documentation also.
If a method is the main mechanism used to obtain a particular object,
or is the main way to consume a particular object, then mention that
in the method's description.
Typedefs should mention at least one place where the signature is used.
These rules result in a chain of breadcrumbs that a reader can follow
to get from any class or method that they might think is relevant to
their task all the way up to the class or method they actually need.
Example:
```dart
// GOOD:
/// An object representing a sequence of recorded graphical operations.
///
/// To create a [Picture], use a [PictureRecorder].
///
/// A [Picture] can be placed in a [Scene] using a [SceneBuilder], via
/// the [SceneBuilder.addPicture] method. A [Picture] can also be
/// drawn into a [Canvas], using the [Canvas.drawPicture] method.
abstract class Picture ...
```
You can also use "See also" links, is in:
```dart
/// See also:
///
/// * [FooBar], which is another way to peel oranges.
/// * [Baz], which quuxes the wibble.
```
Each line should end with a period. Prefer "which..." rather than parentheticals on such lines.
There should be a blank line between "See also:" and the first item in the bulleted list.
### Refactor the code when the documentation would be incomprehensible
If writing the documentation proves to be difficult because the API is
convoluted, then rewrite the API rather than trying to document it.
### Canonical terminology
The documentation should use consistent terminology:
* _method_ - a member of a class that is a non-anonymous closure
* _function_ - a callable non-anonymous closure that isn't a member of a class
* _parameter_ - a variable defined in a closure signature and possibly used in the closure body.
* _argument_ - the value passed to a closure when calling it.
Prefer the term "call" to the term "invoke" when talking about jumping to a closure.
Prefer the term "member variable" to the term "instance variable" when talking about variables associated with a specific object.
Typedef dartdocs should usually start with the phrase "Signature for...".
### Use correct grammar
Avoid starting a sentence with a lowercase letter.
```dart
// BAD
/// [foo] must not be null.
// GOOD
/// The [foo] argument must not be null.
```
Similarly, end all sentences with a period.
### Use the passive voice; recommend, do not require; never say things are simple
Never use "you" or "we". Avoid the imperative voice. Avoid value judgements.
Rather than telling someone to do something, use "Consider", as in "`To obtain the foo, consider using [bar].`".
In general, you don't know who is reading the documentation or why. Someone could have inherited a terrible codebase and be reading our documentation to find out how to fix it; by saying "you should not do X" or "avoid Y" or "if you want Z", you will put the reader in a defensive state of mind when they find code that contradicts the documentation (after all, they inherited this codebase, who are we to say that they're doing it wrong, it's not their fault).
For similar reasons, never use the word "simply", or say that the reader need "just" do something, or otherwise imply that the task is easy.
By definition, if they are looking at the documentation, they are not finding it easy.
### Provide sample code
Sample code helps developers learn your API quickly. Writing sample code also helps you think through how your API is going to be used by app developers.
Sample code should go in a documentation comment that typically begins with `/// {@tool dartpad}`, and ends with `/// {@end-tool}`, with the example source and corresponding tests placed in a file under https://github.com/flutter/flutter/blob/master/examples/api[the API examples directory]. This will then be checked by automated tools, and formatted for display on the API documentation web site https://api.flutter.dev[api.flutter.dev]. For details on how to write sample code, see https://github.com/flutter/flutter/blob/master/examples/api/README.md#authoring[the API example documentation].
#### Provide full application samples.
Our UX research has shown that developers prefer to see examples that are in the context of an entire app. So, whenever it makes sense, provide an example that can be presented as part of an entire application instead of just a snippet that uses the `{@tool snippet}` or ```dart ... ``` indicators.
An application sample can be created using the `{@tool dartpad}` ... `{@end-tool}` or `{@tool sample}` ... `{@end-tool}` dartdoc indicators. See https://github.com/flutter/flutter/blob/master/examples/api/README.md#authoring[here] for more details about writing these kinds of examples.
Dartpad examples (those using the dartdoc `{@tool dartpad}` indicator) will be presented on the https://api.flutter.dev[API documentation website] as an in-page executable and editable example. This allows developers to interact with the example right there on the page, and is the preferred form of example. Here is https://api.flutter.dev/flutter/widgets/AnimatedSwitcher-class.html#widgets.AnimatedSwitcher.1[one such example].
For examples that don't make sense in a web page (for example, code that interacts with a particular platform feature), application examples (using the dartdoc `{@tool sample}` indicator) are preferred, and will be presented on the API documentation website along with information about how to instantiate the example as an application that can be run.
Supported IDEs viewing the Flutter source code using the Flutter plugin also offer the option of creating a new project with either kind of example.
### Provide illustrations, diagrams or screenshots
For any widget that draws pixels on the screen, showing how it looks like in its API doc helps developers decide if the widget is useful and learn how to customize it. All illustrations should be easily reproducible, e.g. by running a Flutter app or a script.
Examples:
* A diagram for the AppBar widget
image::https://flutter.github.io/assets-for-api-docs/assets/material/app_bar.png[]
* A screenshot for the Card widget
image::https://user-images.githubusercontent.com/348942/28338544-2c3681b8-6bbe-11e7-967d-fcd7c830bf53.png[]
When creating diagrams, make sure to provide alternative text https://html.spec.whatwg.org/multipage/images.html#alt[as described in the HTML specification].
### Link to Widget of the Week videos
Link to a widget's Flutter Widget of the Week video if it has one:
```dart
/// {@youtube 560 315 https://www.youtube.com/watch?v=<id>}
```
The first two arguments are the video's width and height. These should be `560` and `315` respectively.
### Clearly mark deprecated APIs
We have conventions around deprecation. See the https://github.com/flutter/flutter/wiki/Tree-hygiene#deprecation[Tree Hygiene] page for more details.
### Use `///` for public-quality private documentation
In general, private code can and should also be documented. If that documentation is of good enough
quality that we could include it verbatim when making the class public (i.e. it satisfies all the
style guidelines above), then you can use `///` for those docs, even though they're private.
Documentation of private APIs that is not of sufficient quality should only use `//`. That way, if
we ever make the corresponding class public, those documentation comments will be flagged as missing,
and we will know to examine them more carefully.
Feel free to be conservative in what you consider "sufficient quality". It's ok to use `//` even if
you have multiple paragraphs of documentation; that's a sign that we should carefully rereview the
documentation when making the code public.
### Dartdoc templates and macros
Dartdoc supports creating templates that can be reused in other parts of the code. They are defined
like so:
```dart
/// {@template <id>}
/// ...
/// {@endtemplate}
```
and used via:
```dart
/// {@macro <id>}
```
The `<id>` should be a unique identifier that is of the form `flutter.library.Class.member[.optionalDescription]`.
For example:
```
// GOOD:
/// {@template flutter.rendering.Layer.findAnnotations.aboutAnnotations}
/// Annotations are great!
/// {@endtemplate
// BAD:
/// {@template the_stuff!}
/// This is some great stuff!
/// {@endtemplate}
```
The `optionalDescription` component of the identifier is only necessary if there is more than one
template defined in one Dartdoc block. If a symbol is not part of a library, or not part of a class, then
just omit those parts from the ID.
### Dartdoc-specific requirements
The first paragraph of any dartdoc section must be a short self-contained sentence that explains the purpose
and meaning of the item being documented. Subsequent paragraphs then must elaborate. Avoid having the first paragraph have multiple sentences. (This is because the first paragraph gets extracted and used in tables of
contents, etc, and so has to be able to stand alone and not take up a lot of room.)
When referencing a parameter, use backticks. However, when referencing a parameter that also corresponds to a property, use square brackets instead. (This contradicts the Dart style guide, which says to use square brackets for both. We do this because of https://github.com/dart-lang/dartdoc/issues/1486[dartdoc issue 1486]. Currently, there's no way to unambiguously reference a parameter. We want to avoid cases where a parameter that happens to be named the same as a property despite having no relationship to that property gets linked to the property.)
```dart
// GOOD
/// Creates a foobar, which allows a baz to quux the bar.
///
/// The [bar] argument must not be null.
///
/// The `baz` argument must be greater than zero.
Foo({ this.bar, int baz }) : assert(bar != null), assert(baz > 0);
```
Avoid using terms like "above" or "below" to reference one dartdoc section from another. Dartdoc sections are often shown alone on a Web page, the full context of the class is not present.
Coding patterns and catching bugs early
---------------------------------------
### Use asserts liberally to detect contract violations and verify invariants
`assert()` allows us to be diligent about correctness without paying a
performance penalty in release mode, because Dart only evaluates asserts in
debug mode.
It should be used to verify contracts and invariants are being met as we expect.
Asserts do not _enforce_ contracts, since they do not run at all in release builds.
They should be used in cases where it should be impossible for the condition
to be false without there being a bug somewhere in the code.
The following example is from `box.dart`:
```dart
abstract class RenderBox extends RenderObject {
// ...
double getDistanceToBaseline(TextBaseline baseline, {bool onlyReal: false}) {
// simple asserts:
assert(!needsLayout);
assert(!_debugDoingBaseline);
// more complicated asserts:
assert(() {
final RenderObject parent = this.parent;
if (owner.debugDoingLayout)
return (RenderObject.debugActiveLayout == parent) &&
parent.debugDoingThisLayout;
if (owner.debugDoingPaint)
return ((RenderObject.debugActivePaint == parent) &&
parent.debugDoingThisPaint) ||
((RenderObject.debugActivePaint == this) && debugDoingThisPaint);
assert(parent == this.parent);
return false;
});
// ...
return 0.0;
}
// ...
}
```
### Prefer specialized functions, methods and constructors
Use the most relevant constructor or method, when there are multiple
options.
Example:
```dart
// BAD:
const EdgeInsets.TRBL(0.0, 8.0, 0.0, 8.0);
// GOOD:
const EdgeInsets.symmetric(horizontal: 8.0);
```
### Minimize the visibility scope of constants
Prefer using a local const or a static const in a relevant class than using a
global constant.
As a general rule, when you have a lot of constants, wrap them in a
class. For examples of this, see
https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/material/colors.dart[lib/src/material/colors.dart].
### Avoid using `if` chains or `?:` or `==` with enum values
Use `switch` with no `default` case if you are examining an enum, since the analyzer will warn you if you missed any of the values when you use `switch`. The `default` case should be avoided so that the analyzer will complain if a value is missing. Unused values can be grouped together with a single `break` or `return` as appropriate.
Avoid using `if` chains, `? ... : ...`, or, in general, any expressions involving enums.
### Avoid using `var` and `dynamic`
All variables and arguments are typed; avoid `dynamic` or `Object` in
any case where you could figure out the actual type. Always specialize
generic types where possible. Explicitly type all list and map
literals. Give types to all parameters, even in closures and even if you
don't use the parameter.
This achieves two purposes: it verifies that the type that the compiler
would infer matches the type you expect, and it makes the code self-documenting
in the case where the type is not obvious (e.g. when calling anything other
than a constructor).
Always avoid `var` and `dynamic`. If the type is unknown, prefer using
`Object` (or `Object?`) and casting, as using `dynamic` disables all
static checking.
### Avoid using `library` and `part of`.
Prefer that each library be self-contained. Only name a `library` if you are documenting it (see the
documentation section).
We avoid using `part of` because that feature makes it very hard to reason about how private a private
really is, and tends to encourage "spaghetti" code (where distant components refer to each other) rather
than "lasagna" code (where each section of the code is cleanly layered and separable).
### Avoid using `extension`.
Extension methods are confusing to document and discover. To an end developer,
they appear no different than the built in API of the class, and discovering
the documentation and implementation of an extension is more challenging than
for class members.
Prefer instead adding methods directly to relevant classes. If that is not
possible, create a method that clearly identifies what object(s) it works with
and is part of.
(A rare exception can be made for extensions that provide temporary workarounds
when deprecating features. In those cases, however, the extensions and all their
members must be deprecated in the PR that adds them, and they must be removed
in accordance with our deprecation policy.)
### Avoid using `FutureOr<T>`
The `FutureOr` type is a Dart-internal type used to explain certain aspects of the `Future` API. In public APIs, avoid the temptation to create APIs that are both synchronous and asynchronous by returning this type, as it usually only results in the API being more confusing and less type safe.
In certain extreme cases where the API absolutely needs to be asynchronous but a synchronous "escape hatch" is needed for performance, consider using `SynchronousFuture` (but be aware that this still suffers from many of the same risks of making the API only subtle and complicated). This is used, for example, when loading images in the Flutter framework.
You may use `FutureOr` to accept a callback that may or may not return a `Future`.
### Avoid using `Expando`
Generally speaking, `Expando` objects are a sign of an architectural problem. Carefully consider whether your usage is actually necessary. When your classes have clear documented ownership rules, there is usually a better solution.
Expando objects tend to invite code that is hard to understand because one cannot simply follow references to find all the dependencies.
### Avoid using `@visibleForTesting`
The https://api.flutter.dev/flutter/meta/visibleForTesting-constant.html[`@visibleForTesting`] annotation marks a public API so that developers that have not disabled the `invalid_use_of_visible_for_testing_member` analyzer error get a warning when they use this API outside of a `test` directory.
This means that the API has to be treated as being public (nothing prevents a developer from using the API even in non-test code), meaning it must be designed to be a public API, it must be documented, it must be tested, etc. At which point, there's really no reason not to just make it a public API. If anything, the use of `@visibleForTesting` becomes merely a crutch to convince ourselves that it's ok that we're making something public that we should really not have made public.
So rather than rely on `@visibleForTesting`, consider designing your APIs so that they are directly testable using the public API, without exposing any sensitive internals.
(One exception is combining `@visibleForTesting` with `@protected`. The `@protected` annotation marks a member as one that is intended for subclasses, so it is already a public API and considered as such. The `@visibleForTesting` annotation in that case merely enables the member to be called directly in tests without having to create a fake subclass and without having to add `//ignore` pragmas.)
### Never check if a port is available before using it, never add timeouts, and other race conditions.
If you look for an available port, then try to open it, it's extremely likely that several times a week some other code will open that port between your check and when you open the port, and that will cause a failure.
> Instead, have the code that opens the port pick an available port and return it, rather than being given a (supposedly) available port.
If you have a timeout, then it's very likely that several times a week some other code will happen to run while your timeout is running, and your "really conservative" timeout will trigger even though it would have worked fine if the timeout was one second longer, and that will cause a failure.
> Instead, have the code that would time out just display a message saying that things are unexpectedly taking a long time, so that someone interactively using the tool can see that something is fishy, but an automated system won't be affected.
Race conditions like this are the primary cause of flaky tests, which waste everyone's time.
Similarly, avoid delays or sleeps that are intended to coincide with how long something takes. You may think that waiting two seconds is fine because it normally takes 10ms, but several times a week your 10ms task will actually take 2045ms and your test will fail because waiting two seconds wasn't long enough.
> Instead, wait for a triggering event.
### Avoid mysterious and magical numbers that lack a clear derivation
Numbers in tests and elsewhere should be clearly understandable. When the provenance of a number is not obvious,
consider either leaving the expression or adding a clear comment (bonus points for leaving a diagram).
```dart
// BAD
expect(rect.left, 4.24264068712);
// GOOD
expect(rect.left, 3.0 * math.sqrt(2));
```
### Have good hygiene when using temporary directories
Give the directory a unique name that starts with `flutter_` and ends with a period (followed by the autogenerated random string).
For consistency, name the `Directory` object that points to the temporary directory `tempDir`, and create it with `createTempSync` unless you need to do it asynchronously (e.g. to show progress while it's being created).
Always clean up the directory when it is no longer needed. In tests, use the `tryToDelete` convenience function to delete the directory. (We use `tryToDelete` because on Windows it's common to get "access denied" errors when deleting temporary directories. We have no idea why; if you can figure it out then that could simplify a lot of code!)
### Perform dirty checks in setters
Dirty checks are processes to determine whether a changed values have been synchronized with the rest of the app.
When defining mutable properties that mark a class dirty when set, use
the following pattern:
```dart
/// Documentation here (don't wait for a later commit).
TheType get theProperty => _theProperty;
TheType _theProperty;
void set theProperty(TheType value) {
assert(value != null);
if (_theProperty == value)
return;
_theProperty = value;
markNeedsWhatever(); // the method to mark the object dirty
}
```
The argument is called 'value' for ease of copy-and-paste reuse of
this pattern. If for some reason you don't want to use 'value', use
'newProperty' (where 'Property' is the property name).
Start the method with any asserts you need to validate the value.
Don't do anything _else_ in setters, other than marking the object as dirty and updating internal state.
Getters and setters should not have significant side-effects. For example, setting a property whose value
is a callback should not result in that callback being invoked. Setting a property whose value is an object
of some sort should not result in any of that object's methods being called.
### Common boilerplates for `operator ==` and `hashCode`
We have many classes that override `operator ==` and `hashCode` ("value classes"). To keep the code consistent,
we use the following style for these methods:
```dart
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) {
return false;
}
return other is Foo
&& other.bar == bar
&& other.baz == baz
&& other.quux == quux;
}
@override
int get hashCode => Object.hash(bar, baz, quux);
```
For objects with a lot of properties, consider adding the following at the top of the `operator ==`:
```dart
if (identical(other, this)) {
return true;
}
```
(We don't yet use this _exact_ style everywhere, so feel free to update code you come across that isn't yet using it.)
In general, consider carefully whether overriding `operator ==` is a good idea. It can be expensive, especially
if the properties it compares are themselves comparable with a custom `operator ==`. If you do override equality,
you should use `@immutable` on the class hierarchy in question.
### Override `toString`
Use `https://api.flutter.dev/flutter/foundation/Diagnosticable-mixin.html[Diagnosticable]` (rather than directly overriding `toString`) on all but the most trivial classes. That allows us to inspect the object from https://pub.dartlang.org/packages/devtools[devtools] and IDEs.
For trivial classes, override `toString` as follows, to aid in debugging:
```dart
@override
String toString() => '${objectRuntimeType(this, 'NameOfObject')}($bar, $baz, $quux)';
```
...but even then, consider using `Diagnosticable` instead. Avoid using `$runtimeType`, since it adds a non-trivial cost even in release and profile mode. The `objectRuntimeType` method handles this for you, falling back to a supplied constant string when asserts are disabled.
### Be explicit about `dispose()` and the object lifecycle
Even though Dart is garbage collected, having a defined object lifecycle and explicit ownership model (describing in the API documentation who is allowed to mutate the object, for instance) is important to avoid subtle bugs and confusing designs.
If your class has a clear "end of life", for example, provide a `dispose()` method to clean up references such as listeners that would otherwise prevent some objects from getting garbage collected. For example, consider a widget that has a subscription on a global broadcast stream (that might have other listeners). That subscription will keep the widget from getting garbage collected until the stream itself goes away (which, for a global stream, might never happen).
In general, pretending that Dart does not have garbage collection is likely to lead to less confusing and buggy code, because it forces you to think about the implications of object ownership and lifecycles.
### Test APIs belong in the test frameworks
Mechanisms that exist for test purposes do not belong in the core libraries, they belong in test harnesses. This keeps the cost of the main library down in production and avoids the risk that people might abuse test APIs.
### Immutable classes should not have hidden state
Immutable classes (those with `const` constructors) should not have hidden state. For example, they should not use private statics or Expandos. If they are stateful, then they should not be `const`.
### Avoid `sync*`/`async*`
Using generator functions (`sync*`/`async*`) can be a powerful improvement when callers will
actually lazily evaluate the iterable and each iteration is expensive _or_ there are a very
large number of iterations.
It should not be used in place of building and returning a `List`, particularly for trivial methods
that only yield a small number of members or when callers will evaluate the whole collection
anyway. It should also be avoided in very large functions.
It incurs runtime overhead in maintaining and using an iterator, and space overhead for the compiler
to actually desugar the generator into something that uses an iterator class.
Writing tests
-------------
### Make each test entirely self-contained
Embrace code duplication in tests. It makes it easier to make new tests by copying and pasting them and
tweaking a few things.
Avoid using `setUp`, `tearDown`, and similar features, as well as test-global variables or other state
shared between tests. They make writing tests easier but make maintaining them, debugging them, and
refactoring code much harder. (These are commonly used in Flutter's codebase today, but that is almost
always a mistake. When you are editing a file that uses those features, aim to reduce the number of
tests using them while you're there.)
Specifically, we are trying to avoid shared state, which could persist across tests, and non-local
side-effects, which would prevent being able to move a test to another file without breaking the test.
(It's fine to factor out code into functions that are called by tests, so long as the functions don't
have side-effects that might change how other tests run.)
### Prefer more test files, avoid long test files
Avoid adding tests to files that already have more than one or two hundred lines of code. It's easier
to understand a test file when it has only a few related tests, rather than when it has an entire test
suite. (It also makes developing the tests faster because you can run the test file faster.)
### Avoid using `pumpAndSettle`
As per the API docs for https://main-api.flutter.dev/flutter/flutter_test/WidgetController/pumpAndSettle.html[pumpAndSettle], prefer using explicit https://main-api.flutter.dev/flutter/flutter_test/WidgetController/pump.html[`pump`] calls rather than `pumpAndSettle`.
Using `pumpAndSettle`, especially without checking its return value, makes it very easy for bugs to sneak in where we trigger animations across multiple frames instead of immediately. It is almost always the case that a call to `pumpAndSettle` is more strictly correctly written as two `pump` calls, one to trigger the animations and one (with a duration) to jump to the point after the animations.
Naming
------
### Begin global constant names with prefix "k"
Examples:
```dart
const double kParagraphSpacing = 1.5;
const String kSaveButtonTitle = 'Save';
```
However, where possible avoid global constants. Rather than `kDefaultButtonColor`, consider `Button.defaultColor`. If necessary, consider creating a class with a private constructor to hold relevant constants.
### Avoid abbreviations
Unless the abbreviation is more recognizable than the expansion (e.g. XML, HTTP, JSON), expand abbrevations
when selecting a name for an identifier. In general, avoid one-character names unless one character is idiomatic
(for example, prefer `index` over `i`, but prefer `x` over `horizontalPosition`).
### Avoid anonymous parameter names
Provide full type information and names even for parameters that are otherwise unused. This makes it easier for
people reading the code to tell what is actually going on (e.g. what is being ignored). For example:
```dart
onTapDown: (TapDownDetails details) { print('hello!'); }, // GOOD
onTapUp: (_) { print('good bye'); }, // BAD
```
### Naming rules for typedefs and function variables
When naming callbacks, use `FooCallback` for the typedef, `onFoo` for
the callback argument or property, and `handleFoo` for the method
that is called. If `Foo` is a verb, prefer the present tense to the
past tense (e.g. `onTap` instead of `onTapped`).
If you have a callback with arguments but you want to ignore the
arguments, give the type and names of the arguments anyway. That way,
if someone copies and pastes your code, they will not have to look up
what the arguments are.
Never call a method `onFoo`. If a property is called `onFoo` it must be
a function type. (For all values of "Foo".)
Prefer using `typedef`s to declare callbacks. Typedefs benefit from having
documentation on the type itself and make it easier to read and find
common callsites for the signature.
### Spell words in identifiers and comments correctly
Our primary source of truth for spelling is the
https://material.google.com/[Material Design Specification].
Our secondary source of truth is dictionaries.
Avoid "cute" spellings. For example, 'colors', not 'colorz'.
Prefer US English spellings. For example, 'colorize', not 'colourise', and 'canceled', not 'cancelled'.
Prefer compound words over "cute" spellings to avoid conflicts with reserved words. For example, 'classIdentifier', not 'klass'.
### Capitalize identifiers consistent with their spelling
In general, we use https://dart.dev/guides/language/effective-dart/style#identifiers[Dart's recommendations]'s for naming identifiers. Please consider the following additional guidelines:
If a word is correctly spelled (according to our sources of truth as described in the previous section) as a single word, then it should not have any inner capitalization or spaces.
For examples, prefer `toolbar`, `scrollbar`, but `appBar` ('app bar' in documentation), `tabBar` ('tab bar' in documentation).
Similarly, prefer `offstage` rather than `offStage`.
Avoid using class names with `iOS` when possible. The capitalization of `iOS` is supposed to be exactly that, but that doesn't work well with camelCase and even less with UpperCamelCase; use alternatives like "Cupertino" or "UIKit" instead when possible. If you really really must use "iOS" in an identifier, capitalize it to `IOS`. Whether or not https://dart.dev/guides/language/effective-dart/style#do-capitalize-acronyms-and-abbreviations-longer-than-two-letters-like-words[the two-letter exception] applies to "iOS" is debatable, but `IOS` is consistent with Dart APIs, and the alternatives (`IOs`, `Ios`) are even more jarring. (Previous versions of this guide incorrectly indicated that `Ios` was the correct capitalization when necessary; this form should not be used in new code.)
### Avoid double negatives in APIs
Name your boolean variables in positive ways, such as "enabled" or "visible", even if the default value is true.
This is because, when you have a property or argument named "disabled" or "hidden", it leads to code such as `input.disabled = false` or `widget.hidden = false` when you're trying to enable or show the widget, which is very confusing.
### Prefer naming the argument to a setter `value`
Unless this would cause other problems, use `value` for the name of a setter's argument. This makes it easier to copy/paste the setter later.
### Qualify variables and methods used only for debugging
If you have variables or methods (or even classes!) that are only used in debug mode,
prefix their names with `debug` or `_debug` (or, for classes, `_Debug`).
Do not use debugging variables or methods (or classes) in production code.
### Avoid naming undocumented libraries
In other words, do not use the `library` keyword, unless it is a
documented top-level library intended to be imported by users.
### Avoid "new/old" modifiers in code
The definition of "New" changes as code grows and time passes. If the code
needed a replacement version the odds of needing another replacement in the
future is higher. Instead find a name that represents the idea being being used
or replaced.
Comments
--------
### Avoid checking in comments that ask questions
Find the answers to the questions, or describe the confusion, including
references to where you found answers.
If commenting on a workaround due to a bug, also leave a link to the issue and
a TODO to clean it up when the bug is fixed.
Example:
```dart
// BAD:
// What should this be?
// This is a workaround.
// GOOD:
// According to this specification, this should be 2.0, but according to that
// specification, it should be 3.0. We split the difference and went with
// 2.5, because we didn't know what else to do.
// TODO(username): Converting color to RGB because class Color doesn't support
// hex yet. See http://link/to/a/bug/123
```
TODOs should include the string TODO in all caps, followed by the GitHub username of
the person with the best _context_ about the problem referenced by the TODO in
parenthesis. A TODO is not a commitment that the person referenced will fix the
problem, it is intended to be the person with enough context to explain the problem.
Thus, when you create a TODO, it is almost always your username that is given.
Including an issue link in a TODO description is required.
_(See also https://github.com/flutter/flutter/issues/37519[#37519],
which tracks a proposal to change the syntax of TODOs to not include usernames.)_
### Comment all `// ignores`
Sometimes, it is necessary to write code that the analyzer is unhappy with.
If you find yourself in this situation, consider how you got there. Is the analyzer actually correct but you
don't want to admit it? Think about how you could refactor your code so that the analyzer is happy. If such a
refactor would make the code better, do it. (It might be a lot of work... embrace the yak shave.)
If you are really really sure that you have no choice but to silence the analyzer, use `// ignore: `. The ignore
directive should be on the same line as the analyzer warning.
If the ignore is temporary (e.g. a workaround for a bug in the compiler or analyzer, or a workaround for some known problem in Flutter that you cannot fix), then add a link to the relevant bug, as follows:
```dart
foo(); // ignore: lint_code, https://link.to.bug/goes/here
```
If the ignore directive is permanent, e.g. because one of our lints has some unavoidable false positives and in this case violating the lint is definitely better than all other options, then add a comment explaining why:
```dart
foo(); // ignore: lint_code, sadly there is no choice but to do
// this because we need to twiddle the quux and the bar is zorgle.
```
### Comment all test skips
On very rare occasions it may be necessary to skip a test. To do that, use the `skip` argument.
Any time you use the `skip` argument, file an issue describing why it is skipped and
include a link to that issue in the code.
### Comment empty closures to `setState`
Generally the closure passed to `setState` should include all the code that changes the state. Sometimes this is not possible because the state changed elsewhere and the `setState` is called in response. In those cases, include a comment in the `setState` closure that explains what the state is that changed.
```dart
setState(() { /* The animation ticked. We use the animation's value in the build method. */ });
```
Formatting
----------
These guidelines have no technical effect, but they are still important purely
for consistency and readability reasons.
We do not yet use `dartfmt` (except in flutter/packages).
Flutter code tends to use patterns that
the standard Dart formatter does not handle well. We are
https://github.com/flutter/flutter/issues/2025[working with Dart team] to make `dartfmt` aware of these patterns.
### In defense of the extra work that hand-formatting entails
Flutter code might eventually be read by hundreds of thousands of people each day.
Code that is easier to read and understand saves these people time. Saving each
person even a second each day translates into hours or even _days_ of saved time
each day. The extra time spent by people contributing to Flutter directly translates
into real savings for our developers, which translates to real benefits to our end
users as our developers learn the framework faster.
### Constructors come first in a class
The default (unnamed) constructor should come first, then the named
constructors. They should come before anything else (including, e.g., constants or static methods).
This helps readers determine whether the class has a default implied constructor or not at a glance. If it was possible for a constructor to be anywhere in the class, then the reader would have to examine every line of the class to determine whether or not there was an implicit constructor or not.
### Order other class members in a way that makes sense
The methods, properties, and other members of a class should be in an order that
will help readers understand how the class works.
If there's a clear lifecycle, then the order in which methods get invoked would be useful, for example an `initState` method coming before `dispose`. This helps readers because the code is in chronological order, so
they can see variables get initialized before they are used, for instance. Fields should come before the methods that manipulate them, if they are specific to a particular group of methods.
> For example, RenderObject groups all the layout fields and layout
> methods together, then all the paint fields and paint methods, because layout
> happens before paint.
If no particular order is obvious, then the following order is suggested, with blank lines between each one:
1. Constructors, with the default constructor first.
2. Constants of the same type as the class.
3. Static methods that return the same type as the class.
4. Final fields that are set from the constructor.
5. Other static methods.
6. Static properties and constants.
7. Members for mutable properties, without new lines separating the members of a property, each property in the order:
- getter
- private field
- setter
8. Read-only properties (other than `hashCode`).
9. Operators (other than `==`).
10. Methods (other than `toString` and `build`).
11. The `build` method, for `Widget` and `State` classes.
12. `operator ==`, `hashCode`, `toString`, and diagnostics-related methods, in that order.
Be consistent in the order of members. If a constructor lists multiple
fields, then those fields should be declared in the same order, and
any code that operates on all of them should operate on them in the
same order (unless the order matters).
### Constructor syntax
If you call `super()` in your initializer list, put a space between the
constructor arguments' closing parenthesis and the colon. If there's
other things in the initializer list, align the `super()` call with the
other arguments. Don't call `super` if you have no arguments to pass up
to the superclass.
```dart
// one-line constructor example
abstract class Foo extends StatelessWidget {
Foo(this.bar, { Key key, this.child }) : super(key: key);
final int bar;
final Widget child;
// ...
}
// fully expanded constructor example
abstract class Foo extends StatelessWidget {
Foo(
this.bar, {
Key key,
Widget childWidget,
}) : child = childWidget,
super(
key: key,
);
final int bar;
final Widget child;
// ...
}
```
### Prefer a maximum line length of 80 characters
Aim for a maximum line length of roughly 80 characters, but prefer going over if breaking the
line would make it less readable, or if it would make the line less consistent
with other nearby lines. Prefer avoiding line breaks after assignment operators.
```dart
// BAD (breaks after assignment operator and still goes over 80 chars)
final int a = 1;
final int b = 2;
final int c =
a.very.very.very.very.very.long.expression.that.returns.three.eventually().but.is.very.long();
final int d = 4;
final int e = 5;
// BETTER (consistent lines, not much longer than the earlier example)
final int a = 1;
final int b = 2;
final int c = a.very.very.very.very.very.long.expression.that.returns.three.eventually().but.is.very.long();
final int d = 4;
final int e = 5;
```
```dart
// BAD (breaks after assignment operator)
final List<FooBarBaz> _members =
<FooBarBaz>[const Quux(), const Qaax(), const Qeex()];
// BETTER (only slightly goes over 80 chars)
final List<FooBarBaz> _members = <FooBarBaz>[const Quux(), const Qaax(), const Qeex()];
// BETTER STILL (fits in 80 chars)
final List<FooBarBaz> _members = <FooBarBaz>[
const Quux(),
const Qaax(),
const Qeex(),
];
```
### Indent multi-line argument and parameter lists by 2 characters
When breaking an argument list into multiple lines, indent the
arguments two characters from the previous line.
Example:
```dart
Foo f = Foo(
bar: 1.0,
quux: 2.0,
);
```
When breaking a parameter list into multiple lines, do the same.
### If you have a newline after some opening punctuation, match it on the closing punctuation.
And vice versa.
Example:
```dart
// BAD:
foo(
bar, baz);
foo(
bar,
baz);
foo(bar,
baz
);
// GOOD:
foo(bar, baz);
foo(
bar,
baz,
);
foo(bar,
baz);
```
### Use a trailing comma for arguments, parameters, and list items, but only if they each have their own line.
Example:
```dart
List<int> myList = [
1,
2,
];
myList = <int>[3, 4];
foo1(
bar,
baz,
);
foo2(bar, baz);
```
Whether to put things all on one line or whether to have one line per item is an aesthetic choice. We prefer whatever ends up being most readable. Typically this means that when everything would fit on one line, put it all on one line, otherwise, split it one item to a line.
However, there are exceptions. For example, if there are six back-to-back lists and all but one of them need multiple lines, then one would not want to have the single case that does fit on one line use a different style than the others.
```dart
// BAD (because the second list is unnecessarily and confusingly different than the others):
List<FooBarBaz> myLongList1 = <FooBarBaz>[
FooBarBaz(one: firstArgument, two: secondArgument, three: thirdArgument),
FooBarBaz(one: firstArgument, two: secondArgument, three: thirdArgument),
FooBarBaz(one: firstArgument, two: secondArgument, three: thirdArgument),
];
List<Quux> myLongList2 = <Quux>[ Quux(1), Quux(2) ];
List<FooBarBaz> myLongList3 = <FooBarBaz>[
FooBarBaz(one: firstArgument, two: secondArgument, three: thirdArgument),
FooBarBaz(one: firstArgument, two: secondArgument, three: thirdArgument),
FooBarBaz(one: firstArgument, two: secondArgument, three: thirdArgument),
];
// GOOD (code is easy to scan):
List<FooBarBaz> myLongList1 = <FooBarBaz>[
FooBarBaz(one: firstArgument, two: secondArgument, three: thirdArgument),
FooBarBaz(one: firstArgument, two: secondArgument, three: thirdArgument),
FooBarBaz(one: firstArgument, two: secondArgument, three: thirdArgument),
];
List<Quux> myLongList2 = <Quux>[
Quux(1),
Quux(2),
];
List<FooBarBaz> myLongList3 = <FooBarBaz>[
FooBarBaz(one: firstArgument, two: secondArgument, three: thirdArgument),
FooBarBaz(one: firstArgument, two: secondArgument, three: thirdArgument),
FooBarBaz(one: firstArgument, two: secondArgument, three: thirdArgument),
];
```
### Prefer single quotes for strings
Use double quotes for nested strings or (optionally) for strings that contain single quotes.
For all other strings, use single quotes.
Example:
```dart
print('Hello ${name.split(" ")[0]}');
```
### Consider using `=>` for short functions and methods
But only use `=>` when everything, including the function declaration, fits
on a single line.
Example:
```dart
// BAD:
String capitalize(String s) =>
'${s[0].toUpperCase()}${s.substring(1)}';
// GOOD:
String capitalize(String s) => '${s[0].toUpperCase()}${s.substring(1)}';
String capitalize(String s) {
return '${s[0].toUpperCase()}${s.substring(1)}';
}
```
### Use `=>` for inline callbacks that just return literals or switch expressions
If your code is passing an inline closure that merely returns a list or
map literal, or a switch expression, or is merely calling another function,
then if the argument is on its own line, then rather than using braces and a
return` statement, you can instead use the `=>` form. When doing this, the
closing `]`, `}`, or `)` bracket will line up with the argument name, for
named arguments, or the `(` of the argument list, for positional arguments.
For example:
```dart
// GOOD, but slightly more verbose than necessary since it doesn't use =>
@override
Widget build(BuildContext context) {
return PopupMenuButton<String>(
onSelected: (String value) { print('Selected: $value'); },
itemBuilder: (BuildContext context) {
return <PopupMenuItem<String>>[
PopupMenuItem<String>(
value: 'Friends',
child: MenuItemWithIcon(Icons.people, 'Friends', '5 new')
),
PopupMenuItem<String>(
value: 'Events',
child: MenuItemWithIcon(Icons.event, 'Events', '12 upcoming')
),
];
}
);
}
// GOOD, does use =>, slightly briefer
@override
Widget build(BuildContext context) {
return PopupMenuButton<String>(
onSelected: (String value) { print('Selected: $value'); },
itemBuilder: (BuildContext context) => <PopupMenuItem<String>>[
PopupMenuItem<String>(
value: 'Friends',
child: MenuItemWithIcon(Icons.people, 'Friends', '5 new')
),
PopupMenuItem<String>(
value: 'Events',
child: MenuItemWithIcon(Icons.event, 'Events', '12 upcoming')
),
]
);
}
```
The important part is that the closing punctuation lines up with the start
of the line that has the opening punctuation, so that you can easily determine
what's going on by just scanning the indentation on the left edge.
### Prefer single line for short collection-if and collection-for
If the code fits in a single line don't split it.
For example:
```dart
// BAD
final List<String> args = <String>[
'test',
if (useFlutterTestFormatter) '-rjson'
else '-rcompact',
'-j1',
if (!hasColor)
'--no-color',
for (final String opt in others)
opt,
];
// GOOD
final List<String> args = <String>[
'test',
if (useFlutterTestFormatter) '-rjson' else '-rcompact',
'-j1',
if (!hasColor) '--no-color',
for (final String opt in others) opt,
];
```
Otherwise indent with 2 spaces
```dart
// GOOD
final List<String> args = <String>[
'test',
if (useFlutterTestFormatter)
'-rjson.very.very.very.very.very.very.very.very.long'
else
'-rcompact.very.very.very.very.very.very.very.very.long',
'-j1',
if (!hasColor)
'--no-color.very.very.very.very.very.very.very.very.long',
for (final String opt in others)
methodVeryVeryVeryVeryVeryVeryVeryVeryVeryLong(opt),
];
```
### Put spread inside collection-if or collection-for on the same line
Spreads inside collection-if or collection-for are used to insert several elements. It's easier to read to have spread on the line of `if`, `else`, or `for`.
```dart
// BAD
final List<String> args = <String>[
'test',
if (condA)
...<String>[
'b',
'c',
]
else
'-rcompact',
for (final String opt in others)
...<String>[
m1(opt),
m2(opt),
],
];
// GOOD
final List<String> args = <String>[
'test',
if (condA) ...<String>[
'b',
'c',
] else
'-rcompact',
for (final String opt in others) ...<String>[
m1(opt),
m2(opt),
],
];
```
### Use braces for long functions and methods
Use a block (with braces) when a body would wrap onto more than one line (as opposed to using `=>`; the cases where you can use `=>` are discussed in the previous two guidelines).
### Separate the 'if' expression from its statement
(This is enforced by the `always_put_control_body_on_new_line` and `curly_braces_in_flow_control_structures` lints.)
Don't put the statement part of an 'if' statement on the same line as
the expression, even if it is short. (Doing so makes it unobvious that
there is relevant code there. This is especially important for early
returns.)
Example:
```dart
// BAD:
if (notReady) return;
// GOOD:
// Use this style for code that is expected to be publicly read by developers
if (notReady) {
return;
}
```
If the body is more than one line, or if there is an `else` clause, wrap the body in braces:
```dart
// BAD:
if (foo)
bar(
'baz',
);
// BAD:
if (foo)
bar();
else
baz();
// GOOD:
if (foo) {
bar(
'baz',
);
}
// GOOD:
if (foo) {
bar();
} else {
baz();
}
```
We require bodies to make it very clear where the bodies belong.
### Align expressions
Where possible, subexpressions on different lines should be aligned, to make the structure of the expression easier. When doing this with a `return` statement chaining `||` or `&&` operators, consider putting the operators on the left hand side instead of the right hand side.
```dart
// BAD:
if (foo.foo.foo + bar.bar.bar * baz - foo.foo.foo * 2 +
bar.bar.bar * 2 * baz > foo.foo.foo) {
// ...
}
// GOOD (notice how it makes it obvious that this code can be simplified):
if (foo.foo.foo + bar.bar.bar * baz -
foo.foo.foo * 2 + bar.bar.bar * 2 * baz > foo.foo.foo) {
// ...
}
// After simplification, it fits on one line anyway:
if (bar.bar.bar * 3 * baz > foo.foo.foo * 2) {
// ...
}
```
```dart
// BAD:
return foo.x == x &&
foo.y == y &&
foo.z == z;
// GOOD:
return foo.x == x &&
foo.y == y &&
foo.z == z;
// ALSO GOOD:
return foo.x == x
&& foo.y == y
&& foo.z == z;
```
### Prefer `+=` over `++`
We generally slightly prefer `+=` over `++`.
In some languages/compilers postfix `++` is an antipattern because of performance reasons, and so it's easier to just avoid it in general.
Because of the former, some people will use the prefix `++`, but this leads to statements that lead with punctuation, which is aesthetically displeasing.
In general, mutating variables as part of larger expressions leads to confusion about the order of operations, and entwines the increment with another calculation.
Using `++` does not make it obvious that the underlying variable is actually being mutated, whereas `+=` more clearly does (it's an assignment with an `=` sign).
Finally, `+=` is more convenient when changing the increment to a number other than 1.
### Use double literals for double constants
To make it clearer when something is a double or an integer, even if the number is a round number, include a decimal point in double literals. For example, if a function `foo` takes a double, write `foo(1.0)` rather than `foo(1)` because the latter makes it look like the function takes an integer.
Conventions
-----------
### Expectations around potential crashes in the engine
The engine should never crash in an uncontrolled fashion.
In unopt mode, the engine C++ code should have asserts that check for contract violations.
In opt debug mode, the `dart:ui` code should have asserts that check for contract violations. These asserts should have messages that are detailed and useful, if they are not self-explanatory.
In opt release mode, the exact behavior can be arbitrary so long as it is defined and non-vulnerable for every input. For example, a contract violation could be checked in Dart, with an exception thrown for invalid data; but equally valid would be for the C++ code to return early when faced with invalid data. The idea is to optimize for speed in the case where the data is valid.
For practical purposes we don't currently check for out-of-memory errors. Ideally we would.
### Features we expect every widget to implement
Now that the Flutter framework is mature, we expect every new widget to implement all of the following:
- full accessibility, so that on both Android and iOS the widget works with the native accessibility tools.
- full localization with default translations for all our default languages.
- full support for both right-to-left and left-to-right layouts, driven by the ambient Directionality.
- full support for text scaling up to at least 3.0x.
- documentation for every member; see the section above for writing prompts to write documentation.
- good performance even when used with large amounts of user data.
- a complete lifecycle contract with no resource leaks (documented, if it differs from usual widgets).
- tests for all the above as well as all the unique functionality of the widget itself.
It's the job of the programmer to provide these before submitting a PR.
It's the job of the reviewer to check that all these are present when reviewing a PR.
### Use of streams in Flutter framework code
In general we avoid the use of `Stream` classes in Flutter framework code (and `dart:ui`). Streams in general are fine and we encourage people to use them. However, they have some disadvantages and we prefer to keep them out of the framework for this reason. For example:
* Streams have a heavy API. For example, they can be synchronous or asynchronous, broadcast or single-client, and they can be paused and resumed. It is non-trivial to determine the right semantics for a particular stream when it will be used in all the ways framework code could be used, and it is non-trivial to fully implement the semantics correctly.
* Streams don't have a "current value" accessor, which makes them difficult to use in `build` methods.
* The APIs for manipulating streams are non-trivial (e.g. transformers).
We generally prefer `Listenable` subclasses (e.g. `ValueNotifier` or `ChangeNotifier`).
In the specific case of exposing a value from `dart:ui` via a callback, we expect the bindings in the framework to register a single listener and then provide a mechanism to fan the notification to multiple listeners. Sometimes this is a rather involved process (e.g. the `SchedulerBinding` exists almost entirely for the purpose of doing this for `onBeginFrame`/`onDrawFrame`, and the `GesturesBinding` exists exclusively for the purpose of doing this for pointer events). Sometimes it's simpler (e.g. propagating changes to life cycle events).
Packages
--------
### Structure
As per normal Dart conventions, a package should have a single import
that reexports all of its API.
> For example,
> https://github.com/flutter/flutter/blob/master/packages/flutter/lib/rendering.dart[rendering.dart]
> exports all of lib/src/rendering/*.dart
If a package uses, as part of its exposed API, types that it imports
from a lower layer, it should reexport those types.
> For example,
> https://github.com/flutter/flutter/blob/master/packages/flutter/lib/material.dart[material.dart]
> reexports everything from
> https://github.com/flutter/flutter/blob/master/packages/flutter/lib/widgets.dart[widgets.dart].
> Similarly, the latter
> https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/widgets/basic.dart[reexports]
> many types from
> https://github.com/flutter/flutter/blob/master/packages/flutter/lib/rendering.dart[rendering.dart],
> such as `BoxConstraints`, that it uses in its API. On the other
> hand, it does not reexport, say, `RenderProxyBox`, since that is not
> part of the widgets API.
Flutter packages should not have "private" APIs other than those that are
prefixed with underscores. Every file in a Flutter package should be exported.
("Private" files can still be imported so they are still actually public APIs;
by not exporting them explicitly we are tricking ourselves into thinking of
them as private APIs which may lead to poor design.)
When developing new features in Flutter packages, one should follow the philosophy:
> Only expose the APIs that are necessities to the features.
Since the private classes in dart language are file-bound, this may often result in
large file sizes. In Flutter, this is considered to be more preferable than creating
multiple smaller files but exposing intermediate classes that are not needed to use
the features.
### Import conventions
Under lib/src, for in-folder import use relative import. For cross-folder import,
import the entire package with absolute import.
When importing the `rendering.dart` library into higher level libraries,
if you are creating new
`RenderObject` subclasses, import the entire library. If you are only
referencing specific `RenderObject` subclasses, then import the
`rendering.dart` library with a `show` keyword explicitly listing the
types you are importing. This latter approach is generally good for
documenting why exactly you are importing particularly libraries and
can be used more generally when importing large libraries for very
narrow purposes.
By convention, `dart:ui` is imported using `import 'dart:ui' show
...;` for common APIs (this isn't usually necessary because a lower
level will have done it for you), and as `import 'dart:ui' as ui show
...;` for low-level APIs, in both cases listing all the identifiers
being imported. See
https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/painting/basic_types.dart[basic_types.dart]
in the `painting` package for details of which identifiers we import
which way. Other packages are usually imported undecorated unless they
have a convention of their own (e.g. `path` is imported `as path`).
The `dart:math` library is always imported `as math`.
### Deciding where to put code
As a general rule, if a feature is entirely self-contained (not requiring low-level integration into the Flutter framework) and is not something with universal appeal, we would encourage that that feature be provided as a package.
We try to be very conservative with what we put in the core framework, because there's a high cost to having anything there. We have to commit to supporting it for years to come, we have to document it, test it, create samples, we have to consider everyone's varied desires which they may have as they use the feature, we have to fix bugs. If there's design problems, we may not find out for a long time but then once we do we then have to figure out how to fix them without breaking people, or we have to migrate all our existing widgets to the new architecture, etc.
Basically, code is expensive. So before we take it, if possible, we like to see if we can prove the code's value. By creating a package, we can see if people use the feature, how they like it, whether it would be useful for the framework, etc, without having to take on the costs.
We have two main kinds of packages that are maintained by the Flutter team, both of which live in https://github.com/flutter/packages[flutter/packages]:
1. Regular packages, which are pure Dart. Packages can also be written and maintained by people outside the Flutter team.
2. Plugin packages, which provide access to platform features and therefore include native code (such as Java or Objective-C) as well as Dart.
You can also consider making an independent package. Packages are published to https://pub.dartlang.org/[pub].
Often once we have made a package we find that that is actually sufficient to solve the problem that the code sets out to solve, and there ends up being no need to bring it into the framework at all.