dart:core 内置类, 集合和其他核心功能. 这个库自动导入到每个Dart程序中
dart:async 支持异步编程, 如Future和Stream类
dart:math 数学常量和函数, 加上随机数的生成
dart:convert JSON和UTF-8数据的编码和反编码
print()方法中传入一个参数(任一对象), 然后打印对象的字符串值(toString()的返回值)
print(anObject);
print('I drink $tea.');
num, int 和 double 类, 有同一个基础的工具来处理数字
使用int和double的parse()方法, 将string转int
assert(int.parse('42') == 42);
assert(int.parse('0x42') == 66);
assert(double.parse('0.50') == 0.5);
使用num的parse()方法, 将string转int或者double
assert(num.parse('42') is int);
assert(num.parse('0x42') is int);
assert(num.parse('0.50') is double);
使用radix参数指定int的基数
assert(int.parse('42', radix: 16) == 66);
- 使用toString()方法, 将int或者double转成string.
- 使用 toStringAsFixed()来明确小数点的精度.
- 使用 toStringAsPrecision()来明确小数点的个数.
// Convert an int to a string.
assert(42.toString() == '42');
// Convert a double to a string.
assert(123.456.toString() == '123.456');
// Specify the number of digits after the decimal.
assert(123.456.toStringAsFixed(2) == '123.46');
// Specify the number of significant figures.
assert(123.456.toStringAsPrecision(2) == '1.2e+2');
assert(double.parse('1.2e+2') == 120.0);
Dart中的字符串是一个不可变的UTF-16序列. 可以使用正则表达式(RegExp 对象)来搜索字符串中的内容或者替换部分字符串
String类定义了split(), contains(), startsWith(), endsWith()等方法
// 是否包含
assert('Never odd or even'.contains('odd'));
// 是否以什么开头
assert('Never odd or even'.startsWith('Never'));
// 是否以什么结尾
assert('Never odd or even'.endsWith('even'));
// 获取字符串所在的位置
assert('Never odd or even'.indexOf('odd') == 6);
提取子字符串, 将字符串分成一个子字符串数组
// Grab a substring.
assert('Never odd or even'.substring(6, 9) == 'odd');
// Split a string using a string pattern.
var parts = 'structured web apps'.split(' ');
assert(parts.length == 3);
assert(parts[0] == 'structured');
// Get a UTF-16 code unit (as a string) by index.
assert('Never odd or even'[0] == 'N');
// Use split() with an empty string parameter to get
// a list of all characters (as Strings); good for
// iterating.
for (var char in 'hello'.split('')) {
print(char);
}
// Get all the UTF-16 code units in the string.
var codeUnitList =
'Never odd or even'.codeUnits.toList();
assert(codeUnitList[0] == 78);
// Convert to uppercase.
assert('structured web apps'.toUpperCase() ==
'STRUCTURED WEB APPS');
// Convert to lowercase.
assert('STRUCTURED WEB APPS'.toLowerCase() ==
'structured web apps');
该方法不适用所有语言. 如土耳其语可能转换失败...
使用trim()
删除左右两边的空格. 使用isEmpty校验一个字符串是否为空
// Trim a string.
assert(' hello '.trim() == 'hello');
// Check whether a string is empty.
assert(''.isEmpty);
// Strings with only white space are not empty.
assert(' '.isNotEmpty);
字符串是不可变的, 只能创建不能更改. 如replaceAll()方法返回了一个新的字符串.
var greetingTemplate = 'Hello, NAME!';
var greeting =
greetingTemplate.replaceAll(RegExp('NAME'), 'Bob');
// greetingTemplate didn't change.
assert(greeting != greetingTemplate);
使用StringBuffer生成一个字符串. StringBuffer不会生成一个新的字符串对象. 除非调用toString()方法. writeAll() 方法有一个可选的第二个参数, 允许指定分隔符(如空格)
var sb = StringBuffer();
sb
..write('Use a StringBuffer for ')
..writeAll(['efficient', 'string', 'creation'], ' ')
..write('.');
var fullString = sb.toString();
assert(fullString ==
'Use a StringBuffer for efficient string creation.');
// Here's a regular expression for one or more digits.
var numbers = RegExp(r'\d+');
var allCharacters = 'llamas live fifteen to twenty years';
var someDigits = 'llamas live 15 to 20 years';
// contains() can use a regular expression.
assert(!allCharacters.contains(numbers));
assert(someDigits.contains(numbers));
// Replace every match with another string.
var exedOut = someDigits.replaceAll(numbers, 'XX');
assert(exedOut == 'llamas live XX to XX years');
可以直接使用RegExp类进行匹配
var numbers = RegExp(r'\d+');
var someDigits = 'llamas live 15 to 20 years';
// Check whether the reg exp has a match in a string.
assert(numbers.hasMatch(someDigits));
// Loop through all matches.
for (var match in numbers.allMatches(someDigits)) {
print(match.group(0)); // 15, then 20
}
lists, sets, and maps
可以使用字面量直接初始化一个List, 也可以使用任意一个List的构造函数进行初始化.
// Use a List constructor.
var vegetables = List();
// Or simply use a list literal.
var fruits = ['apples', 'oranges'];
// Add to a list.
fruits.add('kiwis');
// Add multiple items to a list.
fruits.addAll(['grapes', 'bananas']);
// Get the list length.
assert(fruits.length == 5);
// Remove a single item.
var appleIndex = fruits.indexOf('apples');
fruits.removeAt(appleIndex);
assert(fruits.length == 4);
// Remove all elements from a list.
fruits.clear();
assert(fruits.length == 0);
indexOf()
var fruits = ['apples', 'oranges'];
// Access a list item by index.
assert(fruits[0] == 'apples');
// Find an item in a list.
assert(fruits.indexOf('apples') == 0);
排序sort(). 提供一个排序函数来比较两个对象. (This sorting function must return < 0 for smaller, 0 for the same, and > 0 for bigger)
var fruits = ['bananas', 'apples', 'oranges'];
// Sort a list.
fruits.sort((a, b) => a.compareTo(b));
assert(fruits[0] == 'apples');
指定list中的元素类型
// This list should contain only strings.
var fruits = List<String>();
fruits.add('apples');
var fruit = fruits[0];
assert(fruit is String);
set是一个无序的唯一的元素集合.
var ingredients = Set();
ingredients.addAll(['gold', 'titanium', 'xenon']);
assert(ingredients.length == 3);
// Adding a duplicate item has no effect.
ingredients.add('gold');
assert(ingredients.length == 3);
// Remove an item from a set.
ingredients.remove('gold');
assert(ingredients.length == 2);
使用 contains()和 containsAll() 来检查集合中是否包含某些元素
var ingredients = Set();
ingredients.addAll(['gold', 'titanium', 'xenon']);
// Check whether an item is in the set.
assert(ingredients.contains('titanium'));
// Check whether all the items are in the set.
assert(ingredients.containsAll(['titanium', 'xenon']));
交集
var ingredients = Set();
ingredients.addAll(['gold', 'titanium', 'xenon']);
// Create the intersection of two sets.
var nobleGases = Set.from(['xenon', 'argon']);
var intersection = ingredients.intersection(nobleGases);
assert(intersection.length == 1);
assert(intersection.contains('xenon'));
类似字典或哈希表, 无序的键值对集合.
// Maps often use strings as keys.
var hawaiianBeaches = {
'Oahu': ['Waikiki', 'Kailua', 'Waimanalo'],
'Big Island': ['Wailea Bay', 'Pololu Beach'],
'Kauai': ['Hanalei', 'Poipu']
};
// Maps can be built from a constructor.
var searchTerms = Map();
// Maps are parameterized types; you can specify what
// types the key and value should be.
var nobleGases = Map<int, String>();
add, get, set, remove()
var nobleGases = {54: 'xenon'};
// Retrieve a value with a key.
assert(nobleGases[54] == 'xenon');
// Check whether a map contains a key.
assert(nobleGases.containsKey(54));
// Remove a key and its value.
nobleGases.remove(54);
assert(!nobleGases.containsKey(54));
获取所有key值和value值
var hawaiianBeaches = {
'Oahu': ['Waikiki', 'Kailua', 'Waimanalo'],
'Big Island': ['Wailea Bay', 'Pololu Beach'],
'Kauai': ['Hanalei', 'Poipu']
};
// Get all the keys as an unordered collection
// (an Iterable).
var keys = hawaiianBeaches.keys;
assert(keys.length == 3);
assert(Set.from(keys).contains('Oahu'));
// Get all the values as an unordered collection
// (an Iterable of Lists).
var values = hawaiianBeaches.values;
assert(values.length == 3);
assert(values.any((v) => v.contains('Waikiki')));
使用containsKey() 来判断是否包含某个key. map值可能为空, 因此通过key取值是否为空来判断key是否存在.
var hawaiianBeaches = {
'Oahu': ['Waikiki', 'Kailua', 'Waimanalo'],
'Big Island': ['Wailea Bay', 'Pololu Beach'],
'Kauai': ['Hanalei', 'Poipu']
};
assert(hawaiianBeaches.containsKey('Oahu'));
assert(!hawaiianBeaches.containsKey('Florida'));
putIfAbsent(), 只能map中不存在该key时, 才会添加到map中
var teamAssignments = {};
teamAssignments.putIfAbsent(
'Catcher', () => pickToughestKid());
assert(teamAssignments['Catcher'] != null);
isEmpty 和 isNotEmpty
var coffees = [];
var teas = ['green', 'black', 'chamomile', 'earl grey'];
assert(coffees.isEmpty);
assert(teas.isNotEmpty);
使用forEach(), 实现集合中的每个元素都调用某个方法
var teas = ['green', 'black', 'chamomile', 'earl grey'];
teas.forEach((tea) => print('I drink $tea'));
hawaiianBeaches.forEach((k, v) {
print('I want to visit $k and swim at $v');
// I want to visit Oahu and swim at
// [Waikiki, Kailua, Waimanalo], etc.
});
map()方法, 返回一个集合对象
var teas = ['green', 'black', 'chamomile', 'earl grey'];
var loudTeas = teas.map((tea) => tea.toUpperCase());
loudTeas.forEach(print);
map()返回的可迭代的对象是懒加载的. 当访问对象中的某个元素时, map()函数才会调用.
如果要强制立刻执行map()函数, 使用map().toList() 或 map().toSet()
var loudTeas =
teas.map((tea) => tea.toUpperCase()).toList();
使用Iterable中的where()方法, 来获取所有符合条件的元素. 使用any() 和 every()检查某些元素是否匹配某个条件.
var teas = ['green', 'black', 'chamomile', 'earl grey'];
// Chamomile is not caffeinated.
bool isDecaffeinated(String teaName) =>
teaName == 'chamomile';
// Use where() to find only the items that return true
// from the provided function.
var decaffeinatedTeas =
teas.where((tea) => isDecaffeinated(tea));
// or teas.where(isDecaffeinated)
// 判断是否至少一个元素满足条件
// Use any() to check whether at least one item in the
// collection satisfies a condition.
assert(teas.any(isDecaffeinated));
// 判断是否所有元素满足条件
// Use every() to check whether all the items in a
// collection satisfy a condition.
assert(!teas.every(isDecaffeinated));
URI类提供函数来编码/解码URIs字符串(URL)
encodeFull() 和 decodeFull(), 编码和解码除特殊含义(/, :, &, #)外的所有字符
var uri = 'http://example.org/api?foo=some message';
var encoded = Uri.encodeFull(uri);
assert(encoded ==
'http://example.org/api?foo=some%20message');
var decoded = Uri.decodeFull(encoded);
assert(uri == decoded);
encodeComponent() 和 decodeComponent(), 编码和解码所有字符, 连同特殊字符(/, :, &, #)一起
var uri = 'http://example.org/api?foo=some message';
var encoded = Uri.encodeComponent(uri);
assert(encoded ==
'http%3A%2F%2Fexample.org%2Fapi%3Ffoo%3Dsome%20message');
var decoded = Uri.decodeComponent(encoded);
assert(uri == decoded);
var uri =
Uri.parse('http://example.org:8080/foo/bar#frag');
assert(uri.scheme == 'http');
assert(uri.host == 'example.org');
assert(uri.path == '/foo/bar');
assert(uri.fragment == 'frag');
assert(uri.origin == 'http://example.org:8080');
var uri = Uri(
scheme: 'http',
host: 'example.org',
path: '/foo/bar',
fragment: 'frag');
assert(
uri.toString() == 'http://example.org/foo/bar#frag');
DateTime对象
// Get the current date and time.
var now = DateTime.now();
// Create a new DateTime with the local time zone.
var y2k = DateTime(2000); // January 1, 2000
// Specify the month and day.
y2k = DateTime(2000, 1, 2); // January 2, 2000
// Specify the date as a UTC time.
y2k = DateTime.utc(2000); // 1/1/2000, UTC
// Specify a date and time in ms since the Unix epoch.
y2k = DateTime.fromMillisecondsSinceEpoch(946684800000,
isUtc: true);
// Parse an ISO 8601 date.
y2k = DateTime.parse('2000-01-01T00:00:00Z');
millisecondsSinceEpoch属性, 返回“Unix epoch”—January 1, 1970, UTC至今的毫秒值(时间戳)
// 1/1/2000, UTC
var y2k = DateTime.utc(2000);
assert(y2k.millisecondsSinceEpoch == 946684800000);
// 1/1/1970, UTC
var unixEpoch = DateTime.utc(1970);
assert(unixEpoch.millisecondsSinceEpoch == 0);
Duration类, 计算两个日期的时间差, 也可以获取相差几天前后的日期
var y2k = DateTime.utc(2000);
// Add one year.
var y2001 = y2k.add(Duration(days: 366));
assert(y2001.year == 2001);
// Subtract 30 days.
var december2000 = y2001.subtract(Duration(days: 30));
assert(december2000.year == 2000);
assert(december2000.month == 12);
// Calculate the difference between two dates.
// Returns a Duration object.
var duration = y2001.difference(y2k);
assert(duration.inDays == 366); // y2k was a leap year.
实现Comparable接口来比较两个对象, 通常用来排序(The compareTo() method returns < 0 for smaller, 0 for the same, and > 0 for bigger.)
class Line implements Comparable<Line> {
final int length;
const Line(this.length);
@override
int compareTo(Line other) => length - other.length;
}
void main() {
var short = const Line(1);
var long = const Line(100);
assert(short.compareTo(long) < 0);
}
每个对象都提供一个integer类型的哈希值, 可以做为map中的key. 也可以冲下hashCode的getter方法来自定义的哈希值. 如果重写哈希值, 也需要重写== operator. 通过 == 方法判断对象是都有同一个哈希值. 哈希值不一定要是唯一的
class Person {
final String firstName, lastName;
Person(this.firstName, this.lastName);
// Override hashCode using strategy from Effective Java,
// Chapter 11.
@override
int get hashCode {
int result = 17;
result = 37 * result + firstName.hashCode;
result = 37 * result + lastName.hashCode;
return result;
}
// You should generally implement operator == if you
// override hashCode.
@override
bool operator ==(dynamic other) {
if (other is! Person) return false;
Person person = other;
return (person.firstName == firstName &&
person.lastName == lastName);
}
}
void main() {
var p1 = Person('Bob', 'Smith');
var p2 = Person('Bob', 'Smith');
var p3 = 'not a person';
assert(p1.hashCode == p2.hashCode);
assert(p1 == p2);
assert(p1 != p3);
}
Iterable 和 Iterator 支持 for-in 循环. 当创建的类想要提供Iterators在for-in循环中使用, 可以继承或者实现Iterable. 实现Iterator来实现实际的迭代能力.
class Process {
// Represents a process...
}
class ProcessIterator implements Iterator<Process> {
@override
Process get current => ...
@override
bool moveNext() => ...
}
// A mythical class that lets you iterate through all
// processes. Extends a subclass of [Iterable].
class Processes extends IterableBase<Process> {
@override
final Iterator<Process> iterator = ProcessIterator();
}
void main() {
// Iterable objects can be used with for-in.
for (var process in Processes()) {
// Do something with the process.
}
}
Exceptions和Errors
常见的一对errors
- NoSuchMethodError 找不到方法
- ArgumentError 传入方法的参数错误
通过实现Exception的声明来自定义异常
class FooException implements Exception {
final String msg;
const FooException([this.msg]);
@override
String toString() => msg ?? 'FooException';
}
异步编程通常使用回调函数. Dart提供一种不同方案: Future 和 Stream 对象. Future预设在未来某个时间会提供一个结果值. Stream用来获取一系列值(如事件).
异步编程, 没有必要一定要使用Future 或 Stream, 也可以使用async 和 await关键字
导入库 (dart 2.1中不需要导入)
import 'dart:async';
Future对象, 通常通过异步方法返回. 当预期执行完毕, 它的值就可以使用了.
使用Future的then()方法, 异步处理
runUsingFuture() {
// ...
findEntryPoint().then((entryPoint) {
return runExecutable(entryPoint, args);
}).then(flushThenExit);
}
另一种实现方案, 使用await表达式异步处理, 看起来更像同步代码
runUsingAsyncAwait() async {
// ...
var entryPoint = await findEntryPoint();
var exitCode = await runExecutable(entryPoint, args);
await flushThenExit(exitCode);
}
异步函数可以捕获Futures异常
var entryPoint = await findEntryPoint();
try {
var exitCode = await runExecutable(entryPoint, args);
await flushThenExit(exitCode);
} catch (e) {
// Handle the error...
}
then(), Future执行完后, 提供字符串结果值, 然后执行then后面的代码
HttpRequest.getString(url).then((String result) {
print(result);
});
catchError(), 捕获Future对象可能抛出的异常
HttpRequest.getString(url).then((String result) {
print(result);
}).catchError((e) {
// Handle or ignore the error.
});
then().catchError()是异步版本的try-catch
按一定顺序执行多个异常函数. 如果一个回调的返回值是Future, 那么它注册的then()返回同一个Future. 如果该回调返回其他类型的值, then()会创建一个带该值的Future.
Future result = costlyQuery(url);
result
.then((value) => expensiveWork(value))
.then((_) => lengthyComputation())
.then((_) => print('Done!'))
.catchError((exception) {
/* Handle exception... */
});
以上代码执行顺序如下:
costlyQuery()
expensiveWork()
lengthyComputation()
使用await
try {
final value = await costlyQuery(url);
await expensiveWork(value);
await lengthyComputation();
print('Done!');
} catch (e) {
/* Handle exception... */
}
如果等待多个异步结果值, 才能继续执行代码. 可以使用Future.wait()的静态方法来管理多个Futures, 等待他们执行完毕.
Future deleteLotsOfFiles() async => ...
Future copyLotsOfFiles() async => ...
Future checksumLotsOfOtherFiles() async => ...
await Future.wait([
deleteLotsOfFiles(),
copyLotsOfFiles(),
checksumLotsOfOtherFiles(),
]);
print('Done with all the long steps!');
Stream对象表示一系列数据. 如Html的按钮点击事件就是通过steams来传递的. 读取文件时也是通过steam传递的.
使用Stream的listen()方法订阅文件列表
void main(List<String> arguments) {
// ...
FileSystemEntity.isDirectory(searchPath).then((isDir) {
if (isDir) {
final startingDir = Directory(searchPath);
startingDir
.list(
recursive: argResults[recursive],
followLinks: argResults[followLinks])
.listen((entity) {
if (entity is File) {
searchFile(entity, searchTerms);
}
});
} else {
searchFile(File(searchPath), searchTerms);
}
});
}
使用await for
Future main(List<String> arguments) async {
// ...
if (await FileSystemEntity.isDirectory(searchPath)) {
final startingDir = Directory(searchPath);
await for (var entity in startingDir.list(
recursive: argResults[recursive],
followLinks: argResults[followLinks])) {
if (entity is File) {
searchFile(entity, searchTerms);
}
}
} else {
searchFile(File(searchPath), searchTerms);
}
}
await for 或 listen()
// Find a button by ID and add an event handler.
querySelector('#submitInfo').onClick.listen((e) {
// When the button is clicked, it runs this code.
submitData();
});
onClick属性是 “submitInfo”按钮提供的流对象
如果只需要监听一个事件, 可以使用 first, last, or single. 如果处理事件前想要做一些校验, 可以使用firstWhere(), lastWhere(), or singleWhere()
事件的子集, skip(), skipWhile(), take(), takeWhile(), and where()
使用transform()转换流的数据格式
var lines = inputStream
.transform(utf8.decoder)
.transform(LineSplitter());
异步for循环, 使用try-catch捕获异常
Future readFileAwaitFor() async {
var config = File('config.txt');
Stream<List<int>> inputStream = config.openRead();
var lines = inputStream
.transform(utf8.decoder)
.transform(LineSplitter());
try {
await for (var line in lines) {
print('Got ${line.length} characters from stream');
}
print('file is now closed');
} catch (e) {
print(e);
}
}
Stream API, 使用onError监听错误
var config = File('config.txt');
Stream<List<int>> inputStream = config.openRead();
inputStream
.transform(utf8.decoder)
.transform(LineSplitter())
.listen((String line) {
print('Got ${line.length} characters from stream');
}, onDone: () {
print('file is now closed');
}, onError: (e) {
print(e);
});
...dart:io tour
Math库中包含一些常用功能, 如.正弦/余弦, 最大值/最小值, 常量pi, e. 大部分Math库中函数都是顶级函数.
导入库
import 'dart:math';
// Cosine
assert(cos(pi) == -1.0);
// Sine
var degrees = 30;
var radians = degrees * (pi / 180);
// radians is now 0.52359.
var sinOf30degrees = sin(radians);
// sin 30° = 0.5
assert((sinOf30degrees - 0.5).abs() < 0.01);
这些方法使用的是弧度而不是角度.
assert(max(1, 1000) == 1000);
assert(min(1, -1000) == -1000);
// See the Math library for additional constants.
print(e); // 2.718281828459045
print(pi); // 3.141592653589793
print(sqrt2); // 1.4142135623730951
通过Random类实现随机数
var random = Random();
random.nextDouble(); // Between 0.0 and 1.0: [0, 1)
random.nextInt(10); // Between 0 and 9.
随机布尔值
var random = Random();
random.nextBool(); // true or false
...
JSON 和 UTF-8 转换
导入convert库
import 'dart:convert';
使用jsonDecode(), 将JSON字符串转Dart对象
// NOTE: Be sure to use double quotes ("),
// not single quotes ('), inside the JSON string.
// This string is JSON, not Dart.
var jsonString = '''
[
{"score": 40},
{"score": 80}
]
''';
var scores = jsonDecode(jsonString);
assert(scores is List);
var firstScore = scores[0];
assert(firstScore is Map);
assert(firstScore['score'] == 40);
使用jsonEncode(), 将JSON字符串转Dart对象
var scores = [
{'score': 40},
{'score': 80},
{'score': 100, 'overtime': true, 'special_guest': null}
];
var jsonText = jsonEncode(scores);
assert(jsonText ==
'[{"score":40},{"score":80},'
'{"score":100,"overtime":true,'
'"special_guest":null}]');
只有以下类型: int, double, String, bool, null, List, or Map (key值必须为字符串). List 和 Map 会递归编码.
不能直接编码的对象有两种处理方式. 第一种是调用encode(), 传入第二个参数(一个返回能够直接编码的对象的函数). 另一种是省略第二个参数, 这种情况下会调用对象的toJson()方法.
utf8.decode()
List<int> utf8Bytes = [
0xc3, 0x8e, 0xc3, 0xb1, 0xc5, 0xa3, 0xc3, 0xa9,
0x72, 0xc3, 0xb1, 0xc3, 0xa5, 0xc5, 0xa3, 0xc3,
0xae, 0xc3, 0xb6, 0xc3, 0xb1, 0xc3, 0xa5, 0xc4,
0xbc, 0xc3, 0xae, 0xc5, 0xbe, 0xc3, 0xa5, 0xc5,
0xa3, 0xc3, 0xae, 0xe1, 0xbb, 0x9d, 0xc3, 0xb1
];
var funnyWord = utf8.decode(utf8Bytes);
assert(funnyWord == 'Îñţérñåţîöñåļîžåţîờñ');
To convert a stream of UTF-8 characters into a Dart string, specify utf8.decoder to the Stream transform() method:
var lines = inputStream
.transform(utf8.decoder)
.transform(LineSplitter());
try {
await for (var line in lines) {
print('Got ${line.length} characters from stream');
}
print('file is now closed');
} catch (e) {
print(e);
}
Use utf8.encode() to encode a Dart string as a list of UTF8-encoded bytes:
List<int> encoded = utf8.encode('Îñţérñåţîöñåļîžåţîờñ');
assert(encoded.length == utf8Bytes.length);
for (int i = 0; i < encoded.length; i++) {
assert(encoded[i] == utf8Bytes[i]);
}