# 简介
Dart
语言借鉴了现代编程语言的大部分优点与高级特性,并且它可以运行在自己的虚拟机上,也可以编译成Native Code
运行在Android
或iOS
上(当然他也可以编译成web 代码)。Flutter
选择Dart
作为唯一开发语言的原因是它有以下优点:高效、响应式编程、易学
# 数据类型
Dart
中常见的数据类型有以下几种:
- int:整型,范围在
-2^53 ~ 2^53
- double:64位双精度浮点型
- String:字符串
- bool:布尔
- List:列表
- Set:集合
- Map:键值对映射
# 数字类型
在Dart
中,数字类型有两种形式:int、double
int:整数值不大于64位,具体取决于平台,在本机平台上,值的范围可以是
-2^63
到2^63 - 1
。在 Web 上,整数值表示为 JavaScript 数字(没有小数部分的 64 位浮点值),并且可以是从-2^53
到2^53 - 1
double:64位双精度浮点数,由 IEEE 754 标准指定。
在Dart
中,定义变量为数字可以使用num
,int
,double
。int
与double
都是num
的子类
num a = 18; // num是int和double的父类
int b = 20; // int是num的子类
double c = 20.1; // double是num的子类
print('a---$a---${a.runtimeType}---${a is num}---${a is int}');
print('b---$b---${b.runtimeType}---${b is num}---${b is int}');
print('c---$c---${c.runtimeType}---${c is num}---${c is double}');
print('b/c---${b / c}---${(b / c).runtimeType}---${(b / c) is double}');
从上图我们就能验证出int
与double
都是num
的子类,并且整型乘除浮点型时,结果是浮点型
需要注意的是dart是强类型语言,也就是说当你使用int
将变量声明为整型时,后面你就不能将它改为浮点型,如果想要改变数字类型,你可以使用num
来声明(这里与JS不同)
num a = 18; // num是int和double的父类
int b = 20; // int是num的子类
double c = 20.1; // double是num的子类
a = 10.1; // num可以赋值给double
b = 20.1; // int不能赋值给double
# 字符串类型
在Dart
语言中,字符串支持单引号、双引号以及三引号,其中,单引号和双引号中的双引号需要转义,三引号会将内容原样输出,连注释都无法“逃脱”。另外,${exp}
可以在字符串内插入字符串表达式
String a = 'nanjiu'; // 单引号
String b = "dart"; // 双引号
String c = '''hello world'''; // 三个单引号
String d = """hello world"""; // 三个双引号
print('a---$a---${a.runtimeType}---${a is String}');
print('b---$b---${b.runtimeType}---${b is String}');
print('c---$c---${c.runtimeType}---${c is String}');
print('d---$d---${d.runtimeType}---${d is String}');
String url = 'https://bettersong.github.io/nanjiu/';
print(url.startsWith('https')); // true
print(url.split("://")); // [https, bettersong.github.io/nanjiu/],返回一个数组
print(url.substring(0, 5)); // https
print(url.indexOf('https')); // 0
print(url.length); // 36
print(url.contains('nanjiu')); // true
# 布尔类型
布尔类型只有两个值:true
、false
声明布尔类型使用关键字bool
bool isBoy = true;
bool isOld = false;
if (isBoy) {
print('男孩');
}
# 列表类型
列表可以根据范型盛放多个元素,声明列表类型使用关键字List
,在使用上与JS中的数组类似
List<String> list = ['a', 'b', 'c'];
print(list.length); // 3 // 数组长度
print(list[0]); // a // 获取数组第一个元素
print(list.indexOf('b')); // 1 // 获取元素的索引
list.add('d'); // 添加元素
print(list); // [a, b, c, d]
list.remove('a'); // 删除元素
print(list); // [b, c, d]
list.removeAt(0); // 删除索引为0的元素
print(list); // [c, d]
list.insert(0, 'a'); // 在索引为0的位置插入元素
print(list); // [a, c, d]
list.insertAll(1, ['b', 'e']); // 在索引为1的位置插入元素
print(list); // [a, b, e, c, d]
list.replaceRange(1, 3, ['f', 'g']); // 替换索引为1到3的元素,不包含3
print(list); // [a, f, g, c, d]
list.fillRange(1, 3, 'h'); // 替换索引为1到3的元素,不包含3
print(list); // [a, h, h, c, d]
print(list.isEmpty); // false // 判断数组是否为空
print(list.isNotEmpty); // true // 判断数组是否不为空
print(list.reversed); // (d, c, h, h, a) // 反转数组, 返回一个迭代器
print(list.reversed.toList()); // [d, c, h, h, a] // 反转数组并转换为数组
list.sort(); // 排序
# 集合类型
集合与列表的区别在于集合中的元素不能重复,这点与JS类似。声明集合使用关键字Set
添加重复元素时会返回false,表示没加进去
Set<String> set = {'a', 'b', 'c'};
print(set.length); // 3 // 集合长度
print(set.contains('a')); // true // 判断是否包含某个元素
print(set.add('d')); // true // 添加元素
print(set); // {a, b, c, d}
print(set.add('a')); // false // 不允许重复添加元素
print(set); // {a, b, c, d}
print(set.remove('a')); // true // 删除元素
print(set); // {b, c, d}
print(set.isEmpty); // false // 判断集合是否为空
print(set.isNotEmpty); // true // 判断集合是否不为空
print(set.toList()); // [b, c, d] // 转换为列表
print(set.toList().reversed); // (d, c, b) // 反转列表
print(set.toList().reversed.toList()); // [d, c, b] // 反转列表并转换为列表
# 映射类型
Map作为若干个键值对的容器,享有映射之名。要注意一个Map对象的键不能重复,值可以重复。在Dart语法中,Map比较灵活,和列表之间相互转化非常方便
Map<String, String> map = {'a': '1', 'b': '2', 'c': '3'};
print(map.length); // 3 // 映射长度
print(map['a']); // 1 // 获取key为a的值
print(map.containsKey('a')); // true // 判断是否包含key为a的值
print(map.containsValue('1')); // true // 判断是否包含值为1的值
map['d'] = '4'; // 添加元素
print(map); // {a: 1, b: 2, c: 3, d: 4}
map['d'] = '5'; // 修改元素
print(map); // {a: 1, b: 2, c: 3, d: 5}
map.remove('d'); // 删除元素
print(map); // {a: 1, b: 2, c: 3}
print(map.isEmpty); // false // 判断映射是否为空
print(map.isNotEmpty); // true // 判断映射是否不为空
print(map.keys); // (a, b, c) // 获取所有的key,返回一个迭代器
print(map.values); // (1, 2, 3) // 获取所有的值,返回一个迭代器
print(map.keys.toList()); // [a, b, c] // 获取所有的key并转换为列表
print(map.values.toList()); // [1, 2, 3] // 获取所有的值并转换为列表
# 变量与常量
# 定义变量
与JS
不同的是,Dart
是强类型语言,定义变量除了上面那种以数据类型关键字以外还可以使用var
关键字。
通过var关键字定义变量,不指定类型,由系统自动推断,赋值后类型确定,不能再次改变
var a;
var name = 'nanjiu';
print(a); // null
print(name); // nanjiu
print(a.runtimeType); // Null
print(name.runtimeType); // String
🏠提示:如果只是用var声明变量,未赋值,那么该变量的数据类型是可以修改的。如果声明的同时进行赋值,那么该对象的类型就是固定的,不可修改
var name = 'nanjiu'; // 已经初始化了,不能改为其他类型
print(name); // nanjiu
print(name.runtimeType); // String
name = 10; // 不能将int赋值给String.
建议在编写代码时显式定义变量,提高代码的可读性。
# 定义常量
在Dart
中,使用const
或final
关键字来定义常量,当对final或者const修饰的量再进行赋值时就会报错
var a;
{
const a = 1;
print(a); // 1
final b = 2;
print(b); // 2
}
print(a); // null
它也有作用域,不能再同意作用域中重复定义.
两者的区别在于const是编译期的常量,final是运行时常量。也就是在运行期间才能获取到的常量必须用final定义
# 运算符及条件表达式
Dart内置了一些基本运算符,比如加(+)、减(–)、乘(*)、除(/)、取余(%)等,除了这些基础内容,还有以下这些:
# 判定和转换类型
- is:某个变量是否是指定的类型,若是则返回true
- is!:与
is
相反 - as:用于类型转换,要注意这种转换可能发生类型转换异常
int a = 10;
if (a is int) {
print('a是int类型');
String b = a.toString();
print(b);
print(b.runtimeType); // String
}
String c = 'nanjiu';
print((c as Comparable<String>).compareTo('o')); // -1
# 三元表达式
这点与JS
中类似,condition?expr1:expr2
表示如果condition
为true则执行expr1
,否则执行expr2
bool flag = true;
int a = flag ? 1 : 0;
# 非空条件判断
expr1??expr2
表示如果expr1
不为null
则返回expr1
,否则返回expr2
String a = 'nanjiu';
var b;
String c = b ?? a;
print(c); // nanjiu
# 条件调用
obj?.op()
表示当obj
为null
就不调用op()
方法,返回null
,对象非空时就会正常调用op()
方法
var a = 10;
a = null;
print(a?.abs()); // null
# 级联运算符
正常情况下,我们通过“.”操作符访问对象的方法。如果想链式调用,在Java中我们需要在方法中返回自身,而在Dart中不需要,通过“..”操作符可以达到同样效果,用法如下:
Person()..setName()..setAge();
# if...else
String b = 'nanjiu';
if(b is String) {
print('b是String类型');
}else {
print('b不是String类型');
}
# switch
String b = 'nanjiu';
switch (b) {
case 'nanjiu':
print('nanjiu');
break;
case 'hello':
print('hello');
break;
default:
print('default');
}
# for循环
List list = ['a', 'b', 'c'];
for (var i = 0; i < list.length; i++) {
print(list[i]);
}
# while、do...while循环
int a = 0;
while (a < 10) {
print(a);
a++;
}
int b = 0;
do {
print(b);
b++;
} while (b < 10);
# List遍历
可以使用for
、for...in
、forEach
遍历List
集合
List list = ['a', 'b', 'c'];
for (int i = 0; i < list.length; i++) {
print(list[i]);
}
for (var item in list) {
print(item);
}
list.forEach((element) {
print(element);
});
# Map遍历
Map
是键值对(key-value)形式的集合,可以使用for
和Map.forEach
遍历Map
Map<String, String> map = {'a': '1', 'b': '2', 'c': '3'};
for (int i = 0; i < map.keys.length; i++) {
print(map[map.keys.toList()[i]]);
}
for (var item in map.keys) {
print(map[item]);
}
map.forEach((key, value) {
print('$key---$value');
});
# 函数
函数作为一个逻辑单元的封装,是实现代码复用最直接,最有效的方式。Dart
中的函数与其他语言最大的区别是它没有关键字(如JavaScript中的function,Kotlin中的fun,Swift中的func等),Dart直接使用函数名即可,函数名前是返回值的类型。
# 普通函数
函数包含返回类型、函数名称、参数类型、参数名称
[返回类型] 函数名称 (参数类型 参数1, 参数类型 参数2, ...) {}
比如定义一个加法函数:
int add(int a, int b) {
return a + b;
}
print(add(1, 2)); // 3
返回类型虽然可以省略,但强烈建议不要省略,提高代码的可读性。
# 可选参数
可以参数可以使用[]
或{}
指定若干可选参数,区别在于使用[]
时入参必须按顺序来排列
int add2([int a = 0, int b = 0]) {
return a + b;
}
add2(1, 2); // 3
当参数过多并且希望可以不按入参顺序传入参数,这时就可以使用{}
映射参数,使用方式类似键值对,通过键名来进行传参
int add3(int a, {int b = 0, int c = 0}) {
return a + b + c;
}
add3(1, b: 2, c: 3); // 6
# 匿名函数
正常情况下我们创建的函数都是有函数名的,我们也可以创建没有名字的函数,这种函数称为匿名函数,也叫lambda表达式或者闭包。匿名函数用法如下:
var func = (int a, int b) {
return a + b;
};
func(1, 2); // 3
# 箭头函数
Dart
中的箭头函数与JS
中的箭头函数不一样,当普通函数只有一个语句时,我们可以省略{}
,使用=>
进行缩写,如下所示:
int add4(int a, int b) => a + b;
add4(1, 2); // 3
# 函数类型
Dart语言是完全面向对象的语言,就连函数本身都是对象。对象的运行时类型可以通过XXX.runtimeType
获取。
print(add4.runtimeType); // (int, int) => int
与JS
一样,函数同样可以作为变量,也就可以作为函数参数
int add5(int a, int b, Function square) {
return square(a) + square(b);
}
int square(int a) {
return a * a;
}
var res = add5(1, 2, square);
print(res); // 5
# 类
在Dart
中,使用关键字class
定义类
# 构造函数
在Dart
中,实现构造函数只需要在类中定义一个与类同名的函数即可
class Person {
String name;
int age;
Person(this.name, this.age);
void printInfo() {
print('name---$name---age---$age');
}
}
这其实是一种简写方式,它等同于下面这种写法
class Person {
String name;
int age;
// Person(this.name, this.age);
Person(String name, int age) {
this.name = name;
this.age = age;
}
void printInfo() {
print('name---$name---age---$age');
}
}
当不需要在构造函数中做特殊处理的时候,建议使用上面那种简单的方式,如果没有定义构造函数,则会有一个默认的无参构造函数。另外,也可以通过如下方式构造函数:
class Person {
String name;
int age;
Person(this.name, this.age);
Person.loadData(this.name, this.age);
void printInfo() {
print('name---$name---age---$age');
}
}
# 类的运算符重载
如果我们想让两个对象相加,进而得出相关属性的相加,默认情况下是没有对象相加这个功能的,这时就可以用重载“+”运算符完成上述功能
class Person {
String name;
int age;
Person(this.name, this.age)
: assert(age > 0, 'age不能小于0'),
assert(name != null, 'name不能为null');
operator +(Person p) {
return Person(name + p.name, age + p.age);
}
}
void main() {
Person p = Person('nanjiu', 18);
Person p2 = Person('hello', 20);
Person p3 = p + p2;
print(p3.name); // nanjiuhello
}
注意:Assert是检查语句,这种形式是类的初始化列表形式
# 继承
extends
类的继承使用关键字extends
,使用该关键字只能继承一个类
class Person {
String name;
int age;
Person(this.name, this.age)
: assert(age > 0, 'age不能小于0'),
assert(name != null, 'name不能为null');
operator +(Person p) {
return Person(name + p.name, age + p.age);
}
void sayName() {
print('Person: name---$name');
}
}
class Man extends Person {
Man(String name, int age) : super(name, age); // 调用父类的构造函数
}
void main() {
Person man = Man('nanjiu--', 18);
man.sayName(); //Person: name---nanjiu--
}
如果子类想要重写父类的方法需要使用@override
关键字,调用父类方法要使用super
,子类可以访问父类所有的变量和方法,因为Dart
中没有public
、private
关键字
with
使用with
关键字来继承多个类
class Person1 {}
class Person2 {}
class Person3 {}
class Person4 extends Person1 with Person2,Person3{}
# implements
Dart
中没有interface
关键字定义接口,但是Dart
中的每一个类都是一个隐式的接口,这个接口里包含类的所有方法和变量,因此,当我们实现一个类时,需要在子类里面实现其方法和变量
class Person {
String name;
int age;
Person(this.name, this.age) : assert(age > 0, 'age不能小于0');
String get getName {
return name;
}
}
class Person1 implements Person {
int age;
String name;
Person1(this.name, this.age);
String get getName {
return name;
}
}
# abstract
abstract关键字是定义抽象类的,子类继承抽象类时要实现其抽象方法
abstract class Person {
String name;
int age;
Person(this.name, this.age) : assert(age > 0, 'age不能小于0');
String get getName {
return name;
}
}
class Person1 extends Person {
int age;
String name;
Person1(this.name, this.age) : super('', 0);
String get getName {
return name;
}
}