Dart入门
- Dart安装
- 创建项目
- 安装依赖(以`http`为例)
- 依赖库查询地址
- 添加依赖
- 编写运行示例
- dart常用命令
- 引用核心库、自定义库、第三方库
- 数据类型
- Numbers (int, double)
- Strings (String)
- Booleans (bool)
- Lists (List)
- Maps (Map)
- Sets (Set)
- Null (null)
- Records ((value1, value2, ...))
- Runes (Runes)
- Symbols (Symbol)
- 类型判断
- 单引号、双引号、三引号
- 运算符
- 除以取整(`~/`、`~/=`)
- 取余(`%`、`%=`)
- 如果空(`??`、`??=`)
- 判断类型(`is`、`is!`)
- 类型断言(`as`)
- 条件成员访问(`?`)
- Null断言运算符(`!`)
- 级联表示法(`..`)
- `late`
- 常量`final`和`const`
- 相同点
- 不同点
- 函数
- 基本函数
- 可选参数函数
- 命名参数函数
- 箭头函数
- 自执行函数
- 参数必传函数
- 类
- 示例
- 静态属性和方法
- 泛型
- 多态
- `get`和`set`
- 多接口继承
- `mixin`
- 常量构造函数
Dart安装
$ brew install dart-sdk
$ dart --version
创建项目
$ dart create -t console dart_app
$ cd dart_app
$ dart analyze # 分析项目的Dart源代码
$ dart run ./bin/dart_app.dart # 或 dart ./bin/dart_app.dart 或 dart run
安装依赖(以http
为例)
依赖库查询地址
https://pub.dev/
添加依赖
$ dart pub add http
编写运行示例
import 'dart:convert' as convert;
import 'package:http/http.dart' as http;
void main() async {
var url = Uri.https('www.example.com');
var response = await http.get(url);
if (response.statusCode == 200) {
print(response.body);
} else {
print(response.statusCode);
}
}
dart常用命令
# 添加依赖
$ dart pub add date_format:'^2.0.5'
# 删除依赖
$ dart pub remove http
# 清空全局的本地缓存
$ dart pub cache clean
# 获取所有在当前工作目录下的 pubspec.yaml 文件中列出的依赖项,以及这些依赖项的 间接依赖项,必要时下载该依赖项并更新缓存
$ dart pub get
# 获取当前工作目录下 pubspec.yaml 文件中列出的所有依赖项以及它们 间接依赖项 的最低版本
$ dart pub downgrade
# 识别过期的包依赖项,并获得如何更新它们的建议
$ dart pub outdated
# 与 dart pub get 命令一样,都是用于获取依赖项的。不同的是该命令会忽略掉任何已存在的 lockfile 文件,因此 Pub 可以获取所有依赖项的最新版本
$ dart pub upgrade
引用核心库、自定义库、第三方库
import 'dart:math'; // 核心库
import '../lib/dart_app.dart'; // 自定义库
import 'package:date_format/date_format.dart'; // 第三方库
示例
import 'dart:math'; // 引用全部方法
import 'dart:math' as Math; // 别名
void main() {
print(Random().nextBool());
print(Math.Random().nextBool());
print(max(0, 0));
}
import 'dart:math' show max; // 只引入max
void main() {
print(max(0, 1));
print(Random().nextBool()); // Error
}
import 'dart:math' hide max,Random; // 只引入除了max和Random
void main() {
print(Point(0, 1));
print(max(0, 1)); // Error
print(Random().nextBool()); // Error
}
数据类型
int、double、String、bool、List、Map、Set、null、(value1, value2, …)、Runes、Symbol
Numbers (int, double)
void main() {
var a = 123; // var会自动推断类型为int
int b = 123;
double c = 1;
double d = 1.23;
b = d; // Error: “double”类型的值不能分配给“int”类型的变量.
d = b; // Error: “int”类型的值不能分配给“double”类型的变量.
print(0 / 0); // NaN
print((0 / 0).isNaN); // true
}
Strings (String)
void main() {
var a = "1a2b3c"; // var会自动推断类型为String
String b = "1.2b3c";
print(a.length); // 6
print(int.parse(a));
print(double.parse(b));
String c = "123.4";
print(double.parse(c)); // 123.4
}
Booleans (bool)
void main() {
bool a = true;
bool b = false;
}
Lists (List)
void main() {
List l1 = [];
l1.add("Lee");
l1.add("男");
l1.add("你好");
l1.add(18);
print(l1); // [Lee, 男, 你好, 18]
print(l1.first); // Lee 返回第一个元素
print(l1.last); // 18 返回列表中的最后一个元素
print(l1.isEmpty); // false 如果集合没有元素,则返回true
print(l1.isNotEmpty); // true 如果集合至少包含一个元素,则返回true
print(l1.length); // 4
print(l1.reversed); // (18, 你好, 男, Lee) 以相反的顺序返回包含列表值的可迭代对象
List l2 = [];
print(l2.isEmpty); // true
print(l2.isNotEmpty); // false
List l3 = ["abc"];
print(l3.single); // abc 检查列表是否只有一个元素并返回它,否则抛出异常
List<int> l4 = [1, 2, 3];
l4.add("abc"); // Error: 无法将参数类型“String”分配给参数类型“int”
List l5 = ["Lee", 18, "男", "Tom", 20, "男"];
l5.remove("男"); // [Lee, 18, Tom, 20, 男]
// 创建固定长度的数组
List l6 = List.filled(3, "Lee"); // ["Lee", "Lee", "Lee"]
l6[1] = "Tom"; // [Lee, Tom, Lee]
l6.length = 4; // Error: 不允许修改数组长度
l6.add("Tom"); // Error: 不允许修改数组长度
l6.remove("Lee"); // Error: 不允许修改数组长度
}
Maps (Map)
void main() {
Map data = {
1: "一",
"two": 2,
"three": true
// a: true, // Error: 找不到变量a
};
print(data); // {1: 一, two: 2, three: true}
print(data["two"]); // 2
data["three"] = false;
print(data); // {1: 一, two: 2, three: false}
}
Sets (Set)
void main() {
Set data = {1, 2, 3};
print(data); // {1, 2, 3}
data.add(4);
print(data); // {1, 2, 3, 4}
}
void main() {
Set<String> data = {"苹果", "香蕉"};
print(data); // {苹果, 香蕉}
print(data.toList()); // [苹果, 香蕉]
}
Null (null)
空安全(null safety)
空安全会在编译期防止意外访问 null 变量的错误的产生
void main() {
String name = "Lee";
name = null; // Error: 无法将值“null”分配给“String”类型的变量,因为“String”不可为null
}
允许为空
void main() {
String? name = "Tom";
name = null;
}
Records ((value1, value2, …))
void main() {
var record = (1, b: 2, c: 3, d: true, null, 5, [7], {8}, {"i": 9});
print(record); // (1, null, 5, [7], {8}, {i: 9}, b: 2, c: 3, d: true)
print(record.$1); // 1
print(record.$2); // null
print(record.$3); // 5
print(record.$4); // [7]
print(record.$5); // {8}
print(record.$6); // {i: 9}
print(record.b); // 2
print(record.c); // 3
print(record.d); // true
var (lat, lng) = (1000, 2000);
print(lat); // 1000
print(lng); // 2000
}
Runes (Runes)
void main() {
Runes input = new Runes("\u{1f605}");
print(new String.fromCharCodes(input));
}
Symbols (Symbol)
void main() {
Symbol obj = new Symbol("Lee");
print(obj); // Symbol("Lee")
}
类型判断
void main() {
var a = 123;
print(a is int); // true
print(a is double); // false
}
单引号、双引号、三引号
void main() {
String msg1 = '你好,我叫'
'Lee';
String msg2 = "你好,我叫"
"Lee";
String msg3 = '''你好,我叫
Lee''';
String msg4 = """你好,我叫
Lee""";
print(msg1); // 你好,我叫Lee
print(msg2); // 你好,我叫Lee
print(msg3); // 你好,我叫\n\tLee
print(msg4); // 你好,我叫\n\tLee
}
运算符
除以取整(~/
、~/=
)
void main() {
print(3 ~/ 2); // 1
print(1.2 * 2 ~/ 1); // 2
}
void main() {
var a = 123;
a ~/= 2;
print(a); // 61
}
取余(%
、%=
)
void main() {
print(3 % 2); // 1
print(1.2 * 2 % 2); // 0.3999999999999999 精度问题
}
void main() {
var a = 3;
a %= 2;
print(a); // 1
}
如果空(??
、??=
)
注意:
??
与||
不同
import 'dart:math';
void main() {
var a = Random().nextBool() ? null : 1;
var b = a ?? 2;
print('$a, $b'); // 1, 1 OR null, 2
print(!!null || false); // Error: 无法将值“null”分配给“bool”类型的变量,因为“bool“不可为null”
print(!!0 || false); // Error: 不能将“int”类型的值分配给“bool”类型的变量
}
import 'dart:math';
void main() {
var a = Random().nextBool() ? null : 1;
a ??= 2;
print(a); // 1 OR 2
}
判断类型(is
、is!
)
import 'dart:math';
void main() {
var a = Random().nextBool() ? "Lee" : 1;
print(a is int); // false OR true
print(a is String); // true OR false
print(a is! bool); // true
}
类型断言(as
)
import 'dart:math';
void main() {
var str = "Hello, Lee!!!";
String msg = str as String;
print(msg.length); // 13
var a = Random().nextBool() ? "Lee" : 1;
print((a as String).length); // 3 OR Error: 类型“int”不是类型强制转换中类型“String”的子类型
}
条件成员访问(?
)
示例1
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id="confirm">Confirm</button>
<script src="./out.js"></script>
</body>
</html>
import 'dart:html';
void main() {
var confirm = querySelector('#confirm');
// confirm.onClick.listen((e) => {window.alert('Confirmed!')}); // Error: 无法在“Element?”上访问属性“onClick”因为它可能为空
confirm?.onClick.listen((e) => {window.alert('Confirmed!')}); // ok
}
$ dart compile js ./bin/dart_app.dart
示例2
import 'dart:math';
void main() {
String? name = Random().nextBool() ? "Lee" : null;
print(name?.length); // 3 OR null
}
Null断言运算符(!
)
import 'dart:math';
void main() {
String? name = Random().nextBool() ? "Lee" : null;
print(name!.length); // 3 OR Error: Null值上使用的Null检查运算符
}
级联表示法(..
)
class Person {
String name = "Lee";
int age = 18;
void say(){
print("$name, $age");
}
}
void main() {
Person person = Person();
person
..name = "Tom"
..age = 20
..say(); // Tom, 20
}
late
在运行时而非编译时对变量进行约束
定义一个无初始值的非空字段
class Person {
// String name; // Error: 字段“name”应初始化,因为其类型“String”不允许为null
String? name; // ok
late String name; // ok
}
void main() {}
常量final
和const
相同点
- 用来定义常量,一旦赋值不可改变,并且类型声明可以忽略
void main() {
final a = "Lee";
const b = "Tom";
a = "AAA"; // Error
b = "BBB"; // Error
}
不同点
final
变量的初始值可以在编译时确定,也可以在运行时确定;const
变量的初始值只能是编译时确定的值
void main() {
final a = DateTime.now();
const b = DateTime.now(); // Error: 不能调用需要常量表达式的非“const”构造函数
}
- 内存中的创建。相同的值,
final
变量会重复创建,const
会引用同一份值
void main() {
final a = {"name": "Lee"};
final b = {"name": "Lee"};
const c = {"name": "Lee"};
const d = {"name": "Lee"};
print(a == b); // false
print(b == c); // false
print(c == d); // true
}
const
变量的不可变性是嵌套的,final
不是
void main() {
final a = {"name": "Lee"};
const b = {"name": "Lee"};
a["name"] = "Tom"; // ok
b["name"] = "Tom"; // Error: 无法修改不可修改的映射
}
const
初始化必须赋值,final
初始化可以先不赋值,调用前必须赋值
void main() {
final a;
print(a); // Error: 必须先分配最终变量“a”,然后才能使用它
const b; // Error: 必须初始化常量变量“b”
}
- 在类中,
final
可直接修饰变量;const
需要使用static
修饰变量
class Person {
final a = DateTime.now();
const b = "Lee"; // Error: 只有静态字段才能声明为常量
}
函数
基本函数
String say1(String name, int age) {
String msg = "$name $age";
return msg;
}
// 函数表达式
Function say2 = (String name, int age) {
String msg = "$name $age";
return msg;
};
void main() {
var msg1 = say1("Lee", 18);
var msg2 = say2("Tom", 20);
print(msg1); // Lee 18
print(msg2); // Tom 20
}
可选参数函数
String say(String name, [int? age]) {
String msg = "$name $age";
return msg;
}
void main() {
print(say("Lee")); // Lee null
print(say("Tom", 18)); // Tom 18
}
String say(String name, [int age = 18]) {
String msg = "$name $age";
return msg;
}
void main() {
var msg = say("Lee");
print(msg); // Lee 18
}
命名参数函数
String say(String name, int age, {String? sex, String? desc}) {
return "$name $age $sex $desc";
}
void main() {
print(say("Lee", 18, sex: "男")); // Lee 18 男 null
print(say("Lee", 18)); // Lee 18 null null
print(say("Lee", 18, desc: "不简单啊!")); // Lee 18 null 不简单啊!
print(say("Lee", 18, desc: "不简单啊!", sex: "男")); // Lee 18 男 不简单啊!
}
箭头函数
注意:只允许存在一条语句
var method1 = (String name) => {print("Hi, $name!!!")};
var method2 = (String name) => print("Hi, $name!!!");
void main() {
method1("Lee"); // Hi, Lee!!!
method2("Tom"); // Hi, Tom!!!
}
自执行函数
var msg1 = ((String name) {
return "Hi, $name!!!";
}("Lee"));
var msg2 = ((String name) {
return "Hi, $name!!!";
})("Tom");
void main() {
print(msg1); // Hi, Lee!!!
print(msg2); // Hi, Tom!!!
}
参数必传函数
String say1(String name, {required int age}) {
String msg = "$name $age";
return msg;
}
String say2(String name, {int? age}) {
String msg = "$name $age";
return msg;
}
void main() {
print(say1("Lee")); // Error: 参数“age”为必填参数
print(say1("Lee", age: 18)); // Lee 18
print(say2("Lee")); // Lee null
print(say2("Lee", age: 18)); // Lee 18
}
类
示例
lib/dart_app.dart
// 父类-利用抽象类实现接口
abstract class IParent {
late String lastName;
String say(int age);
}
// 子类接口,继承自父类-利用抽象类实现接口
abstract class ISub extends IParent {
late String firstName;
late String _fullName;
}
// 父类的实现
class Parent implements IParent {
late String lastName;
// 父类构造方法
Parent(this.lastName);
String say(int age) {
print("My lastName is $lastName, $age years old!!!");
return lastName;
}
}
// 子类的实现,继承自父类
class Sub extends Parent implements ISub {
late String firstName;
late String _fullName;
// 子类构造方法
Sub(this.firstName, String lastName) : super(lastName) {
_fullName = "$firstName·${this.lastName}";
}
// 重写say方法
String say(int age) {
super.say(age);
String msg = "Hi, My name is $firstName·$lastName, FullName is $_fullName!!!";
return msg;
}
}
bin/dart_app.dart
import 'package:dart_app/dart_app.dart';
void main() {
Sub lee = Sub("Prosper", "Lee");
print(lee.firstName); // Prosper
print(lee.lastName); // Lee
// print(lee._fullName); // Prosper·Lee 注意:同一文件下可访问; 不同文件下_fullName为私有属性,不可访问;
print(lee.say(18)); // My lastName is Lee, 18 years old!!! Hi, My name is Prosper·Lee, FullName is Prosper·Lee!!!
}
静态属性和方法
class Person {
static late String name;
static setName(String n){
name = n;
}
}
void main() {
Person.setName("Lee");
print(Person.name); // Lee
}
泛型
class CustomList<T> {
List list = <T>[];
List add<T>(T value) {
list.add(value);
return list;
}
}
void main() {
CustomList<String> l1 = CustomList<String>();
l1.add("Lee");
l1.add("Tom");
print(l1.list); // [Lee, Tom]
CustomList l2 = CustomList();
l2.add("Lee");
l2.add(18);
print(l2.list); // [Lee, 18]
}
多态
允许将子类类型的指针赋值给父类类型的指针, 同一个函数调用会有不同的执行效果
class Animal {
void eat() {
print('Animal eating...');
}
}
class Cat extends Animal {
void eat() {
print('Cat eating...');
}
void run() {
print('Cat running...');
}
}
class Dog extends Animal {
void eat() {
print('Dog eating...');
}
void run() {
print('Dog running...');
}
}
void main() {
Animal animal = Animal();
Animal cat = Cat();
Animal dog = Dog();
animal.eat(); // Animal eating...
cat.run(); // Error: Animal类中没有定义run方法;
cat.eat(); // Cat eating...
dog.eat(); // Dog eating...
}
get
和set
class Person {
String name = "Lee";
get GetName {
return name;
}
set SetName(String value) {
name = value;
}
}
void main() {
Person person = Person();
print(person.name); // Lee
print(person.GetName); // Lee
person.SetName = "Tom";
print(person.name); // Tom
print(person.GetName); // Tom
}
多接口继承
abstract class People {
void say();
}
abstract class Fish {
void swim();
}
class Person implements People, Fish {
void say() {
print("say...");
}
void swim() {
print("swim...");
}
}
void main() {
Person person = Person();
person.say(); // say...
person.swim(); // swim...
}
mixin
实现多继承(报错)
class A {
printA() {
print("A");
}
}
class B {
printB() {
print("B");
}
}
class C extends A, B {} // Error: 每个类定义最多可以有一个extends子句
使用mixin
mixin class A {
printA() {
print("A");
}
say() {
print("say A");
}
}
mixin class B {
printB() {
print("B");
}
say() {
print("say B");
}
}
class C with B, A {}
class D extends B with A {}
void main() {
C c = C();
c.printA(); // A
c.printB(); // B
c.say(); // say A 覆盖B
D d = D();
d.printA(); // A
d.printB(); // B
d.say(); // say A 覆盖B
}
常量构造函数
常量构造函数是指返回不可变对象的构造函数,内存中只保留了一个对象
// 常量构造函数
class Person {
final String name;
const Person(this.name);
}
// 非常量构造函数
class Animal {
String name;
Animal(this.name);
}
void main() {
var a = Object();
var b = Object();
print(identical(a, b)); // false 检查两个引用是否指向同一个对象
final p1 = const Person("Lee");
final p2 = const Person("Lee");
final p3 = const Person("Tom");
print(identical(p1, p2)); // true
print(identical(p1, p3)); // false
final a1 = Animal("Lee");
final a2 = Animal("Lee");
final a3 = Animal("Tom");
print(identical(a1, a2)); // false
print(identical(a1, a3)); // false
}