Fultter入门必备 Dart语法篇

407 阅读17分钟

Dart

  1. 基础语法

    • 定义变量

      // 定义变量
      var name = 'bryce';   //使用var 定义变量string类型后无法修改值的类型
      name = 123   // error  无法改变值的类型
      
      // 默认值
      int lineCount;   // 定义一个变量不赋值  默认赋值为Null
      lineCount == null    // true
      
      // 可选的类型
      String name = 'bryce' // 定义一个类型为String的变量类型,
          
      /*tips:添加类型可以更加清晰的表达你的意图。 IDE 编译器等工具有可以使用类型来更好的帮助你, 可以提供代码补全、提前发现 bug 等功能。*/
      
    • 内置类型

      Dart 内置支持下面这些类型:
      1.numbers (数值)
      • int 整数

      • double 浮点数

      • num 基本操作符 是int和double的父类

        例如+,-,/,* 还定义了 abs()ceil()、和 floor() 等 函数。 (位操作符,例如 >> 定义在 int 类中。)

      some Function

      var one = int.parse('1');
      assert(one == 1);
      
      
      // String -> double
      var onePointOne = double.parse('1.1');
      assert(onePointOne == 1.1);  // True
      
      // int -> String
      String oneAsString = 1.toString();
      assert(oneAsString == '1');   //True
      
      // double -> String
      String piAsString = 3.14159.toStringAsFixed(2);
      assert(piAsString == '3.14');  //True
      
      2.strings

      Dart 字符串是 UTF-16 编码的字符序列。 可以使用单引号或者双引号来创建字符串:

      var s1 = 'Single quotes work well for string literals.';
      var s2 = 'It\'s easy to escape the string delimiter.';
      
      
      // 字符串中使用表达式 
      var k1 = 'world';
      var s4 =  'hello ${k1}';
      
      // 字符串连接 使用 + 号 连接字符串
      var t1 = "The + operator"
          	 + "works, as well.“
      // 三个单引号双引号也可以创建多行字符串
      """hello
          world"""
          
      
      3.booleans

      当 Dart 需要一个布尔值的时候,只有 true 对象才被认为是 true。 所有其他的值都是 flase。这点和 JavaScript 不一样, 像 1"aString"、 以及 someObject 等值都被认为是 false。

      4.lists (也被称之为 arrays)

      在 Dart 中数组就是 List 对象。所以 通常我们都称之为 lists

      //下面是一个 Dart list 的示例:
      var list = [1, 2, 3];
      
      /*
      Lists 的下标索引从 0 开始
      第一个元素的索引是 0. list.length - 1 是最后一个元素的索引。 
      访问 list 的长度和元素与 JavaScript 中的用法一样:*/
      var list = [1, 2, 3];
      llist.length  // 3
      list[1] = 1;  //true
      list[1] == 1  // true
          
          
      //在 list 字面量之前添加 const 关键字,可以 定义一个不变的 list 对象(编译时常量):
      var constlist = const [1,2,3] // 定义一个常量值
      constlist[1] = 5   // Uncommenting this causes an error.
          
      
      
      5.maps

      通常来说,Map 是一个键值对相关的对象。 键和值可以是任何类型的对象。每个 只出现一次, 而一个值则可以出现多次。

      // 创建Map的方法和js一样
      var gifts = {
      // Keys      Values
        'first' : 'partridge',
        'second': 'turtledoves',
        'fifth' : 'golden rings'
      };
      
      // 使用 Map 构造函数也可以实现同样的功能:
      var gifts = new Map();
      gifts['first'] = 'partridge';
      gifts['second'] = 'turtledoves';
      gifts['fifth'] = 'golden rings';
      
      // 添加值
      gifts['child'] = 'children';
      
      gifts['child'] == 'children';   // ture
      
      // 获取map键值对的数目
      gifts.length   //  4
          
      // 同样使用 const 可以创建一个 编译时常量的 map:
      final constMap = const {
        2: 'helium',
        10: 'neon',
        18: 'argon'
      }
      coustMap[2] = 'hu';   // Uncommenting this causes an error.
      
      
      
      
          
      
      6.runes (用于在字符串中表示 Unicode 字符)

      在 Dart 中,runes 代表字符串的 UTF-32 code points。

      Unicode 为每一个字符、标点符号、表情符号等都定义了 一个唯一的数值。 由于 Dart 字符串是 UTF-16 code units 字符序列, 所以在字符串中表达 32-bit Unicode 值就需要 新的语法了。

      通常使用 \uXXXX 的方式来表示 Unicode code point, 这里的 XXXX 是4个 16 进制的数。 例如,心形符号 (♥) 是 \u2665。 对于非 4 个数值的情况, 把编码值放到大括号中即可。 例如,笑脸 emoji (😆) 是 \u{1f600}

      String 类 有一些属性可以提取 rune 信息。 codeUnitAtcodeUnit 属性返回 16-bit code units。 使用 runes 属性来获取字符串的 runes 信息。

        var clapping = '\u{1f44f}';
        print(clapping);
        print(clapping.codeUnits);
        print(clapping.runes.toList());
        Runes input = new Runes(
            '\u2665  \u{1f605}  \u{1f60e}  \u{1f47b}  \u{1f596}  \u{1f44d}');
        print(new String.fromCharCodes(input));
      
      7.symbols

