虽然Flutter官网说学习上手Flutter并不需要刻意学习Dart语言 ,当你碰到不懂的地方再开始回头来看Dart语法。但是当我搭建好环境后,练习Flutter中文网的demo时,还是有些吃力,或许是我的语言天赋太差了,亦或者是我的编程基础太差了。所以我决定从头开始学习Dart。这样看起flutter代码来才能事半功倍。
相信准备学习Flutter的开发者们,应该都有多年的编程从业经验了吧,所以我不打算细致的讲解Dart内容,我之前是一位Web开发者,对于javaScript比较熟悉,所以这篇文章我主要从DartjavaScript不同的地方去剖析。

一、Hello Dart

学习任何一门语言都是从”Hello World!”开始,那我们也从这开始吧。

1. Hello World

首先打开我们比较熟悉的编译器,新建一个helloWorld.dart,打开并输入下面这一串代码

  1. main(List<String> args) {
  2. print('Hello World!');
  3. }

然后在终端中输入dart helloWorld.dart,就能看到“hello world”的结果了。

2. 语法解析

针对上面的那一段代码进行简单的解释,也 由此总结下写Dart需要注意什么:

  1. main() 函数是Dart语言的入口函数,也是必不可少的
  2. Dart的入口函数main()是没有返回值的
  3. 传递给main()函数的命令行参数,是通过List完成的,其中ListDart的集合类型
  4. 定义字符串的时候,可以使用单引号或者双引号
  5. 每行语句必须使用分号结尾,对象类型的数据中以逗号分割且最后一个属性也要带上逗号

    二、定义变量和常量

    Dart语言集合了强类型和弱类型语言的特点,既可以制定类型也可以使用泛型。听起来是不是很牛逼 的样子 ,走着…

    1. 制定类型

    指定类型也就是明确声明变量,格式如下:
    1. 变量类型 变量名称 = 赋值
    示例代码如下:
    1. String temp = 'value'; // 字符串类型
    2. int number = 111; // 整数类型
    3. double float = 12.0; // 浮点型
    4. print('$temp $number $float'); // 打印多个变量,使用单引号包括,${...}或者$...的方式表示变量
    需要注意的是,定义的变量可以赋值 ,但是不可以赋值其他 类型的值
    1. String temp = 'Hello World';
    2. temp = 'yeap'; // yeap
    3. temp = 123; // 发生错误,不能赋值非字符串的值

    2. 类型推到

    类型推导声明变量的方式如下:
    1. var/dynamic/final/const 变量名称 = 赋值
    其中dynamic是可以在变量定义以后赋值不同类型的值的,所以通常情况下不使用dynamic定义变量,容易造成潜在的危险;finalconst用来定义常量,定义后不能进行二次赋值。下面我们来看看他们的用法吧: ```dart / var / var temp = ‘123’; temp = ‘xxx’; print(‘$temp ${temp.runtimeType}’); // xxx String 需要注意的是,runtimeType方法可以用来或许该变量或常量的类型 temp = 123; // 报错,var不能赋值其他类型值

/ dynamic / dynamic danger = ‘abc’; danger = 123; print(‘$danger ${danger.runtimeType}’); // 123 int

/ const / const one = 1; print(‘$one ${one.runtimeType}’); // i int one = ‘111’; // 报错,变量不能进行二次赋值

/ final / final temp = 1; print(‘$temp ${temp.runtimeType}’); // 1 int final _temp = () => return ‘get value’; final timestamp = new DateTime.now(); print(‘$_temp ${_temp.runtimeType} $timestamp’); // get value Closure: () => String () => String 时间戳 // final 可以在编译阶段赋值,也就是说可以赋值函数和表达式

  1. <a name="zq1Q2"></a>
  2. ## 三、数据类型
  3. `dart`的数据包括数字类型、布尔类型、字符串类型和集合类型
  4. <a name="krWjv"></a>
  5. ### 1. 数字类型
  6. 数字类型主要包括整数类型(int)和浮点数类型(double),需要说明的是`Dart`的`int`和`double`主要取决于`Dart`的运行环境,其主要的用法如下所示:
  7. ```dart
  8. // 整数类型
  9. int number = 11;
  10. int _number = 0x1B;
  11. print('$number $_number'); // 11 27
  12. // 浮点数类型
  13. double height = 1.80;
  14. print(height); // 1.8
  15. // 数字转字符串
  16. int age = 25;
  17. var str = age.toString();
  18. var num2str = age.toStringAsFixed(2);
  19. print('$age $str $num2str'); // 25 25 25.00
  20. print('${age.runtimeType} ${str.runtimeType} ${num2str.runtimeType}'); // int String String
  21. // 字符串转数字
  22. var num = '22';
  23. var num2Int = int.parse(num);
  24. var num2Double = double.parse(num);
  25. print('$num $num2Int $num2Double'); // 22 22 22.0
  26. print('${num.runtimeType} ${num2Int.runtimeType} ${num2Double.runtimeType}'); // String int double

