KM的博客.

Flutter Dar基础

字数统计: 7.1k阅读时长: 32 min
2021/01/15

Dart基本语法

Dart的设计目标应该是同时借鉴了Java和JavaScript。

Dart在静态语法方面和Java非常相似,如类型定义、函数声明、泛型等,

而在动态特性方面又和JavaScript很像,如函数式特性、异步支持等。

除了融合Java和JavaScript语言之所长之外,Dart也具有一些其它具有表现力的语法,如可选命名参数、..(级联运算符)和?.(条件成员访问运算符)以及??(判空赋值运算符)。

其实,对编程语言了解比较多的读者会发现,在Dart中其实看到的不仅有Java和JavaScript的影子,它还具有其它编程语言中的身影,如命名参数在Objective-C和Swift中早就很普遍

Dart和Java及JavaScript对比

之所以将Dart与Java和JavaScript对比,是因为,这两者分别是强类型语言和弱类型语言的典型代表,并且Dart 语法中很多地方也都借鉴了Java和JavaScript。

Dart vs Java

客观的来讲,Dart在语法层面确实比Java更有表现力;

  • 在VM层面,Dart VM在内存回收和吞吐量都进行了反复的优化,但具体的性能对比,笔者没有找到相关测试数据,但在笔者看来,只要Dart语言能流行,VM的性能就不用担心,毕竟Google在Go(没用VM但有GC)、JavaScript(v8)、Dalvik(Android上的Java VM)上已经有了很多技术积淀。
  • 值得注意的是Dart在Flutter中已经可以将GC做到10ms以内,所以Dart和Java相比,决胜因素并不会是在性能方面。
  • 而在语法层面,Dart要比Java更有表现力,最重要的是Dart对函数式编程支持要远强于Java(目前只停留在Lambda表达式),
  • Dart目前真正的不足是生态,但笔者相信,随着Flutter的逐渐火热,会回过头来反推Dart生态加速发展,对于Dart来说,现在需要的是时间。

Dart vs JavaScript

JavaScript的弱类型一直被抓短,所以TypeScript、CoffeeScript甚至是Facebook的flow(虽然并不能算JavaScript的一个超集,但也通过标注和打包工具提供了静态类型检查)才有市场。

就笔者使用过的脚本语言中(笔者曾使用过Python、PHP),JavaScript无疑是动态化支持最好的脚本语言,比如在JavaScript中,可以给任何对象在任何时候动态扩展属性,对于精通JavaScript的高手来说,这无疑是一把利剑。但是,任何事物都有两面性,JavaScript的强大的动态化特性也是把双刃剑,你可经常听到另一个声音,认为JavaScript的这种动态性糟糕透了,太过灵活反而导致代码很难预期,无法限制不被期望的修改。毕竟有些人总是对自己或别人写的代码不放心,他们希望能够让代码变得可控,并期望有一套静态类型检查系统来帮助自己减少错误。

正因如此,在Flutter中,Dart几乎放弃了脚本语言动态化的特性,如不支持反射、也不支持动态创建函数等。并且Dart在2.0强制开启了类型检查(Strong Mode),原先的检查模式(checked mode)和可选类型(optional type)将淡出,所以在类型安全这个层面来说,Dart和TypeScript、CoffeeScript是差不多的,所以单从这一点来看,Dart并不具备什么明显优势,但综合起来看,Dart既能进行服务端脚本、APP开发、web开发,这就有优势了!

Dart基本语法

1、常量

final和const都用来命名常量

  • final或者const修饰的变量,变量类型可以省略
  • 两者区别在于:const 变量是一个编译时常量-const 在编译时直接转为常量,final变量在第一次使用时被初始化。
1
2
3
4
5
6
7
//可以省略String类型声明
//final String str = "Hello world";
final str = "Hello world";

//const String str1 = "Hello world";
const str1 = "Hello world";

2、变量

2.1 var可接受任意类型,但是一旦赋值其数据类型不能改变了

1
2
3
4
5
var t;
t = "hi world";
// 下面代码在dart中会报错,因为变量t的类型已经确定为String,
// 类型一旦确定后则不能再更改其类型。
t = 1000;