2.Functions(方法)

​ Dart 是一个真正的面向对象语言,方法也是对象并且具有一种 类型, Function。 这意味着,方法可以赋值给变量,也可以当做其他方法的参数。 也可以把 Dart 类的实例当做方法来调用。

下面是定义方法的示例:

bool isNoble(int atomicNumber) {
  return _nobleGases[atomicNumber] != null;
}

/* 解析:bool定义的是方法需要返回一个布尔值
       isNoble 方法名
       int atomicNumber 传入为int形的参数
/*

// 虽然在 Effective Dart 中推荐 在公开的 APIs 上使用静态类型, 你当然也可以选择忽略类型定义:

isNoble(atomicNumber) {
  return _nobleGases[atomicNumber] != null;
}

// Tips: 那这样其实也和咱们js一样了嘛 但是要记住一点 加入类型的断定可以帮助提前发现错误,减少语法bug


//对于只有一个表达式的方法,你可以选择 使用缩写语法来定义:
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;   //语法类似es6箭头函数




Optional parameters(可选参数)

可选参数可以是命名参数或者基于位置的参数,但是这两种参数不能同时当做可选参数。

Optional named parameters(可选命名参数)

调用方法的时候,你可以使用这种形式 *paramName*: *value* 来指定命名参数。例如:

// 定义方法  参数名为:bold ,hidden 
enableFlags({bool bold, bool hidden}) {
  // ...
}

// 传入参数名为:bold,hidden
enableFlags(bold: true, hidden: false);


Optional positional parameters(可选位置参数)

把一些方法的参数放到 [] 中就变成可选 位置参数了:

//  定义一个String类型的位置参数 [String device]
String say(String from, String msg, [String device]) {
  var result = '$from says $msg';
  if (device != null) {
    result = '$result with a $device';
  }
  return result;
}

// 不使用可选参数调用上面方法的示例:
say('Bob', 'Howdy') == 'Bob says Howdy';

// 使用可选参数调用上面方法的示例:
say('Bob', 'Howdy', 'smoke signal') ==
    'Bob says Howdy with a smoke signal';


Default parameter values(默认参数值)

在定义方法的时候,可以使用 = 来定义可选参数的默认值。 默认值只能是编译时常量。 如果没有提供默认值,则默认值为 null

下面是设置可选参数默认值的示例:

void enableFlags({bool bold = false, bool hidden = false}) {
  // ...
}
// 调用enableFlags 因此 enableFlags 内部 bold = true  hidden默认false
enableFlags(bold:true)

    
    

下面的示例显示了如何设置位置参数的默认值:

String say(String from, String msg,[String device = 'carrier pigeon', String mood]) {
  var result = '$from says $msg';
  if (device != null) {
    result = '$result with a $device';
  }
  if (mood != null) {
    result = '$result (in a $mood mood)';
  }
  return result;
}

say('Bob', 'Howdy') == 'Bob says Howdy with a carrier pigeon'  // True
    

还可以使用 list 或者 map 作为默认值。 下面的示例定义了一个方法 doStuff(), 并分别为 listgifts 参数指定了 默认值。

void doStuff({List<int> list = const [1, 2, 3],
    Map<String, String> gifts = const {
      'first': 'paper',
      'second': 'cotton',
      'third': 'leather'
    }}) {
  print('list:  $list');
  print('gifts: $gifts');
}
The main() function(入口函数)

每个应用都需要有个顶级的 main() 入口方法才能执行。 main() 方法的返回值为 void 并且有个可选的 List<String> 参数。

下面是一个 web 应用的 main() 方法:

void main() {
  querySelector("#sample_text_id")
    ..text = "Click me!"
    ..onClick.listen(reverseText);
}

// 注意: 前面代码中的 .. 语法为 级联调用(cascade)。 使用级联调用语法, 你可以在一个对象上执行多个操作。
// 类似js的链式调用
new Promise().then().then().catch()

下面是一个命令行应用的 main() 方法,并且使用了 方法参数作为输入参数:

// Run the app like this: dart args.dart 1 test
void main(List<String> arguments) {
  print(arguments);

  assert(arguments.length == 2);
  assert(int.parse(arguments[0]) == 1);
  assert(arguments[1] == 'test');
}
Functions as first-class objects(一等方法对象)

可以把方法当做参数调用另外一个方法。例如:

printElement(element) {
  print(element);  
}

var list = [1, 2, 3];

// Pass printElement as a parameter.
list.forEach(printElement);

方法也可以赋值给一个变量:

var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
assert(loudify('hello') == '!!! HELLO !!!');
Anonymous functions(匿名方法)

大部分方法都带有名字,例如 main() 或者 printElement()。 你有可以创建没有名字的方法,称之为 匿名方法

/*
下面的代码定义了一个参数为i (该参数没有指定类型)的匿名函数。 list 中的每个元素都会调用这个函数来 打印出来,同时来计算了每个元素在 list 中的索引位置。
/*
var list = ['apples', 'oranges', 'grapes', 'bananas', 'plums'];
list.forEach((i) {
  print(list.indexOf(i).toString() + ': ' + i);
});

//如果方法只包含一个语句,可以使用`箭头函数`
list.forEach((i) => print(list.indexOf(i).toString() + ': ' + i));
Lexical scope(静态作用域)

Dart 是静态作用域语言,变量的作用域在写代码的时候就确定过了。 基本上大括号里面定义的变量就 只能在大括号里面访问

var topLevel = true;

main() {
  var insideMain = true;

  myFunction() {
    var insideFunction = true;

    nestedFunction() {
      var insideNestedFunction = true;

      assert(topLevel);
      assert(insideMain);
      assert(insideFunction);
      assert(insideNestedFunction);
    }
  }
}

// 以上nestedFunction方法里可以访问任何上面定义的任何变量
Lexical closures(闭包)

一个 闭包 是一个方法对象,不管该对象在何处被调用, 该对象都可以访问其作用域内 的变量。

Function makeAdder(num addBy) {
  return (num i) => addBy + i;
}

main() {
  // Create a function that adds 2.
  var add2 = makeAdder(2);

  // Create a function that adds 4.
  var add4 = makeAdder(4);

  assert(add2(3) == 5);
  assert(add4(3) == 7);
}
Testing functions for equality(测试函数是否相等)
foo() {}               // A top-level function

class A {
  static void bar() {} // A static method
  void baz() {}        // An instance method
}

main() {
  var x;

  // Comparing top-level functions.
  x = foo;
  assert(foo == x);

  // Comparing static methods.
  x = A.bar;
  assert(A.bar == x);

  // Comparing instance methods.
  var v = new A(); // Instance #1 of A
  var w = new A(); // Instance #2 of A
  var y = w;
  x = w.baz;
    
    /*
    这些闭包引用同一个实例,
    所以它们是相等的。
    */
  assert(y.baz == x);
    /*
    这些闭包引用不同的实例,
    所以他们是不平等的。
    */
  assert(v.baz != w.baz);
}

