【Flutter】之 Dart 语法知识点汇总(带示例及详解)

827 阅读15分钟

前言

最近在做一个IM即时通讯的项目,需要用到Flutter来开发,Flutter框架用的是Dart 语言来完成的,文章记录了我学习Dart语言的路径,后面还会有关于Flutter的文章出锅,敬请期待。如果你现在也在写Flutter,欢迎一起来讨论哦,哈哈哈哈😄 更新日期:2022-12-02

ps : 其实Dart的语法和Javascript是有类似的,如果有Javascript基础,那上手应该是蛮容易的。

一、数据类型

内置数据类型:( 这里的类型是区分大小写的 )

  • 数值型 num int double

  • 字符串 String

  • 列表 List

  • 键值对 Map

  • 动态类型 dynamic

  • 枚举 enum

  • Runes

  • Symbols


1. num: 数值型

类似于我们熟悉的TS中的 number,在Dart中是要区分整型int和浮点型double,如果不能确定是否是整型或者浮点型,也可以用num代替。

num : 数值型的总称

int : 整型

double : 浮点型

数值型常用操作

+-* 乘

/ 除

~/ 取整

% 取余

示例:

void main() {

    var a = 2; 

    var b = 3.2;

    print(a + b);  // 5

    print(a - b);  // -1

    print(a * b); // 6

    print(a / b); // 0.66666666666

    print(a ~/ b); // 0

    print(a % b); // 2
}

常用属性:

isNaN   判断是否是 NaN

isEven  判断是否是偶数

isOdd   判断是否是奇数

示例:

void main() {

    var a = 2;

    var b = 3.2;
    
    int d = 11;

    print(a.isEven); // true

    print(a.isOdd);  // false

    print(d.isEven); // false

    print(d.isOdd); // true
}

常用的方法:

abs()  返回表示数字绝对值的整数

round()  返回四舍五入最接近的整数

floor()  返回向下取整得到的整数

ceil()  返回向上取整得到的整数

toInt()  返回整数的表现形式

toDouble()  返回数字值的 double 类型表现形式

示例:

  • abs返回表示数字绝对值的整数
void main() {

    int e = -100;
    
    print(e.abs());  // 100
}
  • round返回四舍五入最接近的整数
void main() {

    double f = 10.5;
    
    print(f.round()); // 11
}
  • floor返回向下取整得到的整数
void main() {

    double f = 10.5;
    
     print(f.floor()); // 10
}
  • ceil返回向上取整得到的整数
void main() {

    double f = 10.5;
    
    print(f.ceil()); // 11
}
  • toInt返回整数的表现形式
void main() {

    double f = 10.5;
    
    print(f.toInt());  //10   
}
  • toDouble返回整数的表现形式
void toDouble() {

    int d = 2 ;
    
    print(d.toDouble()); // 2.0
}

2. String: 字符串

字符串支持单引号、双引号、 三个引号(多行字符串)

void main() {

    // 普通消息

    String str1 = '1';
    
    
    // 多行信息

    String str2 = ''' hellow

    Dart'''; 
    
    print(str2);
    

    String str3 = 'hellow \n Dart';

    print(str3);
    

    String str4 = r'hellow \n Dart';

    print(str4);
}

打印效果:

image.png

字符串的常用操作:

+  字符串相加

*  返回一定倍数的字符串的组合

==  两个字符串是否相等

[]  取出指定位置的字符串

$  字符串变量

示例:

  • + 字符串相加
void main() {

    String str1 = 'my name is';

    String str2 = 'zhangsan';

    print(str1 + str2);   //'my name iszhangsan'
}
  • * 返回一定倍数的字符串的组合
void main() {

    String str1 = 'my name is';

    print(str1 * 3); //  'my name ismy name ismy name is'
}
  • == 两个字符串是否相等
void main() {

    String str1 = 'my name is';
    
    String str2 = 'zhangsan';

    print(str1 == str2);  // false
}
  • [] 取出指定位置的字符串
void main() {
    
    String str2 = 'zhangsan';

    print(str2[1]); //  h
}
  • $ 字符串变量
void main() {
    
    int a = 1;

    int b = 2;

    print("a + b =${a + b}");  //  "a + b =3"

    print("a + b + $a");  // "a + b + 1"
}

常用属性:

length  字符串的长度

isEmpty  是否为空

isNotEmpty  是否不为空

常用方法:

contains()  是否包含指定内容

subString()  字符串截取

startsWith()  是否以某个指定字符串开始

endsWith()  是否以某个指定字符串结尾

indexOf()  查找指定内容在字符串第一次出现的位置,没有返回-1

lastIndexOf() 查找指定内容在字符串最后一次出现的位置,没有返回-1