2. 布尔类型

布尔类型大致和js一样,但是dart的判断语句不能使用隐式转换,也就是说,Dart中不能使用!0 即真或!’’即真。

  1. // boolean
  2. var flag = true;
  3. print('$flag ${flag.runtimeType}');
  4. // 条件语句
  5. /* 正确写法 */
  6. if (flag) {
  7. // ...
  8. }
  9. /* 错误写法 */
  10. var str = 'message';
  11. if (str) {
  12. print('this is true');
  13. }

3. 字符串类型

Dart字符串是UTF-16编码单元的序列,使用方法和js类似,需要注意的是多行字符串的使用方法需要用到’’’,
需要用到字符串拼接时,使用${=expression}表示,当拼接字符串和标识符时可以省略{},直接使用$variable

  1. // String
  2. var str = 'this is a single string';
  3. var _str = "this is a single string";
  4. var multiline = '''first line
  5. secone line
  6. third line''';
  7. print('$str $_str $multiline'); // this is a single string this is a single string first line
  8. // secone line
  9. // third line
  10. // 字符串拼接的方式在上面 实例中都有展示
  11. var name = 'jack';
  12. var age = 24;
  13. print('he is $name, his age is $age, his name type is ${name.runtimeType}'); // he is jack, his age is 24, his name type is String

4. 集合类型

1. 集合类型的定义

Dart中常用的集合类型为List/Set/Map
它们的定义分别是这样的

  1. /* List 定义 */
  2. // 1. 使用类型推导定义
  3. var letters = ['a', 'b', 'c', 'd'];
  4. print('$letters ${letters.runtimeType}'); // [a, b, c, d] List<String>
  5. // 2. 明确指定类型
  6. List<String> list = ['message', 'abc'];
  7. print('$lsit ${list.runtimeType}'); // [message, abc] List<String>
  8. /* Set 定义 (List和Set的区别是,Set是无序的,且其中的元素是不重复的) */
  9. // 1. 使用类型推导定义
  10. var lettersSet = {'a', 'b', 'c'};
  11. print('$lettersSet ${lettersSet.runtimeType}'); // {a, b, c} _CompactLinkedHashSet<String>
  12. // 2. 明确制定类型定义
  13. Set temp = {1, 2, 3, 4};
  14. print('$temp ${temp.runtimeType}'); // {1, 2, 3, 4} _CompactLinkedHashSet<dynamic>
  15. /* Map 定义 Map定义元素是一组键值对 */
  16. // 1. 使用类型推导定义
  17. var map1 = { 'name': 'jack', age: 22 };
  18. print('$map1 ${map1.runtimeType}'); // {name: jack, 24: 22} _InternalLinkedHashMap<dynamic, Object>
  19. // 2. 明确指定类型定义 (键和值的类型只能按照约定类型来定义, Object可以定义字符串和数字类型)
  20. Map<String, Object> document = { 'head': '<head></head>', 'text': 123};
  21. print('$document ${document.runtimeType}'); // {head: <head></head>, text: 123} _InternalLinkedHashMap<String, Object>

2. 集合类型的常见操作