3.Operators(操作符)

下表是 Dart 中定义的操作符。 很多操作符都可以重载,详情参考 Overridable operators

描述 操作符
unary postfix *expr*++ *expr*-- () [] . ?.
unary prefix -*expr* !*expr* ~*expr* ++*expr* --*expr*
multiplicative * / % ~/
additive + -
shift << >>
bitwise AND &
bitwise XOR ^
bitwise OR |
relational and type test >= > <= < as is is!
equality == !=
logical AND &&
logical OR ||
if null ??
conditional *expr1* ? *expr2* : *expr3*
cascade ..
assignment = *= /= ~/= %= += -= <<= >>= &= ^= |= ??=
Type test operators(类型判定操作符)

asis、 和 is! 操作符是在运行时判定对象 类型的操作符:

Type test operators(类型判定操作符)

asis、 和 is! 操作符是在运行时判定对象 类型的操作符:

操作符 解释
as 类型转换
is 如果对象是指定的类型返回 True
is! 如果对象是指定的类型返回 False
Conditional expressions(条件表达式)

Dart 有一个特殊的操作符

*expr1* ?? *expr2*

如果 expr1 是 non-null,返回其值; 否则执行 expr2 并返回其结果。

类似 JavaScript: expr1 || expr2

Cascade notation (..)(级联操作符)