2.2 dynamicObject

  • Object 是Dart所有对象的根基类,也就是说所有类型都是Object的子类(包括Function和Null),所以任何类型的数据都可以赋值给Object声明的对象. dynamicvar一样都是关键词,声明的变量可以赋值任意对象。

  • dynamicObject相同之处在于,他们声明的变量可以在后期改变数据类型。

    1
    2
    3
    4
    5
    6
    7
    dynamic t;
    Object x;
    t = "hi world";
    x = 'Hello Object';
    //下面代码没有问题
    t = 1000;
    x = 1000;
  • dynamicObject不同的是,dynamic声明的对象编译器会提供所有可能的组合, 而Object声明的对象只能使用Object的属性与方法, 否则编译器会报错。如下:变量a不会报错, 变量b编译器会报错

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    dynamic a;
    Object b;
    main() {
    a = "";
    b = "";
    printLengths();
    }

    printLengths() {
    // no warning
    print(a.length);
    // warning:
    // The getter 'length' is not defined for the class 'Object'
    print(b.length);
    }

空安全(null-safety)

Dart 中一切都是对象,这意味着如果我们定义一个数字,在初始化它之前如果我们使用了它,假如没有某种检查机制,则不会报错,比如:

1
2
3
4
test() {
int i;
print(i*8);
}

在 Dart 引入空安全之前,上面代码在执行前不会报错,但会触发一个运行时错误,原因是 i 的值为 null 。但现在有了空安全,则定义变量时我们可以指定变量是可空还是不可空。

1
2
3
4
5
6
7
int i = 8; //默认为不可空,必须在定义时初始化。
int? j; // 定义为可空类型,对于可空变量,我们在使用前必须判空。

// 如果我们预期变量不能为空,但在定义时不能确定其初始值,则可以加上late关键字,
// 表示会稍后初始化,但是在正式使用它之前必须得保证初始化过了,否则会报错
late int k;
k=9;

如果一个变量我们定义为可空类型,在某些情况下即使我们给它赋值过了,但是预处理器仍然有可能识别不出,这时我们就要显式(通过在变量后面加一个”!“符号)告诉预处理器它已经不是null了,比如:

1
2
3
4
5
6
7
8
9
10
11
12
class Test{
int? i;
Function? fun;
say(){
if(i!=null) {
print(i! * 8); //因为已经判过空,所以能走到这 i 必不为null,如果没有显式申明,则 IDE 会报错
}
if(fun!=null){
fun!(); // 同上
}
}
}

上面中如果函数变量可空时,调用的时候可以用语法糖:

1
fun?.call() // fun 不为空时则会被调用

String 基础用法

字符串截取

var string = ‘dartlang’;
string.substring(1); // ‘artlang’
string.substring(1, 4); // ‘art’

double基本用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// 转成字符串
print(d0.toString());

// 取整数, 小数点舍去
print(d0.toInt());

// 比较大小, 0:相同、1:大于、-1:小于
print(d0.compareTo(30));

// 获取绝对值
print(d0.abs());

// 四舍五入
print(d0.round()); // 13
// 向上取整
print(d0.ceil()); // 14
// 向下取整
print(d0.floor()); // 13

// 输出的double类型, 相当于d0.round().toDouble()
print(d0.roundToDouble()); // 13.0
print(d0.ceilToDouble()); // 14.0
print(d0.floorToDouble()); // 13.0

// 保留指定的小数位数(四舍五入), 不足补0, 字符串返回
print(d0.toStringAsFixed(2)); // 13.10

// 保留变量的位数(小数点前后的总位数), 不足补0, 多余的四舍五入
print(d0.toStringAsPrecision(10)); // 13.09870000

/** toStringAsExponential
* 1.toStringAsExponential(); // 1e+0
* 1.toStringAsExponential(3); // 1.000e+0
* 123456.toStringAsExponential(); // 1.23456e+5
* 123456.toStringAsExponential(3); // 1.235e+5
* 123.toStringAsExponential(0); // 1e+2
*/


/** toStringAsPrecision
* 1.toStringAsPrecision(2); // 1.0
* 1e15.toStringAsPrecision(3); // 1.00e+15
* 1234567.toStringAsPrecision(3); // 1.23e+6
* 1234567.toStringAsPrecision(9); // 1234567.00
* 12345678901234567890.toStringAsPrecision(20); // 12345678901234567168
* 12345678901234567890.toStringAsPrecision(14); // 1.2345678901235e+19
* 0.00000012345.toStringAsPrecision(15); // 1.23450000000000e-7
* 0.0000012345.toStringAsPrecision(15); // 0.00000123450000000000
*/

类型转换

String to int

1
2
3
4
5
var n1 = int.parse('-42');
// -42

var n2 = int.parse('+42');
// 42

String to double

1
var scoreDouble = double.parse(item.areaPerformanceScore);

double保留两位小数

