前言
最近在做一个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);
}
打印效果:
字符串的常用操作:
+ 字符串相加
* 返回一定倍数的字符串的组合
== 两个字符串是否相等
[] 取出指定位置的字符串
$ 字符串变量
示例:
+
字符串相加
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
类型只有两个值:true
和false
基本等同于 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
类型,并把索引作为key
,list
中的值作为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”; // 类型报错,非同类型的不可赋值
}
打印结果:
属性方法:
[index] 获取数组中指定位置上的元素,index 为索引
length 返回数组长度
keys 键
values 值
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
: 类型
可以是任何动态类型,类似
ts
的any
类型
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);
}
打印结果:
6. enum
: 枚举
枚举是一种有穷序列集的数据类型,使用关键字
enum
定义一个枚举,常用于代替常量,控制语句等。
示例:
enum Season {
spring,
summber,
autumn,
winter
}
可获取当前枚举值的
index
属性,枚举从0
开始累加,并且不能指定原始值
print(Season.spring.index) // 0
7. runes
在 Unicode
中对应的编码
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
中还有一些特殊作用的类型,这些类型一般情况下只用做参数类型约束
Object
: 是除了Null
类型以外所有类型的超集Future
和Stream
:用于异步Iterable
:用于for-in
循环和同步的generator
构造器Never
: 象征表达式用永远无法被到达,多用于函数抛出异常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
- 循环语句:
for
、for..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 循环
while
、do while
示例:
void main() {
int count = 0;
while (count < 5) {
print(count++);
}
print('------$count------‘);
do {
print(count--);
} while (count > 0 && count < 5);
}
打印结果:
break
、continue
: 符合某个条件后的操作
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);
}
}
打印结果:
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”);
}
打印结果:
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);
}
示例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++);
}
}
三、面向对象
dart
中的重头戏,一切都是类,一切都是对象。
1. 类与对象
使用关键字class 声明一个类
使用关键字new 创建一个对象,new 可省略
所有对象都继承于Object类
2. 属性与方法
属性默认会生成getter 和setter方法
使用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;
}
编辑器中的展现:
可是当person
中的方法属性是私有的(定义时变量前带下划线 _
),就无法进行调用了,如下:
class Person {
String _name = '';
int _age = 0;
final String _address = '北京';
void work() {
print('Name is $_name,Age is $_age');
}
}
报错了--------
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 继承一个类
子类会继承父类可见的属性和方法,不会继承构造方法
子类能够复写父类的方法,getter 和 setter
单继承,多态性
下面是多文件的示例:
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");
}
}
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 方法
- 重写大于号 >
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
future
和js
中的Promise
差不多,要使用async
和await
来让代码变成异步的。
范例:
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...
// 处理代码执行错误...
}
}