The Fuchsia project follows the guidelines in Effective Dart, but with some additions.
All code must be formatted using dartfmt
before being checked in.
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.
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.
children.add(Expanded( child: Center( child: Container( width: 64.0, height: 64.0, child: FuchsiaSpinner())), ));
children.add(Expanded( child: Center( child: Container( width: 64.0, height: 64.0, child: FuchsiaSpinner(), ), ), ));
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 doesn’t appear to be available outside of the supported IDEs.
This is a slight amendment from the general Dart rule. 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.
/lib
and /public/lib
with lib.
Dart_library("lib.settings") { package_name = "lib.settings" ... }
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.
.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:
/// 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;
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.
import 'access_point.dart';
import 'package:wifi/access_point.dart';
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.
import 'package:fidl_fuchsia_file/fidl.dart' as file_fidl; ... file_fidl.File.get(...) ...
import 'package:fidl_fuchsia_file/fidl.dart'; ... File.get(...) ...
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.
import 'dart:ui' as ui; import 'package:flutter/material.dart'; ... ui.Image(...) ...
import 'dart:ui'; import 'package:flutter/material.dart'; ... Image(...) ... // Which Image is this?
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.
import 'package:fancy_style_guide/style.dart' as style; import 'package:flutter/material.dart'; import 'package:math/simple_functions.dart' show Addition, Subtraction;
import 'package:flutter/material.dart show Container, Row, Column, Padding, Expanded, ...;
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:
This guidance will eventually be part of Effective Dart due to the changes for Dart 2.
final foo = Foo(); const foo = Foo(); const foo = const <Widget>[A(), B()]; const Foo(): bar = Bar();
final foo = new Foo(); const foo = const Foo(); foo = const [const A(), const B()]; const Foo(): bar = const Bar();
Code inside assert statements are not executed in production code. Asserts should only check conditions and be side-effect free.
const
over final
over var
.This minimizes the mutability for each member or local variable.
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.
Widget returnContainerWidget() { return Container(); }
Container returnContainerWidget() { return Container(); }
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
:
Examples of stuff to store in a StatefulWidget
's State
:
Instead, @required
should be used in place of required positional 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.
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, });
int add({int a, int b}); Foo fromJson({@required String json}); Widget buildButton( Widget child, VoidCallback onTap, [ double width, bool isDisabled = false, ]);