级联操作符 (..) 可以在同一个对象上 连续调用多个函数以及访问成员变量。 使用级联操作符可以避免创建 临时变量, 并且写出来的代码看起来 更加流畅:

querySelector('#button') // Get an object.
  ..text = 'Confirm'   // Use its members.
  ..classes.add('important')
  ..onClick.listen((e) => window.alert('Confirmed!'));

类似 JavaScript: 链式调用

上面的代码和下面的代码功能一样:

var button = querySelector('#button');
button.text = 'Confirm';
button.classes.add('important');
button.onClick.listen((e) => window.alert('Confirmed!'));
Other operators(其他操作符)

下面是其他操作符:

Operator Name Meaning
() 使用方法 代表调用一个方法
[] 访问 List 访问 list 中特定位置的元素
. 访问 Member 访问元素,例如 foo.bar 代表访问 foobar 成员
?. 条件成员访问 . 类似,但是左边的操作对象不能为 null,例如 foo?.bar 如果 foo 为 null 则返回 null,否则返回 bar 成员

4.Control flow statements(流程控制语句)

可以使用下面的语句来控制 Dart 代码的流程:

  • if and else
  • for loops
  • while and do-while loops
  • break and continue
  • switch and case
  • assert

Dart for 循环中的闭包会捕获循环的 index 索引值, 来避免 JavaScript 中常见的问题。例如:

var callbacks = [];
for (var i = 0; i < 2; i++) {
  callbacks.add(() => print(i));
}
callbacks.forEach((c) => c());
Assert(断言)

如果条件表达式结果不满足需要,则可以使用 assert 语句俩打断代码的执行。 下面介绍如何使用断言。 下面是一些示例代码:

// Make sure the variable has a non-null value.
assert(text != null);

// Make sure the value is less than 100.
assert(number < 100);

// Make sure this is an https URL.
assert(urlString.startsWith('https'));

注意: 断言只在检查模式下运行有效,如果在生产模式 运行,则断言不会执行。

这里不过多介绍了 其他用法与javaScript都是一样的

5.Exceptions(异常)

代码中可以出现异常和捕获异常。异常表示一些 未知的错误情况。如果异常没有捕获, 则异常会抛出,导致 抛出异常的代码终止执行。

Throw

下面是抛出或者 扔出一个异常的示例:

throw new FormatException('Expected at least 1 section');

还可以抛出任意的对象:

throw 'Out of llamas!';
Catch