所有集合都支持获取长度的属性length

  1. // 获取集合的长度
  2. List<String> list = ['message', 'abc'];
  3. Set temp = {1, 2, 3, 4};
  4. Map<String, Object> document = { 'head': '<head></head>', 'text': 123};
  5. print(list.length); // 2
  6. print(temp.length); // 4
  7. print(document.length); // 2
  8. /* List的增加/删除/包含方法 */
  9. list.add('add a lsit');
  10. print(list); // [message, abc, add a lsit]
  11. list.remove('abc'); // 根据元素删除
  12. print(list); // [message, add a lsit]
  13. list.removeAt(1); // 根据索引值删除, 索引值从0开始
  14. print(list); // [message]
  15. print(list.contains('message')); // true
  16. /* Map集合的操作,Map可以针对键值或者其中的一项进行操作,所以方法相对来说多一些 */
  17. Map<String, Object> map1 = { 'name': 'jack', 'age': 22 };
  18. // 1. 根据Key获取value
  19. print(map1['name']);
  20. // 2. 获取所有的entries
  21. print('${map1.entries} ${map1.entries.runtimeType}'); // (MapEntry(name: jack), MapEntry(age: 22)) MappedIterable<String, MapEntry<String, Object>>
  22. // 3. 获取所有的keys
  23. print('${map1.keys} ${map1.keys.runtimeType}'); // (name, age) _CompactIterable<String>
  24. // 4. 获取所有的values
  25. print('${map1.values} ${map1.values.runtimeType}'); // (jack, 22) _CompactIterable<Object>
  26. // 5. 判断是否包含某个Key和value
  27. print('${map1.containsKey('name')} ${map1.containsValue(22)}'); // true true
  28. // 6. 根据key删除元素
  29. map1.remove('age');
  30. print(map1); // {name: jack}

四、函数

Dart是一种面向对象的语言,所以即使函数也是对象,所以也有类型,函数类型是Function,函数是一等公民,这也就意味着函数可以作为变量定义或者作为其他函数的参数或者返回值.
函数的定义:

  1. 返回值类型 函数的名称(参数列表) {
  2. 函数体
  3. return 返回值
  4. }

按照上面定义,我们实现一个完整函数:

  1. int sum(num sum1, num sum2) {
  2. return sum1 + sum2;
  3. }
  4. // 其中函数返回值的类型定义是可以省略的
  5. sum(num sum1, num sum2) {
  6. return sum1 + sum2;
  7. }
  8. // 如果函数只有一个表达式也可以写成箭头函数的形式
  9. sum(num1, num2) => num1 + num2;

Dart 函数和js中比较大的区别是,该函数的可选参数分为位置可选函数(用[]表示)命名可选参数(用{}表示)。
定义方式:

  1. 命名可选参数:{param1, param2, ...}
  2. 位置可选参数:[param1, param2, ...]
  3. // 需要注意的是命名可选参数和位置可选参数不能同时使用

可选参数的示例,如下所示:

  1. // 命名可选参数
  2. person(String name, {int age, double height }) {
  3. print('my name is $name, my height is $height, $age years old this years!');
  4. };
  5. person('jack', age: 22); // my name is jack, my height is null, 22 years old this years!
  6. // 位置可选参数
  7. person(String name, [ int age, double height ]) {
  8. print('my name is $name, my height is $height, $age years old this years!');
  9. };
  10. person('jack', 22); // my name is jack, my height is null, 22 years old this years!

可选参数还可以指定默认值,需要注意的是必须参数不能指定默认值,可选参数默认值在定义变量后用等号连接,如下所示:

  1. person(String name, { int age = 25, double height = 1.80 }) {
  2. print('my name is $name, my height is $height, $age years old this years!');
  3. };
  4. person('nick'); // my name is nick, my height is 1.8, 25 years old this years!

Dart中函数的作用域和闭包使用方法和js类似,不同的是Dart中函数的返回值是一定存在的,默认值为null,隐式附加到函数体:

  1. main(List<String> args) {
  2. print(foo()); // this is a Function null
  3. };
  4. foo() {
  5. print('this is a Function');
  6. };

五、运算符

Dart中运算符相对来说 多一些,就不一一进行总结了 ,只针对相对于js不同的地方进行举例说明,这些运算符可能看起来有点不习惯,但是很大的方便了我们的开发。

数值整除、取整、取模

var num = 7;
print(num / 2); // 3.5
print(num ~/ 2); // 3
print(num % 2); // 1

??=赋值操作