1
final scoreWithTwoDecimal = scoreDouble.toStringAsFixed(2);

List 基本用法

Creates a list of the given length.

NOTICE: This constructor cannot be used in null-safe code. Use [List.filled] to create a non-empty list. This requires a fill value to initialize the list elements with. To create an empty list, use [] for a growable list or List.empty for a fixed length list (or where growability is determined at run-time).

The created list is fixed-length if [length] is provided.

var fixedLengthList = List(3);
fixedLengthList.length; // 3
fixedLengthList.length = 1; // Error

The list has length 0 and is growable if [length] is omitted.

var growableList = List();
growableList.length; // 0;
growableList.length = 3;

To create a growable list with a given length, for a nullable element type, just assign the length right after creation:

List growableList = []..length = 500;

For a non-nullable element type, an alternative is the following:

List growableList = List.filled(500, 0, growable: true);

The [length] must not be negative or null, if it is provided.

If the element type is not nullable, [length] must not be greater than zero.

‘List’ is deprecated and shouldn’t be used. Use a list literal, [], or the List.filled constructor instead.
Try replacing the use of the deprecated member with the replacement.dart(deprecated_member_use)

The default ‘List’ constructor isn’t available when null safety is enabled.
Try using a list literal, 'List.filled' or 'List.generate'.

3、函数

Dart是一种真正的面向对象的语言,所以即使是函数也是对象,并且有一个类型Function。这意味着函数可以赋值给变量或作为参数传递给其他函数,这是函数式编程的典型特征。

3.1函数声明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
bool isNoble(int atomicNumber) {
return _nobleGases[atomicNumber] != null;
}

