Flutter Dart学习笔记

概括

重要的概念

在学习 Dart 语言时, 应该基于以下事实和概念:

  • 任何保存在变量中的都是一个 对象,并且所有的对象都是对应一个 类 的实例。无论是数字,函数和 null 都是对象。所有对象继承自 Object 类。

  • 尽管 Dart 是强类型的,但是 Dart 可以推断类型,所以类型注释是可选的。 在上面的代码中, number 被推断为 int 类型。 如果要明确说明不需要任何类型, 需要使用特殊类型 dynamic

  • Dart 支持泛型

  • 与 Java 不同,Dart 没有关键字 publicprotectedprivate 。 如果标识符以下划线(_)开头,则它相对于库是私有的,class是没有私有变量的

class Test {
    // 没有用 还是可以在外部访问
    Object _name;
}

函数的定义

printInteger(int aNumber) {
    print('The number is $aNumber.'); // 打印到控制台。
}

main() {

    var number = 42; // 声明并初始化一个变量。
    printInteger(number); // 调用函数。
}

// 字符串常量。
'...' (or "...")

// 字符串插值: 包括字符串文字内部的变量或表达式的字符串。 有关更多信息,参考 Strings.
$variableName (或 ${expression})

// 程序开始执行函数,该函数是特定的、必须的、顶级函数。 有关更多信息,参考 The main() function.
main()

// 定义变量,通过这种方式定义变量不需要指定变量类型。
var

Final 和 const

使用过程中从来不会被修改的变量, 可以使用 final 或 const, 而不是 var 或者其他类型, final 变量的值只能被设置一次; const 变量在编译时就已经固定 (Const 变量 是隐式 final 的类型.) 最高级 final 变量或类变量在第一次使用时被初始化。

var foo = const [];
final bar = const [];
const baz = []; // Equivalent to `const []`

内建类型

  • Numbers
    Dart 语言的 numbers 有两种类型: int 和 double

    // int
    var x = 1;
    var hex = 0xDEADBEEF;
    // double
    var y = 1.1;
    var exponents = 1.42e5;
    
  • Strings
    Dart 字符串是一组 UTF-16 单元序列。 字符串通过单引号或者双引号创建。

    // 单引号
    
    var single_spear = 'Single quotes work well for string literals.';
    // 双引号
    var double_spear = "Double quotes work just as well.";
    
    // 使用连续三个单引号或者三个双引号实现多行字符串对象的创建:
    var s1 = '''
    You can create
    multi-line strings like this one.
    ''';
    var s2 = """This is also a
    multi-line string.""";
    
    // 使用 r 前缀,可以创建 “原始 raw” 字符串
    var s = r"In a raw string, even \n isn't special.";
    
  • Booleans
    Dart 使用 bool 类型表示布尔值。 Dart 只有字面量 true and false 是布尔类型, 这两个对象都是编译时常量。
    Dart 的类型安全意味着不能使用 if (nonbooleanValue) 或者 assert (nonbooleanValue)。 而是应该像下面这样,明确的进行值检查

// 检查空字符串。
var fullName = '';
assert(fullName.isEmpty);

// 检查 0 值。
var hitPoints = 0;
assert(hitPoints <= 0);

// 检查 null 值。
var unicorn;
assert(unicorn == null);

// 检查 NaN 。(检查参数中是否是非数字值)
var iMeantToDoThis = 0 / 0;
assert(iMeantToDoThis.isNaN);        
  • Lists
    几乎每种编程语言中最常见的集合可能是 array 或有序的对象集合。 在 Dart 中的 array 就是 List 对象, 通常称之为 lists 。
    Dart 中的 list 字面量非常像 JavaScript 和 iOS 中的 array 字面量。 下面是一个 Dart list 的示例:
var list = [1, 2, 3];
提示: 分析器推断 list 的类型为 List<int> 。 如果尝试将非整数对象添加到此 list 中, 则分析器或运行时会引发错误。 有关更多信息,请阅读 类型推断。

Lists 的下标索引从 0 开始,第一个元素的索引是 0。 list.length - 1 是最后一个元素的索引。 访问 list 的长度和元素与 JavaScript 中的用法一样:

