Important concepts
- Unlike Java, Dart doesn’t have the keywords
public
,protected
, andprivate
. We usually prepend an underscore ==(_)== to denote the constant is only used within this file, it means that members or classes that start with_
are private. - After Dart 2,
new Widget()
is equal toWidget()
, it means thatnew
andconst
keywords were made optional - Dart is ==single-threaded==.
Built-in types
Lists (Arrays)
- In Dart, ==arrays== are List objects.
Useful Methods
List.generate()
Variables
- According to the style guide recommendation , using
var
, rather than type annotations, for ==local variables==.
Types
根据情况判断哪些场景可以省略类型声明
Type annotating (显式声明)
-
不能一眼就看出 type 的 public fields 和 top-level variables
-
如果感觉 type 不够明显,就尽管显式声明 type
-
generic invocations 的某些情况:
var thing = Set<String>();
-
当 Dart 推断类型的结果不是你想要的时候:For example, you may want a variable’s type to be a supertype of the initializer’s type so that you can later assign some other sibling type to the variable.
-
Dart 在很多时候允许省略类型声明,因为它会尝试去推断变量的类型。但是推断也会有失败的时候,这是它会默默地将其类型置为
dynamic
。但是这对于别人来说会导致代码的可读性降低。如果真的想用dynamic
作为变量的类型,最好使用显式声明。 -
signatures in function type annotations : 关于
Function
这种 special type。
Omit types (省略声明)
-
在 private fields 中,可以更多地考虑省略类型声明,前提是变量的类型可以比较容易地推断出来
-
像 local variables (比如 function)这种作用域(scope)比较小的情况下,可以省略类型声明(使用
var
来代替) -
可推断参数类型(inferred parameter types)的函数表达式(function expressions):比如匿名函数,会被立即传入到一个带有某种 type 声明 callback 的方法中,比如
/* Function's parameter type is inferred based on the type of callback that map() expects */ // do var names = people.map((person) => person.name); // don't var names = people.map((Person person) => person.name);
-
Redundant type arguments on generic invocations
// do Set<String> things = Set(); // don't Set<String> things = Set<String>();
-
Don’t specify a return type for a setter, because setters always return
void
in Dart.
声明类型的选择
- DON’T use the legacy typedef syntax : 关于 defining a named
typedef
for a function type. 使用 new syntax 一个明显的好处就是 lacks the error-prone misfeature where a single identifier is treated as the parameter’s name instead of its type. - CONSIDER using function type syntax for parameters :当
Function
作为 parameters 的时候,如何书写格式能更直观地反映出此 parameter 的 type 是Function
和它的返回值是什么类型 - Do annotate with
Object
instead ofdynamic
to indicate any object is allowed: 要正确地针对不同的使用场景来使用这两种类型,因为 Two types in Dart permit all values:Object
anddynamic
。 - 当不需要 async function 返回有效值的时候,使用
Future<void>
。在旧的 Dart 版本,可以见到在这种情况下会使用Future
或者Future<Null>
,那是因为在旧版本中不支持void
as a type argument。 - AVOID using
FutureOr<T>
as a return type :避免增加额外的检查代码,统一将它当成Future
来处理。
Identifier
const
For variables
- The
const
keyword became optional within a constant context in Dart 2. - Use
const
for variables that you want to be compile-time constants. If the const variable is at the ==class level==, mark itstatic const
. Where you declare the variable, set the value to a compile-time constant such as a number or string literal, a const variable, or the result of an arithmetic operation on constant numbers
For constructors
- 如果一个 class 含有
const constructor
, 在创建实例的时候,如果想要这个实例是 compile-time constant ,在调用对象的构造函数时需要显式使用const
作为 prefix,否则只会创建一个普通的新实例,因为const constructor
同样也可以作为普通的构造函数来使用。 - If you have a class where all the fields are final, and the constructor does nothing but initialize them, you can make that constructor
const
.
covariant
The
covariant
keyword was introduced in 1.22. It replaces the@check
annotation.
-
这个关键字用来限定参数的子类型
- can be used in either the superclass or the subclass method, usually the best place is superclass
- The
covariant
keyword applies to a single parameter (这里并不是指使用了该 keyword 的 method 只能接受单个 parameter) and is also supported on setters and fields.
Functions
- 函数也是对象,它的数据类型是
Function
,因此可以赋值给变量,也可以作为参数传递给其他的函数 => expr
表示{ return expr; }
,在这个语句后面只能跟==一个==表达式 (expression)。有时候会成为 arrow syntaxparameters
可以连类型(tpyes)声明都省略,直接用参数名
Parameters
- 为了提高可读性,避免使用 positional ==boolean== parameters,应该考虑使用 named arguments, named constructors or named constants。
- 当一个 parameter 存在会被省略的可能,将它设置成 optional parameter 而不是强制用户传入
null
或者空字符串这类值作为参数,这样还可以避免一些 bug。 - DO use inclusive start and exclusive end parameters to accept a range
Optional parameters
Optional named parameters
如果想要在传入调用函数的参数使用 paramName: value
这种格式,则需要在定义函数的时候使用 {param1, param2, ...}
(注意不要漏掉了 {}
)。使用这种方式来指定参数的时候,如果想让某个参数为必填,则需要在参数类型前使用 @required
来注解,否则参数都是 optional 的
// define
// 'name' is required; 'age' and 'weight' are optional
void printInfo({@required String name, int age, int weight}) {...}
// call
printInfo(name: "Singor", age: 20);
Optional positional parameters
optional 的参数都放在 []
里面,否则参数都是 required 的
// define
// 'name' is required; 'age' and 'weight' are optional
void printInfo(String name, [int age, int weight]) {...}
// call
printInfo('Singor', 20);
Operators
条件表达式 (Conditional expressions)
// 跟 Java 的三目运算符一样
// condition ? expr1 : expr2
var visibility = isPublic ? 'public' : 'private';
// 非空返回
// expr1 ?? expr2
String playerName(String name) => name ?? 'Guest';
Control flow statements
Assert
- 如果结果为
false
的话,可以中断程序执行 - ==Assert== statements 不会对 production 的程序造成影响,它只会在 development 起作用
Generics
- Dart generic types are reified, which means that they carry their type information around at runtime
==Note:== Generics in Java use erasure , which means that generic type parameters are removed at runtime. In Java , you can test whether an object is a List
, but you can’t test whether it’s a List<String>
var names = List<String>();
names.addAll(['name', 'age', 'gender']);
print(names is List<String>); // true
- Restricting the parameterized type, use
extends
class Foo<T extends SomeBaseClass> { ... }
在新建实例的时候,可以使用 SomeBaseClass
它本身或者它的子类作为泛型参数,甚至不指明泛型参数也可以。
- Now, Dart’s generic supports classes, and generic methods allows type arguments on methods and functions.
Classes
-
use
?.
instead of.
to avoid an exception when the leftmost operand is ==null== -
新建对象的时候,从 Dart 2 开始,
new
关键字变成是 optional 的,用不用都无所谓 -
Constructing two identical compile-time constants results in a single, canonical instance
var a = const ImmutablePoint(1, 1); var b = const ImmutablePoint(1, 1); assert(identical(a, b)); // They are the same instance!
-
To get an object’s type at runtime, use Object’s
runtimeType
property. -
this
keyword: Thethis
keyword refers to the current instance. Usethis
only when there is a name conflict. Otherwise, Dart style omits thethis
.
Constructor
Types:
- Default constructor
- Named constructor
- Redirecting constructor
- Constant constructor
- Factory constructor
- The default constructor has no arguments and invokes the no-argument constructor in the superclass.
- ==Named constructors== is for implementing multiple constructors for a class or to provide extra clarity. 如果想让一个类有多个构造函数,在 Dart 中的语法会有所不同。
- Constructors are not inherited, which means that a superclass’s named constructor is not inherited by a subclass.
- By default, a constructor in a subclass calls the superclass’s unnamed, no-argument constructor. The superclass’s constructor is called at the beginning of the constructor body.
- Invoking a non-default superclass constructor:
- initializer list
- superclass’s no-arg constructor
- main class’s no-arg constructor
- If the superclass doesn’t have an unnamed, no-argument constructor, then you must manually call one of the constructors in the superclass. Specify the superclass constructor after a colon (
:
), just before the constructor body (if any) - The arguments to the superclass constructor are evaluated before invoking the constructor.
- Arguments to the superclass constructor do not have access to
this
. For example, arguments can call static methods but not instance methods. Initializer list
: 除了调用父类的构造函数之外,使用initializer list
可以在运行 constructor body 之前初始化实例变量。- During development, you can validate inputs by using
assert
in theinitializer list
. Initializer lists
are handy when setting up final fields.- A
redirecting constructor
’s body is empty. Constant constructors
: If your class produces objects that never change, you can make these objects compile-time constants. To do this, define aconst
constructor and make sure that all instance variables arefinal
.Factory constructors
: 使用factory
来修饰 constructor,这种构造函数可以从 cache 中返回实例或者返回 subtype 的实例,而==不会总是返回新创建==的实例。Factory constructors have no access tothis
.
Methods
- Getters and setters 使用
get
andset
keywords,这跟 Java 有所不同,跟 Kotlin 类似 Abstract methods
: 1. only exist inabstract classes
; 2. use a semicolon (;) instead of a method body.- 工具类可以使用 ==top-level== 机制的优势来建立
Abstract classes
Abstract classes
can’t be instantiated.- Useful for defining interfaces, often with some implementation.
Implicit interface
Every class implicitly defines an interface containing all the instance members of the class and of any interfaces it implements. A class implements one or more interfaces by declaring them in an
implements
clause and then providing the APIs required by the interfaces.
在 Dart 中没有单纯的 interface (即像其他语言一样使用 interface
关键字修饰的类),它令每个类都具有隐式 interface 的功能(即每个类都可以被 implemented)。Implement 了一个类之后可以使用它的 instance member(变量和方法)
Extending a class
Use
extends
to create a subclass, andsuper
to refer to the superclass.
Overridable operators
: 可以在类的内部对运算符的功能进行重新,比如+
、-
等。noSuchMethod()
: 这个方法用来处理:当程序想要调用一个类并未定义的方法时,这个方法就会被调用,可以在这个方法里面编写自己所需要的逻辑。这个方法用于调试目的。
Enumerated types
- Each value in an enum has a
index
getter, zero-based of the value in the enum declaration. - To get a list of all the values in the enum, use the enum’s
values
constant.
Singleton
- 可以使用
Factory constructors
, 参考这里 - 直接使用 ==top-level== variables 的天然优势
Mixins
Mixins are a way of reusing a class’s code in multiple class hierarchies.
- To use a mixin, use the
with
keyword followed by one or more mixin names.
Static variables & methods
Use the
static
keyword to implement class-wide variables and methods.
- Static variables aren’t initialized until they’re used.
- Using ==lowerCamelCase== for constant names follow by style guide recommendation.
- Consider using top-level functions, instead of static methods, for common or widely used utilities and functionality.
- You can use static methods as compile-time constants. For example, you can pass a static method as a parameter to a constant constructor.
Exception
includes Throw, Catch and Finally
- In contrast to Java, all of Dart’s exceptions are unchecked exceptions. Methods do not declare which exceptions they might throw, and you are not required to catch any exceptions
Catch
- You can use either
on
orcatch
or ==both==. Useon
when you need to specify the exception type. Usecatch
when your exception handler needs the exception object. catch()
can have one or two parameters. The first is the exception that was thrown, and the second is the stack trace.- To partially handle an exception, while allowing it to propagate, use the
rethrow
.
Libraries and visibility
Using libraries
For built-in libraries, the URI has the special dart:
scheme. For other libraries, you can use a file system path or the package:
scheme. The package:
scheme specifies libraries provided by a package manager such as the ==pub tool==.
Specifying a library prefix
If you import two libraries that have conflicting identifiers, then you can specify a prefix for one or both libraries.
import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;
Element ele1 = Element();
lib2.Element ele2 = lib2.Element();
Importing partly
If you want to use only part of a library, you can selectively import the library. ( Tree-shaking can avoid this automatically )
// Import only foo.
import 'package:lib1/lib1.dart' show foo;
// Import all names EXCEPT foo.
import 'package:lib2/lib2.dart' hide foo;
Lazily loading (Deffered loading)
Load a library on demand, if and when it’s needed.
Use cases:
- To reduce an app’s initial startup time.
- To perform A/B testing—trying out alternative implementations of an algorithm, for example.
- To load rarely used functionality, such as optional screens and dialogs.
// To lazily load a library, using `deferred as`
import 'package:greetings/hello.dart' deferred as hello;
// When you need the library, invoke loadLibrary() using the library's identifier
Future greet() async {
await hello.loadLibrary();
hello.printGreeting();
}
Keep in mind the following when you use deferred loading:
- You can invoke
loadLibrary()
multiple times on a library without problems. The library is loaded only once. - A deferred library’s constants aren’t constants in the importing file. Remember, these constants don’t exist until the deferred library is loaded.
- You can’t use types from a deferred library in the importing file. Instead, consider moving interface types to a library imported by both the deferred library and the importing file.
- Dart implicitly inserts
loadLibrary()
into the namespace that you define usingdeferred as
namespace. TheloadLibrary()
function returns a ==Future==.
如何创建自己的 library package
Asynchrony
Code that uses
async
andawait
is asynchronous.
-
To use
await
, code must be in an async function - a function marked asasync
Future checkVersion() async { var version = await lookUpVersion(); }
-
Before you directly use the Future API, consider using
await
instead. Code that usesawait
expressions can be easier to understand than code that uses the Future API. -
In
await <expression>
, the value of expression is usually a ==Future==; if it isn’t, then the value is automatically wrapped in a Future. The await expression makes execution pause until that object is available. -
Although an async function might perform time-consuming operations, it doesn’t wait for those operations. Instead, the async function executes only until it encounters its first
await
expression. Then it returns a Future object, resuming execution only after theawait
expression completes.
Future API vs Await
- If the callback registered with
then()
returns a Future,then()
returns an ==equivalent Future==. If the callback returns a value of any other type,then()
==creates a new Future== that completes with the value.
// Use future api
Future result = costlyQuery(url);
result.then((value) => expensiveWork(value))
.then((_) => lengthyComputation())
.then((_) => print('Done!'))
.catchError((exception) {
// then().catchError() pattern is the asynchronous version of try-catch
/* Handle exception... */
});
// Use await
try {
final value = await costlyQuery(url);
await expensiveWork(value);
await lengthyComputation();
print('Done!');
} catch (e) {
/* Handler exception... */
}
- Version note: In Dart 1.x, async functions immediately suspended execution. In Dart 2, instead of immediately suspending, async functions execute synchronously until the first
await
orreturn
. - By convention, an unused argument is named
_
(undersocre).
Waiting for multiple futures
Use the
Future.wait()
static method to manage multiple Futures and wait for them to complete before continuing.
Future deleteLotsOfFiles() async => ...;
Future copyLotsOfFiles() async => ...;
Future checksumLotsOfOtherFiles() async => ...;
await Future.wait([
deleteLotsOfFiles(),
copyLotsOfFiles(),
checksumLotsOfOtherFiles(),
]);
print('Done with all the long steps!');