Dart环境搭建
Windows 系统安装 Dart
- 命令行安装
# 安装dart-sdk
$ choco install dart-sdk
# 更新dart-sdk
$ choco upgrade dart-sdk
- 软件安装 SDK(推荐)
下载dart的SDK软件
Mac系统安装Dart
# 跟新追踪brew的包管理信息,扩展可安装软件的选择
$ brew tap dart-lang/dart
# 使用brew安装dart
$ brew install dart
Dart开发工具
Dark 常见的开发工具有:IntelliJ IDEA、Webstorm、Atom、Vscode 等。
- Dart的提示插件
- Dart 的运行插件 code runner
Dart的初次编写
main() {
print("hello dart");
}
Dart基础
Dart强类型语言。
变量
定义变量
- 方法一:通过
var
关键字。
var str = 'This is var'
- 方法二: 通过类型关键字
String str = 'this is a string';
print(str)
int num = 123;
print(num)
注:var 不要和类型关键字一起使用
可空变量
如果你开启了 空安全,变量在未声明为可空类型时不能为 null。你可以通过在类型后加上问号 (?) 将类型声明为可空。
String? name; //默认值为null。
name = 'changan';
任意类型常量
如果一个对象的引用不局限于单一的类型,可以将其指定为 Object(或 dynamic)类型。
如果你想要显式地声明允许任意类型,使用 Object?(如果你 开启了空安全)、 Object 或者 特殊类型 dynamic 将检查延迟到运行时进行。
Object name = 'changan';
print(name);
name = 3;
print(name);
dynamic name = 'changan';
print(name);
name = 3;
print(name);
常量
如果你不想更改一个变量,可以使用关键字 final
或者 const
修饰变量,这两个关键字可以替代 var 关键字或者加在一个具体的类型前。
final
定义的变量,在第一次使用的时候初始化。,而const
必须在定义时就初始化。
final是运行时常量,并且是惰性初始化,即运行时第一次使用前才初始化,而const是编译时常量,在定义时必须要初始化。
- const常量
const PI = 3.14159;
print(PI)
- final常量
//运行时才能获取到日期
final now = new DateTme.now();
print(now)
数据类型
(1)数值类型
- num
- int
- double
(2)字符串 - String
(3)布尔 - bool
(4)数组 - 在 Dart 中,数组是列表对象,所以大多数人只是称它们为列表
(5)字典 - 通常来说,Map 是一个键值对相关的对象。键和值可以是任何类型的对象。每个键只出现一次,而一个值则可以出现多次。映射是动态集合。换句话说,Maps 可以在运行时增长和缩小。dart:core 库中的 Map 类提供了相同的支持。
数值类型
void main() {
int a = 123;
double b = 23.5;
//int和double相加会自动进行类型转换,转换成double。
var c = a + b;
print(c);
num d = a + b;
print(d);
}
布尔类型
/*
bool 值true/false
*/
void main() {
var flag = true;
if(flag) {
print("真");
}else{
print("假");
}
}
Set类型
Dart 中的 set 是无序且唯一的元素集合。Dart 通过 set 字面量和 Set 类型来支持 set。
void main() {
var set = {1,2,3,4,5};
print(set);
//转换成list类型
var list = set.toList();
print(list);
var set2 = new Set();
set2.add(6);
set2.addAll(set);
print(set2);
print(set2.length);
}
Runes
在 Dart 中,runes 表示字符串中 UTF-32 编码的码位。
Unicode 为世界上所有的书写系统中的每个字母、数字和符号都定义了一个唯一数值。因为 Dart 的字符串是 UTF-16 码位的序列,在字符串中表示32位 Unicode 值需要特殊的语法。
main() {
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));
}
Symbols
Symbols 对象表示在 Dart 程序中声明运算符或标识符。您可能永远不需要使用符号,但它们对于按名称引用标识符的 API 非常有用,因为缩小会更改标识符名称而不会更改标识符符号。要获取标识符的符号,请使用符号文字,它只是#后跟标识符。
#radix
#bar
Symbol 字面量是编译期常量。
字符串类型
基础用法
void main() {
// 字符串定义的几种方式
//自定推断类型
var str1 = 'this is str1';
var str2 = "this is str2";
print(str1);
print(str2);
//指定类型
String str3 = 'this is str3';
String str4 = "this is str4";
print(str3);
print(str4);
String str5 = "'this is str5'";
print(str5);
//换行字符串
String str6 = ''' this is str6
this is str6
this is str6
''';
print(str6);
//字符串插值表达式
var str7 = "nihao";
print("say:$str7");
var num1 = 2;
var num2 = 3;
print("sum:${num1 + num2}");
}
常用属性
- 获取字符串的长度
print(text.length); // 8
- 当前字符串是否为空
print(text.isEmpty); // false
- 当前字符串是否不为空
print(text.isNotEmpty); // true
- 运行时的数据类型
print(text.runtimeType); // String
常用方法
- 字符串类型转换 toString
int a = 10;
print(a.toString()); // int 类型的 10 转换为 String 类型的 "10"
List list = [1, 3, 5];
print(list.toString()); // List 数组类型 [1, 3, 5] 转换为 String 类型的 "[1, 3, 5]"
Map map = {"name": "Allen Su", "age": 18};
print(map.toString()); // Map 字典类型转换为 String 类型的 "{"name": "Allen Su", "age": 18}"
- indexOf 字符串查找
由前向后查找指定字符,返回值是 int 类型,如果有符合条件的则返回对应字符所在的索引,没有找到则返回 -1。
String text = "abcDcbaE";
// 01 查找字符 c 第一次出现时的索引
print(text.indexOf('c')); // 返回 2
// 02 从索引 3 位置开始查找字符 c 第一次出现时的索引
print(text.indexOf('c', 3)); // 返回 4
// 03 查找字符 p 第一次出现时的索引
print(text.indexOf('p')); // // 返回 -1
// 04 使用正则表达式,查找第一次出现大写字母的字符时的索引
print(text.indexOf(RegExp('[A-Z]'))); // 返回 3
- lastIndexOf
由后向前查找指定字符,返回值是 int 类型,如果有符合条件的则返回对应字符所在的索引,没有找到则返回 -1。
String text = "abcDcbaE";
// 01 倒序查找字符 a 第一次出现时的索引
print(text.lastIndexOf('a')); // 返回 6
// 02 从索引 2 位置倒序查找字符 a 第一次出现时的索引
print(text.lastIndexOf('a', 2)); // 返回 0
// 03 倒序查找字符 p 第一次出现时的索引
print(text.lastIndexOf('p')); // 返回 -1
// 04 使用正则表达式,倒序查找第一次出现大写字母的字符时的索引
print(text.lastIndexOf(RegExp('[A-Z]'))); // 返回 7
- contains 判断字符串中是否包含指定字符
判断字符串中是否包含指定字符,存在返回 true,不存在返回 false。
String text = "https://allensu.blog.csdn.net";
// 01 判断字符串中是否包含字符 allensu
print(text.contains('allensu')); // 返回 true
// 02 判断字符串中是否包含字符 CSDN
print(text.contains('CSDN')); // 返回 false
// 03 判断从索引 8 位置处开始,字符串中是否包含字符 /
print(text.contains('/', 8)); // 返回 false
- startsWith 判断字符串是否以指定字符开头,成立返回 true,不成立返回 false。
String text = "https://allensu.blog.csdn.net";
// 01 判断字符串是否以字符 https 开头
print(text.startsWith('https')); // 返回 true
// 02 判断字符串是否以字符 w 开头
print(text.startsWith('w')); // 返回 false
// 03 判断从索引 6 位置处开始,字符串是否以字符 // 开头
print(text.startsWith('//', 6)); // 返回 true
- endsWith 判断字符串是否以指定字符结尾,成立返回 true,不成立返回 false
String text = "https://allensu.blog.csdn.net";
// 01 判断字符串是否以字符 net 结尾
print(text.endsWith('net')); // 返回 true
// 02 判断字符串是否以字符 w 结尾
print(text.endsWith('w')); // 返回 false
- substring 字符串分割
String text = "aaabbbccc";
// 01 从索引 3 位置到末尾,分割字符串
print(text.substring(3)); // 返回 bbbccc
// 02 从索引 3 位置到索引 8 位置,分割字符串
print(text.substring(3, 8)); // 返回 bbbcc
- split
以指定字符分割字符串,返回类型为 List ,被分割的字符不会添加到数组中。
// 01 以字符 % 分割字符串
String str1 = "a%b%c%d%e%f";
List<String> l1 = str1.split('%');
print(l1); // 返回长度为 6 的字符串列表 [a, b, c, d, e, f]
// 02 以空格分割字符串
String str2 = "abcdef";
List<String> l2 = str2.split('');
print(l2); // 返回长度为 6 的字符串列表 [a, b, c, d, e, f]
// 03 当指定的字符不在字符串中时
String str3 = "xyz";
List<String> l3 = str3.split('+');
print(l3); // 返回 [xyz]
- splitMapJoin
第一个参数可以是一个字符串 String 或一个正则 RegExp 对象。
第二个参数是可选参数,表示将每个和指定的分割字符匹配的,替换为自定义返回的字符串。
第三个参数也是可选参数,但和第二个参数刚好相反,表示将每个和指定的分割字符不匹配的,替换为自定义返回的字符串。
String text = "a,b,c,d";
String val = text.splitMapJoin(
',', // 以逗号 , 为分割字符
onMatch: (Match match) => "y", // 将字符串中是逗号的替换为字符 y
onNonMatch: (String nonMatch) => "x", // 将字符串中不是逗号的替换为字符 x
);
print(val); // 返回 xyxyxyx
- 字符串转换大小写 toUpperCase
String text = "abcDe123";
print(text.toUpperCase()); // 返回 ABCDE123
- toLowerCase
String text = "123AbCdE";
print(text.toLowerCase()); // 返回 123abcde
- padLeft 字符串补齐
字符串左补齐,指定 width 后,如果有剩余位则使用指定字符替换。如果指定 width 小于字符串长度,则返回原字符串。
第一个参数 width 不是补齐后的字符串总长度,你可以用 width 减去原来字符串的长度,得到的结果表示将会在字符串左边添加多少个补齐的字符(见下面的 01、02、03 例子)
第二个参数 padding 为可选参数,表示要补齐的字符,不指定时默认补上空格。
String text = "5";
// 01 在指定字符串左边,添加 (4-text.length=3) 个空格至原来的字符串
print(text.padLeft(4)); // 返回 " 5",注意,字符 5 左边有三个空格
// 02 在指定字符串左边,添加 (4-text.length=3) 个字符 2 至原来的字符串
print(text.padLeft(4, '2')); // 返回 2225
// 03 在指定字符串左边,添加 (4-text.length=3) 个字符 22 至原来的字符串
print(text.padLeft(4, '22')); // 返回 2222225
// 04 当时间的分钟数是个位数时,可以通过左边补 0 达到双位数显示的效果
print(text.padLeft(2, "0")); // 返回 05
// 05 如果 width 小于字符串的长度,则返回原字符串
print(text.padLeft(1, '2')); // 返回 5
- padRight
字符串右补齐,指定 width 后,如果有剩余位则使用指定字符替换。如果指定 width 小于字符串长度,则返回原字符串。
第一个参数 width 不是补齐后的字符串总长度,你可以用 width 减去原来字符串的长度,得到的结果表示将会在字符串右边添加多少个补齐的字符(见下面的 01、02、03 例子)
第二个参数 padding 为可选参数,表示要补齐的字符,不指定时默认补上空格。
String text = "5";
// 01 在指定字符串右边,添加 (4-text.length=3) 个空格至原来的字符串
print(text.padRight(4)); // 返回 "5 ",注意,字符 5 右边有三个空格
// 02 在指定字符串右边,添加 (4-text.length=3) 个字符 2 至原来的字符串
print(text.padRight(4, '2')); // 返回 5222
// 03 在指定字符串右边,添加 (4-text.length=3) 个字符 22 至原来的字符串
print(text.padRight(4, '22')); // 返回 5222222
// 04 如果 width 小于字符串的长度,则返回原字符串
print(text.padRight(1, '2')); // 返回 5
- trim字符串去除空格
String text = " a b c "; // 长度为 9 的字符串
print(text.trim()); // 返回长度为 5 的字符串 "a b c"
- trimLeft 去除字符串中左边的空格,如果左边没有空格,则返回原字符串。
String text = " a b c "; // 长度为 9 的字符串
print(text.trimLeft()); // 返回长度为 6 的字符串 "a b c ",字符 c 右边的空格被保留
- trimRight
去除字符串中右边的空格,如果右边没有空格,则返回原字符串。
String text = " a b c "; // 长度为 9 的字符串
print(text.trimRight()); // 返回长度为 8 的字符串 " a b c",字符 a 左边的三个空格被保留
- replaceAll 字符串替换
替换掉字符串中全部符合条件的字符。如果没有符合条件的,则返回原字符串。
String text = "abcdabcd";
print(text.replaceAll("ab", "xy")); // 返回 xycdxycd
- replaceFirst
替换掉指定范围内的字符。有效取值范围是 0 <= start <= end <= 字符串.length。
第一个参数 start 表示从哪个索引位置开始替换,包括该索引。
第二个参数 end 表示从哪个索引位置结束替换,不包括该索引。因为 end 为可空参数,当 end 为 null 时,表示到字符串末尾。
第三个参数 replacement 表示替换为什么字符。
String text = "abcdabcd";
// 01 从索引 0 位置开始,到索引 3 位置结束(但不包括该索引),中间的字符被替换为 m
print(text.replaceRange(0, 3, "m")); // 返回 mdabcd
// 02 从索引 0 位置开始到末尾,中间的字符被替换为 m
print(text.replaceRange(0, null, "m")); // 返回 m
- replaceAllMapped
替换掉所有符合条件的字符。用方法的返回值替换指定的字符,如果没有匹配的字符,则返回原字符串。
第一个参数 from 表示字符串中被替换掉的字符,
第二个参数 replace 表示如果有匹配的字符,则用指定的字符作为返回值替,换掉字符串中符合条件的字符。最终的效果其实和方法 replaceAll 是一样的。
String text = "abcdabcd";
// 用 xy 替换掉字符串中的 ab
String str = text.replaceAllMapped("ab", (Match match) => "xy");
print(str); // 返回 xycdxycd
- replaceFirstMapped
只替换掉第一个符合条件的字符。用方法的返回值替换指定的字符,如果没有匹配的字符,则返回原字符串。
第一个参数 from 表示字符串中被替换掉的字符,
第二个参数 replace 表示如果有匹配的字符,则用指定的字符作为返回值,替换掉字符串中第一个符合条件的字符。最终的效果其实和方法 replaceFirst 是一样的。
第三个参数 startIndex 为可选参数,表示从哪个索引位置开始替换,默认为 0,不能大于字符串的长度。
String text = "abcdabcd";
// 01 用 xy 替换掉字符串中第一个符合条件的 ab
String str1 = text.replaceFirstMapped("ab", (Match match) => "xy");
print(str1); // 返回 xycdabcd
// 02 从索引 1 位置处开始,用 xy 替换掉字符串中第一个符合条件的 ab
String str2 = text.replaceFirstMapped("ab", (Match match) => "xy", 1);
print(str2); // 返回 abcdxycd
- compareTo字符串先后比较
比较字符在 ASCII 码的位置。
如果当前字符在被比较字符后面,返回 1,位置相等返回 0,如果当前字符在被比较字符前面,返回 -1。
在 ASCII 码中,48~57 为 0到9十个阿拉伯数字,65~90 为 26 个大写英文字母,97~122 为 26 个小写英文字母。
String text = "b";
print(text.compareTo('a')); // b>a,返回 1
print(text.compareTo('b')); // b=b,返回 0
print(text.compareTo('c')); // b<c,返回 -1
print(text.compareTo('7')); // 字母在数字后面,返回 1
print(text.compareTo('B')); // 小写字母在大写字母后面,返回 1
- codeUnitAt 获取字符串的 Unicode 编码
返回指定索引处字符的 Unicode 十进制编码。
String text = "Allen Su";
print(text.codeUnitAt(0)); // 索引 0 处的字符是 A,A 的 Unicode 十进制编码是 108
print(text.codeUnitAt(5)); // 索引 5 处的字符是空格,空格的 Unicode 十进制编码是 32
List类型
基础用法
void main() {
var list = ["1","2","3","4","5"];
List<String> list2 = ["1","2"];
print(list);
print(list.length);
print(list[2]);
//可以使用扩展运算符 (...) 来向一个列表中插入另一个列表的所有元素。
var list2 = ["0",...list];
print(list2);
}
常用属性
- length 长度
- reversed 翻转
- isEmpty 是否为空
- isNotEmpty 是否不为空
常用方法
- add增加一个元素
List myList = ["1","2","3"];
//添加元素
myList.add("4");
print(myList); //["1","2","3","4"]
- addAll拼接数组
List myList = ["1","2","3"];
myList.addAll(["4","5"]);
print(myList); //["1","2","3","4","5"]
- insert(index,value) 在指定位置插入一个元素
List myList = ["1","2","3"];
myList.insert(0,'0'); //["0","1","2","3"]
print(myList);
- insertAll(index,list) 在指定位置插入多个元素
List myList = ["1","2","3"];
myList.insertAll(3,['4','5']);
print(myList); //["1","2","3","4","5"]
- remove 移除数组中的某个值
List myList = ["1","2","3"];
myList.remove('1');
print(myList); //["2","3"]
- removeAt 根据索引值移除数组中的某个值
List myList = ["1","2","3"];
myList.removeAt(0);
print(myList); //["2","3"]
- removeLast 删除数组的最后一个元素
List<String> list = [
"2",
"3"
];
list.removeLast();
print(list); //["3"]
- clear 清空数组
List<String> list = [
"2",
"3"
];
list.clear();
print(list); //[]
- removeWhere 根据指定条件删除元素
List<int> list = [1,2,3,4,5,6];
list.removeWhere((value){
return value > 3;
});
print(list); //[1,2,3]
关于以上删除元素的方法
如果数组中有该数据或者满足删除的条件,则删除,原数组发生改动。
如果要删除的数据不在数组中,或者删除元素的条件不成立,也不会报错,返回原数组。
- removeRange 删除指定索引范围内的元素(含头不含尾)
List<int> list = [1,2,3,4,5,6];
list.removeRange(0,2); //0..<2
print(list);//[3,4,5,6]
- fillRange(start,end,value) 用相同的值替换指定索引范围内的所有元素(含头不含尾)
替换区间:start..<end
List myList = ["1","2","3"];
//替换区间: 3..<3;空
myList.fillRange(3,3,'helloWorld!');
print(myList); //["1","2","3"] 没有替换成功
//替换区间: 2..<3;索引值为2
myList.fillRange(2,3,'helloworld!');
print(myList); //打印结果 ["1", "2", "helloworld!"]
- replaceRange 用某一数组替换指定索引范围内的所有元素(含头不含尾)
List<String> l1 = ["周一", "周二", "周三"];
l1.replaceRange(1, 2, ["周四", "周五", "周六", "周日"]);
print(l1); // [周一, 周四, 周五, 周六, 周日, 周三]
- setRange 范围替换数组中的值(含头不含尾)
List<String> l1 = ["11", "22", "33"];
List<String> l2 = ["aa", "bb", "cc"];
l1.setRange(0, 2, l2); // 用 l2 数组中索引为 0 1 位置处元素的值,替换掉 l1 数组中索引 0 1 位置处元素的值
print(l1); // [aa, bb, 33]
- setAll 从指定索引位置开始,使用第二个数组内的元素依次替换掉第一个数组中的元素
List<String> l1 = ["周一", "周二", "周三", "周四"];
List<String> l2 = ["周五", "周六", "周日"];
l1.setAll(1, l2);
print(l1); // [周一, 周五, 周六, 周日]
- contains是否包含某个元素
List myList = ["1","2","3"];
print(myList.contains('1')); //true
- indexOf查找元素在数组中的索引值
List myList = ["1","2","3"];
//查找不到返回-1 查找到返回索引值。
var index = myList.indexOf("1");
print(index); //索引值0
- lastIndexOf 从后向前查找指定元素在数组中的索引
List<String> l1 = ["周一", "周二", "周三", "周四", "周三"];
int a = l1.lastIndexOf("周三");
int b = l1.lastIndexOf("周三", 3);
int c = l1.lastIndexOf("周日");
print(a); // 4
print(b); // 2
print(c); // -1
- indexWhere 返回第一个满足条件的元素的索引
List<String> l1 = ["周一", "周二", "周三", "周四", "周三"];
int a = l1.indexWhere((e) => e == "周三");
print(a); // 2
- lastIndexWhere 从后向前找,返回第一个满足条件的元素的索引
List<String> l1 = ["周一", "周二", "周三", "周四", "周三"];
int a = l1.lastIndexWhere((e) => e == "周三");
print(a); // 4
- where 根据指定条件,函数筛选每个元素,符合条件的元素组成一个新的 Iterable
List<int> l1 = [8, 12, 4, 1, 17, 33, 10];
Iterable<int> l2 = l1.where((e) => e > 10);
print(l2); // (12, 17, 33)
List<int> l3 = l1.where((e) => e >= 10).toList();
print(l3); // [12, 17, 33]
- lastWhere 从后向前查找第一个满足条件的元素
List<int> l1 = [8, 12, 4, 1, 17, 33, 10];
int a = l1.lastWhere((e) => e > 10);
print(a); // 33
- singleWhere 获取满足指定条件的唯一元素
List<int> l1 = [8, 12, 4, 1, 17, 33, 10];
int a = l1.singleWhere((e) => e > 30);
int b = l1.singleWhere((e) => e > 33, orElse: () => -1);
int c = l1.singleWhere((e) => e > 10, orElse: () => null);
print(a); // 33
print(b); // -1。数组中没有满足条件的元素,返回 orElse 方法指定的返回值(-1是自己填的,你也可以设置为其它值)
print(c); // 报错。当数组中有多个满足条件的元素时,即使你设置了 orElse 的返回值,用 singleWhere 也会报错
- retainWhere 保留满足条件的元素(改变了原数组)
List<int> l1 = [8, 12, 4, 1, 17, 33, 10];
l1.retainWhere((e) => e > 10);
print(l1); // [12, 17, 33]
- 判断数组中是否有满足指定条件的元素用 any() 方法
//是否有大于1的元素
var arr = [1,2,3];
var a = arr.any((value){
return value>1;
});
print(a); //true
- every 判断数组中是否每个元素都满足指定的条件
List<int> l1 = [8, 12, 4, 1, 17, 33, 10];
bool a = l1.every((e) => e > 1);
print(a); // false
- take 从索引 0 位置处,取指定个数的元素
List<int> l1 = [8, 12, 4, 1, 17, 33, 10];
Iterable<int> l2 = l1.take(3);
print(l2); // (8, 12, 4)
- takeWhile 从索引 0 位置处,查询满足指定条件的元素,直到出现第一个不符合条件的元素,然后返回前面符合条件的元素
List<int> l1 = [8, 12, 4, 1, 17, 33, 10];
Iterable<int> l2 = l1.takeWhile((e) => e > 1);
print(l2); // (8, 12, 4)
- skip 跳过指定个数的元素,返回后面的元素
List<int> l1 = [8, 12, 4, 1, 17, 33, 10];
Iterable<int> l2 = l1.skip(4);
print(l2); // (17, 33, 10)
- skipWhile 根据指定条件,找到第一个不符合条件的元素,然后将该元素后面的所有元素返回
List<int> l1 = [8, 12, 4, 1, 17, 33, 10];
Iterable<int> l2 = l1.skipWhile((e) => e < 17);
print(l2); // (17, 33, 10)
- sublist 从指定索引处截取数组
List<int> l1 = [8, 12, 4, 1, 17, 33, 10];
List<int> l2 = l1.sublist(5);
print(l2); // [33, 10]
List<int> l3 = l1.sublist(2, 5);
print(l3); // [4, 1, 17] 含头不含尾
- getRange 截取指定索引范围内的元素
List<int> l1 = [8, 12, 4, 1, 17, 33, 10];
Iterable<int> l2 = l1.getRange(2,5);
print(l2); // (4, 1, 17)
- whereType 从混合类型的数组中,筛选出指定类型的元素
List l1 = ["a", 15, "b", false, true, 20, "c",{"name":"AllenSu"}];
Iterable<String> l2 = l1.whereType();
Iterable<int> l3 = l1.whereType();
Iterable<bool> l4 = l1.whereType();
Iterable<Map> l5 = l1.whereType();
print(l2); // (a, b, c)
print(l3); // (15, 20)
print(l4); // (false, true)
print(l5); // ({name: AllenSu})
- join() List转换成字符串
List myList = ["1","2","3"];
var str = myList.join('-');
print(str);//1-2-3
- split 字符串转化成 List
var str = '1-2-3';
var arr = str.split('-');
print(arr); //["1","2","3"];
- toSet 将 List 转换为 Set,得到去重后的元素
List<int> l1 = [8, 12, 8, 6, 22, 12, 10];
Set<int> l2 = l1.toSet();
print(l2); // {8, 12, 6, 22, 10}
- toList() 其他类型转换成 List
//Set类型转换为List类型
var set = {"1","2","3"};
var arr = set.toList();
print(arr); //["1","2","3"]
- sort 数组排序(原数组发生改变)
List<int> l1 = [8, 12, 8, 6, 22, 12, 10];
l1.sort();
List<String> l2 = ["f","d","b","c","a","e"];
l2.sort();
print(l1); // [6, 8, 8, 10, 12, 12, 22]
print(l2); // [a, b, c, d, e, f]
- forEach遍历数组
var arr = [1,2,3];
arr.forEach((value){
print(value);
});
- asMap 获取数组每一项的索引值
var arr = ["a","b","c"];
var indexs = arr.asMap();
print(indexs); //{0: a, 1: b, 2: c}
print(indexs is Map); //true
- map方法 遍历数组中的所有元素,可以对元素进行处理,并返回新的 Iterable
var arr = [1,2,3];
var newArr = arr.map((value){
return value*value;
});
print(newArr); //(1,4,9);
print(newArr.toList())//[1,4,9];
- reduce 用指定的函数方式对数组中的所有元素做连续操作,并将结果返回
List<int> l1 = [1, 2, 3, 4];
int res1 = l1.reduce((a, b) => (a * b)); // 元素依次相乘
int res2 = l1.reduce((a, b) => (a + b)); // 元素依次相加
print(res1); // 1*2*3*4 = 24
print(res2); // 1+2+3+4 = 10
- fold 根据一个现有数组和一个初始参数值 initValue,指定参数条件操作现有数组的所有元素,并返回处理的结果
List<int> l1 = [1, 2, 3, 4];
// 2 为初始参数值,后面定义的方法为该初始值和原数组之间的处理方式
int res1 = l1.fold(2, (a, b) => (a * b)); // 相乘
int res2 = l1.fold(2, (a, b) => (a + b)); // 相加
print(res1); // 2*(1*2*3*4) = 48
print(res2); // 2+(1+2+3+4) = 12
- expand 根据现有数组,指定一个处理方式,返回一个 Iterable
List<int> l1 = [1, 2, 3, 4];
// 将 l1 数组内的每一个元素都和指定的表达式组相操作
Iterable<int> l2 = l1.expand((e) => [e + 1]);
Iterable<int> l3 = l1.expand((e) => [e + 1, e + 2]);
Iterable<int> l4 = l1.expand((e) => [e + 2, e * 2]);
Iterable<num> l5 = l1.expand((e) => [e * 2, e / 2]);
Iterable<int> l6 = l1.expand((e) => [e, e + 1, e + 2]);
print(l2); // (2, 3, 4, 5)
print(l3); // (2, 3, 3, 4, 4, 5, 5, 6)
print(l4); // (3, 2, 4, 4, 5, 6, 6, 8)
print(l5); // (2, 0.5, 4, 1.0, 6, 1.5, 8, 2.0)
print(l6); // (1, 2, 3, 2, 3, 4, 3, 4, 5, 4, 5, 6)
- shuffle 随机排列指定数组(修改了原数组)
List<int> l1 = [1, 2, 3, 4];
l1.shuffle();
print(l1); // [1, 4, 2, 3]
l1.shuffle();
print(l1); // [2, 1, 3, 4]
- where
相当于其他语言的filter
var arr = [1,2,3];
var a = arr.where((value){
return value > 1;
});
print(a); //(2,3)
print(a.toList()); //[2,3]
Map类型
基础用法
通常来说,映射是一个关联了键和值的对象。键和值都可以是任意类型的对象。“键”是唯一的,但是你可以多次使用相同的“值”。Dart 通过映射字面量和 Map 类型来支持映射。
void main() {
var person = {
"name":"Json",
"age":21,
"works":["程序员","产品经理"],
"married":false
};
print(person);
print(person["name"]);
var map = new Map();
map["skills"] = "ios";
print(map["skills"]);
}
常用属性
- keys 获取所有的 key 值
- values 获取所有的 value 值
- isEmpty 是否为空
- isNotEmpty 是否不为空
常用方法
- remove(key) 删除指定 key 的数据
- addAll({…}) 合并映射 给映射内增加属性
- containsValue 查看映射内的值 返回 true/false
- forEach
- map
- where
- any
- every
运算符
算术运算符
void main() {
//+ - * / ~/(取整) %(取余)
int a = 13;
int b = 5;
print(a+b);
print(a-b);
print(a*b);
print(a/b);
print(a%b);
print(a~/b);
}
关系运算符
void main() {
// == != > < >= <=
int a = 5;
int b = 3;
print(a==b);
print(a!=b);
print(a>b);
print(a<b);
print(a>=b);
print(a<=b);
if(a>b) {
print('a>b');
}else{
print('a<b');
}
}
逻辑运算符
void main(){
/* ! 取反 */
bool flag=false;
print(!flag); //取反
/* &&并且:全部为true的话值为true 否则值为false */
bool a = true;
bool b = false;
print(a && b);
/* ||或者:全为false的话值为false 否则值为true */
bool c = false;
bool d = false;
print(c || d);
// 如果一个人的年龄是20 并且 sex是女的话我们打印这个人
int age = 20;
String sex = "女";
if (age == 20 && sex == "女") {
print("$age --- $sex");
} else {
print("不打印");
}
int age2 = 30;
String sex2 = "女";
if (age2 == 20 && sex2 == "女") {
print("$age2 --- $sex2");
} else {
print("不打印");
}
//如果一个人的年龄是20 或者 sex是女的话我们打印这个人
int age3 = 30;
String sex3 = "女";
if (age3 == 20 || sex3 == "女") {
print("$age3 --- $sex3");
} else {
print("不打印");
}
}
赋值运算符
void main(){
int a = 10;
int b = 20;
int c = a + b; //从右向左
var f = 12;
f = f + 10;
print(f);
var g = 13;
g += 10;
print(g);
var h = 4;
h *= 3;
print(h);
var i = 7;
i %= 3;
print(i);
var j = 7;
j ~/=3;
print(j);
}
类型判断运算符
as、is、is!
运算符是在运行时判断对象类型的运算符。
as:
类型转换is
: 如果对象是指定类型则返回true。is!
:如果对象是指定类型则返回false。
(employee as Person).firstName = 'Bob';
```swift
var str = 'str';
if (str is String) {
print("$str 是字符串类型");
} else if (str is num) {
print("$str 是数字类型");
} else {
print("$str 是其他类型");
}
var doubleNum = 123.5;
if (doubleNum is String) {
print("$doubleNum 是字符串类型");
} else if (doubleNum is int) {
print("$doubleNum 是整数类型");
} else if (doubleNum is double) {
print("$doubleNum 是浮点数类型");
} else {
print("$doubleNum 是其他类型");
}
}
### 条件表达式
#### 三目运算符
* 条件 ? 表达式 1 : 表达式 2
```swift
// 2、三目运算符
var falg = true;
var a;
if (falg) {
a = '我是true';
} else {
a = "我是false";
}
print(a);
bool flag2 = false;
String b = flag2 ? '我是true' : '我是false';
print(b);
?? 运算符
如果表达式1不为空,则显示表达式1,否则显示表达式2。
- 表达式 1 ?? 表达式 2
//3 ??运算符
var c;
var d = c ?? 10;
print(d); // 10
var e = 22;
var f = e ?? 10;
print(f); // 22
级联运算符
级联运算符 (…, ?..) 可以让你在同一个对象上连续调用多个对象的变量或方法。
var paint = Paint()
..color = Colors.black
..strokeCap = StrokeCap.round
..strokeWidth = 5.0;
等同于
var paint = Paint();
paint.color = Colors.black;
paint.strokeCap = StrokeCap.round;
paint.strokeWidth = 5.0;
querySelector('#confirm') // Get an object.
?..text = 'Confirm' // Use its members.
..classes.add('important')
..onClick.listen((e) => window.alert('Confirmed!'));
等同于
var button = querySelector('#confirm');
button?.text = 'Confirm';
button?.classes.add('important');
button?.onClick.listen((e) => window.alert('Confirmed!'));
Dart 类型转换
Number与String类型之间的转换
- (1)String类型转成Number类型
int.parse()
//字符串转int
String str = "123";
var myNum = int.parse(str);
print(myNum);
print(myNum is int);
//字符串转double
String str2 = "123.1";
var myNum2 = double.parse(str2);
print(myNum2);
print(myNum2 is double);
//空值处理,需要把空值转换为'0',否则转换的时候会报错。
String price = '';
price = (price == '') ? '0' : price;
var myNum3 = double.parse(price);
print(myNum3);
print(myNum3 is double);
//通过try...catch捕获异常
String price2 = '';
try {
var myNum4 = double.parse(price2);
}catch(error) {
print('转化失败,发生异常');
}
- (2)Number类型转换成String类型
toString()
void main(){
//数值转字符串
var myNum1 = 123;
var str1 = myNum1.toString();
print(str1);
print(str1 is String);
var myNum2 = 123.45;
var str2 = myNum2.toString();
print(str2);
print(str2 is String);
}
- (3)其他类型转换成Boolean类型
void main(){
//1、字符串转布尔类型
//isEmpty:判断字符串是否为空。
var str = '';
if(str.isEmpty) {
print('空字符串');
}else{
print('非空字符串');
}
//打印结果:空字符串
//2、数值转布尔类型
var myNum = 123;
if(myNum == 0) {
print('0');
}else{
print('非0');
}
//打印结果非0
//3、
var myNum2;
if(myNum2 == 0) {
print('0');
}else{
print('非0');
}
//打印结果:非0
//4、
var myNum3;
if(myNum3 == null) {
print('空');
}else{
print('非空');
}
//打印结果:空
//5、
var myNum4 = 0 / 0;
if(myNum4.isNaN) {
print('myNum is NaN');
}
//打印结果: myNum is NaN
}
流程控制语句
If 和 Else
if (isRaining()) {
you.bringRainCoat();
} else if (isSnowing()) {
you.wearJacket();
} else {
car.putTopDown();
}
for循环语句
void main(){
for(int i = 0; i<10; i++) {
print(i);
}
print('-----------------------');
for(var i = 0; i<=10;i++) {
print(i);
}
}
while do…while 循环语句
void main(){
int i = 1;
while(i<=10) {
print(i);
i++;
}
int k = 1;
do{
print(k);
k++;
}while(k<=5);
}
Switch 和 Case
Switch 语句在 Dart 中使用 ==
来比较整数、字符串或编译时常量,比较的两个对象必须是同一个类型且不能是子类并且没有重写 ==
操作符。 枚举类型非常适合在 Switch 语句中使用。
var command = 'OPEN';
switch (command) {
case 'CLOSED':
executeClosed();
break;
case 'PENDING':
executePending();
break;
default:
executeUnknown();
}
break与continue关键词
break:直接结束本次循环。
continue:跳脱本层循环,继续下一层。
断言
在开发过程中,可以在条件表达式为 false 时使用 — 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'));
assert 的第二个参数可以为其添加一个字符串消息。
assert(urlString.startsWith('https'),
'URL ($urlString) should start with "https".');
函数
Dart 同样也是一种面向对象的语音。所以即便函数也是一个对象。类型为 Function
,这意味着函数可做作为变量,也也可以作为函数的参数。
函数的基本使用
返回类型 方法名称(参数1,参数2,...){
方法体
return 返回值;
}
函数的定义
void printInfo() {
print('我是一个自定义方法');
}
int getNum() {
var myNum = 123;
return myNum;
}
传递参数
函数可以有两种形式的参数:必要参数 和 可选参数。
必要参数定义在参数列表前面,可选参数则定义在必要参数后面。可选参数可以是 命名的
或 位置的
。
可选命名参数
命名参数默认是可选参数,除非它们被特别表示为required。
定义函数时,使用 {参数1, 参数2, …} 来指定命名参数:
required声明name是必填的,sex带有默认值为"男",不可以为null,age可以选填的,默认值为null
String printUserInfo({required String name, String sex = "男", int? age}) {
return "姓名:$name,性别:$sex,年龄:$age";
}
printUserInfo(name: "李连杰");
可选位置参数
(1)用[]来包装一些列的函数参数,来表示他们是 位置可选参数:
String printUserInfo([String? name, String sex = "男", int? age]) {
return "姓名:$name,性别:$sex,年龄:$age";
}
printUserInfo("李连杰");
默认参数值
可以用 = 为函数的命名参数和位置参数定义默认值,默认值必须为编译时常量,没有指定默认值的情况下默认值为 null。
String printUserInfo({required String name, String sex = "男", int? age}) {
return "姓名:$name,性别:$sex,年龄:$age";
}
printUserInfo(name: "李连杰");
main()函数
每个 Dart 程序都必须有一个 main() 顶级函数作为程序的入口, main() 函数返回值为 void 并且有一个 List 类型的可选参数。
// 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');
}
高阶函数
函数作为参数
方法一:
typedef int Map(int value);
List<int> mapFunc(List<int> list,Map map){
List<int> arr = [];
for(var i = 0; i<list.length;i++){
arr.add(map(list[i]));
}
return arr;
}
void main(){
var arr = mapFunc([2,4,6],(value){
return value*value;
});
print(arr);
}
方法二:
List<int> mapFunc(List<int> list,int map(int value)){
List<int> arr = [];
for(var i = 0; i<list.length;i++){
arr.add(map(list[i]));
}
return arr;
}
函数作为返回值
typedef int Add(int a);
Add add(int a) {
int getNum(int x) {
return x+a;
}
return getNum;
}
void main(){
var result = add(10)(10);
print(result);
}
箭头函数
// 1. 需求:使用forEach打印下面List里面的数据
List list = ['苹果', '香蕉', '西瓜'];
list.forEach((value) {
print(value);
});
list.forEach((value) => print(value));
list.forEach((value) => {
print(value)
});
匿名函数
你可以创建一个没有名字的方法,称之为 匿名函数、 Lambda 表达式 或 Closure 闭包。你可以将匿名方法赋值给一个变量然后使用它。
void main(){
// 匿名函数
var printNum = () {
print(123);
};
printNum();
var printNum2 = (int n) {
print(n + 2);
};
printNum2(12);
}
自执行函数
void main(){
(() {
print('我是自执行方法');
})();
// 传参
((int n) {
print(n);
print('我是自执行方法');
})(12);
}
词法作用域
Dart 是词法有作用域语言,变量的作用域在写代码的时候就确定了,大括号内定义的变量只能在大括号内访问。
bool topLevel = true;
void main() {
var insideMain = true;
void myFunction() {
var insideFunction = true;
void nestedFunction() {
var insideNestedFunction = true;
assert(topLevel);
assert(insideMain);
assert(insideFunction);
assert(insideNestedFunction);
}
}
}
词法闭包
闭包 即一个函数对象,即使函数对象的调用在它原始作用域之外,依然能够访问在它词法作用域内的变量。
函数可以封闭定义到它作用域内的变量。接下来的示例中,函数 makeAdder() 捕获了变量 addBy。无论函数在什么时候返回,它都可以使用捕获的 addBy 变量。
/// Returns a function that adds [addBy] to the
/// function's argument.
Function makeAdder(int addBy) {
return (int i) => addBy + i;
}
void 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);
}
异常处理
Dart 代码可以抛出和捕获异常。异常表示一些未知的错误情况,如果异常没有捕获则会被抛出从而导致抛出异常的代码终止执行。
与 Java 不同的是,Dart 的所有异常都是非必检异常,方法不必声明会抛出哪些异常,并且你也不必捕获任何异常。
抛出异常
throw FormatException('Expected at least 1 section');
你也可以抛出任意的对象:
throw 'Out of llamas!';
捕获异常
try {
breedMoreLlamas();
} on OutOfLlamasException {
buyMoreLlamas();
}
对于可以抛出多种异常类型的代码,也可以指定多个 catch 语句,每个语句分别对应一个异常类型,如果 catch 语句没有指定异常类型则表示可以捕获任意异常类型:
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 可以将捕获的异常再次抛出:
void misbehave() {
try {
dynamic foo = true;
print(foo++); // Runtime error
} 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();
}
枚举
枚举类型是一种特殊的类型,也称为 enumerations 或 enums,用于定义一些固定数量的常量值。
enum Sex {
male,
female
}
每一个枚举值都有一个名为 index 成员变量的 Getter 方法,该方法将会返回以 0 为基准索引的位置值。
print(Sex.male.index); //0
想要获得全部的枚举值,使用枚举类的 values 方法获取包含它们的列表:
print(Sex.values);
你可以在 Switch 语句中使用枚举,但是需要注意的是必须处理枚举值的每一种情况,即每一个枚举值都必须成为一个 case 子句,不然会出现警告:
switch (sex) {
case Sex.male:
print("男性");
break;
case Sex.female:
print("女性");
break;
default:
print("未知");
}
对象
Dart 是支持基于 mixin 继承机制的面向对象语言,所有对象都是一个类的实例,而除了 Null 以外的所有的类都继承自 Object 类。
定义
class Person {
String name = '张三'; //不可以为null
int? age; //int?表示age初始化为null,意味着age可以为null
String getInfo(){
if (age == null) {
return "姓名:$name,年龄保密";
}else{
return "姓名:$name,年龄:$age";
}
}
void setAge(int age) {
this.age = age;
}
}
void main(){
var p = new Person();
var message = p.getInfo();
print(message);
p.setAge(19);
print(p.getInfo());
}
构造函数
class Person {
String name = '张三';
int age = 20;
// 默认构造函数
Person() {
print('这是构造函数里面的内容 这个方法在实例化的时候触发');
}
void printInfo() {
print("${this.name}----${this.age}");
}
}
void main(){
var p1 = new Person();
}
- 使用构造函数初始化类的属性
class Person {
String name;
int age;
//默认构造函数
Person(String name, int age) {
this.name = name;
this.age = age;
}
void printInfo() {
print("${this.name}----${this.age}");
}
}
void main(){
Person p1 = new Person('张三', 20);
p1.printInfo();
Person p2 = new Person('李四', 25);
p2.printInfo();
}
- 构造函数的简写
对于大多数编程语言来说在构造函数中为实例变量赋值的过程都是类似的,而 Dart 则提供了一种特殊的语法糖来简化该步骤:
class Person {
String name;
int age;
//默认构造函数的简写
Person(this.name, this.age);
void printInfo() {
print("${this.name}----${this.age}");
}
}
void main(){
Person p1 = new Person('张三', 20);
p1.printInfo();
Person p2 = new Person('李四', 25);
p2.printInfo();
}
命名构造函数
可以为一个类声明多个命名式构造函数来表达更明确的意图
class Person {
String name;
int age;
//默认构造函数的简写
Person(this.name, this.age);
Person.now() {
print('我是命名构造函数');
}
Person.setInfo(String name, int age) {
this.name = name;
this.age = age;
}
void printInfo() {
print("${this.name}----${this.age}");
}
}
void main() {
var d = new DateTime.now(); // 实例化DateTime调用它的命名构造函数
print(d);
Person p1 = new Person('张三', 20); // 默认实例化类的时候调用的是 默认构造函数
p1.printInfo();
Person p2 = new Person.now(); // 调用命名构造函数
Person p3 = new Person.setInfo('李四', 30);
p3.printInfo();
}
常量构造函数
如果类生成的对象都是不变的,可以在生成这些对象时就将其变为编译时常量。
你可以在类的构造函数前加上 const 关键字并确保所有实例变量均为 final 来实现该功能。
使用常量构造函数,在构造函数名之前加 const 关键字,来创建编译时常量时:
class ImmutablePoint {
static const ImmutablePoint origin = ImmutablePoint(0, 0);
final double x, y;
const ImmutablePoint(this.x, this.y);
}
var a = const ImmutablePoint(1, 1); // Creates a constant
两个使用相同构造函数相同参数值构造的编译时常量是同一个对象:
var a = const ImmutablePoint(1, 1);
var b = const ImmutablePoint(1, 1);
assert(identical(a, b)); // They are the same instance!
在 常量上下文 场景中,你可以省略掉构造函数或字面量前的 const 关键字。例如下面的例子中我们创建了一个常量 Map:
// Lots of const keywords here.
const pointAndLine = const {
'point': const [const ImmutablePoint(0, 0)],
'line': const [const ImmutablePoint(1, 10), const ImmutablePoint(-2, 11)],
};
根据上下文,你可以只保留第一个 const 关键字,其余的全部省略:
// Only one const, which establishes the constant context.
const pointAndLine = {
'point': [ImmutablePoint(0, 0)],
'line': [ImmutablePoint(1, 10), ImmutablePoint(-2, 11)],
};
获取对象的类型
可以使用 Object 对象的 runtimeType 属性在运行时获取一个对象的类型,该对象类型是 Type 的实例。
print('The type of a is ${a.runtimeType}');
类的私有属性和方法
Dart 和其他面向对象语言不一样,Data 中没有public private protected这些访问修饰符合。
但是我们可以使用_
把一个属性或者方法定义成私有(类必须在单独的文件中,才能将属性或方法私有)。
- index.dart
import 'lib/Animal.dart';
void main(){
Animal a = new Animal('小狗', 3);
print(a.age); // 间接的获取私有属性
print(a.getName()); // 间接的获取私有属性
a.execRun(); // 间接的调用私有方法
}
- lib/Animal.dart
class Animal {
String _name; // 私有属性
int age;
// 默认构造函数的简写
Animal(this._name, this.age);
void printInfo() {
print("${this._name}----${this.age}");
}
String getName() {
return this._name;
}
void _run() {
print('这是一个私有方法');
}
execRun() {
this._run(); // 类里面方法的相互调用
}
}
类的 getter 和 setter
- 使用 get 获取(计算属性)
class Rect {
num height;
num width;
Rect(this.height, this.width);
get area {
return this.height * this.width;
}
}
void main() {
Rect r = new Rect(10, 2);
print("面积:${r.area}"); // 注意调用直接通过访问属性的方式访问area
}
- 使用 set 存储
class Rect {
num height;
num width;
Rect(this.height, this.width);
get area {
return this.height * this.width;
}
set areaHeight(value) {
this.height = value;
}
}
void main() {
Rect r = new Rect(10, 4);
r.areaHeight = 6;
print("面积:${r.area}"); // 注意调用直接通过访问属性的方式访问area
}
类的初始化列表
Dart 中我们也可以在构造函数运行之前初始化实例变量,每个实例变量之间使用逗号分隔。
class Rect{
int height;
int width;
Rect():height=2,width=10{
print("${this.height}---${this.width}");
}
getArea(){
return this.height*this.width;
}
}
void main(){
Rect r=new Rect();
print(r.getArea());
}
静态成员
1、使用 static 关键字来实现类级别的变量和函数
2、静态方法不能访问非静态成员,非静态方法可以访问静态成员
class Person {
static String name = '张三';
static void show() {
print(name);
}
}
void main() {
print(Person.name);
Person.show();
}
非静态方法访问静态成员和非静态成员
class Person {
static String name = '张三';
int age = 20;
static void show() {
print(name);
}
void printInfo() {
/*非静态方法可以访问静态成员以及非静态成员*/
print(name); // 访问静态属性
print(this.age); // 访问非静态属性
show(); // 调用静态方法
}
}
void main(){
Person p = new Person();
p.printInfo();
}
静态方法只能访问静态成员
class Person {
static String name = '张三';
int age = 20;
static void show() {
print(name);
}
static void printUserInfo() {
// 静态方法
print(name); // 静态属性
show(); // 静态方法
// print(age); // 静态方法没法访问非静态的属性
// print(this.age); // 静态方法没法访问非静态的属性
// this.printInfo(); // 静态方法没法访问非静态的方法
// printInfo(); // 静态方法没法访问非静态的方法
}
}
void main(){
Person.printUserInfo();
}
类的继承
简单继承
- 子类使用 extends 关键词来继承父类
- 子类会继承父类里面可见的属性和方法 但是不会继承构造函数
- 子类能复写父类的方法 getter 和 setter
class Person {
String name = '张三';
num age = 20;
void printInfo() {
print("${this.name}---${this.age}");
}
}
class Web extends Person {
}
main() {
Web w = new Web();
print(w.name);
w.printInfo();
}
super关键词的使用
显示调用父类构造函数
class Person {
String name;
num age;
Person(this.name, this.age);
void printInfo() {
print("${this.name}---${this.age}");
}
}
class Web extends Person {
Web(String name, num age) : super(name, age) {
}
}
void main(){
Person p = new Person('李四', 20);
p.printInfo();
Person p1 = new Person('张三', 20);
p1.printInfo();
Web w = new Web('王五', 12);
w.printInfo();
}
- 继承类的属性扩展
class Person {
String name;
num age;
Person(this.name, this.age);
void printInfo() {
print("${this.name}---${this.age}");
}
}
class Web extends Person {
String sex;
Web(String name, num age, String sex) : super(name, age) {
this.sex = sex;
}
run() {
print("${this.name}---${this.age}--${this.sex}");
}
}
void main(){
Person p = new Person('李四', 20);
p.printInfo();
Person p1 = new Person('张三', 20);
p1.printInfo();
Web w = new Web('王五', 12);
w.printInfo();
w.run();
}
- super 给命名构造函数传参
class Person {
String name;
num age;
Person.setInfo(this.name, this.age);
void printInfo() {
print("${this.name}---${this.age}");
}
}
class Web extends Person {
String sex;
Web(String name, num age, this.sex) : super.setInfo(name, age) {}
run() {
print("${this.name}---${this.age}--${this.sex}");
}
}
main() {
Web w = new Web('张三', 12, "男");
w.printInfo();
w.run();
}
调用父类中的方法
子类可以重写父类的实例方法(包括 操作符)、 Getter 以及 Setter 方法。你可以使用 @override 注解来表示你重写了一个成员,也可以不写。
class Person {
String name;
num age;
Person(this.name, this.age);
void printInfo() {
print("${this.name}---${this.age}");
}
work() {
print("${this.name}在工作...");
}
}
class Web extends Person {
Web(String name, num age) : super(name, age);
run() {
print('run');
super.work(); //自类调用父类的方法
}
}
main() {
Web w = new Web('李四', 20);
w.run();
}
重写父类的方法
class Person {
String name;
num age;
Person(this.name, this.age);
void printInfo() {
print("${this.name}---${this.age}");
}
work() {
print("${this.name}在工作...");
}
}
class Web extends Person {
Web(String name, num age) : super(name, age);
run() {
print('run');
}
// 重写父类的方法
@override //可以写也可以不写 建议在重写父类方法的时候加上 @override
void printInfo() {
print("姓名:${this.name}---年龄:${this.age}");
}
@override
work() {
print("${this.name}的工作是写代码");
}
}
void main(){
Web w = new Web('李四', 20);
w.printInfo();
w.work();
}
抽象类
创建抽象类
Dart 抽象类主要用于定义标准,子类可以继承抽象类,也可以实现抽象类接口。
- (1)抽象类通过 abstract 关键字来定义
- (2)Dart 中的抽象方法不能用 abstract 声明,Dart 中没有方法体的方法我们称为抽象方法。
- (3)如果子类继承抽象类必须得实现里面的抽象方法
- (4)如果把抽象类当做接口实现的话必须得实现抽象类里面定义的所有属性和方法。
- (5)抽象类不能被实例化,只有继承它的子类可以
extends 抽象类 和 implements 的区别:
- 如果要复用抽象类里面的方法,并且要用抽象方法约束自类的话我们就用 extends 继承抽象类
- 如果只是把抽象类当做标准的话我们就用 implements 实现抽象类
//抽象类
abstract class Animal {
void eat();
void run();
printInfo() {
print("我是一个普通的方法");
}
}
//遵守抽象类
class Tiger extends Animal {
void eat(){
print("老虎吃肉");
}
void run() {
print("老虎跑的快");
}
}
//遵守抽象类
class Rabbit extends Animal {
void eat(){
print("兔子吃草");
}
void run() {
print("兔子跑得慢");
}
}
void main(){
var tiger = new Tiger();
tiger.eat();
tiger.run();
tiger.printInfo();
var rabbit = new Rabbit();
rabbit.eat();
rabbit.run();
rabbit.printInfo();
}
多态
多态:允许将子类类型的指针赋值给父类类型的指针, 同一个函数调用会有不同的执行效果。
子类的实例赋值给父类的引用。
多态就是父类定义一个方法不去实现,让继承他的子类去实现,每个子类有不同的表现。
void main(){
Animal dog = new Dog();
Animal cat = new Cat();
//多态
dog.eat();
cat.eat();
(dog as Dog).run(); //转换成Dog类型
(cat as Cat).run(); //转换成Cat类型
}
abstract class Animal {
eat(); //抽象方法
}
class Dog extends Animal {
@override
eat() {
print('小狗在啃骨头');
}
run() {
print('小狗在跑');
}
}
class Cat extends Animal {
@override
eat() {
print('小猫在吃鱼');
}
run() {
print('小猫在走');
}
}
接口
dart和Java一样,dart也有接口,但是和Java还是有区别的。
首先,dart的接口没有interface关键字定义接口,而是普通类或抽象类都可以作为接口被实现。
同样使用implements关键字进行实现。
但是dart的接口有点奇怪,如果实现的类是普通类,会将普通类和抽象中的属性的方法全部需要覆写一遍。
而因为抽象类可以定义抽象方法,普通类不可以,所以一般如果要实现像 Java 接口那样的方式,一般会使用抽象类。
建议使用抽象类定义接口。
/*
定义一个DB库 支持 mysql mssql mongodb
mysql mssql mongodb三个类里面都有同样的方法
*/
abstract class Db {
//当做接口 接口:就是约定 、规范
String uri; //数据库的链接地址
add(String data);
save();
delete();
}
class Mysql implements Db {
@override
String uri;
Mysql(this.uri);
@override
add(data) {
// TODO: implement add
print('这是mysql的add方法' + data);
}
@override
delete() {
// TODO: implement delete
return null;
}
@override
save() {
// TODO: implement save
return null;
}
remove() {}
}
class MsSql implements Db {
@override
String uri;
@override
add(String data) {
print('这是mssql的add方法' + data);
}
@override
delete() {
// TODO: implement delete
return null;
}
@override
save() {
// TODO: implement save
return null;
}
}
void main() {
Mysql mysql = new Mysql('xxxxxx');
mysql.add('1243214');
}
接口文件分离
- lib/Db.dart
abstract class Db{ //当做接口 接口:就是约定 、规范
String uri; //数据库的链接地址
add(String data);
save();
delete();
}
- lib/MySql.dart
import 'Db.dart';
class Mysql implements Db{
@override
String uri;
Mysql(this.uri);
@override
add(data) {
print('这是mysql的add方法'+data);
}
@override
delete() {
return null;
}
@override
save() {
return null;
}
}
- lib/MsSql.dart
import 'Db.dart';
class MsSql implements Db{
@override
String uri;
@override
add(String data) {
print('这是mssql的add方法'+data);
}
@override
delete() {
return null;
}
@override
save() {
return null;
}
}
- index.dart
import 'lib/MsSql.dart';
import 'lib/MySql.dart';
main() {
Mysql mysql = new Mysql('xxxxxx');
mysql.add('1243214');
MsSql mssql = new MsSql();
mssql.uri = '127.0.0.1';
mssql.add('增加的数据');
}
一个类实现多个接口
abstract class A {
String name;
printA();
}
abstract class B {
printB();
}
class C implements A, B {
@override
String name;
@override
printA() {
print('printA');
}
@override
printB() {
// TODO: implement printB
return null;
}
}
void main(){
C c = new C();
c.printA();
}
类的混入
mixins的中文意思是混入,就是在类中混入其他功能。在Dart中可以使用mixins实现类似多继承的功能。
因为mixins使用的条件,随着Dart版本一直在变,这里讲的是Dart2.x中使用mixins的条件:
- 作为 mixins 的类只能继承自 Object,不能继承其他类
- 作为 mixins 的类不能有构造函数
- 一个类可以 mixins 多个 mixins 类
- mixins 绝不是继承,也不是接口,而是一种全新的特性
类仅混入
class A {
String info = "this is A";
void printA() {
print("A");
}
}
class B {
void printB() {
print("B");
}
}
class C with A, B {}
void main() {
var c = new C();
c.printA();
c.printB();
print(c.info);
}
类继承的同时混入
class Person {
String name;
num age;
Person(this.name, this.age);
printInfo() {
print('${this.name}----${this.age}');
}
void run() {
print("Person Run");
}
}
class A {
String info = "this is A";
void printA() {
print("A");
}
void run() {
print("A Run");
}
}
class B {
void printB() {
print("B");
}
void run() {
print("B Run");
}
}
class C extends Person with B, A {
C(String name, num age) : super(name, age);
}
void main() {
var c = new C('张三', 20);
c.printInfo(); // 张三----20
c.printB(); // B
print(c.info); // this is A
c.run(); // A Run
}
当混入的类存在相同的方法的时候,后混入的类会覆盖前混入的类。当继承和混入存在相同方法的时候,混入会覆盖继承的方法。
类混入的实例类型
很简单,mixins混入的类型就是其超类的子类型。
class A {
String info = "this is A";
void printA() {
print("A");
}
}
class B {
void printB() {
print("B");
}
}
class C with A, B {}
void main() {
var c = new C();
print(c is C); //true
print(c is A); //true
print(c is B); //true
var a = new A();
print(a is Object); //true a也是Object的子类型,因为所有的类都继承于Object类
}
泛型
通俗理解:泛型就是解决类接口方法的复用性、以及对不特定数据类型的支持(类型校验)
泛型方法
期望方法传入什么类型返回什么类型
- 泛型使用
T getData<T>(T value) {
return value;
}
void main(){
print(getData<String>('你好')); // 指定类型
print(getData<int>(123)); // 指定类型
}
泛型类
class PrintClass<T> {
List<T> list = [];
void add(T value){
this.list.add(value);
}
}
void mian() {
var p = PrintClass<String>();
p2.add("你好");
p2.add("HelloWorld!");
var p2 = PrintClass<Int>();
p2.add(1);
p2.add(2);
}
泛型接口
abstract class Cache<T> {
getByKey(String key);
void setByKey(String key, T value);
}
class FileCache<T> extends Cache<T> {
@override
getByKey(String key) {
throw UnimplementedError();
}
@override
void setByKey(String key, T value) {
print('this is FileCache key=${key} value=${value}');
}
}
class MemoryCache<T> extends Cache<T> {
@override
getByKey(String key) {
throw UnimplementedError();
}
@override
void setByKey(String key, T value) {
print('this is MemoryCache key=${key} value=${value}');
}
}
void main() {
MemoryCache m = new MemoryCache<String>();
m.setByKey('index', '首页数据');
MemoryCache m1 = new MemoryCache<Map>();
m1.setByKey('index', {"name": "张三", "age": 20});
}
泛型约束
//接口
abstract class Log {
String log();
}
String getLogger<T extends Log>(T logger) {
return logger.log();
}
库
在Dart中,库的使用时通过import关键字引入的。
library指令可以创建一个库,每个Dart文件都是一个库,即使没有使用library指令来指定。
Dart 中的库主要有三种:
- 我们自定义的库
import 'lib/xxx.dart';
- 系统内置库
import 'dart:math';
import 'dart:io';
import 'dart:convert';
- Pub包管理系统中的库
https://pub.dev/packages
https://pub.flutter-io.cn/packages
https://pub.dartlang.org/flutter/
(1)需要在自己项目根目录新建一个 pubspec.yaml
(2)在 pubspec.yaml 文件 然后配置名称 、描述、依赖等信息
(3)然后运行 pub get 获取包下载到本地
(4)项目中引入库 import ‘package:http/http.dart’ as http; 看文档使用
自定义库
- ./index.dart
import 'lib/Animal.dart';
main() {
var a = new Animal('小黑狗', 20);
print(a.getName());
}
- lib/Animal.dart
class Animal {
String _name; //私有属性
int age;
//默认构造函数的简写
Animal(this._name, this.age);
void printInfo() {
print("${this._name}----${this.age}");
}
String getName() {
return this._name;
}
void _run() {
print('这是一个私有方法');
}
execRun() {
this._run(); //类里面方法的相互调用
}
}
系统内置库
系统内置 MATH 库
// import 'dart:io';
import "dart:math";
main() {
print(min(12,23));
print(max(12,25));
}
系统内置 IO 库
import 'dart:io'; // 请求相关
import 'dart:convert'; // 转换编码格式
//api接口: http://news-at.zhihu.com/api/3/stories/latest
getDataFromZhihuAPI() async{
//1、创建HttpClient对象
var httpClient = new HttpClient();
//2、创建Uri对象
var uri = new Uri.http('news-at.zhihu.com','/api/3/stories/latest');
//3、发起请求,等待请求
var request = await httpClient.getUrl(uri);
//4、关闭请求,等待响应
var response = await request.close();
//5、解码响应的内容
return await response.transform(utf8.decoder).join();
}
void main() async{
var result = await getDataFromZhihuAPI();
print(result);
}
引入第三方模块
dart 的三方库都是用 pub 包管理系统进行管理的。
-
(1)从下面网址找到要用的库
https://pub.dev/packages
https://pub.flutter-io.cn/packages
https://pub.dartlang.org/flutter/ -
(2)创建一个 pubspec.yaml 文件,内容如下
name: xxx
description: A new flutter module project.
dependencies:
http: ^0.12.0+2
date_format: ^1.0.6
- (3)配置 dependencies
- (4)运行 pub get 获取远程库
- (5)看文档引入库使用
import 'dart:convert' as convert;
import 'package:http/http.dart' as http;
import 'package:date_format/date_format.dart';
main() async {
var url =
"http://www.phonegap100.com/appapi.php?a=getPortalList&catid=20&page=1";
// Await the http get response, then decode the json-formatted responce.
var response = await http.get(url);
if (response.statusCode == 200) {
var jsonResponse = convert.jsonDecode(response.body);
print(jsonResponse);
} else {
print("Request failed with status: ${response.statusCode}.");
}
print(formatDate(DateTime(1989, 2, 21), [yyyy, '*', mm, '*', dd]));
}
库的重命名
冲突解决:当引入两个库中有相同名称标识符的时候,
如果是 java 通常我们通过写上完整的包名路径来指定使用的具体标识符,
甚至不用 import 都可以,但是 Dart 里面是必须 import 的。
当冲突的时候,可以使用 as 关键字来指定库的前缀。如下例子所示:
- ./index.dart
import 'package:lib/Person1.dart';
import 'package:lib/Person2.dart' as lib2;
Person p1 = new Person(); // Uses Person from Person1.
lib2.Person p2 = new lib2.Person(); // Uses Person from Person2.
- lib/Person1.dart
class Person{
String name;
int age;
//默认构造函数的简写
Person(this.name,this.age);
Person.setInfo(String name,int age){
this.name=name;
this.age=age;
}
void printInfo(){
print("Person1:${this.name}----${this.age}");
}
}
- lib/Person2.dart
class Person{
String name;
int age;
//默认构造函数的简写
Person(this.name,this.age);
Person.setInfo(String name,int age){
this.name=name;
this.age=age;
}
void printInfo(){
print("Person1:${this.name}----${this.age}");
}
}
部分导入
如果只需要导入库的一部分,有两种模式:
- 模式一:只导入需要的部分,使用 show 关键字,如下例子所示:
import 'package:lib1/lib1.dart' show foo;
- 模式二:隐藏不需要的部分,使用 hide 关键字,如下例子所示:
import 'package:lib2/lib2.dart' hide foo;
默认导入
- ./index.dart
import 'lib/myMath.dart'
void main(){
getName();
getAge();
}
- lib/myMath.dart
void getName(){
print('张三');
}
void getAge(){
print(20);
}
void getSex(){
print('男');
}
部分导入
- ./index.dart
import 'lib/myMath.dart' show getName;
void main(){
getName();
}
- lib/myMath.dart
void getName(){
print('张三');
}
void getAge(){
print(20);
}
void getSex(){
print('男');
}
隐藏部分
- ./index.dart
import 'lib/myMath.dart' hide getName;
void main(){
// getName(); // 报错
getAge();
}
- lib/myMath.dart
void getName(){
print('张三');
}
void getAge(){
print(20);
}
void getSex(){
print('男');
}
延迟加载
也称为懒加载,可以在需要的时候再进行加载。
懒加载的最大好处是可以减少 APP 的启动时间。
懒加载使用 deferred as 关键字来指定,如下例子所示:
import 'package:deferred/hello.dart' deferred as hello;
当需要使用的时候,需要使用 loadLibrary()方法来加载:
import 'package:deferred/hello.dart' deferred as hello;
greet() async {
await hello.loadLibrary();
hello.printGreeting();
}
异步支持
async 和 await 关键字用于实现异步编程,并且让你的代码看起来就像是同步的一样。
这两个关键字的使用只需要记住两点:
- 只有 async 方法才能使用 await 关键字调用方法
- 如果调用别的 async 方法必须使用 await 关键字
async 是让方法变成异步。await 是等待异步方法执行完成。
testAsync() {
return 'Hello async';
}
void main(){
var result = testAsync();
print(result);
}
改为异步
testAsync() async{
return 'Hello async';
}
void main() async{
var result = await testAsync();
print(result);
}