var list = [1, 2, 3];
assert(list.length == 3);
assert(list[1] == 2);

list[1] = 1;
assert(list[1] == 1);
  • Maps

通常来说, map 是用来关联 keys 和 values 的对象。 keys 和 values 可以是任何类型的对象。在一个 map 对象中一个 key 只能出现一次。 但是 value 可以出现多次。 Dart 中 map 通过 map 字面量 和 Map 类型来实现。

下面是使用 map 字面量的两个简单例子:

var gifts = {
  // Key:    Value
  'first': 'partridge',
  'second': 'turtledoves',
  'fifth': 'golden rings'
};

var nobleGases = {
  2: 'helium',
  10: 'neon',
  18: 'argon',
};

提示: 分析器会将 gifts 的类型推断为 Map<String, String>, nobleGases 的类型推断为 Map<int, String>。 如果尝试在上面的 map 中添加错误类型,那么分析器或者运行时会引发错误。 有关更多信息,请阅读类型推断。。
以上 map 对象也可以使用 Map 构造函数创建:

var gifts = Map();
gifts['first'] = 'partridge';
gifts['second'] = 'turtledoves';
gifts['fifth'] = 'golden rings';

var nobleGases = Map();
nobleGases[2] = 'helium';
nobleGases[10] = 'neon';
nobleGases[18] = 'argon';
  • => expr简写语法 (=> 符号 有时也被称为 箭头 语法。)
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
// 等价于
bool isNoble(int atomicNumber) {
    return _nobleGases[atomicNumber] != null;
} 
  • 命名可选参数
// 可选参数可以是命名参数或者位置参数,但一个参数只能选择其中一种方式修饰。
enableFlags(bold: true, hidden: false);

// 定义函数是,使用 {param1, param2, …} 来指定命名参数:
// Sets the [bold] and [hidden] flags ...
void enableFlags({bool bold, bool hidden}) {...}
  • 位置可选参数 (只能放最后一个)
// 将参数放到 [] 中来标记参数是可选的:
String say(String from, String msg, [String device]) {
  var result = '$from says $msg';
  if (device != null) {
    result = '$result with a $device';
  }
  return result;
}
  • 默认参数值
// 在定义方法的时候,可以使用 = 来定义可选参数的默认值。 
// 默认值只能是编译时常量。 
// 如果没有提供默认值,则默认值为 null。
// 下面是设置可选参数默认值示例:

void enableFlags({bool bold = false, bool hidden = false}) {...}

  • 函数也是对象

一个函数可以作为另一个函数的参数。 例如:

void printElement(int element) {
  print(element);
}
var list = [1, 2, 3];
// 将 printElement 函数作为参数传递。
list.forEach(printElement);

同样可以将一个函数赋值给一个变量,例如:

var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
assert(loudify('hello') == '!!! HELLO !!!');
  • 匿名函数

多数函数是有名字的, 比如 main() 和 printElement()。 也可以创建没有名字的函数,这种函数被称为 匿名函数, 有时候也被称为 lambda 或者 closure 。 匿名函数可以被复制到一个变量中, 举个例子,在一个集合中可以添加或者删除一个匿名函数。匿名函数和命名函数看起来类似— 在括号之间可以定义一些参数或可选参数,参数使用逗号分割。

后面大括号中的代码为函数体:

([[Type] param1[, …]]) { 
  codeBlock; 
}; 

var list = ['apples', 'bananas', 'oranges'];
list.forEach((item) {
  print('${list.indexOf(item)}: $item');
});

// 一个简单的block
doSome(VoidCallback voidCallBack) {
    voidCallBack();
}

如果函数只有一条语句, 可以使用箭头简写。粘贴下面代码到 DartPad 中 并点击运行按钮,验证两个函数是等价性。

list.forEach(
    (item) => print('${list.indexOf(item)}: $item'));
  • 词法闭包

闭包 即一个函数对象,即使函数对象的调用在它原始作用域之外, 依然能够访问在它词法作用域内的变量。
函数可以封闭定义到它作用域内的变量。 接下来的示例中, makeAdder() 捕获了变量 addBy。
无论在什么时候执行返回函数,函数都会使用捕获的 addBy 变量。