捕获异常可以避免异常继续传递(你重新抛出rethrow异常除外)。 捕获异常给你一个处理 该异常的机会:

try {
  breedMoreLlamas();
} on OutOfLlamasException {
  // A specific exception
  buyMoreLlamas();
} on Exception catch (e) {
  // Anything else that is an exception
  print('Unknown exception: $e');
} catch (e) {
  // No specified type, handles all
  print('Something really unknown: $e');
}

如之前代码所示,你可以使用on 或者 catch 来声明捕获语句,也可以 同时使用。使用 on 来指定异常类型,使用 catch 来 捕获异常对象。

使用 rethrow 关键字可以 把捕获的异常给 重新抛出。

final foo = '';

void misbehave() {
  try {
    foo = "You can't change a final variable's value.";
  } catch (e) {
    print('misbehave() partially handled ${e.runtimeType}.');
    rethrow; // Allow callers to see the exception.
  }
}

void main() {
  try {
    misbehave();
  } catch (e) {
    print('main() finished handling ${e.runtimeType}.');
  }
}
Finally

要确保某些代码执行,不管有没有出现异常都需要执行,可以使用 一个 finally 语句来实现。如果没有 catch 语句来捕获异常, 则在执行完 finally 语句后, 异常被抛出了:

try {
  breedMoreLlamas();
} finally {
  // Always clean up, even if an exception is thrown.
  cleanLlamaStalls();
}

6.Classes

Dart 是一个面向对象编程语言,同时支持基于 mixin 的继承机制。 每个对象都是一个类的实例,所有的类都继承于 Object.基于 Mixin 的继承 意味着每个类(Object 除外) 都只有一个超类,一个类的代码可以在其他 多个类继承中重复使用。

使用 new 关键字和构造函数来创建新的对象。 构造函数名字可以为 *ClassName* 或者*ClassName*.*identifier*。例如:

var jsonData = JSON.decode('{"x":1, "y":2}');

// Create a Point using Point().
var p1 = new Point(2, 2);

// Create a Point using Point.fromJson().
var p2 = new Point.fromJson(jsonData);

有些类提供了常量构造函数。使用常量构造函数 可以创建编译时常量,要使用常量构造函数只需要用 const 替代 new 即可:

var p = const ImmutablePoint(2, 2);

两个一样的编译时常量其实是 同一个对象:

var a = const ImmutablePoint(1, 1);
var b = const ImmutablePoint(1, 1);

assert(identical(a, b)); // They are the same instance!

每个实例变量都会自动生成一个 getter 方法(隐含的)。 Non-final 实例变量还会自动生成一个 setter 方法。详情, 参考 Getters and setters

class Point {
  num x;
  num y;
}

main() {
  var point = new Point();
  point.x = 4;          // Use the setter method for x.
  assert(point.x == 4); // Use the getter method for x.
  assert(point.y == null); // Values default to null.
}
Constructors

定义一个和类名字一样的方法就定义了一个构造函数 还可以带有其他可选的标识符,详情参考 Named constructors)(命名构造函数)。 常见的构造函数生成一个 对象的新实例:

class Point {
  num x;
  num y;

  Point(num x, num y) {
    // There's a better way to do this, stay tuned.
    this.x = x;
    this.y = y;
  }
}

this 关键字指当前的实例。

注意: 只有当名字冲突的时候才使用 this。否则的话, Dart 代码风格样式推荐忽略 this

Invoking a non-default superclass constructor(调用超类构造函数)

默认情况下,子类的构造函数会自动调用超类的 无名无参数的默认构造函数。 超类的构造函数在子类构造函数体开始执行的位置调用。 如果提供了一个 initializer list(初始化参数列表) ,则初始化参数列表在超类构造函数执行之前执行。 下面是构造函数执行顺序:

  1. initializer list(初始化参数列表)
  2. superclass’s no-arg constructor(超类的无名构造函数)
  3. main class’s no-arg constructor(主类的无名构造函数)

如果超类没有无名无参数构造函数, 则你需要手工的调用超类的其他构造函数。 在构造函数参数后使用冒号 (:) 可以调用 超类构造函数。