toLowerCase()  转小写

toUpperCase()  转大写

trim()  去掉前后空格

trimLeft()  去掉左侧空格

trimRight()  去掉右侧空格

split() 以指定内容对字符串进行分割

replaceAll() 将A 替换成B

示例:

  • length 字符串的长度
void main() {

    String str1 = 'my name is zhangsan';

    print(str1.length);  //  19
}
  • isEmpty 是否为空
void main() {

    String str1 = 'my name is zhangsan';

    print(str1.isEmpty);  //  false
}
  • isNotEmpty 是否不为空
void main() {

    String str1 = 'my name is zhangsan';

    print(str1.isNotEmpty);  //  true
}
  • contains 否包含指定内容
void main() {

    String str1 = ' my name is zhangsan ';

    print(str1.contains('my'));  //  true

}
  • substring 符串截取,前包后不包
void main() {

    String str1 = 'my name is zhangsan ';

    print(str1.substring(0, 4));  // 'my n'
}
  • startsWith 否以某个指定字符串开始
void main() {

    String str1 = 'my name is zhangsan ';

    print(str1.startsWith('m'));  // true

}
  • endsWith 否以某个指定字符串结束
void main() {

    String str1 = 'my name is zhangsan ';

    print(str1.endsWith('m'));  // false
}
  • indexOf 查找指定内容在字符串第一次出现的位置,没有返回-1