/// 返回一个函数,返回的函数参数与 [addBy] 相加。
Function makeAdder(num addBy) {
  return (num i) => addBy + i;
}

void main() {
  // 创建一个加 2 的函数。
  var add2 = makeAdder(2);

  // 创建一个加 4 的函数。
  var add4 = makeAdder(4);

  assert(add2(3) == 5);
  assert(add4(3) == 7);
}
  • If 和 else

Dart 支持 if - else 语句,其中 else 是可选的, 比如下面的例子

if (isRaining()) {
  you.bringRainCoat();
} else if (isSnowing()) {
  you.wearJacket();
} else {
  car.putTopDown();
}

注意:和 JavaScript 不同, Dart 的判断条件必须是布尔值,不能是其他类型

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

  • 使用类的成员变量

对象的由函数和数据(即方法和实例变量)组成。 方法的调用要通过对象来完成: 调用的方法可以访问其对象的其他函数和数据。
使用 (.) 来引用实例对象的变量和方法:

var p = Point(2, 2);

// 为实例的变量 y 设置值。
p.y = 3;

// 获取变量 y 的值。
assert(p.y == 3);

// 调用 p 的 distanceTo() 方法。
num distance = p.distanceTo(Point(4, 4));

// 使用 ?. 来代替 . , 可以避免因为左边对象可能为 null , 导致的异常:
// 如果 p 为 non-null,设置它变量 y 的值为 4。
p?.y = 4;
  • 使用构造函数

通过 构造函数 创建对象。 构造函数的名字可以是 ClassName 或者 ClassName.identifier。
例如, 以下代码使用 Point 和 Point.fromJson() 构造函数创建 Point 对象:

var p1 = Point(2, 2);
var p2 = Point.fromJson({'x': 1, 'y': 2});
  • 获取对象的类型
    使用对象的 runtimeType 属性, 可以在运行时获取对象的类型, runtimeType 属性回返回一个 Type 对象。
print('The type of a is ${a.runtimeType}');
  • 重写类成员

子类可以重写实例方法,getter 和 setter。 可以使用 @override 注解指出想要重写的成员:

class SmartTelevision extends Television {
  @override
  void turnOn() {...}
  // ···
}
  • 重写运算符

下面示例演示一个类重写 + 和 - 操作符:

class Vector {
  final int x, y;

  Vector(this.x, this.y);

  Vector operator +(Vector v) => Vector(x + v.x, y + v.y);
  Vector operator -(Vector v) => Vector(x - v.x, y - v.y);

  // 运算符 == 和 hashCode 部分没有列出。 有关详情,请参考下面的注释。
  // ···
}
void main() {
  final v = Vector(2, 3);
  final w = Vector(2, 2);

  assert(v + w == Vector(4, 5));
  assert(v - w == Vector(0, 1));
}
  • noSuchMethod()

当代码尝试使用不存在的方法或实例变量时, 通过重写 noSuchMethod() 方法,来实现检测和应对处理:

class A {
  // 如果不重写 noSuchMethod,访问
  // 不存在的实例变量时会导致 NoSuchMethodError 错误。
  @override
  void noSuchMethod(Invocation invocation) {
    print('You tried to use a non-existent member: ' +
        '${invocation.memberName}');
  }
}
  • 枚举类型

枚举类型也称为 enumerations 或 enums , 是一种特殊的类,用于表示数量固定的常量值。

enum Color { 
    red, 
    green, 
    blue 
}
  • Mixin继承是什么意思呢?

mixins是一个很强大的概念,可以让您跨多个类层次结构重用代码。

Mixins通过普通的类声明隐式定义:
class Walker {
  void walk() {
    print("I'm walking");
  }
}
要使用mixin的话,你需要使用with关键字,后跟一个或多个mixin的名称:
class Cat extends Mammal with Walker {}

class Dove extends Bird with Walker, Flyer {}
理解后就觉得很简单,但是这个设计思想很值得学习
class A {
  String getMessage() => 'A';
}