class Person {
  String firstName;

  Person.fromJson(Map data) {
    print('in Person');
  }
}

class Employee extends Person {
  // Person does not have a default constructor;
  // you must call super.fromJson(data).
  Employee.fromJson(Map data) : super.fromJson(data) {
    print('in Employee');
  }
}

main() {
  var emp = new Employee.fromJson({});

  // Prints:
  // in Person
  // in Employee
  if (emp is Person) {
    // Type check
    emp.firstName = 'Bob';
  }
  (emp as Person).firstName = 'Bob';
}

由于超类构造函数的参数在构造函数执行之前执行,所以 参数可以是一个表达式或者 一个方法调用:

class Employee extends Person {
  // ...
  Employee() : super.fromJson(findDefaultData());
}

注意: 如果在构造函数的初始化列表中使用 super(),需要把它放到最后。 详情参考 Dart 最佳实践

警告: 调用超类构造函数的参数无法访问 this。 例如,参数可以为静态函数但是不能是实例函数。

Initializer list(初始化列表)

在构造函数体执行之前除了可以调用超类构造函数之外,还可以 初始化实例参数。 使用逗号分隔初始化表达式。

class Point {
  num x;
  num y;

  Point(this.x, this.y);

  // Initializer list sets instance variables before
  // the constructor body runs.
  Point.fromJson(Map jsonMap)
      : x = jsonMap['x'],
        y = jsonMap['y'] {
    print('In Point.fromJson(): ($x, $y)');
  }
}

警告: 初始化表达式等号右边的部分不能访问 this

Redirecting constructors(重定向构造函数)

有时候一个构造函数会调动类中的其他构造函数。 一个重定向构造函数是没有代码的,在构造函数声明后,使用 冒号调用其他构造函数。

class Point {
  num x;
  num y;

  // The main constructor for this class.
  Point(this.x, this.y);

  // Delegates to the main constructor.
  Point.alongXAxis(num x) : this(x, 0);
}
Constant constructors(常量构造函数)

如果你的类提供一个状态不变的对象,你可以把这些对象 定义为编译时常量。要实现这个功能,需要定义一个 const 构造函数, 并且声明所有类的变量为 final

class ImmutablePoint {
  final num x;
  final num y;
  const ImmutablePoint(this.x, this.y);
  static final ImmutablePoint origin =
      const ImmutablePoint(0, 0);
}
Factory constructors(工厂方法构造函数)

如果一个构造函数并不总是返回一个新的对象,则使用 factory 来定义 这个构造函数。例如,一个工厂构造函数 可能从缓存中获取一个实例并返回,或者 返回一个子类型的实例。

下面代码演示工厂构造函数 如何从缓存中返回对象。

class Logger {
  final String name;
  bool mute = false;

  // _cache is library-private, thanks to the _ in front
  // of its name.
  static final Map<String, Logger> _cache =
      <String, Logger>{};

  factory Logger(String name) {
    if (_cache.containsKey(name)) {
      return _cache[name];
    } else {
      final logger = new Logger._internal(name);
      _cache[name] = logger;
      return logger;
    }
  }

  Logger._internal(this.name);

  void log(String msg) {
    if (!mute) {
      print(msg);
    }
  }
}

注意: 工厂构造函数无法访问 this

使用 new 关键字来调用工厂构造函数。

var logger = new Logger('UI');
logger.log('Button clicked');
Getters and setters

Getters 和 setters 是用来设置和访问对象属性的特殊 函数。每个实例变量都隐含的具有一个 getter, 如果变量不是 final 的则还有一个 setter。 你可以通过实行 getter 和 setter 来创建新的属性, 使用 getset 关键字定义 getter 和 setter:

class Rectangle {
  num left;
  num top;
  num width;
  num height;

  Rectangle(this.left, this.top, this.width, this.height);

  // Define two calculated properties: right and bottom.
  num get right             => left + width;
      set right(num value)  => left = value - width;
  num get bottom            => top + height;
      set bottom(num value) => top = value - height;
}

