文章内容是我学习时的点滴笔记,如有不好的地方,欢迎指出。
上一篇文章Flutter初探我们知道了Flutter是一个使用Dart语言开发的跨平台移动UI框架。Dart又是什么呢?Dart 是一种易于学习、易于扩展、并且可以部署到任何地方的应用编程语言。
那Flutter和Dart什么关系?Flutter为什么选用dart?Dart又有哪些优势?Dart...吧啦吧啦~~~
咳...咳...咳...大家查阅相关资料了解上述问题后可以悄悄告诉小老弟。好,名人不说暗话,下面让小老弟带各位大佬走进Dart语言世界。
1 变量
1.1 变量的声明
var
、dynamic
、Object
①未初始化变量声明
var
、dynamic
、Object
若未初始化的变量的话,变量值是可以动态改变的,看下面示例
void main() {
var data;
data = 1;
data = "data";
dynamic data2;
data2 = 1;
data2 = "data2";
Object data3;
data3 = 333;
data3 = "data3";
}
②初始化变量声明
相同点:
都可以直接声明变量,看下面示例
var d1 = 123; //d1类型被推断为int类型
dynamic d2 = "123"; //d2类型被推断为字符串类型
Object d3 = "1234";
不同点:
var声明初始化后不可再改变类型,而dynamic和Object可以。
dynamic 编译阶段不检查类型,而Object会在编译阶段会检查类型;
看下面示例
var d1 = 123; //d1类型被推断为int类型
d1 = 1234;
// d1 = "1123";//报错
dynamic d2 = "123"; //d2类型被推断为字符串类型
d2 = 123; //可以
Object d3 = 123; //d3类型被推断为int类型
d3 = "123"; //可以
dynamic d = "1234";
Object o = "121";
d.test();
// o.test();//编译不过
1.2 默认值
①没有初始化的变量自动获取一个默认值为null
②一切皆对象!!!对象的默认值为null
1.3 final和const
共同点:
- 声明的类型可省略,看下面示例
final str = "str"; //final String str = "str"; String这个类型 可省略
const str2 = "str2"; //const String str2 = "str2";与final一样,String可省略
- 初始化后不能再赋值(这与java一样);
- 不能和var同时使用,看下面示例
final ~~var~~ f1 = ""; //报错 不可与var同时使用
const ~~var~~ f2 = ""; //报错 不可与var同时使用
区别(需要注意的地方):
-
类级别常量,使用static const;以下图DateTime源码为例
- const可使用其他const 常量的值来初始化其值;看下面示例
const a = 1;
const b = 2;
var c = 3;
final d =4;
const sum = a+b;//可以,正常运行
const sum2 = a+c;//报错
const sum3 = a+d;//报错
- 使用const赋值声明,const可省略;看下面示例
const list1 = [];
const list2 = const [];//const 可省略
- 可以更改非final、非const变量的值,即使曾经具有const值;看下面示例
var list = const [1, 2, 3];
list = [3,2,1];
print(list);//输出结果为 [3,2,1]
- const导致的不可变性是可传递的;看下面示例
final list1 = [1,2,3];
list1[0] = 0;
print(list1);//输出[0,2,3]
const list2 = [1,2,3];
list2[0] = 0;//运行报错,不可修改
print(list2);//运行报错,list2不可修改
- 相同的const常量不会在内存中重复创建;看下面示例
const a = 1;
const b =1;
const c =2;
print(identical(a,b));//true
print(identical(a,c));//false
- const需要是编译时常量;
const dt = DateTime.now();//报错,const必须是编译时常量
final dt1 = DateTime.now();//可以,正常运行
2 内置类型
类型 | 解释 | 类型 | 解释 |
---|---|---|---|
Numbers | 数值 | Sets | 集合 |
Strings | 字符串 | Maps | 集合 |
Booleans | 布尔值 | Runes | 符号字符 |
Lists | 列表(数组) | Symbols | 标识符 |
2.1 内置类型-num、int、double
- int : 整数值;
- double : 64-bit双精度浮点数;
- int和double是num的子类;
2.2 内置类型-String
- Dart 字符串是 UTF-16 编码的字符序列,可以使用单引号或者双引号来创建字符串;
- 可以使用三个单引号或者双引号创建多行字符串对象;
- 可以使用 r 前缀创建”原始raw”字符串;看下面示例
String s1 = "123" 'abc' "okok";
String s2 = "123" + 'abc';
String s3 = '''ewrwerAeawrwer
eee''';
String s4 = '''ewrwerAeawrwer\neee''';
String s5 = r'''ewrwerAeawrwer\neee''';
print(s1);
print(s2);
print(s3);
print(s4);
print(s5);
运行结果:
既然尝试了String,那再试一下StringBuffer吧,示例如下
StringBuffer sb1 = StringBuffer();
StringBuffer sb2 = StringBuffer();
sb1.write("333");
sb1.write("aaa");
sb1.write("666");
sb2..write("333")..write("aaa")..writeAll(['6','6','6']);//..级联符,可链式调用里面方法
print(sb1);//333aaa666
print(sb2);//333aaa666
StringBuffer对象不用调用.toString()方法就可以输出打印,为啥呢?我们看一下print()内部是如何实现的呢?看下图,原来它打印是调用的对象的toString()方法。
- 可以在字符串中使用表达式:
${expression}
,如果表达式是一个标识符,可以省略 {},如果表达式的结果为一个对象,则 Dart 会调用对象的 toString() 函数来获取一个字符串;看下面示例
var a = 1;
var b = 2;
print("${a + b}"); //输出结果:3
2.3 内置类型-bool
- 与java不同的是,bool对象未初始化的默认值是null;看下面示例
bool bo;
print(bo);//输出:null
if(bo){//报错,因为bool未初始化默认值是null,所以这样调用运行时会报错
}
2.4 内置类型-List
Dart里的List和我们理解的List不太一样,它和Java中的 数组 有些类似。
- Dart中可以直接打印list包括list的元素,List也是对象。java中直接打印list结果是地址值。
- Dart中List的下标索引和java一样从0开始。
- 和java一样支持泛型。
- 有增删改查,支持倒序,自带排序、洗牌,可使用+将两个List合并。
//声明
//自动长度
List growableList = List();
// List growableList = new List()..length = 3;
growableList..add(1)..add(2)..add('damon');
print('growableList: ${growableList}');
//固定长度
var list = List(3); //List的声明,可以用var也可用List。
list[0] = 1; //下标索引从0开始
list[1] = 2;
list[2] = 'damon';
print('list: ${list}');
//元素类型固定
var typeList = List<int>();
typeList.add(1);
typeList.add(2);
typeList.add(3);
print('typeList: ${typeList}');
//常用属性
int first = typeList.first;
print('typeList.first: ${first}'); //第一个元素
int last = typeList.last;
print('typeList.last: ${last}'); //最后一个元素
int length = typeList.length;
print('typeList.length: ${length}'); //元素个数
bool isEmpty = typeList.isEmpty;
print('typeList.isEmpty: ${isEmpty}'); //是否为空
bool isNotEmpty = typeList.isNotEmpty;
print('typeList.isNotEmpty: ${isNotEmpty}'); //是否不为空
Iterable reversed = typeList.reversed;
print('typeList.reversed: ${reversed}'); //倒序
//常用方法 增删改查,排序,洗牌,复制子列表
var list4 = [];
//增
list4.add(1);
print('add 1 :${list4}');
list4.addAll([2, 3, 4]);
print('addAll [2, 3, 4] :${list4}');
list4.insert(0, 0);
print('insert(0, 0) :${list4}');
list4.insertAll(1, [5, 6, 7]);
print('insertAll(1, [5, 6, 7]) :${list4}');
//删
list4.remove(5);
print('remove 5 :${list4}');
list4.removeAt(2);
print('remove at 0 :${list4}');
//改
list4[4] = 5;
print('update list4[4] to 5 :$list4}');
//range
list4.fillRange(0, 3, 9);
print('fillRange update list4[0]-list4[2] to 9 :$list4}');
Iterable getRange = list4.getRange(0, 3);
print('getRange list4[0]-list4[2] :$getRange}');
//查
var contains = list4.contains(5);
print('list4 contains 5 :${contains}');
var indexOf = list4.indexOf(1);
print('list4 indexOf 1 :${indexOf}');
int indexWhere = list4.indexWhere((test) => test == 5);
print('list4 indexWhere 5 :${indexWhere}');
//排序
list4.sort();
print('list4 sort :${list4}');
//洗牌
list4.shuffle();
print('list4 shuffle :${list4}');
//复制子列表
var list5 = list4.sublist(1);
print('sublist(1) list5 :${list5}');
//操作符
var list6 = [8, 9];
print('list6 :${list6}');
var list7 = list5 + list6;
print('list5 + list6 :${list7}');
看下图,这是List有一些常用的属性,我们用的时候就知道,这里就不一一举例说明了。
2.5 内置类型-Map
//声明
//动态类型
var dynamicMap = Map();
dynamicMap['name'] = 'dongnao';
dynamicMap[1] = 'android';
print('dynamicMap :${dynamicMap}');
//强类型
var map = Map<int, String>();
map[1] = 'android';
map[2] = 'flutter';
print('map :${map}');
//也可以这样声明
var map1 = {'name': 'dongnao', 1: 'android'};
map1.addAll({'name':'damon'});
print('map1 :${map1}');
//常用属性
// print(map.isEmpty); //是否为空
// print(map.isNotEmpty); //是否不为空
// print(map.length); //键值对个数
// print(map.keys); //key 集合
// print(map.values); //value集合
Map和Java类似,键值对,我们用的时候就知道,这里也就不一一举例说明了。
2.6 内置类型-Set无重复列表
var dynamicSet = Set();
dynamicSet.add('dongnao');
dynamicSet.add('flutter');
dynamicSet.add(1);
dynamicSet.add(1);
print('dynamicSet :${dynamicSet}');
//常用属性与list类似
Set的基本用法也是和Java类似,但有几点和Java不太一样的地方。
- set1.difference(set2):返回set1集合里有但set2里没有的元素集合;
- set1.intersection(set2):返回set1和set2的交集;
- set1.union(set2):返回set1和set2的并集;
- set1.retainAll():set1只保留某些元素(要保留的元素要在原set中存在);
看下面示例:
Set set1 = Set();
set1.addAll(['a', 'b', 'a', 'c', 'd']);
Set set2 = Set();
set2.addAll(['a', 'b', 'a', 'e', 'f']);
// print(set1.difference(set2));//补集,输出{c, d}
// print(set1.intersection(set2));//交集,输出{a, b}
// print(set1.union(set2));//并集,输出{a, b, c, d, e, f}
set1.retainAll(['a', 'b']);//set1只保留某些元素(要保留的元素要在原set中存在)
print(set1);//输出{a, b}
2.7 内置类型-Runes
Dart 中 Runes 是UTF-32字符集的String 对象。
- Runes用于在字符串中表示Unicode字符;
- 使用String.fromCharCodes显示字符图形;
- 如果非4个数值,需要把编码值放到大括号中;
看下面示例
Runes runes = new Runes('\u6787 \u2665 \u{1f605} \u{1f60e} \u{1f44d}');
print(String.fromCharCodes(runes));//输出:枇 ♥ 😅 😎 👍
输出结果:
CopyChar|免费特殊符号大全,可以玩一下。
2.8 内置类型-Symbol
- symbol字面量是编译时常量,在标识符前面加#;看下面示例
#foo_lib
#runtimeType
- 如果是动态确定,则使用Symbol构造函数,通过new来实例化;看下面实例
Symbol obj = new Symbol('name');
Symbol标识符,主要是反射用,现在mirrors模块已经被移除(所以用不了反射了),所以了解一下就可以。
3 函数
3.1 函数定义
- Dart中函数是Function类型的对象;
- 所有的函数都有返回值。如果没有指定返回值,则默认把语句
return null;
作为函数的最后一个语句执行; - 定义函数时可省略类型(不建议);看下面示例
/**
* 类型未省略类型
*/
int add2(int a, int b) {
return a + b;
}
/**
1. 参数类型和方法返回类型均省略(不建议省略)
*/
add(a,b){
return a+b;
}
- 对于只有一个表达式的方法,你可以选择 使用缩写语法=>表达式来定义(Kotlin是用=来实现),有点像Lambda;看下面实例
/**
3. 未缩写
*/
int add(int a, int b) {
return a + b;
}
/**
4. 缩写
*/
int add(int a, int b) =>a+b;
- 可在函数内部定义函数,支持嵌套;看下面实例
add1() {
int add2(int a, int b) => a + b;
}
3.2 可选参数
- 可选命名参数:使用 {param1, param2, …} 的形式来指定命名参数。
int add({int x, int y, int z}) {
x ??= 1;
y ??= 2;
z ??= 3;
return x + y + z;
}
print(add());
print(add(x : 1, y : 2));
print(add(x : 1, y : 2, z : 0));
print(add3(z : 1, y : 2));
- 可选位置参数:把可选参数放到 [] 中,必填参数要放在可选参数前面。
int add4(int x, [int y, int z]) {
y ??= 2;
z ??= 3;
return x + y + z;
}
print(add4(1));
print(add4(1, 3));
print(add4(1, 6, 6));
- 可选命名参数默认值(默认值必须是编译时常量),可以使用等号‘=’或冒号’:‘。(Dart SDK 1.21 之前只能用冒号,冒号的支持以后会移除,所以建议使用等号)
int add5(int x, {int y = 2, int z = 3}) {
return x + y + z;
}
//前面的必填参数没有名字
print(add5(1, y: 10, z: 2));
- 可选位置参数默认值(默认值必须是编译时常量),只能使用等号'='。
int add6(int x, [int y = 2, int z = 3]) {
return x + y + z;
}
print(add6(1));
- 可使用list或map作为默认值,但必须是const。
void func(
{List list = const [1, 2, 3],
Map map = const {1: 1, 'name': 'dongnao'}}) {
//TODO ...
}
3.3 匿名函数
- 无参匿名
var printFunc = () => print("666");
printFunc();
//语法上也支持这样调用,但不建议
(() => print("666"))();
- 有参匿名
var printFunc1 = (name) => print("$name");
printFunc1('秃头老王');
- 匿名函数传参
List test(List ls, String fun(str)) {
for (int i = 0; i < ls.length; i++) {
ls[i] = fun(ls[i]);
}
return ls;
}
List ls = ['a', 'b', 'c'];
print( test(ls, (str) => str * 2));
输出结果:[aa, bb, cc]
我们看一下List的forEach源码:
其实是循环调用传入的
f(element)
方法,看下面实例:
List ls = ['a', 'b', 'c'];
ls.forEach((ele) => print(ele));
输出结果:
- 闭包(返回Function对象)
Function makeAddFun(int a) {
a++;
return (int b)=>a+b;
}
print(makeAddFun(1)(2));//相当于: var f = makeAddFun(1); print(f(2));
输出结果:4
- 函数别名
void main(){
MyFunc myFunc = subtsract(2, 1);
myFunc = add(2, 2);
myFunc = divide(4, 2);
calculator(2, 1, subtsract);
}
typedef MyFunc(int a, int b);
//根据MyFunc相同的函数签名定义两个函数
add(int a, int b) {
print('add:${a + b}');
}
subtsract(int a, int b) {
print('subtsract: ${a - b}');
}
divide(int a, int b) {
print('divide: ${a / b}');
}
输出结果:
4 操作符
4.1 后缀操作符 ?.
条件成员访问 和 . 类似,但是左边的操作对象不能为 null,例如 foo?.bar 如果 foo 为 null 则返回 null,否则返回 bar 成员
String a;
print(a?.length);
输出结果:null
4.2 取商操作符 ~/
被除数 ÷ 除数 = 商 ... 余数,A ~/ B = C,这个C就是商。相当于Java里的 /
print(2 / 3); //输出结果:0.6666666666666666
print(2 ~/ 3); //输出结果:0
4.3 类型判定操作符
类型判定操作符:as
、is
、is!
在运行时判定对象类型
//as 类型转换
num iNum = 1;
num dNum = 1.0;
int i = iNum as int;
double d = dNum as double;
print([i, d]);
// String s = iNum as String; //不能转换,报错
//is 如果对象是指定的类型返回 True
print(iNum is int);
Child child;
Child child1 = new Child();
print(child is Parent); //child is Null
print(child1 is Parent);
//is! 如果对象是指定的类型返回 False
print(iNum is! int);
4.4条件表达式
bool isFinish = true;
String txtVal = isFinish ? 'yes' : 'no';
// expr1 ?? expr2,如果 expr1 是 non-null,返回其值; 否则执行 expr2 并返回其结果。
bool isPaused;
isPaused = isPaused ?? false;
//或者
isPaused ??= false;
4.5级联操作符
..
可以在同一个对象上 连续调用多个函数以及访问成员变量。
严格来说, 两个点的级联语法不是一个操作符。 只是一个 Dart 特殊语法。
StringBuffer sb = new StringBuffer();
sb
..write('dongnao')
..write('flutter')
..write('\n')
..writeln('damon');