class B {
  String getMessage() => 'B';
}

class P {
  String getMessage() => 'P';
}

class AB extends P with A, B {}

class BA extends P with B, A {}

void main() {
  String result = '';

  AB ab = AB();
  result += ab.getMessage();

  BA ba = BA();
  result += ba.getMessage();

  print(result);
}

AB和BA类都使用A和B mixins继承至P类,但顺序不同。所有的A(3个),B和P类都有一个名为getMessage的方法。

首先,我们调用AB类的getMessage方法,然后调用BA类的getMessage方法。

  • A. BA
  • B. AB
  • C. BAAB
  • D. ABBA

线性化

实际上,这段代码等同于

class PA = P with A;
class PAB = PA with B;

class AB extends PAB {}

class PB = P with B;
class PBA = PB with A;

class BA extends PBA {}
  • 指定库前缀

如果导入两个存在冲突标识符的库, 则可以为这两个库,或者其中一个指定前缀。
例如,如果 library1 和 library2 都有一个 Element 类, 那么可以通过下面的方式处理:

import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;

// 使用 lib1 中的 Element。
Element element1 = Element();

// 使用 lib2 中的 Element。
lib2.Element element2 = lib2.Element();

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

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

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

  1. 减少 APP 的启动时间。
  2. 执行 A/B 测试,例如 尝试各种算法的 不同实现。
  3. 加载很少使用的功能,例如可选的屏幕和对话框。
    要延迟加载一个库,需要先使用 deferred as 来导入:
import 'package:greetings/hello.dart' deferred as hello;

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

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

在前面的代码,使用 await 关键字暂停代码执行一直到库加载完成。 关于 async 和 await 的更多信息请参考 异步支持。
在一个库上你可以多次调用 loadLibrary() 函数。但是该库只是载入一次。
使用延迟加载库的时候,请注意一下问题:

  1. 延迟加载库的常量在导入的时候是不可用的。 只有当库加载完毕的时候,库中常量才可以使用.
  2. 在导入文件的时候无法使用延迟库中的类型。 如果你需要使用类型,则考虑把接口类型移动到另外一个库中, 让两个库都分别导入这个接口库。
  3. Dart 隐含的把 loadLibrary() 函数导入到使用 deferred as 的命名空间 中。 loadLibrary() 方法返回一个 Future。
  • 处理 Stream

当需要从 Stream 中获取数据值时, 可以通过以下方式:
异步for循环的使用形式

await for (varOrType identifier in expression) {
  // Executes each time the stream emits a value.
}

上面 表达式 返回的值必须是 Stream 类型。 执行流程如下:

  1. 等待,直到流发出一个值。
  2. 执行 for 循环体,将变量设置为该发出的值
  3. 重复1和2,直到关闭流。
  • 可调用类

通过实现类的 call() 方法, 能够让类像函数一样被调用。
在下面的示例中,WannabeFunction 类定义了一个 call() 函数, 函数接受三个字符串参数,函数体将三个字符串拼接,
字符串间用空格分割,并在结尾附加了一个感叹号。

class WannabeFunction {
  call(String a, String b, String c) => '$a $b $c!';
}

main() {
  var wf = new WannabeFunction();
  var out = wf("Hi","there,","gang");
  print('$out');
}
  • 元数据

使用元数据可以提供有关代码的其他信息。 元数据注释以字符 @ 开头, 后跟对编译时常量 (如 deprecated) 的引用或对常量构造函数的调用。
对于所有 Dart 代码有两种可用注解:@deprecated 和 @override。 关于 @override 的使用, 参考 扩展类(继承)。 下面是使用

@deprecated(被弃用的) 注解的示例:

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

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

可以自定义元数据注解。 下面的示例定义了一个带有两个参数的 @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');
}

所有代码中可用的注解

  1. @deprecated 被弃用的
  2. @override 重载
  3. @proxy 代理
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,402评论 6 499
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,377评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,483评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,165评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,176评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,146评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,032评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,896评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,311评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,536评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,696评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,413评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,008评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,815评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,698评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,592评论 2 353

推荐阅读更多精彩内容