blob: 6077a252f52b0808d0fcff9fed1617a2bc4748e2 [file] [log] [blame] [view] [edit]
Dart style guide
================
The Fuchsia project follows the guidelines in [Effective Dart][effective-dart],
but with some additions.
All code must be formatted using `dartfmt` before being checked in.
# Additional Style Rules
### DON'T follow the Flutter repository style guide.
The Flutter project's style guide is meant to be used for code in the Flutter
framework itself. It is not intended as style guidance for projects using
Flutter. All code in the Fuchsia repository should follow the standard
Dart style. Although we are not following the style, the Flutter's guide on
documentation and development is still useful.
### DO use trailing commas on all tree structures longer than one line.
Without trailing commas, code that builds widget trees or similar types of
code tends to be hard to read. Adding the trailing commas allows `dartfmt`
to do its job correctly.
#### Without trailing commas:
``` dart
children.add(Expanded(
child: Center(
child: Container(
width: 64.0, height: 64.0, child: FuchsiaSpinner())),
));
```
#### With trailing commas:
``` dart
children.add(Expanded(
child: Center(
child: Container(
width: 64.0,
height: 64.0,
child: FuchsiaSpinner(),
),
),
));
```
### DO order members using the Dart Analyzer.
In Visual Studio Code, this is the Dart: Organize Members command available
in the Command Palette. (Control+Shift+P or View -> Command Palette)
This formatter doesnt appear to be available outside of the supported IDEs.
### PREFER to keep lines below 80 characters unless it would be more readable.
This is a slight amendment from the general Dart [rule][dartstyle-80-chars].
Unlike that rule, it is fine to have lines above 80 characters in the Fuchsia
repository, as long as it improves readability, and dartfmt won't automatically
truncate the line.
# Additional Usage Rules
## Repositories and Files
### DO prefix library names in `/lib` and `/public/lib` with `lib.`
#### Example:
```
Dart_library("lib.settings") {
package_name = "lib.settings"
...
}
```
### PREFER minimizing the number of public members exposed in a package.
This can be done by only making things public when needed, and keeping all
implementation detail libraries in the `/src` directory. Assume anything
public in the `lib` directory will be re-used.
### CONSIDER exporting publicly visible classes in a single `.dart` file.
For multiple classes that are used together but are in different files,
it’s more convenient for users of your library to import a single file rather
many at once. If the user wants narrower imports they can always restrict
visibility using the `show` keyword.
This also helps minimize the publicly visible surface.
Example:
``` dart
/// In src/apple.dart
class Apple {}
/// In src/orange.dart
class Orange {}
/// In src/veggies.dart
class Potato {}
class Tomato {}
/// In botanical_fruits.dart
export 'src/apple.dart';
export 'src/orange.dart';
// Can also be: export 'src/veggies.dart' hide Potato;
export 'src/veggies.dart' show Tomato;
/// In squeezer.dart
import 'package:plants/botanical_fruits.dart' show Orange;
```
### DO import all files within a package using relative paths.
Mixing and matching relative and absolute paths within a single package
causes Dart to act as if there were two separate imports of identical files,
which will introduce errors in typechecking. Either format works as long as
it is consistent. Within the Fuchsia repository, relative paths are used.
This does not apply to external libraries, as only the absolute path can be
used.
#### Good:
``` dart
import 'access_point.dart';
```
#### Bad:
``` dart
import 'package:wifi/access_point.dart';
```
### DO use namespacing when you import FIDL packages.
This adds clarity and readability. FIDL namespaces (library statements) are not
respected in Dart (e.g. `fuchsia.io.Node` becomes `Node`).
Because of tight namespaces, people tend to use more generic names in FIDL
(Error, File, Node, etc.), which result in more collisions/ambiguity in Dart.
#### Good:
``` dart
import 'package:fidl_fuchsia_file/fidl.dart' as file_fidl;
...
file_fidl.File.get(...) ...
```
#### Bad:
``` dart
import 'package:fidl_fuchsia_file/fidl.dart';
...
File.get(...) ...
```
### DO use namespacing when there is ambiguity, e.g. in class names.
There are often functions or classes that can collide, e.g. `File` or `Image`.
If you don't namespace, there will be a compile error.
#### Good:
``` dart
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
...
ui.Image(...) ...
```
#### Bad:
``` dart
import 'dart:ui';
import 'package:flutter/material.dart';
...
Image(...) ... // Which Image is this?
```
### PREFER to use `show` if you only have a few imports from that package. Otherwise, use `as`.
Using `show` can avoid collisions without requiring you to prepend
namespaces to types, leading to cleaner code.
#### Good:
``` dart
import 'package:fancy_style_guide/style.dart' as style;
import 'package:flutter/material.dart';
import 'package:math/simple_functions.dart' show Addition, Subtraction;
```
#### Bad:
``` dart
import 'package:flutter/material.dart show Container, Row, Column, Padding,
Expanded, ...;
```
## Coding practices
### DON'T use `new` or use `const` redundantly.
Dart 2 makes the `new` optional for constructors, with an aim at removing them
in time. The `const` keyword is also optional where it can be inferred by the
compiler.
`const` can be inferred in:
* A const collection literal.
* A const constructor call.
* A metadata annotation.
* The initializer for a const variable declaration.
* A switch case expression—the part right after case before the :, not
the body of the case.
This guidance will eventually be part of Effective Dart due to the changes for
Dart 2.
#### Good:
``` dart
final foo = Foo();
const foo = Foo();
const foo = const <Widget>[A(), B()];
const Foo(): bar = Bar();
```
#### Bad:
``` dart
final foo = new Foo();
const foo = const Foo();
foo = const [const A(), const B()];
const Foo(): bar = const Bar();
```
### DON'T do useful work in assert statements.
Code inside assert statements are not executed in production code. Asserts
should only check conditions and be side-effect free.
### PREFER to use `const` over `final` over `var`.
This minimizes the mutability for each member or local variable.
### PREFER return `Widget` instead of a specific type of Flutter widget.
As your project evolves, you may change the widget type that is returned in your
function. For example, you might wrap your widget with a Center. Returning
`Widget` simplifies the refactoring, as the method signature wouldn't have to
change.
#### Good:
``` dart
Widget returnContainerWidget() {
return Container();
}
```
#### Bad:
``` dart
Container returnContainerWidget() {
return Container();
}
```
# Additional Design Rules
### PREFER storing state in Models instead of state.
When storing state that Flutter widgets need to access, prefer to use
`ScopedModel` and `ScopedModelDescendant` instead of `StatefulWidget`.
A `StatefulWidget` should contain only internal widget state that can be lost
without any consequences.
Examples of stuff to store in a `ScopedModel`:
* User selections
* App state
* Anything that needs to be shared by widgets
Examples of stuff to store in a `StatefulWidget`'s `State`:
* Animation state that doesn't need to be controlled
### AVOID mixing named and positional parameters.
Instead, `@required` should be used in place of required positional parameters.
### PREFER named parameters.
In most situations, named parameters are less error prone and easier to read
than positional parameters, optional or not. They give users to pass in the
parameters in whatever order they please, and make Flutter trees especially
clearer.
In the Fuchsia repository, positional parameters should be reserved for simple
operational functions with only a few parameters.
#### Good:
``` dart
int add(int a, int b);
int addNumbers(int a, [int b, int c, int d]);
Foo fromJson(String json);
void load(String file);
Widget buildButton({
@required Widget child,
VoidCallback onTap,
double width,
bool isDisabled = false,
});
```
#### Bad:
``` dart
int add({int a, int b});
Foo fromJson({@required String json});
Widget buildButton(
Widget child,
VoidCallback onTap, [
double width,
bool isDisabled = false,
]);
```
### DO add [logging statements][dart-logging]
[effective-dart]: https://www.dartlang.org/guides/language/effective-dart
[dart-logging]: /docs/development/languages/dart/logging.md
[dartstyle-80-chars]: https://www.dartlang.org/guides/language/effective-dart/style#avoid-lines-longer-than-80-characters