dart语言基础

2023/9/9 flutter

# 简介

Dart语言借鉴了现代编程语言的大部分优点与高级特性,并且它可以运行在自己的虚拟机上,也可以编译成Native Code运行在AndroidiOS上(当然他也可以编译成web 代码)。Flutter选择Dart作为唯一开发语言的原因是它有以下优点:高效、响应式编程、易学

# 数据类型

Dart中常见的数据类型有以下几种:

  • int:整型,范围在-2^53 ~ 2^53
  • double:64位双精度浮点型
  • String:字符串
  • bool:布尔
  • List:列表
  • Set:集合
  • Map:键值对映射

# 数字类型

Dart中,数字类型有两种形式:intdouble

  • int:整数值不大于64位,具体取决于平台,在本机平台上,值的范围可以是-2^632^63 - 1。在 Web 上,整数值表示为 JavaScript 数字(没有小数部分的 64 位浮点值),并且可以是从 -2^532^53 - 1

  • double:64位双精度浮点数,由 IEEE 754 标准指定。

Dart中,定义变量为数字可以使用num,int,doubleintdouble都是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}');

从上图我们就能验证出intdouble都是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

# 布尔类型

布尔类型只有两个值:truefalse

声明布尔类型使用关键字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中,使用constfinal关键字来定义常量,当对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()表示当objnull就不调用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遍历

可以使用forfor...inforEach遍历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)形式的集合,可以使用forMap.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中没有publicprivate关键字

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;
  }
}