main() {
  var rect = new Rectangle(3, 4, 20, 15);
  assert(rect.left == 3);
  rect.right = 12;
  assert(rect.left == -8);
}

getter 和 setter 的好处是,你可以开始使用实例变量,后来 你可以把实例变量用函数包裹起来,而调用你代码的地方不需要修改。

注意: 像 (++) 这种操作符不管是否定义 getter 都会正确的执行。 为了避免其他副作用, 操作符只调用 getter 一次,然后 把其值保存到一个临时变量中。

Implicit interfaces(隐式接口)

每个类都隐式的定义了一个包含所有实例成员的接口, 并且这个类实现了这个接口。如果你想 创建类 A 来支持 类 B 的 api,而不想继承 B 的实现, 则类 A 应该实现 B 的接口。

一个类可以通过 implements 关键字来实现一个或者多个接口, 并实现每个接口定义的 API。 例如:

// A person. The implicit interface contains greet().
class Person {
  // In the interface, but visible only in this library.
  final _name;

  // Not in the interface, since this is a constructor.
  Person(this._name);

  // In the interface.
  String greet(who) => 'Hello, $who. I am $_name.';
}

// An implementation of the Person interface.
class Imposter implements Person {
  // We have to define this, but we don't use it.
  final _name = "";

  String greet(who) => 'Hi $who. Do you know who I am?';
}

greetBob(Person person) => person.greet('bob');

main() {
  print(greetBob(new Person('kathy')));
  print(greetBob(new Imposter()));
}

7.Enumerated types(枚举类型)

枚举类型通常称之为 enumerations 或者 enums, 是一种特殊的类,用来表现一个固定 数目的常量。

Using enums

使用 enum 关键字来定义枚举类型:

enum Color {
  red,
  green,
  blue
}

枚举类型中的每个值都有一个 index getter 函数, 该函数返回该值在枚举类型定义中的位置(从 0 开始)。 例如,第一个枚举值的位置为 0, 第二个为 1.

assert(Color.red.index == 0);
assert(Color.green.index == 1);
assert(Color.blue.index == 2);

枚举的 values 常量可以返回 所有的枚举值。

List<Color> colors = Color.values;
assert(colors[2] == Color.blue);
Class variables and methods(类变量和函数)

使用 static 关键字来实现类级别的变量和函数。

Static variables(静态变量)

静态变量对于类级别的状态是 非常有用的:

class Color {
  static const red =
      const Color('red'); // A constant static variable.
  final String name;      // An instance variable.
  const Color(this.name); // A constant constructor.
}

main() {
  assert(Color.red.name == 'red');
}

静态变量在第一次使用的时候才被初始化。

注意: 这里准守代码风格推荐 命名规则,使用 lowerCamelCase 来命名常量。

Static methods(静态函数)

静态函数不再类实例上执行, 所以无法访问 this。例如:

import 'dart:math';

class Point {
  num x;
  num y;
  Point(this.x, this.y);

  static num distanceBetween(Point a, Point b) {
    var dx = a.x - b.x;
    var dy = a.y - b.y;
    return sqrt(dx * dx + dy * dy);
  }
}

main() {
  var a = new Point(2, 2);
  var b = new Point(4, 4);
  var distance = Point.distanceBetween(a, b);
  assert(distance < 2.9 && distance > 2.8);
}

注意: 对于通用的或者经常使用的静态函数,考虑 使用顶级方法而不是静态函数。

8.Libraries and visibility(库和可见性)

使用 importlibrary 指令可以帮助你创建 模块化的可分享的代码。库不仅仅提供 API, 还是一个私有单元:以下划线 (_) 开头的标识符只有在库 内部可见。每个 Dart app 都是一个库, 即使没有使用 library命令也是一个库。

库可以使用 Dart package 工具部署。参考 Pub Package 和 Asset Manager 来获取关于 pub(Dart 的包管理工具) 的更多信息。

Using libraries(使用)

使用 import 来指定一个库如何使用另外 一个库。