void main() {

    String str1 = 'my name is zhangsan ';

    print(str1.indexOf('hhh');  // -1
}
  • lastIndexOf 查找指定内容在字符串最后一次出现的位置,没有返回-1
void main() {

    String str1 = 'my name is zhangsan ';

    print(str1.lastIndexOf('my'  // 0
}
  • toLowerCase 转小写
void main() {

    String str1 = 'MY';

    print(str1.toLowerCase()); // 'my'
}

toUpperCase 转大写

void main() {

    String str1 = 'my';

    print(str1.toUpperCase()); // 'MY'
}
  • trim 去掉前后空格
void main() {

    String str1 = '  my hhh iii ppp ';

    print(str1.trim());  //'my hhh iii ppp';
}
  • trimLeft 去掉左侧空格
void main() {

    String str1 = '  my hhh iii ppp ';

    print(str1.trimLeft());  //'my hhh iii ppp ';

}
  • trimRight 去掉右侧空格
void main() {

    String str1 = '  my hhh iii ppp ';

    print(str1.trimRight());  //'  my hhh iii ppp';
}
  • split 以指定内容对字符串进行分割
void main() {

    String str1 = 'welcom';

    print(str1.split('')); // ['w', 'e', 'l', 'c', 'o', 'm'] 
    
    String str2 = 'abbaa';
    
    print(str2.split('a')); // ['', 'bb', '', '']
    
    String str3 = 'Hello word!';
    
    print(str3.split(' ')); // ['Hello','word!']
}
  • replaceAll 将字符串 A ,替换成字符串 B
void main() {

    String str1 = 'my name is zhangsan';

    print(str1.replaceAll('my', 'My')); // 'My name is zhangsan'
}

3. bool: 布尔型

bool类型只有两个值: truefalse

基本等同于 ts 的布尔值,这里就不再赘述。

4. List: 数组

等同于js中的Array,但在数组操作方法上,dart 中的List 有很多js中没有的方法。

var list = [1,2,3,’dd’,true]

不可变的list

var list2 = const [1,2,3];

常用的操作:

[index]  获取数组中指定位置上的元素,index 为索引

length  数组长度

add()  给数组添加元素

addAll()  把一个数组元素全部添加到另一个数组中

insert()  向指定位置添加元素

remove()  删除指定元素

removeLast()  删除最后一个元素

removeAt()  删除指定位置的元素

removeRange()  删除指定区域的元素

removeWhere()  删除符合某个条件的全部元素

clear()  清空数组

indexOf()  找出某个元素在数组中的索引位置,如果没有返回 -1

lastIndexOf() 找出某个元素在数组中最后一次出现的位置,如果没有返回-1

sort()  按照回调中return 结果正负数来排序

sublist()  返回指定区间的值

shuffle()  随机打乱数组中元素的顺序

first() 返回数组中的第一个元素

last()  返回数组中最后一个元素

asMap()  将数组转为Map类型,并把索引作为key,list中的值作为value

forEach()  循环

示例:

  • length 返回数组长度
void main() {

    var list = ['hello', 'dart'];

    print(list.length);  // 2
}
  • add 给数组添加元素
void main() {

    var list = ['hello', 'dart'];

    list.add('new');
    
    print(list);  //   ['hello', 'dart', 'new'];
}
  • addAll 把一个数组元素全部添加到另一个数组中
void main() {

    var list = ['hello', 'dart'];

    list.addAll(['new', '123']);
    
    print(list);   //  ['hello', 'dart', 'new', '123']; 

}
  • insert 向指定位置添加元素
void main() {

    var list = ['hello', 'dart'];

    list.insert(0, '777');

    print(list);  // ['777', 'hello', 'dart']
}
  • remove 删除指定元素
void main() {

    var list = ['hello', 'dart'];

    list.remove('dart');

    print(list);  // ['hello'] 

}
  • removeLast 删除最后一个元素
void main() {

    var list = ['hello', 'dart','123'];

    list.removeLast();

    print(list);  // ['hello', 'dart']
}
  • removeAt 删除指定位置的元素
void main() {

    var list = ['hello', 'dart','123'];

    list.removeAt(1);

    print(list);  // ['hello', '123']
}
  • removeRange 删除指定位置的元素 ,前包后不包
void main() {

    var list = ['hello', 'dart','123', '999'];

    list.removeRange(0, 2);

    print(list);  // ['123', '999']

}
  • removeWhere 删除符合某个条件的全部元素
void main() {

    var list = [22, 19, 1, 99, 6];

    list.removeWhere((age)=> age>10 );

    print(list);  // [1, 6]
}
  • clear 清空数组
void main() {

    var list = ['hello', 'dart'];

    list.clear();

    print(list);  // []
}
  • indexOf 找出某个元素在数组中的索引位置,如果没有返回 -1
void main() {

   var list = ['new', 'language', 'name'];
   
   print(list.indexOf('new'));  // 0

}
  • lastIndexOf 找出某个元素在数组中最后一次出现的位置,如果没有返回-1
void main() {

   var list = ['new', 'language', 'new'];
   
   print(list.lastIndexOf('new'));  // 2
}
  • sort 按照回调中return 结果正负数来排序
void main() {

   var list = [3, 2, 6, 4, 6];
   
   list.sort(a, b){
     return a - b ;   
   };
   
   print(list);  // [6, 6, 4, 3, 2]
}
  • sublist 返回指定区间的值,前包后不包
void main() {

   var list = [3, 2, 6, 4, 6];
   
   print(list.sublist(2));  // [6, 4, 6] 返回索引从2开始的所有数据
   
   print(list.sublist(1, 3));  // [2, 6] 返回索引从2到4的数据
}
  • shuffle 随机打乱数组中元素的顺序
void main() {

   var list = [3, 2, 6, 4, 6];
   
   print(list.shuffle());  // [6, 3, 6, 2, 4]  
}
  • first 返回数组中的第一个元素
void main() {

   var list = [3, 2, 6, 4, 6];
   
   print(list.first());  //  3  等同与 list[0]  
}
  • last 返回数组中最后一个元素
void main() {

   var list = [3, 2, 6, 4, 6];
   
   print(list.last());  //  6  等同与 list[list.length-1]  
}
  • asMap 将数组转为Map类型,并把索引作为keylist中的值作为value
void main() {

   var list = [3, 2, 6];
   
   var map = list.asMap()
   
   print(map);  // {0: 3, 1: 2, 2: 6}
   
   // 当然你想把转为Map对象再重新转回来,如下操作:
   
   map.values.toList();  //  [3, 2, 6]
   
   // 当然你也可以只取key
   
   map.keys.toList();  //  [0, 1, 2]
}
  • forEach 循环数组
void main() {

   var list = [3, 4, 6];
   
   list.forEach(element){
     return element ;   
   }
   
   print(list);  // 3 4 6 
}

4. map: 类型

void main() {

    var map1 = {"first": "Dart", 1: true};
    

    print(map1);  // {"first": "Dart", 1: true}

    print(map1['first']);  // Dart

    print(map1[1]);  // true

    map1[1] = false;

    print(map1);  // {"first": "Dart", 1: false}

    var map2 = const {1: 'Dart', 2: 'Javascript'};

    map2[1] = "222”; // 类型报错,非同类型的不可赋值
}

打印结果:

image.png

属性方法:

[index]  获取数组中指定位置上的元素,index 为索引

length  返回数组长度

keysvalues  值

isEmpty  是否为空

isNotEmpty  是否不为空

containsKey   是否包含指定的 key

containsValue   是否包含指定的 value

remove   删除指定 key 的健值对

removeWhere 删除此映射中满足给定条件的所有元素

forEach  循环遍历

示例:

  • length 返回数组长度
void main() {

    var map1 = {"first": "Dart", 'second': 'Java', 'third': 'Python'};

    print(map1.length);  // 3
}
  • keys 返回Map 对象中所有的健
void main() {

    var map1 = {"first": "Dart", 'second': 'Java', 'third': 'Python'};

    print(map1.keys);  // first  second  third
}
  • values 返回Map 对象中所有的值
void main() {

    var map1 = {"first": "Dart", 'second': 'Java', 'third': 'Python'};

    print(map1.values);  // Dart  Java  Python
}
  • isEmpty 是否为空
void main() {

    var map1 = {"first": "Dart", 'second': 'Java', 'third': 'Python'};

    print(map1.isEmpty);  // fasle
    
    
    var map2 = {};

    print(map1.isEmpty);  // true
}
  • isNotEmpty 是否不为空
void main() {

    var map1 = {"first": "Dart", 'second': 'Java', 'third': 'Python'};

    print(map1.isNotEmpty);  // true
    
    
    var map2 = {};

    print(map1.isNotEmpty);  // fase
}
  • containsKey 是否包含指定的 key
void main() {

    var map1 = {"first": "Dart", 'second': 'Java', 'third': 'Python'};

    print(map1.containsKey('second'));  // true
}
  • containsValue 是否包含指定的 value
void main() {

    var map1 = {"first": "Dart", 'second': 'Java', 'third': 'Python'};

    print(map1.containsValue('javascript'));  // false
}
  • remove 删除指定 key 的键值对,返回指定的键对应的值
void main() {

    var map1 = {"first": "Dart", 'second': 'Java', 'third': 'Python'};

    print(map1.remove('first'));  // Dart
    
    print(map1)  // {'second': 'Java', 'third': 'Python'};
}
  • removeWhere 删除此映射中满足给定条件的所有元素
void main() {

    var map1 = {"first": "Dart", 'second': 'Java', 'third': 'Python'};

    map1.removeWhere((key,value) => value.startsWith('P')); 
    
    print(map1)  // {"first": "Dart", 'second': 'Java'};
}
  • forEach 循环遍历
void main() {

    var map1 = {"first": "Dart", 'second': 'Java', 'third': 'Python'};

    map1.forEach((key, value) {

        print("key = $key,value = $value");

    });
    
      // key = first, value = Dart
      
      // key = second, value = Java
      
      // key = third,value = Python
}

5. dynamic: 类型

可以是任何动态类型,类似tsany 类型

void main() {

    var a;

    a = 10;

    a = 'Dart';

    dynamic b = 23;

    b = 'javascript';

    var list1 = <dynamic>[]; // 等同于 new List<dynamic>() ,但是这种写法现在不提倡了


    list1.add('1');

    list1.add('hhh');

    list1.add(false);

    print(list1);    
}

打印结果:

image.png


6. enum: 枚举

枚举是一种有穷序列集的数据类型,使用关键字 enum 定义一个枚举,常用于代替常量,控制语句等。

示例:

enum Season { 

    spring, 

    summber, 

    autumn,

    winter 
}

可获取当前枚举值的 index 属性,枚举从 0 开始累加,并且不能指定原始值

print(Season.spring.index) // 0

7. runesUnicode 中对应的编码

Unicode为每一个字符、标点符号、表情符号等都定义了一个唯一的数值(即Unicode码点),可以表示任何字符。由于Dart字符串是UTF-16的字符序列,所以在字符串中表达32位的字符序列就需要特殊的语法。

  • 可以通过创建runes对象来拿到对应的元素
Runes runes = new Runes('\u2665,\u{1f605},\u{1f60e}'); 

print(runes); // (9829, 44, 128517, 44, 128526)

print(String.fromCharCodes(runes)); // ♥,😅,😎

  • 也可以通过renes来反向拿到元素对应Unicode编码

可以单个获取

const emojiMan = '👨';

const emojiLaughing = '😆'; 


print(emojiMan.runes); // (128104) 

print(emojiLaughing.runes); //(128518)

也可以批量获取

const string = 'Dart';

final runes = string.runes.toList(); 

print(runes); // [68, 97, 114, 116]

8. 特殊作用的类型

除了上面的内置类型外,dart 中还有一些特殊作用的类型,这些类型一般情况下只用做参数类型约束

  1. Object: 是除了Null 类型以外所有类型的超集
  2. FutureStream:用于异步
  3. Iterable:用于 for-in 循环和同步的 generator 构造器
  4. Never: 象征表达式用永远无法被到达,多用于函数抛出异常
  5. void: 多用于函数中,表示没有返回值

9. T 泛型

Dart 中类型是可选的,既可以使用指定类型,也可使用泛型,通常使用场景为:规定入参和出参的类型一致,但入参的类型不确定时。

  • 指定类型(常用)
void main() {

    var list = <String>[]; // 指定类型

    list.add(2);
}
  • 在类上使用泛型
void main() {

    var utils = Utils<int>();

    utils.put(3);

}

class Utils<T> {

    late T element;

    void put(T element) {

        this.element = element;
    }
}
  • 在方法上使用泛型
void main() {

    var utils = Utils<int>();

    utils.put<int>(3);
}

class Utils<T> {

    late T element;

    void put<K>(K element) {

        print(element);
    }
}

10. 获取对象类型

在上面我们列举了常用的数据类型,那么如果我们想知道某个对象数据的类型时,就可以使用以下方式来进行获取。

var s = 'string';
var n = 987;

pring(s.runtimeType); // String
print(n.runtimeType); // int

二、运算符、表达式、循环

包含算数运算符、关系运算符、逻辑运算符、赋值运算符、表达式以及循环。

1. 算术运算符

+  加
-  减
*  乘
/  除

运算符在前先计算,运算符在后则后计算

++   加加
— —  减减  

示例:

var a=2;

print(++a) // 3

print(a++) // 3

print(a++) //4

2. 关系运算符,结果为布尔值

== !== > < >= <=

判断内容相等用 ==,语法中没有三个等号 === ,其他同js

3. 逻辑运算符,结果为布尔值 true false

! 

&&

!!

4. 赋值运算符

基础运算符: = 、 ??=

复合运算符: += -= *= /= %= ~/=
  • 基础运算符示例:
int b = 3;

b ??= 10; // 如果b 没有值就去赋值,如果有值就使用它自己的值

print(b); // 3
  • 复合运算符示例:
void main() {

    int a = 10;

    a += 2;

    print(a);  // 12

    a -= 5;

    print(a);  // 7 

    a *= 3;

    print(a);  // 21
    
    //  a /= 2;    // 结果可能为浮点型,但我们当前声明为整型

    a %= 2;

    print(a);  // 1

    a ~/= 2;

    print(a);  // 0
}

5. 表达式和循环

  • 条件表达式
? :   三目运算符

??     如果第一个为空,则使用第二个,第一个不为空就用第一个

示例:

void main() {

    int gender = 0;

    String str = gender == 0 ? 'Male' : 'Female';

    print(str); // Male

    String a = '3';

    String b = 'Java';

    String c = a ?? b;

    print(c); // 3
}

  • 条件语句:

if -> else -> else if 等同与js


  • 循环语句:

forfor..in

void main() {

var list = [1, 2, 3, 4, 5];

    for (var index = 0; index < list.length; index++) {

        print(list[index]);
    }

    for (var item in list) {

        print(item);
    }
} 

  • while 循环

whiledo while

示例:

void main() {

    int count = 0;

    while (count < 5) {

        print(count++);
    }

    print('------$count------‘);

    do {
       print(count--);

    } while (count > 0 && count < 5);
}

打印结果:

image.png


  • breakcontinue: 符合某个条件后的操作
void main() {

    var list = [1, 2, 3];

    for (var item in list) {

        if (item == 2) {

            break;
        }
        print(item);
    }

    print('------------‘);

    for (var item in list) {

        if (item == 2) {
            continue;
        }
        print(item);
    }
}

打印结果:

image.png


  • switch...case : 循环遍历,匹配不同条件,执行不同操作

示例:

void main() {

    String language = 'Dart';

    switch (language) {

        case 'Dart': 

        print('Dart is my favorite’);  //  Dart is my favorite

        break;

        case 'Java':

        print('Java is my favorite');

        break;

        default:

        print('none');        
    }
}

三、function 函数方法

void main(List args) {

    print(args);

    print(getPerson('章三', 18));

    printPerson('李四', 20);
}


String getPerson(String name, int age) {

    return "name=$name,age=$age";
}

void printPerson(String name, int age) {

    print("name=$name,age=$age”);
}

打印结果:

image.png

1. 类型也可以省略

虽然说类型是可以省略的,但为了保证一个好的代码习惯和严谨的态度,尽量都加上,当然不加也不会报错。

普通函数:

void printPerson(String name, int age) {

      print("name=$name,age=$age”);
  }


修改为:

printPerson(name, age) {

    print("name=$name,age=$age”);
 }

箭头函数:

String getPerson(String name, int age) {

        return "name=$name,age=$age";
}

// 修改为:

 getPerson(name, age) => "name=$name,age=$age”;

2. 可选参数

可选参数必须要位于必填参数之后

void main(List args) {

    printPerson('章三’);  // name=章三,age=null,gender=null

    printPerson('章三', age: 20, gender: '女’); // name=章三,age=20,gender=女

    printPerson2('小红', 18, '女’);  //  name=小红,age=18,gender=女
}


void printPerson(String name, {int? age, String? gender}) {

    print("name=$name,age=$age,gender=$gender”);  
}


void printPerson2(String name, [int? age, String? gender]) {

    print("name=$name,age=$age,gender=$gender");
}

3. 默认参数

void main(List args) {

    printPerson(    

        '章三',

        age: 10,
    );
}

void printPerson(String name, {int? age = 20, String? gender = '女'}) {

    print("name=$name,age=$age,gender=$gender");  //   name=章三,age=10,gender=女
}

4. 方法对象

可以作为对象赋值给其他变量

void main(List args) {

    Function func = printHello;

    func();
}

void printHello() {

    print('Hello’); // Hello

}
  • 可以作为参数传递给其他方法

示例1:

void main(List args) {

    var list = [1, 2, 3, 4];

    list.forEach(print);
}

image.png

示例2:

void main(List args) {

    List list1 = ['h', 'e', 'l', 'l', 'o'];

    print(listTimes(list1, times));
}

List listTimes(List list, String times(str)) {


    for (var index = 0; index < list.length; index++) {

        list[index] = times(list[index]);
    }
    return list;
}

String times(str) {

    return str * 3;
}

// [hhh, eee, lll, lll, ooo]

5. 匿名方法

可赋值给变量,通过变量进行调用,可在其他方法中直接调用或传递给其他方法

void main(List args) {

    var fn = (str) {
        print('hello$str’);

    };

    fn('999’);  // hello999
}

6. 闭包

闭包是一个对象方法

闭包定义在其他方法内部

闭包能够访问外部方法内的局部变量,并持有其状态

示例:

void main(List args) {

    var func = a();

        func();

        func();

    }

    a() {
    
        int count = 0;

        return () {
            print(count++);
        }

    }

image.png

三、面向对象

dart中的重头戏,一切都是类,一切都是对象。

1. 类与对象

使用关键字class 声明一个类

使用关键字new 创建一个对象,new 可省略

所有对象都继承于Object

2. 属性与方法

属性默认会生成gettersetter方法

使用final 声明的属性只有getter方法,只读

属性和方法通过 . 访问

方法不能被重载,不能重名

示例:

void main() {


    var person = Person();

    person.name = '章三';

    person.age = 19;

    print(person.age); // 19

    person.work(); //  Name is 章三,Age is 19
}


class Person {

    String name = '';

    int age = 0;

    final String address = '北京’; // 只读

    void work() {

        print('Name is $name,Age is $age');

    }
}

3. 类及成员可见性

Dart 中的可见性以library(库)为单位

默认情况下,每一个 Dart 文件就是一个库

使用 _ 表示库的私有性

使用 import 导入库

示例:

import 'person.dart’; // 此时我们直接引用的是另一个文件的的Person 类

void main() {

    var person = Person();

    person.name = '章三';

    person.age = 19;

    print(person.age);

    person.work();

    person.address;

}

编辑器中的展现:

image.png

可是当person 中的方法属性是私有的(定义时变量前带下划线 _),就无法进行调用了,如下:

class Person {

    String _name = '';

    int _age = 0;

    final String _address = '北京';

    void work() {

        print('Name is $_name,Age is $_age');
    }
}

报错了--------

image.png


4. 计算属性

顾名思义,计算属性的值是通过计算而来,本身不存储值

计算属性赋值,其实是通过计算转换到其他实例变量

示例:

void main() {

    var rect = new Rectangle1();

    rect.width = 20;

    rect.height = 10;

    print(rect.area()); // 200

}

class Rectangle1 {

    num width = 0, height = 0;

    num area() {
        return width * height;
    }
}

将方法转为属性,方法一般表示的是行为和动作,属性则表示的是自身的属性结果

void main() {

    var rect = new Rectangle1();

    rect.width = 10;

    rect.height = 20;

    print(rect.area); // 200

    print(rect.width); // 20 
}


class Rectangle1 {

    num width = 0, height = 0;

    num get area{

        return width * height;
    }

    //计算属性的赋值 set :

         set area(value){

            width=value / 10 ;
    }
}

5. 构造方法

如果没有自定义构造方法,则会有个默认构造方法

void main() {

    var person = new Person();

    person.name = 'Tom';

    person.age = 20;
}

class Person {

    String name = '';

    int age = 0;

    final String gender = '女';
}

如果存在自定义构造方法,则默认构造方法无效

void main() {


var person = new Person('Tom', 20,'女');

    // person.name = 'Tom’; // 此行报错,在已经有自定义的构造函数后,默认构造函数将不可再使用

    // person.age = 20;
}


class Person {

    String name = '';

    int age = 0;

    final String gender = '女';

    Person(String name, int age, String gender) {

        this.name = name;

        this.age = age;

        this.gender = gender; // 此行报错,在构造函数内是不允许修改final 定义的值

    }
}

语法糖:

    Person(this.name,this.age)  // 等同于上面👆的书写方式,

    // 在语法糖里可以直接对 finnal 定义值进行赋值,因为赋值在构造函数定义之前,但是此操作编辑器报错

6. 命名构造方法

使用命名构造方法,可以实现多个构造方法      

使用类名 . 方法的形式实现

示例:

void main() {

    var person = new Person('Tom', 20);

    new Person.withName('小红');
}


class Person {

    String name = '';

    int age = 0;

    final String gender = '女';

    Person(this.name, this.age)

    Person.withName(this.name);
}

7. 常量构造方法

如果类是不可变状态,可以把对象定义为编译时常量

使用 const 声明构造方法,并且所有变量都为 final

使用 const 声明对象,可以省略

8. 初始化列表

初始化列表会在构造方法体执行之前执行

使用逗号分隔初始化表达式

初始化列表常用语设置 final 变量的值

示例:

void main() {
    var person = Person('Tom', 20);

    person.work();
}

class Person {

    String name = '';

    int age = 0;

    final String gender = '女';

    Person(this.name, this.age);

    Person.withMap(Map map) : gender = map['gender'] {

        this.name = name;
    }
}

9. 静态成员

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

静态成员不能访问非静态成员,非静态成员可以访问静态成员

类中的常量需要使用static const 声明

示例:

void main() {

    var page = new Page();

    page.scrollUp();    

    Page.scrollDown();
}

class Page {

    static const int maxPage = 10;

    static int currentPage = 1;

    static void scrollDown() {

        currentPage = 1;

        print('down');
    }

    void scrollUp() {

        currentPage++; // 非静态成员可以访问静态成员

        print('up');
    }
}

10. 对象操作符

  • as 类型转换运算符
void main() {

    var person;

    person = '';

    person = new Person();

    (person as Person).work(); // 类型指定
}


class Person {

    void work() {

        print('work...');
    }
}
  • is / is! 是否包含

void main() {

    var person;

    person = ‘';

    person = new Person();

    if (person is Person) { // 使用is 去判断

        person.work();
    }

}


class Person {

    void work() {

        print('work...');
    }
}

11. .. ?..级连操作符

可以让你在同一个对象上连续调用多个对象的变量或方法

var person = new Person();

person.name=’Tom’;

Person.age=20;


优化后:

void main() {

    var person = new Person();

    person

        ..name = 'Tom'

        ..age = 20

        ..work();
}


class Person {

    String name = '';

    int age = 0;

    void work() {

        print('work...');
    }
}


再优化

void main() {

    new Person()

    ?..name = 'Tom'

    ..age = 20

    ..work();
}


class Person {

    String name = '';

    int age = 0;

    void work() {

        print('work...');
    }
}

12. 对象call 方法

如果类实现了call()方法,则该类的对象可以作为方法使用

void main() {

    var person = new Person();

    print(person('test', 20));
}

class Person {

    String name = '';

    int age = 0;

   // 名称必须叫call,可以有返回值,也可以不返回
   
   String call(String name, int age) { 

       return "name is $name,age is $age";
    }
}

四、面向对象扩展

1. 继承

使用关键字 extend 继承一个类

子类会继承父类可见的属性和方法,不会继承构造方法

子类能够复写父类的方法,gettersetter

单继承,多态性

下面是多文件的示例:

person.dart

class Person {

    String name = '';

    int age = 0;

    bool get isAdult => age > 18;

    void run() {

        print('person run ...');
    }
}

main.datr

import 'person.dart';

void main() {

    var student = new Student();

    student.study();

    student.name = '张三’; // 父类中的属性和方法都是可以拿到的

    student.age = 20;

    student.isAdult;

    student.run();
}


class Student extends Person {

    void study() {

        print("student study");
    }
}

image.png


2. 复写

void main() {

    var student = new Student();

    student.study();

    student.name = '张三';

    student.age = 20;

    student.isAdult;

    student.run();
}


class Student extends Person {

    void study() {

    print("student study");

}

  // 复写父类中的方法和计算属性

    @override
    
    bool get isAdult => age > 10;


    @override

    void run() {

        print('Student run...');
    }
}

3. 继承中的构造方法

子类的构造方法默认会调用父类的无名无参构造方法

void main() {

    var student = new Student(); // 创建子类的构造方法,回默认调用父类的构造方法
}

class Person {

    String name = '';

    Person() {

        print('Person...');
    }
}

class Student extends Person {

    int age = 1;
}

如果父类没有无名无参构造方法,则需要显示调用父类构造方法,在构造方法参数后使用冒号 : 显示调用父类构造方法

void main() {

    var student = Student('章三');
}

class Person {

    String name = '';

    Person(this.name);

    Person.withName(this.name);
}

class Student extends Person {

    int age = 1;

    Student(String name) : super(name);

    Student.withName(String name):super.withName(name);
}
  • 构造方法执行顺序
父类的构造方法在子类构造方法体开始执行的位置调用

如果有初始化列表,初始化列表会在父类构造方法前执行

4. 抽象类

抽象类使用abstract 表示,不能直接被实例化

void main() {

    var student = Person(); // 这里报错,不可被实例化

}

    abstract class Person {}

抽象方法不用abstract 修饰,无实现,抽象类可以没有实现方法,有抽象方法的类一定得声明为抽象类

void main() {

    var student = Student();
}

abstract class Person {

    void run();
}


class Student extends Person {

    @override

    void run() {

        print('run');

    }
}

5. 接口

类和接口是统一的,类就是接口

每个类都隐式的定义了一个包含所有实例成员的接口

如果是复用已有类的实现,使用继承 extends

如果只是使用已有类的外在行为,使用接口 implements

示例:

void main() {

    var student = Student();
}

class Person {

    String name = '2';

    int get age => 18;

    void run() {

        print('Person run..');
    }
}

class Student implements Person {

    @override

    String name = '4';

    @override

    int get age => 16;

    @override

    void run() {}
}

推荐使用抽象类来实现接口,如下:

void main() {

    var student = Student();
}

abstract class Person {

    void run();
}


class Student implements Person {

    @override

    void run() {

        print('Student...');
    }

}
  • Mixins
Minxins 类似于多继承,是在多类继承中重用一个类代码的方式

可以使用with 来连接继承多个类

示例:

void main() {

    var d = D();

    d.a();  // 这里D就可以继承A B C 的所有方法// 假设有A B C 三个类,类D需要继承前面的三个,但是又没有多继承的方法 

class A {

    void a() {

        print("A.a()...");
    }
}

class B {

    void b() {

        print("B.b()...");
    }
}

class C {

    void c() {

        print("C.c()...");
    }
}

class D extends A with B, C {}  

// 当继承的A B C 中都有同名的方法,最后调用的顺序和这个继承顺序相关,最后继承的谁,调用的就是谁

作为Mixin 的类不能有显示声明构造方法

void main() {

    var d = D();

    d.a();
}

class A {

    A() {}

    void a() {

        print("A.a()...");
    }
}

class B {

    B() {} // 不能有显示声明的构造方法

    void b() {

        print("B.b()...");
    }
}

class C {

    void c() {

        print("C.c()...");
    }
}


class D extends A with B, C {} // 会报错

复杂场景,合并继承多个类,根据需求灵活组合

void main() {

    var bus = Bus();

    print(bus.work);

    var car = Car();

    print(car.work);
}

abstract class Engine {

    void work();
}


class OilEngine implements Engine {

    @override

    void work() {

        print("Work with oil...");
    }
}


class ElectricEngine implements Engine {

    @override

    void work() {

        print('Work with Electric...');
    }
}

class Tyre {

    String name = '';

    void run() {}
}


class Car = Tyre with ElectricEngine;

class Bus = Tyre with OilEngine;

6. 操作符复写

复写操作符需要在类中定义

返回类型 operator 操作符(参数1,参数2…){

    实现体

    return 返回值
}

如果复写 == ,还需要复写对象的 hashCode getter 方法

image.png

  • 重写大于号  >
void main() {

    var person1 = Person(12);

    var person2 = Person(16);

    print(person2 > person1);  // true 
}

class Person {

    int age;

    Person(this.age);

    bool operator >(Person person) {

        return age > person.age; 
    }
}
  • 重写中括号 [ ] 操作符
void main() {

    var person1 = Person(12);

    print(person1['age']); //12
}

class Person {

    int age;

    Person(this.age);

    int operator [](String str) {

        if ('age' == str) {

            return age;
        }
            return 0;
    }
}

六、Future

futurejs中的Promise 差不多,要使用 asyncawait 来让代码变成异步的。

范例:

Future<void> checkVersion() async { 
    var version = await lookUpVersion();
    // Do something with version 
}

实际使用:

  Future<IResponse> getConversationList({
    required IOptions options,
  }) =>
      _channel
          .invokeMethod(
              'getConversationList',
              _buildParam({
                "options": convert.jsonEncode(options),
              }))
          .then((value) => Utils.toResponse(value));

异常处理:

可使用try、catch 以及finally 来处理 await导致的异常。

Future<void> printDailyNewsDigest() async {

  try {

    var newsDigest = await gatherNewsReports();

    print(newsDigest);

  } catch (e) {

    // Handle error...

    // 处理代码执行错误...

  }

}