??=这个操作符比较神奇,可以取代js中的三目运算符,而且代码也没那么冗长,首先解释下他的用法:

  • 当变量为null 时,使用后面的值来赋值
  • 当变量有值时,使用前面的值来赋值

    1. mian(List<String> args) {
    2. var name = 'dart';
    3. var name1 = null;
    4. name1 ??= name;
    5. print(name1); // dart
    6. }

    条件运算符

    expr1 ??pxpr2 这就是dart中的条件运算符,他的用法是:

  • 如果expr1null ,则返回 expr2 的结果

  • 如果 expr1 不是 null ,则直接使用expr1的值

    1. var temp = 'dart';
    2. var name = temp ?? 'no value';
    3. var str = null;
    4. var name1 = str ?? 'no value';
    5. print('this name is $name, this name1 is $name1'); // this name is dart, this name1 is no value

    级联语法

    有些时候需要对一个类进行连续操作,这个时候可以使用级联语法: ```dart class Person() { String name;

    void run() {

    1. print('$name is running');

    }

    void eat() {

    1. print('$name is eating');

    }

    void say() {

    1. print('Hi! $name');

    } };

main(List args) { final p1 = new Person(); p1.name = ‘jack’; p1.run(); // jack is running p1.eat(); // jack is eating p1.say(); // Hi! jack

final p2 = new Person() .name = ‘nick’ .run() // nick is running .eat() // nick is eating .say(); // Hi! nick }

  1. <a name="ojTun"></a>
  2. ## 六、流程控制
  3. `dart` 的流程控制和js基本类似,需要注意的是其中的`if/else`语句中的判断条件只能为`boolean`类型。
  4. <a name="Ncldb"></a>
  5. ## 七、类和对象
  6. `Dart` 是一个面向对象的语言,面向对象中非常重要的概念就是类,类产生了对象
  7. <a name="fo39F"></a>
  8. ### 1. 类的定义
  9. 定义类使用`class关键字`,类通常有两部分组成:成员(member)和方法(method)。<br />定义的伪代码:
  10. ```dart
  11. class 类名 {
  12. 类型 成员名;
  13. 返回值类型 方法名(参数列表) {
  14. 方法体
  15. }
  16. }

编写一个简单的Person类:

  • 需要注意的是:在方法体内使用成员变量时,不需要加this;
  • 当成员变量存在命名冲突 时,this不能省略
  • 创建实例对象时,new关键字可以省略(dart version >= 2.0) ```dart class Person() { String name;

    eat() {

    1. print('$name is eating');

    }; };

main(List args) { var p1 = new Person() // ==> var p1 = Person() .name = ‘jack’; .eat(); // jack is eating };

  1. <a name="EvlmY"></a>
  2. ### 2. 构造方法
  3. <a name="AJFIz"></a>
  4. #### 2.1 普通构造方法
  5. 当通过类创建一个对象时,会调用这个类的构造函数。
  6. - 当类中没有明确的指定构造方法时,将默认拥有一个无参的构造方法
  7. - 上面的Person中就是在调用这个构造方法
  8. 我们也可以根据自己的需求自定义构造方法:
  9. - 当有了自己的构造方法时,默认的构造函数会失效,不能使用。**当然,你可能希望明确的写一个默认的构造方法 ,但是会和我们自定义的构造方法冲突;这是因为Dart本身不支持函数的重载【名称相同,参数不同的方式】**
  10. - 示例实现toString方法:
  11. ```dart
  12. class Person() {
  13. String name;
  14. int age;
  15. Person(String name, int age) {
  16. this.name = name;
  17. this.age = age;
  18. }
  19. @override String toString() {
  20. return 'name = $name, age = $age';
  21. };
  22. };

在实现构造方法时,通常做的事就是通过参数给属性赋值,为了简化这一过程,Dart提供了语法糖简化这一过程:

  1. Person(String name, int age) {
  2. this.name = name;
  3. this.age = age;
  4. }
  5. // 等价于
  6. Person(String name, int age);

2.2 命名构造方法

在开发中,我们确实希望实现更多的构造方法,怎么办呢?
因为不支持函数(方法)的重载,所以我们没办法创建相同名称的构造方法
需要使用命名构造函数:

  1. class Person() {
  2. String name;
  3. int age;
  4. Person() {
  5. name = '';
  6. age = 0;
  7. }
  8. // 命名构造方法
  9. Person.withArguments(String name, int age) {
  10. this.name = name;
  11. this.age = age;
  12. };
  13. @override
  14. String toString() {
  15. return 'name = $name, age = $age';
  16. };
  17. };
  18. // 创建对象
  19. var p1 = new Person();
  20. print(p1); // name = , age = 0
  21. var p2 = new Person.withArguments('teacher', 22);
  22. print(p2); // name = teacher, age = 22

2.3 重定向构造函数

在某些情况下,传入相同值时,希望返回同一个对象,这个时候可以使用常量构造方法。默认情况创建对象时,即使传入相同的参数,创建出来的也不是同一个对象