This article is from the official Dart documentation.
The specification is mainly divided into four parts:
- Style specification
- Document specification
- Use standard
- The design specification
Each section has numerous examples, each of which begins with one of these five words:
- DO: indicates a practice that you need to follow
- DONT: Thats a very bad thing to do
- PREFER: In most cases, it is recommended
- AVOID: Something that should be avoided in most situations
- -Leonard: CONSIDER a course of action that requires your own deliberation
In my opinion, coding habits are individual and there is no single best solution.
If you’re developing alone, you don’t need to worry about these issues, but if your code needs to be shown to others, or if you need to collaborate with others, coding specifications are absolutely necessary.
Below, some of the most basic, most typical and high incidence situations will be selected from official documents as specifications.
✅ is a positive approach, ❌ is a negative approach
Style specification
named
DO: Classes, enumerations, type definitions, and generics all need to use camel – case nomenclature
✅ class SliderMenu {... } class HttpRequest { ... } typedef Predicate<T> = bool Function(T value);Copy the code
This should also be the case when using annotations
✅ class Foo {const Foo([arg]); } @Foo(anArg) class A { ... } @Foo() class B { ... }Copy the code
However, to annotate a class constructor, you may need to create an annotation variable that begins with a lower case
✅ const foo = foo (); @foo class C { ... }Copy the code
DO: Named libraries, packages, directories, and DART files should all be lowercase and underlined
✅ library peg_parser. Source_scanner; import'file_system.dart';
import 'slider_menu.dart';
Copy the code
❌ library pegparser. SourceScanner; import'file-system.dart';
import 'SliderMenu.dart';
Copy the code
DO: Names that will be referenced using as conversions should also be lowercase underscores
✅
import 'dart:math' as math;
import 'package:angular_components/angular_components'
as angular_components;
import 'package:js/js.dart' as js;
Copy the code
❌
import 'dart:math' as Math;
import 'package:angular_components/angular_components'
as angularComponents;
import 'package:js/js.dart' as JS;
Copy the code
DO: Variable names, method names, and parameter names should all start with a lower case camel case nomenclature
✅
var item;
HttpRequest httpRequest;
void align(bool clearItems) {
// ...
}
Copy the code
✅
const pi = 3.14;
const defaultTimeout = 1000;
final urlScheme = RegExp('^([a-z]+):');
class Dice {
static final numberGenerator = Random();
}
Copy the code
❌
const PI = 3.14;
const DefaultTimeout = 1000;
final URL_SCHEME = RegExp('^([a-z]+):');
class Dice {
static final NUMBER_GENERATOR = Random();
}
Copy the code
Curly braces
DO: When there is only one if statement with no else, and it is well represented on one line, curly braces are not needed
✅
if (arg == null) return defaultValue;
Copy the code
But if a line is too tight, curly braces are needed:
✅
if(overflowChars ! = other.overflowChars) {return overflowChars < other.overflowChars;
}
Copy the code
❌
if(overflowChars ! = other.overflowChars)return overflowChars < other.overflowChars;
Copy the code
Document specification
DO: In the Dart comment, it is recommended to use // rather than //
✅
/// The number of characters in this chunk when unsplit.
int get length => ...
Copy the code
❌
// The number of characters in this chunk when unsplit.
int get length => ...
Copy the code
As for why it was done, officials say it was for historical reasons and they thought it would be easier to read in some cases.
DO: Documentation comments should begin with a simple sentence
✅ /// Deletes the file at [path] from the file system. Void delete(String path) {... Deletes the file at [path] from the file system. }Copy the code
❌
/// Depending on the state of the file system and the user's permissions,
/// certain operations may or may not be possible. If there is no file at
/// [path] or it can't be accessed, this function throws either [IOError]
/// or [PermissionError], respectively. Otherwise, this deletes the file.
void delete(String path) {
...
}
Copy the code
DO: Separate the first sentence of the comment from the rest
✅
/// Deletes the file at [path].
///
/// Throws an [IOError] if the file could not be found. Throws a
/// [PermissionError] if the file is present but could not be deleted.
void delete(String path) {
...
}
Copy the code
❌
/// Deletes the file at [path]. Throws an [IOError] if the file could not
/// be found. Throws a [PermissionError] if the file is present but could
/// not be deleted.
void delete(String path) {
...
}
Copy the code
DO: Use square brackets to declare parameters, return values, and thrown exceptions
/// /// @param name the name of the flag. // @param abbr the abbreviationfor the flag.
/// @returns The new flag.
/// @throws ArgumentError If there is already an option with
/// the given name or abbreviation.
Flag addFlag(String name, String abbr) => ...
Copy the code
✅ // Defines a flag. /// /// Throws an [ArgumentError]if there is already an option named [name] or
/// there is already an option using abbreviation [abbr]. Returns the new flag.
Flag addFlag(String name, String abbr) => ...
Copy the code
Use standard
Rely on
PREFER: It is recommended to import dependencies using relative paths
If the project structure is as follows:
├─ trash ├─ trash ├─ trash ├─ trashCopy the code
You want to import utils.dart in api.dart
✅
import 'src/utils.dart';
Copy the code
❌
import 'package:my_package/src/utils.dart';
Copy the code
The assignment
DO: use?? Perform a conversion to the null value
In the dart?? The operator assigns a value when it is null. The following data
❌
if(optionalThing? .isEnabled) {print("Have enabled thing.");
}
Copy the code
When optionalThing is empty, the null pointer is abnormal.
So here’s the explanation. ? The isEnabled operator is called only if the optionalThing is not empty. If the optionalThing is empty, it returns null by default
Here’s how to do it
✅ // If you want to return when emptyfalseWords: optionalThing? .isEnabled ??false; // If you want to return ture when null: optionalThing? .isEnabled ??true;
Copy the code
❌ optionalThing? .isEnabled ==true; optionalThing? .isEnabled ==false;
Copy the code
string
Using + to concatenate two strings is not recommended in DART
DO: Use the enter key to separate strings directly
✅ raiseAlarm ('ERROR: Parts of the spaceship are on fire. Other '
'parts are overrun by martians. Unclear which are which.');
Copy the code
❌ raiseAlarm ('ERROR: Parts of the spaceship are on fire. Other ' +
'parts are overrun by martians. Unclear which are which.');
Copy the code
PREFER: use ${} to concatenate strings with variable values
✅
'Hello, $name! You are ${year - birth} years old.';
Copy the code
❌
'Hello, ' + name + '! You are ' + (year - birth).toString() + ' y... ';
Copy the code
A collection of
Dart creates an empty extensible List in two ways: [] and List(); There are three ways to create an empty HashMap: {}, Map(), and LinkedHashMap().
If you want to create an unextensible list or some other custom collection type, be sure to use constructors.
DO: Create collections using as simple literals as possible
✅
var points = [];
var addresses = {};
Copy the code
❌
var points = List();
var addresses = Map();
Copy the code
When you want to specify a type
✅
var points = <Point>[];
var addresses = <String, Address>{};
Copy the code
❌
var points = List<Point>();
var addresses = Map<String, Address>();
Copy the code
DON’t: DON’t use it.lenght
To indicate that a set is empty
✅
if (lunchBox.isEmpty) return 'so hungry... ';
if (words.isNotEmpty) return words.join(' ');
Copy the code
❌
if (lunchBox.length == 0) return 'so hungry... ';
if(! words.isEmpty)return words.join(' ');
Copy the code
CONSIDER using higher-order methods to transform sequences
var aquaticNames = animals
.where((animal) => animal.isAquatic)
.map((animal) => animal.name);
Copy the code
AVOID using iterable.foreach () with function literals
The forEach () function is widely used in JavaScript because the built-in for-in loop doesn’t do what you normally want. In Dart, if you want to iterate over a sequence, the conventional approach is to use loops.
✅
for (var person in people) {
...
}
Copy the code
❌ people. ForEach ((person) {... });Copy the code
DON’t: Do not use list.from () unless you intend to change the type of the result
There are two ways to get Iterable: list.from () and iterable.tolist ().
✅ // Create a List<int>: var iterable = [1, 2, 3]; / / output"List<int>":
print(iterable.toList().runtimeType);
Copy the code
❌ // Create a List<int>: var iterable = [1, 2, 3]; / / output"List<dynamic>":
print(List.from(iterable).runtimeType);
Copy the code
DO: Use whereType() to filter a collection by type
❌
var objects = [1, "a", 2, "b", 3];
var ints = objects.where((e) => e is int);
Copy the code
❌
var objects = [1, "a", 2, "b", 3];
var ints = objects.where((e) => e is int).cast<int>();
Copy the code
✅
var objects = [1, "a", 2, "b", 3];
var ints = objects.whereType<int>();
Copy the code
parameter
DO: use=
Set default values for the parameters
✅ void insert(Object item, {int at = 0}) {... }Copy the code
❌ void insert(Object item, {int at: 0}) {... }Copy the code
DON’t: Do not set the default value of the parameter to NULL
✅
void error([String message]) {
stderr.write(message ?? '\n');
}
Copy the code
❌
void error([String message = null]) {
stderr.write(message ?? '\n');
}
Copy the code
variable
AVOID: AVOID storing computable values
❌ class Circle {num _radius; num get radius => _radius;set radius(num value) {
_radius = value;
_recalculate();
}
num _area;
num get area => _area;
num _circumference;
num get circumference => _circumference;
Circle(this._radius) {
_recalculate();
}
void _recalculate() { _area = pi * _radius * _radius; _circumference = PI * 2.0 * _radius; }}Copy the code
✅ class Circle {num radius; Circle(this.radius); num get area => pi * radius * radius; Num get circumference => PI * 2.0 * RADIUS; }Copy the code
Members of the
DON’t: DON’t write unnecessary getters and setters
✅ class Box {var contents; }Copy the code
❌ class Box {var _contents; get contents => _contents;setcontents(value) { _contents = value; }}Copy the code
The constructor
DO: Use a simple initialization form whenever possible
❌ class Point {num x, y; Point(num x, num y) { this.x = x; this.y = y; }}Copy the code
✅ class Point {num x, y; Point(this.x, this.y); }Copy the code
DON’t: DON’t use itnew
To create an object
Dart does not require new
✅
Widget build(BuildContext context) {
return Row(
children: [
RaisedButton(
child: Text('Increment'),
),
Text('Click! '),]); }Copy the code
❌
Widget build(BuildContext context) {
return new Row(
children: [
new RaisedButton(
child: new Text('Increment'),
),
new Text('Click! '),]); }Copy the code
DON’t: DON’t use extraconst
Modify the object
✅ const primaryColors = [Color()"red", [255, 0, 0]),
Color("green", [0, 255, 0]),
Color("blue", [0, 0, 255]),];Copy the code
❌ const primaryColors = const [const Color()"red", const [255, 0, 0]),
const Color("green", const [0, 255, 0]),
const Color("blue", const [0, 0, 255]),
];
Copy the code
Exception handling
DO: userethrow
Rethrow an exception
❌
try {
somethingRisky();
} catch (e) {
if(! canHandle(e)) throw e; handle(e); }Copy the code
✅
try {
somethingRisky();
} catch (e) {
if(! canHandle(e)) rethrow; handle(e); }Copy the code
The design specification
AVOID: AVOID having a method return this in order to implement a streaming call
✅
var buffer = StringBuffer()
..write('one')
..write('two')
..write('three');
Copy the code
❌ var buffer = StringBuffer().write('one')
.write('two')
.write('three');
Copy the code
AVOID using FutureOr<T> as the return type
✅
Future<int> triple(FutureOr<int> value) async => (await value) * 3;
Copy the code
❌
FutureOr<int> triple(FutureOr<int> value) {
if (value is int) return value * 3;
return (value as Future<int>).then((v) => v * 3);
}
Copy the code
AVOID using a bool as an input parameter
❌ new Task (true);
new Task(false);
new ListBox(false.true.true);
new Button(false);
Copy the code
✅ Task. OneShot (); Task.repeating(); ListBox(scroll:true, showScrollbars: true);
Button(ButtonState.enabled);
Copy the code
DON’t: DON’t customize= =
Nulls in the operator method
✅ class Person {final String name; // ··· bool operator ==(other) => other is Person && name == other.name; int gethashCode => name.hashCode;
}
Copy the code
❌ class Person {final String name; // ··· bool operator ==(OTHER) => OTHER! = null && ... }Copy the code
The appendix
At last.
To learn about Flutter, you also need to know about this beautiful app😄
Todo-List