例如, Dart web 应用通常使用 dart:html 库,然后可以这样导入库:

import 'dart:html';

import 必须参数为库 的 URI。 对于内置的库,URI 使用特殊的 dart: scheme。 对于其他的库,你可以使用文件系统路径或者 package: scheme。 package: scheme 指定的库通过包管理器来提供, 例如 pub 工具。

import 'dart:io';
import 'package:mylib/mylib.dart';
import 'package:utils/utils.dart';

注意: URI 代表 uniform resource identifier。 URLs (uniform resource locators) 是一种常见的 URI。

Specifying a library prefix(指定库前缀)

如果你导入的两个库具有冲突的标识符, 则你可以使用库的前缀来区分。 例如,如果 library1 和 library2 都有一个名字为 Element 的类, 你可以这样使用:

import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;
// ...
Element element1 = new Element();           // Uses Element from lib1.
lib2.Element element2 = new lib2.Element(); // Uses Element from lib2.

Importing only part of a library(导入库的一部分)

如果你只使用库的一部分功能,则可以选择需要导入的 内容。例如:

// Import only foo.
import 'package:lib1/lib1.dart' show foo;

// Import all names EXCEPT foo.
import 'package:lib2/lib2.dart' hide foo;

Lazily loading a library(延迟载入库)

Deferred loading (也称之为 lazy loading) 可以让应用在需要的时候再 加载库。 下面是一些使用延迟加载库的场景:

  • 减少 APP 的启动时间。
  • 执行 A/B 测试,例如 尝试各种算法的 不同实现。
  • 加载很少使用的功能,例如可选的屏幕和对话框。

要延迟加载一个库,需要先使用 deferred as 来 导入:

import 'package:deferred/hello.dart' deferred as hello;

当需要使用的时候,使用库标识符调用 loadLibrary() 函数来加载库:

greet() async {
  await hello.loadLibrary();
  hello.printGreeting();
}

在前面的代码, 使用 await 关键字暂停代码执行一直到库加载完成。 关于 asyncawait 的更多信息请参考 异步支持

Asynchrony support(异步支持)

Dart 有一些语言特性来支持 异步编程。 最常见的特性是 async 方法和 await 表达式。

Dart 库中有很多返回 Future 或者 Stream 对象的方法。 这些方法是 异步的: 这些函数在设置完基本的操作 后就返回了, 而无需等待操作执行完成。 例如读取一个文件,在打开文件后就返回了。

有两种方式可以使用 Future 对象中的 数据:

看到这里我觉得大家都应该懂了 ,这里就不多做介绍了

await lookUpVersion()

要使用 await,其方法必须带有 async 关键字:

checkVersion() async {
  var version = await lookUpVersion();
  if (version == expectedVersion) {
    // Do something.
  } else {
    // Do something else.
  }
}

Metadata(元数据)

使用元数据给你的代码添加其他额外信息。 元数据注解是以 @ 字符开头,后面是一个编译时 常量(例如 deprecated)或者 调用一个常量构造函数。

有三个注解所有的 Dart 代码都可以使用: @deprecated@override、 和 @proxy。关于 @override@proxy 示例请参考 Extending a class。 下面是使用 @deprecated 的 示例:

class Television {
  /// _Deprecated: Use [turnOn] instead._
  @deprecated
  void activate() {
    turnOn();
  }

  /// Turns the TV's power on.
  void turnOn() {
    print('on!');
  }
}

你还可以定义自己的元数据注解。 下面的示例定义了一个带有两个参数的 @todo 注解:

library todo;

class todo {
  final String who;
  final String what;

  const todo(this.who, this.what);
}

使用 @todo 注解的示例:

import 'todo.dart';

@todo('seth', 'make this do something')
void doSomething() {
  print('do something');
}

元数据可以在 library、 class、 typedef、 type parameter、 constructor、 factory、 function、 field、 parameter、或者 variable 声明之前使用,也可以在 import 或者 export 指令之前使用。 使用反射可以在运行时获取元数据 信息。

以上大部分例子引用官方,大家一起学习一起进步,