/// Dart函数声明如果没有显式声明返回值类型时会默认当做`dynamic`处理,注意,函数返回值没有类型推断:
typedef bool CALLBACK();
//不指定返回类型,此时默认为dynamic,不是bool
isNoble(int atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
void test(CALLBACK cb){
print(cb());
}
//报错,isNoble不是bool类型
test(isNoble);

3.2 简写一个表达式的函数

1
bool isNoble (int atomicNumber)=> _nobleGases [ atomicNumber ] != null ;

3.3函数作为变量

1
2
3
4
var say = (str){
print(str);
};
say("hi world");

3.4函数作为参数传递

1
2
3
4
void execute(var callback) {
callback();
}
execute(() => print("xxx"))

3.5可选位置参数[param1, param2]

  • 包装一组函数参数,用[]标记为可选的位置参数,并放在参数列表的最后面:
1
2
3
4
5
6
7
8
9
10
11
12
String say(String from, String msg, [String device]) {
var result = '$from says $msg';
if (device != null) {
result = '$result with a $device';
}
return result;
}

say('Bob', 'Howdy'); //没有可选参数: Bob says Howdy
say('Bob', 'Howdy', 'smoke signal'); //有可选参数:Bob says Howdy with a smoke signal


3.6可选命名参数{param1, param2}

  • 定义函数时,使用{param1, param2, …},放在参数列表的最后面,用于指定命名参数。例如:
1
2
3
4
5
6
7
//设置[bold]和[hidden]标志
void enableFlags({bool bold, bool hidden}) {

}
//调用函数时,可以使用指定命名参数。例如:paramName: value
enableFlags(bold: true, hidden: false);

注意,不能同时使用可选的位置参数和可选的命名参数

异步函数

Dart类库有非常多的返回Future或者Stream对象的函数。 这些函数被称为异步函数

它们只会在设置好一些耗时操作之后返回,比如像 IO操作/网络请求。而不是等到这个操作完成。

Future与JavaScript中的Promise非常相似,表示一个异步操作的最终完成(或失败)及其结果值的表示。简单来说,它就是用于处理异步操作的,异步处理成功了就执行成功的操作,异步处理失败了就捕获错误或者停止后续操作。

请记住,Future 的所有API的返回值仍然是一个Future对象,所以可以很方便的进行链式调用。

一个Future只会对应一个结果,要么成功,要么失败。

Future.then

1
2
3
4
5
Future.delayed(new Duration(seconds: 2),(){
return "hi world!";
}).then((data){
print(data);
});

Future.catchError/Future.onError

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Future.delayed(new Duration(seconds: 2),(){
//return "hi world!";
throw AssertionError("Error");
}).then((data){
//执行成功会走到这里
print("success");
}).catchError((e){
//执行失败会走到这里
print(e);
});

Future.delayed(new Duration(seconds: 2), () {
//return "hi world!";
throw AssertionError("Error");
}).then((data) {
print("success");
}, onError: (e) {
print(e);
});

Future.whenComplete

有些时候,我们会遇到无论异步任务执行成功或失败都需要做一些事的场景,比如在网络请求前弹出加载对话框,在请求结束后关闭对话框。这种场景,有两种方法,第一种是分别在thencatch中关闭一下对话框,第二种就是使用FuturewhenComplete回调:

1
2
3
4
5
6
7
8
9
10
11
12
Future.delayed(new Duration(seconds: 2),(){
//return "hi world!";
throw AssertionError("Error");
}).then((data){
//执行成功会走到这里
print(data);
}).catchError((e){
//执行失败会走到这里
print(e);
}).whenComplete((){
//无论成功或失败都会走到这里
});

Future.wait

有些时候,我们需要等待多个异步任务都执行结束后才进行一些操作.

比如我们有一个界面,需要先分别从两个网络接口获取数据,获取成功后,我们需要将两个接口数据进行特定的处理后再显示到UI界面上,应该怎么做?

答案是Future.wait,它接受一个Future数组参数,只有数组中所有Future都执行成功后,才会触发then的成功回调,只要有一个Future执行失败,就会触发错误回调。下面,我们通过模拟Future.delayed 来模拟两个数据获取的异步任务,等两个异步任务都执行成功时,将两个异步任务的结果拼接打印出来,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Future.wait([
// 2秒后返回结果
Future.delayed(new Duration(seconds: 2), () {
return "hello";
}),
// 4秒后返回结果
Future.delayed(new Duration(seconds: 4), () {
return " world";
})
]).then((results){
print(results[0]+results[1]);
}).catchError((e){
print(e);
});

执行上面代码,4秒后你会在控制台中看到“hello world”。

回调地狱(Callback Hell)

如果代码中有大量异步逻辑,并且出现大量异步任务依赖其它异步任务的结果时,必然会出现Future.then回调中套回调情况

1
2
3
4
5
6
7
8
9
10
login("alice","******").then((id){
//登录成功后通过,id获取用户信息
getUserInfo(id).then((userInfo){
//获取用户信息后保存
saveUserInfo(userInfo).then((){
//保存用户信息,接下来执行其它操作
...
});
});
})

如果业务逻辑中有大量异步依赖的情况,将会出现上面这种在回调里面套回调的情况,过多的嵌套会导致的代码可读性下降以及出错率提高,并且非常难维护,这个问题被形象的称为回调地狱(Callback Hell)。回调地狱问题在之前JavaScript中非常突出,也是JavaScript被吐槽最多的点,但随着ECMAScript6和ECMAScript7标准发布后,这个问题得到了非常好的解决,

而解决回调地狱的两大神器正是ECMAScript6引入了Promise

以及ECMAScript7中引入的async/await

而在Dart中几乎是完全平移了JavaScript中的这两者:Future相当于Promise,而async/await连名字都没改。接下来我们看看通过Futureasync/await如何消除上面示例中的嵌套问题。

使用Future消除Callback Hell
1
2
3
4
5
6
7
8
9
10
login("alice","******").then((id){
return getUserInfo(id);
}).then((userInfo){
return saveUserInfo(userInfo);
}).then((e){
//执行接下来的操作
}).catchError((e){
//错误处理
print(e);
});

正如上文所述, Future 的所有API的返回值仍然是一个Future对象,所以可以很方便的进行链式调用” ,如果在then中返回的是一个Future的话,该future会执行,执行结束后会触发后面的then回调,这样依次向下,就避免了层层嵌套。

使用async/await消除callback hell

通过Future回调中再返回Future的方式虽然能避免层层嵌套,但是还是有一层回调,有没有一种方式能够让我们可以像写同步代码那样来执行异步任务而不使用回调的方式?答案是肯定的,这就要使用async/await了,下面我们先直接看代码,然后再解释,代码如下:

1
2
3
4
5
6
7
8
9
10
11
task() async {
try{
String id = await login("alice","******");
String userInfo = await getUserInfo(id);
await saveUserInfo(userInfo);
//执行接下来的操作
} catch(e){
//错误处理
print(e);
}
}
  • async用来表示函数是异步的,定义的函数会返回一个Future对象,可以使用then方法添加回调函数。
  • await 后面是一个Future,表示等待该异步任务完成,异步完成后才会往下走;await必须出现在 async 函数内部。

可以看到,我们通过async/await将一个异步流用同步的代码表示出来了。

其实,无论是在JavaScript还是Dart中,async/await都只是一个语法糖,编译器或解释器最终都会将其转化为一个Promise(Future)的调用链。

Stream

Stream 也是用于接收异步事件数据,和Future 不同的是,它可以接收多个异步操作的结果(成功或失败)。 也就是说,在执行异步任务时,可以通过多次触发成功或失败事件来传递结果数据或错误异常。

Stream 常用于会多次读取数据的异步任务场景,如网络内容下载、文件读写等。

举个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Stream.fromFutures([
// 1秒后返回结果
Future.delayed(new Duration(seconds: 1), () {
return "hello 1";
}),
// 抛出一个异常
Future.delayed(new Duration(seconds: 2),(){
throw AssertionError("Error");
}),
// 3秒后返回结果
Future.delayed(new Duration(seconds: 3), () {
return "hello 3";
})
]).listen((data){
print(data);
}, onError: (e){
print(e.message);
},onDone: (){

});

上面的代码依次会输出:

1
2
3
I/flutter (17666): hello 1
I/flutter (17666): Error
I/flutter (17666): hello 3

代码很简单,就不赘述了。

思考题:既然Stream可以接收多次事件,那能不能用Stream来实现一个订阅者模式的事件总线?

List

‘List’ is deprecated and shouldn’t be used. Use a list literal, [], or the List.filled constructor instead.

默认的 List 构造有什么改动?

你可能会遇到这样的错误:

1
The default 'List' constructor isn't available when null safety is enabled. #default_list_constructor

默认的列表构造会将列表用 null 填充,会造成问题。

将它变为 List.filled(length, default) 即可。

(deprecated) List List([int? length])

dart:core


Creates a list of the given length.

NOTICE: This constructor cannot be used in null-safe code. Use [List.filled] to create a non-empty list. This requires a fill value to initialize the list elements with. To create an empty list, use [] for a growable list or List.empty for a fixed length list (or where growability is determined at run-time).

The created list is fixed-length if [length] is provided.

var fixedLengthList = List(3);
fixedLengthList.length; // 3
fixedLengthList.length = 1; // Error

The list has length 0 and is growable if [length] is omitted.

var growableList = List();
growableList.length; // 0;
growableList.length = 3;

To create a growable list with a given length, for a nullable element type, just assign the length right after creation:

List growableList = []..length = 500;

For a non-nullable element type, an alternative is the following:

List growableList = List.filled(500, 0, growable: true);

The [length] must not be negative or null, if it is provided.

If the element type is not nullable, [length] must not be greater than zero.

‘List’ is deprecated and shouldn’t be used. Use a list literal, [], or the List.filled constructor instead.
Try replacing the use of the deprecated member with the replacement.dart(deprecated_member_use)

The default ‘List’ constructor isn’t available when null safety is enabled.
Try using a list literal, ‘List.filled’ or ‘List.generate’.

  • List 中元素重新赋值
1
2
3
list1[0] = '第一个元素替换';
print(list1); //[第一个元素替换, z, f, y]
复制代码
  • setRange() 范围内修改List
1
2
3
4
5
//在range 范围内修改List 超出范围报错
list1.setRange(0, 4, ['a','b','c','d']);
print(list1);//[a, b, c, d]

复制代码
  • replaceRange() 范围内替换List
1
2
3
4
5
6
7
8
9
10
11
//在range 范围内替换List 超出范围报错 包含头不包含尾
List<int> list10 = List();
list10.add(0);
list10.add(1);
list10.add(2);
list10.add(3);
list10.add(4);
list10.replaceRange(1, 4, [7,8,9,10,11]);
print(list10); //[0, 7, 8, 9, 10, 11, 4]

复制代码
  • fillRange(start,end,value) 在start end 每个元素用value替换
1
2
3
4
5
6
7
8
9
10
11
List<int> list11 = List();
list11.add(0);
list11.add(1);
list11.add(2);
list11.add(3);
list11.add(4);
list11.add(5);
list11.fillRange(1, 5,9);
print(list11);//[0, 9, 9, 9, 9, 5]

复制代码
  • retainWhere(()=>(bool)) 根据条件筛选元素
1
2
3
4
5
6
7
8
9
List<int> list12 = List();
list12.add(0);
list12.add(1);
list12.add(2);
list12.add(3);
list12.add(4);
list12.retainWhere((element) => (element > 2)); //根据条件保留元素
print(list12);//[3, 4]
复制代码
  • setAll(index,list) 从index开始,使用list内的元素逐个替换本list中的元素
1
2
3
4
5
6
7
8
9
10
List<int> list13 = List();
list13.add(0);
list13.add(1);
list13.add(2);
list13.add(3);
list13.add(4);
list13.setAll(1, [5,6,7,8]); //从index开始替换,但是替换的List长度不能超过现有List的长度
print(list13); //[0, 5, 6, 7, 8]

复制代码
  • indexOf(element,[start]) 查找指定元素在list中的索引
1
2
3
4
5
6
7
8
9
10
11
12
List<int> list14 = List();
list14.add(0);
list14.add(1);
list14.add(2);
list14.add(3);
list14.add(4);
list14.add(4);
int index14 = list14.indexOf(4); //默认从索引0开始查找指定元素,返回指定元素的索引 如果存在多个相同的元素,则返回第一个
int index141 = list14.indexOf(2,3); //从3 开始查找 2,如果存在返回指定元素索引,反则返回 -1
print(index14); //4
print(index141); //-1
复制代码
  • lastIndexOf(element,[start]) 从后往前查找指定元素在list中的索引
1
2
3
4
5
6
7
8
9
10
11
12
13
List<int> list15 = List();
list15.add(0);
list15.add(1);
list15.add(2);
list15.add(3);
list15.add(4);

int index15 = list15.lastIndexOf(2);
int index151 = list15.lastIndexOf(1,4); //从指定索引位置往后查找指定元素的索引位置,查到返回该元素的索引值,没找到返回-1
print(index15); //2
print(index151); //1

复制代码
  • elementAt(index) 根据索引值获取元素
1
2
3
4
5
6
7
8
9
10
List<int> list16 = List();
list16.add(0);
list16.add(1);
list16.add(2);
list16.add(3);
list16.add(4);
var resultList16 = list16.elementAt(3);
print(resultList16); //3

复制代码
  • any((element) => (bool)) 判断List中是否任意一个元素符合条件赛选
1
2
3
4
5
6
7
8
9
10
List<int> list17 = List();
list17.add(1);
list17.add(2);
list17.add(3);
list17.add(4);
list17.add(5);
//every((element)=>(bool)) 判断List中是否有元素符合参数函数
bool resultList17 = list17.any((element) => (element >3));//判断List元素中是否有任意一个元素满足给定的条件,如果满足则返回true,否则返回flase
print(resultList17); //true
复制代码
  • firstWhere((element) =>(bool)) 返回第一个满足条件的元素
1
2
3
4
5
6
7
8
9
10
11
12
List<int> list18 = List();
list18.add(1);
list18.add(2);
list18.add(3);
list18.add(4);
list18.add(5);
list18.add(1);
list18.add(2);

var resultList18 = list18.firstWhere((element) => (element > 2)); //找不到则会报错 Bad state: No element
print(resultList18); //3
复制代码
  • int lastIndexWhere(bool test(E element), [int start]); 返回第一个满足条件的元素下标,start表示从第几个下标开始查找,不存在则返回-1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
List<int> list19 = List();
list19.add(1);
list19.add(2);
list19.add(3);
list19.add(4);
list19.add(5);
var resultList19 = list19.indexWhere((element) => (element >3)); //不存在则返回-1
var resultList191 = list19.indexWhere((element) => (element > 4),2);
var resultList192 = list19.lastIndexWhere((element) => (element > 5));
var resultList193 = list19.lastIndexWhere((element) => (element > 2),1);
print(resultList19);//3
print(resultList191);//4
print(resultList192);//-1
print(resultList193);//-1
复制代码
  • lastWhere(bool test(E element), {E orElse()}) 从后往前查找,返回第一个满足条件的元素
1
2
3
4
5
6
7
8
9
10
11
List<int> list20 = List();
list20.add(1);
list20.add(1);
list20.add(3);
list20.add(4);
list20.add(5);
var resultList20 = list20.lastWhere((element) => element >2);
var resultList21 = list20.lastWhere((element) => element > 6,orElse:()=>(66));
print(resultList20); //5
print(resultList21); //66
复制代码
  • forEach 遍历List中的每个元素 进入文档内查看,forEach方法内部其实也是forIn方法进行遍历
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
List list21 = List();
list21.add(1);
list21.add(2);
list21.add(3);
list21.add(4);
list21.add(5);
list21.forEach((element) { //遍历时不可以add或remove,但是可以修改element内的值
element += 1; //这里修改了element的值,但是遍历完,list21里面的值是不变的
print(element); //2,3,4,5,6;
list21[1] = 66;
list21[2] = 77;
});
print(list21); //[1, 66, 77, 4, 5]
for (var list21Element in list21) {
print(list21Element); // 1, 66, 77, 4, 5;
}

复制代码
  • fold(T initialValue, T combine(T previousValue, E element)) 根据现有的List和给定的initialValue,指定一个参数函数规则,对List每个元素做操作,并将结果返回
1
2
3
4
5
6
7
List<int> list22 = [1,2,3,4,5,6];
var resultList22 = list22.fold(2, (a, element) => (a * element)); //2 *(1*2*3*4*5*6)
var resultList221 = list22.fold(2, (a, element) => (a + element)); // 2+(1+2+3+4+5+6)
print(resultList22);//1440
print(resultList221);//23

复制代码
  • reduce(E combine(E value, E element)) 用指定的函数对元素做连续操作,将结果返回
1
2
3
4
5
6
7
8
9
10
11
List<int> list23 = [1,2,3,4,5,6];
var resultList23 = list23.reduce((a, b) => (a +b )); //1+2+3+4+5+6
var resultList231 = list23.reduce((a, b) => (a * b)); //1*2*3*4*5*6
print(resultList23);//21
print(resultList231);//720
//skip(count) 越过count个元素后,开始返回Iterable;
List<int> list24 = [1,2,3,4,5];
Iterable<int> resultList24 = list24.skip(2);
print('$resultList24,${list24.runtimeType}');//(3, 4, 5),List<dynamic>

复制代码
  • skipWhile(bool test(E value)) 根据参数函数,找到第一个不符合条件的元素,然后将其及以后的元素返回,如果都符合就返回一个空List,如果都不符合则全部返回
1
2
3
4
5
List list25 = [3,1,2,4,5,6];
Iterable resultList25 = list25.skipWhile((value) => (value > 1));
print(resultList25);//[]

复制代码
  • take(int count) 从0 - count 获取元素 并返回
  • takeWhile(bool test(E value)) 从0 开始获取,直至第一个不符合函数的元素,将其前面的元素都返回
1
2
3
4
5
6
List list26 = [2,4,5,6,1];
var resultList26 = list26.take(2);
var resultList261 = list26.takeWhile((value) => value > 1.2);
print('$resultList26,${resultList26.runtimeType}'); //(2, 4),SubListIterable<dynamic>
print('$resultList261,${resultList261.runtimeType}'); //(2, 4, 5, 6),TakeWhileIterable<dynamic>
复制代码
  • where(bool test(E element)) => WhereIterable(this, test) 根据指定的函数筛选,符合条件的元素组成新的Iterable
1
2
3
4
List list27 = [3,1,0,5,7];
Iterable resultList27 = list27.where((element) => element >2);
print(resultList27); //(3, 5, 7)
复制代码
  • singleWhere(bool test(E element), {E orElse()}) 根据函数找出唯一的元素,如果不唯一,则报错:Too many elements
1
2
3
4
List list28 = [1,3,2,5,8];
var resultList28 = list28.singleWhere((element) => (element >7),orElse:()=>(7));
print(resultList28);//8
复制代码
  • whereType() 从无指定类型的List中,筛选出指定类型的数据
1
2
3
4
5
6
7
List list29 = [1,9,8,2,7,1.11,'abc'];
Iterable<int> resultList29 = list29.whereType();
Iterable<double> resultList291 = list29.whereType();
Iterable<String> resultList292 = list29.whereType();
print('$resultList29,$resultList291,$resultList292'); //(1, 9, 8, 2, 7),(1.11),(abc)

复制代码
  • cast() 将List的泛型提升到期祖父类
1
2
3
4
5
6
List<String> list30 = ['aa','bb','cc','dd'];
List<Object> list31 = list30.cast();
list31.add('ff');
print('$list31,${list31.runtimeType}'); //[aa, bb, cc, dd],CastList<String, Object>

复制代码
  • expand() 根据现有的List,指定一个规则,生成新的List
1
2
3
4
5
List list32 = [1,4,3,6];
var resultList32 = list32.expand((element) => ([element +1,element +2]));
print('$resultList32,${resultList32.runtimeType}'); //(2, 3, 5, 6, 4, 5, 7, 8) ExpandIterable<dynamic, dynamic>

复制代码
  • toSet() 将List转化为Set 并去除后面重复的元素
1
2
3
4
5
List list33 = [1,3,2,7,6,1,2];
Set set33 = list33.toSet();
print('$set33,${set33.runtimeType}'); //{1, 3, 2, 7, 6},_CompactLinkedHashSet<dynamic>

复制代码
  • asMap() 将List转化为Map 索引为key,元素为value
1
2
3
4
5
List list34 = [1,2,3,6,7,9];
Map map34 = list34.asMap();
print('$map34,${map34.runtimeType}'); //{0: 1, 1: 2, 2: 3, 3: 6, 4: 7, 5: 9},ListMapView<dynamic>

复制代码
  • shuffle() 将List内元素重新随机排列,并且该函数没有返回值,直接修改该List
1
2
3
4
List list35 = [9,1,2,5,3];
list35.shuffle();
print('$list35'); //[9, 2, 3, 1, 5]
复制代码

--

  • sort() List自身排序
1
2
3
4
5
6
7
List list36 = [1,5,6,7,8,8,9,3,8,9,8];
// list36.sort();
// print(list36); //[1, 3, 5, 6, 7, 8, 8, 8, 8, 9, 9]
list36.sort((a,b) =>(a>b ? 1:-1));
print(list36);//[1, 3, 5, 6, 7, 8, 8, 8, 8, 9, 9]

复制代码
  • sublist(int start, [int end]) 截取List 返回截取后的List
1
2
3
4
5
6
List list37 = [2,5,1,7,9,10];
var result37 = list37.sublist(2);
print('$result37,${result37.runtimeType}'); //[1, 7, 9, 10],List<dynamic>
var result371 = list37.sublist(1,3);
print('$result371,${result371.runtimeType}'); //[5, 1],List<dynamic>
复制代码
  • getRange(int start, int end) 在List 中截取 start-end 之间
1
2
3
4
5
List list38 = [8,9,1,0,3,5];
var resultList38 = list38.getRange(1, 3);
print('$resultList38,${resultList38.runtimeType}'); //(9, 1),SubListIterable<dynamic>

复制代码
  • join()拼接字符,返回String类型
1
2
3
4
List list39 = ['a','cc','fc','xx'];
var resultList39 = list39.join('-');
print('$resultList39,${resultList39.runtimeType}'); //a-cc-fc-xx,String
复制代码
  • 清除所有元素 clear();
1
2
3
4
5
//清除所有元素 clear()
List list40 = ['c','b','z'];
list40.clear(); //清除所有元素 如果是固定长度的则会报异常2
print('$list40,${list40.runtimeType}'); //[],List<dynamic>
复制代码

**
**

CATALOG
  1. 1. Dart基本语法
    1. 1.1.
    2. 1.2. Dart和Java及JavaScript对比
      1. 1.2.1. Dart vs Java
      2. 1.2.2. Dart vs JavaScript
    3. 1.3. Dart基本语法
      1. 1.3.1. 1、常量
        1. 1.3.1.1. final和const都用来命名常量
      2. 1.3.2. 2、变量
        1. 1.3.2.1. 2.1 var可接受任意类型,但是一旦赋值其数据类型不能改变了
        2. 1.3.2.2. 2.2 dynamic和Object
        3. 1.3.2.3. 空安全(null-safety)
      3. 1.3.3. String 基础用法
        1. 1.3.3.0.1. 字符串截取
    4. 1.3.4. double基本用法
    5. 1.3.5. 类型转换
    6. 1.3.6. String to int
    7. 1.3.7. String to double
    8. 1.3.8. double保留两位小数
    9. 1.3.9. List 基本用法
    10. 1.3.10. 3、函数
      1. 1.3.10.1. 3.1函数声明
      2. 1.3.10.2. 3.2 简写一个表达式的函数
      3. 1.3.10.3. 3.3函数作为变量
      4. 1.3.10.4. 3.4函数作为参数传递
      5. 1.3.10.5. 3.5可选位置参数[param1, param2]
      6. 1.3.10.6. 3.6可选命名参数{param1, param2}
    11. 1.3.11. 异步函数
      1. 1.3.11.1. Future.then
      2. 1.3.11.2. Future.catchError/Future.onError
      3. 1.3.11.3. Future.whenComplete
      4. 1.3.11.4. Future.wait
      5. 1.3.11.5. 回调地狱(Callback Hell)
        1. 1.3.11.5.1. 使用Future消除Callback Hell
      6. 1.3.11.6. 使用async/await消除callback hell
  2. 1.4. Stream
  3. 1.5. List
    1. 1.5.1. ‘List’ is deprecated and shouldn’t be used. Use a list literal, [], or the List.filled constructor instead.
  4. 1.6. 默认的 List 构造有什么改动?
    1. 1.6.0.1. The created list is fixed-length if [length] is provided.
    2. 1.6.0.2. The list has length 0 and is growable if [length] is omitted.
  • 1.7.