携手创作,一同生长!这是我参加「日新计划 8 月更文挑战」的第1天,点击查看活动详情

前言

很高兴遇见你~

在本系列的上一篇文章中,咱们介绍了 Flutter 开发环境搭建,以及运用 AndroidStudio 运转你的榜首个 Flutter 项目,体会了热重载。还没有看过上一篇文章的朋友,主张先去阅览Flutter 系列(一):运转你的榜首个 Flutter 运用,在我看来,Dart 在设计时应该是借鉴了百家言语之所长:Java,Kotlin等:

1、在静态语法方面,如:类型界说,办法声明,泛型等,和 Java 十分相似

2、一些语法特性,如:函数式特性,空安全,函数默许值等,和 Kotlin 十分相似

3、Dart 还有一些自己独创的语法,如:命名结构办法,级联操作符等

总之,了解之后,你会发现 Dart 是一门十分有意思的编程言语,接下来就让咱们一同进入 Dart 的语法学习吧

留意: Dart 语法和 Java,Kotlin 真的很像,尤其是 Java。其他假如对 Kotlin 语法不熟的,可以去看我的其他一篇文章:”Kotlin”系列: 一、Kotlin入门

一、变量和办法

1.1、变量

1)、Dart 可以显现指明类型来声明一个可变的变量。且指明的类型分为可空和非空

2)、Dart 也可以运用 var 关键字来声明一个可变的变量,此刻编译器会依据变量初始值主动揣度类型

3)、Dart 运用 final 关键字来声明一个不行变的变量,且可以替代 var 或加在类型前面

4)、Dart 中变量假如对错空类型,那么有必要给一个默许值,不然无法编译经过。假如是可空类型,默许值都为 null

5)、Dart 中每一行代码都要加 ; ,走回头路了,有点鸡肋

//1、Dart 可以显现指明类型来声明一个可变的变量。且指明的类型分为可空和非空
//1.1、类比 Java,显现指明类型来声明一个可变的变量
//dart 写法:
int a = 10;
bool b = true;
​
//Java 写法
int a = 10;
boolean b = true;
​
//1.2、指明的类型分为可空和非空
//dart 写法
int? a = 10;
bool? b = true;
​
//kotlin 写法
var a: Int? = 10
var b: Boolean? = true
 
//2、Dart 也可以运用 var 关键字来声明一个可变的变量,此刻编译器会依据变量初始值主动揣度类型
//dart 写法
var a = 10;
var b = true;
​
//kotlin 写法
var a = 10
var b = true
 
//3、Dart 运用 final 关键字来声明一个不行变的变量,且可以替代 var 或加在类型前面
//dart 写法
final a = 10;
final int aa = 10;
final b = true;
final bool bb = true;
​
//Java 写法
final int a = 10;
final boolean b = true;
​
//5、Dart 中变量假如对错空类型,那么有必要给一个默许值。假如是可空类型,默许值都为 null
void main() {
 String s = "erdai";
 int? i;
 print('$s $i'); //打印成果:erdai null
}

小主张:界说变量,优先运用主动揣度,来自 Dart 官方的主张

留意: Dart 彻底扔掉了 Java 中的根本数据类型,全部都是目标数据类型

5)、Dart 中还可以运用 Object 和 dynamic 关键字来声明一个变量

//1、Dart 中还可以运用 Object 和 dynamic 关键字来声明一个变量
//1.1、Object 声明变量,这一点和 Java 没任何差异
Object a = 10;
Object b = true;
Object str = "erdai666";
​
//1.2、dynamic 声明变量,这一点是 Java 所没有的
dynamic a = 10;
dynamic b = true;
dynamic str = "erdai666";

考虑一个问题:Object 和 dynamic 有啥差异呢?️

答:Object 是一切类的基类,相当于一个可以兼容一切类型的超级类型,这点和 Java 相似。dynamic 便是一个界说动态类型的关键字

//1、声明一个 Object 类型的变量调用 substring,此刻会编译报错,由于 Object 没有 substring 办法
Object str = "erdai666";
str.substring(1); //编译报错:The method 'substring' isn't defined for the type 'Object'.//2、运用 dynamic 界说一个变量调用 substring,此刻可以绕过编译查看
dynamic str = "erdai666";
str.substring(1);

留意:运用 dynamic 界说的变量调用相关指定类型 api 时,由于会绕过编译器查看,所以别写错了,不然运转时就会报找不到此 api,如下:

Flutter 系列(二):Dart 语法筑基

可以看到,编译器提示: String 类没有 subString 办法。便是由于咱们 api 写错了,将 substring 写成了 subString 导致的

1.2、常量

1)、Dart 运用 const 关键字来界说一个常量

2)、Dart 可以运用 const 关键字替代 var 或加在类型前面

3)、Dart 还可以运用 const 关键字来创立一个常量

//1、Dart 运用 const 关键字来界说一个常量
//2、Dart 可以运用 const 关键字替代 var 或加在类型前面
const a = 10;
const b = true;
const int aa = 10;
const bool bb = true;
​
//3、Dart 还可以运用 const 关键字来创立一个常量
//创立一个内容和引证都不行变的 list 数组
var list = const [1,2,3];
//创立一个内容和引证都不行变的 set 调集
var set = const {1,2,3};

这儿我有一个疑问:那 const 和 final 有啥异同呢?

答:

异:

1、final 可以一开始不赋值,假如赋值了则不行变。const 一开始就需求赋值且不行变

2、const 有必要给一个清晰的编译常量值(即编译期间就确定的值)

3、final 可以经过核算或许办法获取一个值(即运转期间确定的值)

4、final 表明引证不行变,但内容是可变的。const 表明内容和引证都不行变

同:

1、final,const 关键字都可以用来界说一个常量

//1、final 可以一开始不赋值,假如赋值了则不行变。const 一开始就需求赋值且不行变
//2、const 有必要给一个清晰的编译常量值(即编译期间就确定的值)
//3、final 可以经过核算/办法获取一个值(即运转期间确定的值)final a;//编译经过
a = 10;//编译经过const b;//编译报错
b = 10;//编译报错//4、final 表明引证不行变,但内容是可变的。const 表明内容和引证都不行变
final set = {1,2,3};
set.add(4);
​
var list = const [1,2,3];
list.add(4);//运转报错,const list 不行新增元素

1.3、办法

1.3.1、办法界说

1)、办法和函数是同一个概念,在 Java 中咱们习气叫办法 (method)。在 Kotlin 中咱们习气叫函数 (function)。因 Dart 更像 Java ,因而这儿主张咱们也叫办法 (method)

2)、办法是运转代码的载体,像咱们运用过的 main 办法便是一个办法

Dart 中界说办法的语法规矩:

回来参数类型 办法名(参数1,参数2,参数3…) {

办法体

}

//1、Dart 写法1:办法的参数运用:var 参数名
String methodName(var name,var age){
 return "erdai666";
}
​
//2、Dart 写法2:办法的参数运用:类型 参数名
String methodName(String name,int age){
 return "erdai666";
}
​
//3、Dart 写法3:办法的回来类型可省掉,依据办法体最终一行代码进行回来类型揣度
methodName(var name,var age){
 return "erdai666";
}
​
//4、假如没有写回来类型,且办法体最终一行代码没有清晰写回来句子,那么默许履行:return null
methodName(var name,var age){
 
}
​
//5、Dart 写法4:无回来值运用 void 关键字
void methodName(var name,var age){
 
}
​
//6、Dart 写法5:假如办法体只需一行表达式,可将其改成单行办法款式,办法名和办法体用 => 衔接
String methodName(var name,var age) => "erdai666";

办法语法解说:

  • 一切办法都有回来值,即便回来值是 void

  • 办法的回来类型,可写可不写。假如不写,会依据办法体里边最终一行代码进行类型揣度

  • 假如没有写回来类型,且办法体最终一行代码没有清晰写回来句子,那么默许履行:return null

  • 办法名称可以随意取,就像 Java ,Kotlin 里边界说办法名相同

  • 办法名里边的参数可以有恣意多个,参数的声明格局有两种:

    1、var 参数名

    2、类型 参数名

  • 假如办法体只需一行表达式,可将其改成单行办法款式,办法名和办法体用 => 衔接

小主张:界说一个办法时,主张把回来类型给写出来,可读性强

1.3.2、可选参数 & 命名参数 & 默许参数

可选参数

1)、可选参数望文生义便是可以挑选的参数,运用 [] 表明可选的方位参数,如下:

void optionFunction(var value1,[var value2 = 2,var value3 = 3]){
 print('$value1 $value2 $value3');
}
​
void main(){
 optionFunction(1);
}
​
//打印成果
1 2 3

疑问:假如我只想给 value1 和 value3 传参:可以做到吗?

答:不能。假如想做到,就需求运用命名参数

命名参数

1)、命名参数默许都为可选参数。假如是必要参数,则需求用 required 关键字,且运用 required 润饰的参数不能供给默许值

2)、运用 {} 来指定命名参数

3)、命名参数有必要以 key: value 的形式去指定

如下:

//1、运用 {} 来指定命名参数
void optionFunction(var value1,{var value2 = 2,var value3 = 3}){
 print('$value1 $value2 $value3');
}
​
void main(){
 optionFunction(1,value3: 4);
}
//打印成果
1 2 4//2、假如是必要参数,则需求用 required 关键字,且运用 required 润饰的参数不能供给默许值
//此刻 value2 为必传的参数
void optionFunction(var value1,{required var value2,var value3 = 3}){
 print('$value1 $value2 $value3');
} 
​
void main(){
 optionFunction(1,value2: 4);
}
//打印成果
1 4 3
默许参数

如上咱们方才给可选参数和命名参数供给的默许值

1)、默许参数便是给可选参数供给默许值,以便在未供给相应实参时运用

2)、默许值有必要是编译时常量

3)、假如可选参数没有供给默许值,那默许值就为 null

//1、假如可选参数没有供给默许值,那默许值就为 null
void optionFunction(var value1,{var value2 = 2,var value3}){
 print('$value1 $value2 $value3');
}
void main(){
 optionFunction(1);
}
//打印成果
1 2 null

1.3.3、匿名办法(又称闭包)

1)、匿名办法望文生义便是没有名字的办法,语法规矩如下:

//1、办法一:
(var 参数名){
  办法体
}
​
//2、办法二:
(类型 参数名){
  办法体
}
​
//3、办法三:假如办法体只需一行代码可以将匿名办法用单行表明
(var 参数名) => 办法体
(类型 参数名) => 办法体
 
//4、办法四:省掉 var 或类型
(参数名){
  办法体
}
​
(参数名) => 办法体

2)、匿名办法一般会作为参数或赋值给一个变量

//1、匿名办法当作参数运用
void main(){
 const list = [1,2,3];
 list.forEach((element){
  print(element);
  });
 //可简化成如下写法:
 //list.forEach((element) => print(element));
}
​
//2、匿名办法赋值给一个变量
void main(){
 const list = [1,2,3];
 var function = (element){
  print(element);
  };
 //可简化成如下写法:
 //var function = (element) =>  print(element);
 list.forEach(function);
}

3)、匿名办法当即履行

//1、办法1
void main(){
 var func = (){
  print('666');
  };
  (func)();
}
​
//2、办法2
void main(){
  ((){
  print('666');
  })();
}
​
//打印成果
666

4)、匿名办法内部可以引证包含该匿名办法的一切层级作用域中的变量,与匿名办法调用的方位无关,如下:

//makeAdder 回来一个匿名办法
Function makeAdder(num addBy){
 //匿名办法拜访 addBy 参数
 return (num i) => addBy + i;
}
​
void main(){
 //创立一个匿名办法,传入实参为 2
 var add1 = makeAdder(2);
 //创立一个匿名办法,传入实参为 3
 var add2 = makeAdder(3);
​
 //别离调用两个匿名办法,并打印成果
 print(add1(3));
 print(add2(4));
}
​
//打印成果
5
7

留意:Dart 中的办法也是一种类型,对应 Function 类,所以办法可以被赋值给变量或作为参数传入另一个办法

1.3.4、静态办法

1)、运用 static 关键字润饰的办法即为静态办法,因静态办法不归于类实例,所以也无法拜访类成员

2)、静态办法可以运用类名直接调用

class Test{
 static String staticFunction1(){
  return "";
  }
​
 static void staticFunction2(){
  }
}
​
void main(){
 Test.staticFunction1();
 Test.staticFunction2();
}

二、根本类型和运算符

2.1、根本类型

前面提到过:Dart 彻底扔掉了 Java 中的根本数据类型,全部都是目标数据类型。因而咱们这儿讲的根本类型,也是目标数据类型,只不过是 Dart 默许给咱们供给的

2.1.1、数字类型

1)、在dart言语中数字类型首要有下面三种:

int:整数类型

double:浮点数类型

num:数字类型,int和double都是它的子类

var x = 1;  // 初始化为 int 类型
var y = 1.1; // 包含小数,初始化为 double// 清晰指定数据类型
double z = 1;
num d = 100;

2)、数字类型和字符串类型相互转化

// 将 String 类型转化成 int 类型
var one = int.parse('1');
​
// 将 String 类型转化成 double 类型
var onePointOne = double.parse('1.1');
​
​
// 将 int 类型转化成 String 类型
String oneAsString = 1.toString();
​
// 将 double 类型转化成 String 类型, 保留两位小数
String piAsString = 3.14159.toStringAsFixed(2);

2.1.2、字符串类型

1)、字符串类型运用单引号或许双引号包裹字符串都可以

var s1 = 'Hello';
var s2 = "erdai";
2.1.2.1、字符串内嵌表达式

1)、Dart 支撑在字符串中内嵌变量,或许爽性内嵌表达式

// 内嵌变量
var s3 = '你好: $s2';
​
// 内嵌表达式
var s4 = "转大写:${s2.toUpperCase()}";
2.1.2.2、字符串相加(衔接)

字符串相加,便是将两个字符串衔接起来,dart 言语中有以下两种办法完结字符串衔接:

1)、接连的字面字符串界说,默许会将字符串衔接起来

2)、运用 + 加号衔接字符串

void main(){
 //1、接连的字面字符串界说,默许会将字符串衔接起来
 var str1 = "erdai" "666";
 //2、运用 + 加号衔接字符串
 var str2 = "erdai" + "666";
 print(str1);
 print(str2);
}
​
//打印成果
erdai666
erdai666
2.1.2.3、多行字符串界说

1)、运用 ”’ 三引号界说多行字符串,这种办法可以保留字符串的换行符

void main() {
 var s1 = '''
这是榜首行字符串。
这是第二行字符串。
''';
 print(s1);
}
​
//打印成果
这是榜首行字符串。
这是第二行字符串。

2.1.3、布尔类型

布尔类型就两种值:true 或许 false, 别离表明真和假

var isOk = false;
bool status = true;

2.1.4、枚举类型

1)、枚举类型其实便是一组常量的调集,都是只读的

// 运用 enum 关键字,界说 Color 枚举类型,Color 包含了3个常量 red、green、blue
enum Color { red, green, blue }
​
// 读取 Color.blue 枚举常量
var aColor = Color.blue;
​
// 运用 switch 句子判别 aColor 变量值
switch (aColor) {
 case Color.red: // 运用枚举常量作为检测条件,aColor == Color.red 则建立。
  print('Red as roses!');
  break;
 case Color.green:
  print('Green as grass!');
  break;
 default: // 默许条件
  print(aColor); // 'Color.blue'
}

2)、枚举常量都有一个从 0 开始数字编号,榜首个常量是 0,第二个是 1,以此类推

void main() {
 print(Color.green.index);
 print(Color.blue.index);
}
​
//打印成果
1
2

2.2、运算符

运算符这一块,除了级联调用是 Java 和 Kotlin 所没有的,其他运算符根本相似

2.2.1、级联调用

1)、级联调用便是经过 .. (两个接连的点) 接连调用目标的特点和办法

querySelector('#confirm') // 经过 querySelector 查询取得一个目标
  ..text = 'Confirm' // 设置目标 text 特点
  ..classes.add('important'); // 调用目标的 classes 特点的 add 函数//等价如下代码// 经过 querySelector 查询取得一个目标
var button = querySelector('#confirm');
// 设置目标 text 特点
button.text = 'Confirm';
// 调用目标的 classes 特点的 add 函数
button.classes.add('important');

留意:咱们先关注语法即可

2.2.2、赋值运算符

//1、运用 = 进行赋值
a = 100;
​
//2、复合赋值运算符
a *= 3; // 等价于 a = a * 3
a -= 3; // 等价于 a = a - 3
a += 3; // 等价于 a = a + 3
a /= 3; // 等价于 a = a / 3
a %= 3; // 等价于 a = a % 3

2.2.3、管用运算符

运算符 阐明
+
-expr 管用取反
*
/
~/ 除法,成果取整
% 求余
++ 支撑前置自增和后置自增
支撑前置自减和后置自减
// a = 5
var a = 2 + 3;
// a1 = -1
var a1 = 2 - 3;
// a2 = 6
var a2 = 2 * 3;
// a3 = 2.5
var a3 = 5 / 2;
// 整除,a4 = 2
var a4 = 5 ~/ 2;
// 求余数 a5 = 1
var a5 = 5 % 2;
//a++ 和 ++a 差异:a++ 先用在加,++a先加在用
a++; // 相当于 a = a + 1
++a; 
a--; // 相当于 a = a - 1
--a;

2.2.4、联系运算符

联系运运算符常用于条件表达式中,判别条件是否建立

运算符 阐明
== 判别两个值是否相等
!= 判别两个值是否不相等
> 大于
< 小于
>= 大于等于
<= 小于等于

2.2.5、类型测验运算符

运算符 阐明
as 用于类型转化,将一个目标类型转化成其他一种目标类型,一般用于子类目标转化成父类目标。
is 用于检测一个变量是否归于某种目标类型
is! 用于检测一个变量不归于某种目标类型
// 假如 emp 变量是 Person 类型则条件为 true
if (emp is Person) {
 // 忽略代码
}
​
// 永远回来 true, 由于一切类型都承继了 Object。
if (emp is Object) {
  // 忽略代码
}

留意:假如变量是某个类的子类的实例,那么这个变量也归于父类类型,is 条件回来 true

2.2.6、逻辑运算符

运算符 阐明
!expr 表达式条件取反
|| 逻辑或
&& 逻辑与
//常用于条件句子组合表达式
if (!done && (col == 0 || col == 3)) {
 //...
}

2.2.7、位运算符

二进制位运算符

运算符 阐明
&
|
异或
~expr 按位取反
<< 左移
>> 右移
// a = 256
var a = 1 << 8; // 1 左移 8 位相当于:1 * 2 的 8 次方

2.2.8、条件运算符

Dart 中有两种条件运算符:

1、语法规矩:condition ? expr1 : expr2 ,相似 Java 三目运算符。condition 表达式为真,则履行并回来 expr1 的值, 不然履行 expr2

2、语法规矩:expr1 ?? expr2 ,相似 Kotlin 的 ?: 。假如 expr1 不等于 null, 则履行 expr1 并回来 expr1 的值,不然履行并回来 expr2 的值

// 假如isPublic为真,visibility = 'public' 不然 visibility = 'private'
var visibility = isPublic ? 'public' : 'private';
​
// 这种语法在处理参数默许值十分有用。
// 假如name不等于null, 则payerName = name 不然payerName = 'Guest'
String payerName = name ?? 'Guest';

三、数组和调集

3.1、List 数组

3.1.1、界说

1)、与 Java 的数组类型不同,Dart 中的数组类型便是 List,它是泛型类型数据结构,支撑恣意数据类型的数组

2)、List 数组界说的元素有序可重复,相似 Java 的 List 调集

3)、Dart 中 List 数组首要分为两种类型:

1、可变长度数组

2、固定长度数组

不管是哪种类型的数组,他们的操作办法是相同的

//1、可变长度数组
//界说一个 int 类型的可变数组
List<int> a = []; //等价:var a = <int>[];//界说一个 String 类型的可变数组
var strs = <String>[]; //等价 List<String> strs = [];//如下这种界说办法,编译器会给咱们揣度出是字符串类型的可变数组
var strs = ['字符串'];

1、了解 Java 的人都知道,上述这种界说便是泛型类型的语法,<> 符号界说的是 List 的元素类型

2、上述界说数组咱们并没有指定数组巨细,因而他们界说的都是可变数组,可变数组可以往数组中刺进不约束数量的元素 (只需没超过内存约束)

//2、固定长度数组
//界说一个固定长度为 3 ,类型为 int 的数组
var list = List.filled(3,0);
​
//测验一
void main() {
 //界说一个固定长度为 3 ,类型为 int 的数组
 var list = List.filled(3,0);
 list[0] = 1;
 list[1] = 2;
 list[2] = 3;
 //此刻假如咱们添加第 4 个元素,则会报数组下标越界反常
 //list[3] = 4; //数组下标越界
 for (var value in list) {
  print(value);
  }
}
//打印成果
1
2
3
 
//测验二:
void main() {
 //界说一个固定长度为 2 ,类型为 String 的数组
 var list = List.filled(2,"");
 list[0] = "erdai";
 list[1] = "666";
​
 for (var value in list) {
  print(value);
  }
}
//打印成果
erdai
666

留意:

1、上述 filled 办法的两个参数:榜首个表明数组长度,第二个表明存放的元素类型初始值

2、固定长度的数组,只能经过数组下标的办法读写数组,不能运用 add,insert 办法修正数组,不然会报错

3.1.2、扩展运算符

假如咱们想将一个 List 数组的元素填充到其他一个数组去,咱们可以运用扩展运算符 ,如下:

var list = [1, 2, 3];
var list2 = [0, ...list]; // 将 list 数组的一切元素一个个展开来,刺进到 list2 中
// 等价代码 var list2 = [0, 1, 2, 3]

3.1.3、常用 Api 介绍

//1、首要界说一个 int 类型的可变数组
var a = <int>[];
​
//2、往数组尾巴添加元素 a:[1,2,3]
a.add(1);
a.add(2);
a.add(3);
​
//3、修正榜首个元素的值 a:[0,2,3]
a[0] = 0;
​
//4、在数组 0 方位,刺进 100 a:[100,0,2,3]
a.insert(0,100);
​
//5、删去一个元素 a:[100,0,2]
//依据元素删去
a.remove(3);
​
//依据下标删去 a:[100,2]
a.removeAt(1);
​
//6、获取数组巨细
print(a.length); //打印:2
 
//7、数组排序:默许数组从小到大排序 a:[2,100]
a.sort();
​
//8、判别数组是否包含指定元素
a.contains(2); // true//9、清空 List,删去一切数据 a:[]
a.clear();

3.2、Set 调集

3.2.1、界说

1)、Dart 中的 Set 是无序调集类型,Set 跟 List 都能保存一组数据,差异便是 Set 的元素都是仅有的,和 Java 的 Set 调集相似

2)、Set 支撑恣意类型数据,首要有下面三种办法初始化:

//1、办法一:运用 {} 界说一个 String 类型的 Set
var strSet = {"str"};
​
//2、办法二:界说一个空的 String 类型的 Set
var names = <String>{};
​
//3、办法三:经过 Set 目标界说一个可以保存 String 类型的 Set
var names = Set<String>();

3.2.2、常用 Api 介绍

//1、首要界说一个 set 调集
var names = <String>{};
​
//2、添加一个元素 names:{"Dart"}
names.add("Dart");
​
//3、添加一个 List 数组 names:{"Dart","Flutter"}
var titles = ["Flutter"];
names.addAll(titles);
​
//4、获取 Set 巨细
print(names.length); //打印:2//5、删去元素
//依据元素进行删去 names:{"Dart"}
names.remove("Flutter");
​
//6、判别 Set 是否包含指定元素
names.contains("Dart"); //true//7、清空 Set 一切元素 names:{}
names.clear();

3.3、Map 调集

3.3.1、界说

1)、Dart 中 map 类型,便是一种哈希类型数据,map 类型的数据都是由 key 和 value 两个值组成,key 是仅有的,value 不必仅有,读写数据都是经过 key 进行,map 也是泛型类型,支撑恣意类型数据,key 和 value 可以是恣意类型数据

2)、map 首要有以下四种办法初始化:

//1、办法一:直接以key, value 的办法初始化一个 map 类型变量, key 和 value 都是 String 类型
var map1 = {
 //格局  Key: Value
 'first': 'partridge',
 'second': 'turtledoves',
 'fifth': 'golden rings'
};
​
//2、办法二:直接经过 Map 类结构一个 map 类型变量, key 类型为 String, value 类型为 int
var map2 = Map<String, int>();
​
//3、办法三:界说一个 key 类型为 String, value 类型为 int 的空 Map
var map3 = <String,int>{}
​
//4、办法四:界说一个绕过编译查看的动态 key,value 类型 Map
var map4 = Map(); //等价于:var map4 = {};

3.3.2、常用 Api 介绍

//1、首要界说一个 map
var map = {};
​
//2、向 map 添加数据 map:{"key1":"value1","key2":"value2"}
//运用 [] 操作符读写 map 数据,语法:map变量[key]
map["key1"] = "value1";
map["key2"] = "value2";
​
//3、查询 map:运用[]操作符,依据 key 查询对应的 value 值,假如 key 不存在则回来 null
var v1 = map["key1"];
var v2 = map["key2"];
​
//4、更新 map 数据:依据 key 更新 map 数据和添加 map 数据用法相同
//假如 key 值不存在则添加,不然更新 key 的数据
//map:{"key1":"value1","key2":"value2"}
map["key2"] = "erdai";
​
//5、获取 map 巨细
print(map.length); //打印:2//6、遍历 map
map.forEach((k, v) {
 print('$k $v');
});
​
//7、删去 map 数据:经过 remove 函数可以删去指定的 key 数据
//map:{"key2":"value2"}
map.remove("key1");
​
//8、清空一切 map 数据 map:{}
map.clear();

四、程序的逻辑操控

这个章节相对简略,咱们就简略举个比如

4.1、if-else

if (isRaining()) {
 // 代码1
} else if (isSnowing()) {
 // 代码2
} else {
 // 代码3
}

else是可选的,依据需求组合即可

4.2、for-i 和 for-in 循环

// 界说 int 数组
var list = [5,1,2,6,3];
​
//1、for-i 循环遍历数组
for (var i = 0; i < list.length; i++) {
 // 打印数组元素
  print(list[i]); 
}
​
//2、for-in 循环遍历数组变量 list
for (var v in list) {
 // 打印数组元素 v
  print(v); 
}
​
//打印成果
5
1
2
6
3

4.3、switch句子

1)、switch 句子的作用跟 if 句子相似,用于检测各种条件是否建立,然后履行相应分支的代码

2)、switch 支撑检测 int,String 类型变量的检测,当然假如你自界说的类重载了 == 操作符,也可以在 switch 条件中运用

// 条件状况变量
var command = 'OPEN';
​
switch (command) { // 需求检测的变量 
 case 'CLOSED': //case 句子用于设置检测条件
  executeClosed(); // 假如 command = 'CLOSED',履行当时分支代码。
  break; // 完毕当时分支履行逻辑
 case 'PENDING':
  executePending();
  break;
 case 'APPROVED':
  executeApproved();
  break;
 default: // 假如上面的 case 句子都没有匹配成功,则履行 default 分支的逻辑。
  executeUnknown();
}

4.4、while 和 do-while 循环句子

//1、while 循环句子比如
void main() {
 // 界说 int 数组
 var list = [5, 1, 2, 6, 3];
​
 // 循环遍历数组
 var i = 0;
 while (i < list.length) {
  // 条件为 true 则履行循环体代码
  print(list[i]); // 打印数组元素
  i++; // 数组下标递加
  }
}
​
//2、do-while 循环句子比如
void main() {
 // 界说 int 数组
 var list = [5, 1, 2, 6, 3];
​
 // 循环遍历数组
 var i = 0;
 do {
  print(list[i]);
  i++;
  } while (i < list.length); // 先履行循环体代码后再检测循环条件,条件为 true 则持续履行循环
}
​
//打印成果
5
1
2
6
3

五、面向目标编程

Dart 是面向目标编程言语,目标都是由类创立的,一切类都是由 Object 类派生出来的子类,除了 Object , 一切类只需一个父类(即只能承继一个父类)

尽管 Dart 言语中一个类只能承继一个父类,可是 Dart 言语供给了 mixin 机制,可以复用多个类,达到相似多承继的作用

5.1、类和目标

1)、Dart 没有 public、protected 和 private 等成员拜访限定符。默许状况下特点,办法,类等都是共有的,相似 Java 的 public。假如想要表明私有,则以下划线 _ 最初去命名

2)、Dart 中实例化目标和 Java 相似,new 关键字可写可不写

3)、当咱们在类中创立私有特点时,咱们应该给私有特点供给 getter 和 setter 办法供外界拜访:

get 办法语法格局:回来值类型 get 办法名 { 办法体 }

set 办法语法格局:set 办法名 ( 参数 ) { 办法体 }

class Person {
 // 界说类成员特点,默许类的成员特点和办法都是共有的,相似 java 的 public
 var name;
 // 以下划线 ( _ ) 最初命名的特点代表私有成员特点
 var _age;
​
 // 跟类名同名的办法,为结构办法
 // 这儿自界说了一个带着参数的结构办法。
 // 假如咱们没有自界说结构办法,会主动生成一个不带参数的默许结构办法
 Person(var name, var age) {
  // 由于参数名和类特点名同名,可以运用this引证当时目标
  this.name = name;
  // 可以忽略this关键字,直接引证类成员
  _age = age;
  }
 
 //为 _age 供给 getter 和 setter 办法
 int get age{
  return _age;
  }
 //getter 办法还可以简化为此写法:int get age => _age;set age(int age){
  _age = age;
  }
​
 // 定一个 public 的办法
 String greet(String who) => 'Hello, $who. I am $name, my age is $_age !';
}
​
void main(){
 var person = Person("erdai",18);
 //下面这句便是调用了 age 的 set 办法
 person.age = 20;
 var greet = person.greet("lucy");
 print(greet);
}
​
//打印成果
Hello, lucy. I am erdai, my age is 20 !

5.2、结构办法

假如咱们没有自界说一个结构办法,会主动生成一个不带参数的默许结构办法

// 这个类会生成默许的结构办法
class Person {
  String name;
}
​
// 经过默许结构办法实例化目标
var p = Person();

5.2.1、自界说结构办法

class Point{
 var x,y;
 
 Point(var x,var y){
  // 经过this拜访成员特点,当然一般除非出现命名抵触,不然可以忽略this
  this.x = x;
  this.y = y;
  }
}

关于结构办法中,简略的赋值操作,Dart言语供给了更简练的语法,如下:

class Point{
 var x,y;
​
 // 直接将结构办法的榜首个参数赋值给this.x, 第二个参数赋值给this.y
 Point(this.x,this.y);
}

5.2.2、初始化参数列表

Dart 还为结构办法供给了 参数初始化列表 的语法,用于初始化目标参数

class Point{
 var x,y;
​
 // 冒号 : 后边的表达式便是参数初始化列表,每个表达式用逗号分隔
 Point(var x,var y): this.x = x,this.y = y{
  // 运用参数初始化列表初始化目标特点,这儿假如没有其他初始化工作要做,可以是空的
  }
}

5.2.3、命名结构办法

1)、Dart 可以运用命名结构办法语法,创立多个结构办法,命名结构办法语法格局: 类名.结构办法名(参数列表)

class Point{
 var x,y;
​
 Point(this.x,this.y);
​
 // 命名结构办法 namedConstructor
 Point.namedConstructor(){
  x = 0;
  y = 0;
  }
}
​
void main(){
 // 运用命名结构办法实例化目标
 var point = Point.namedConstructor();
}

上面的比如也可以改写为:

class Point{
 var x,y;
​
 Point(this.x,this.y);
 // 命名结构办法 namedConstructor
 // 这儿运用参数初始化列表,直接经过 this 调用上面的结构办法,传入两个参数 0,初始化目标
 Point.namedConstructor():this(0,0);
}

5.2.4、factory 结构办法

1)、Dart 供给了一个特别的结构办法,相似设计形式中的工厂形式,用来创立目标

2)、factory 结构办法只能拜访静态特点和静态成员办法,因而不能拜访 this 引证

//1、界说个日志类
class Logger {
 final String name;
 bool mute = false;
​
 // 界说一个私有的_cache特点,用来保存创立好的Logger目标
 static final Map<String, Logger> _cache = {};
​
 // 留意这个结构办法,前面运用了factory关键字润饰,这代表这个结构办法是一个工厂结构办法
 // 工厂结构办法不会每次都创立一个新的Logger目标
 factory Logger(String name) {
  // 依据name判别缓存的Logger目标是否存在
  if (_cache.containsKey(name)) {
   // 回来缓存的Logger目标
   return _cache[name]!;
   } else {
   // 假如没有缓存,则调用命名结构办法_internal创立一个Logger目标
   final logger = Logger._internal(name);
   // 依据name缓存logger
   _cache[name] = logger;
   // 回来新的Logger目标
   return logger;
   }
  }
​
 // 留意这个是一个私有的命名结构办法。
 Logger._internal(this.name);
​
 void log(String msg) {
  if (!mute) print(msg);
  }
}
​
//2、测验
void main(){
 var logger = Logger("erdai");
 logger.log(logger.name);
}
​
//打印成果
erdai

5.3、承继和多态

5.3.1、承继

1)、Dart 经过 extend 关键字承继一个类,和 Java 相似

2)、子类会承继父类可见的特点和办法,不会承继结构办法

3)、子类可以复写父类的 getter,setter,以及一般办法,运用 @override 表明覆写

class Parent{
 String name = "";
 int age = 0;
​
 //核算特点
 bool get adult => this.age > 18;
​
 //私有特点,关于子类不行见
 String _address = "";
​
 void method(){
  print('Parent');
  }
}
​
class Children extends Parent{
 
 void specificMethod(){
  print('Children specificMethod');
  }
}
​
void main(){
 var child = Children();
 //调用子类自己的办法
 child.specificMethod();
 //拜访父类的特点
 child.name = "erdai";
 child.age = 18;
 print('${child.name} ${child.age}');
 //调用父类的办法
 child.method();
 //拜访父类的核算特点
 print('${child.adult}');
}
​
//打印成果
Children specificMethod
erdai 18
Parent
false

5.3.2、多态

1)、简略的了解:多态便是将子类的目标赋值给父类的引证,同一个办法调用会有不同的履行作用

2)、多态的体现:父类界说一个办法,让承继它的子类去完结,每个子类有不同的表现

class Animal{
 void animalType(){
​
  }
}
​
class Dog extends Animal{
​
 @override
 void animalType() {
  print('I am dog');
  }
}
​
class Pig extends Animal{
​
 @override
 void animalType() {
  print('I am pig');
  }
}
​
void main(){
 //子类的目标赋值给父类的引证
 Animal animal1 = Dog();
 Animal animal2 = Pig();
 //同一个办法调用会有不同的履行作用
 animal1.animalType();
 animal2.animalType();
}
​
//打印成果
I am dog
I am pig

5.4、笼统类和笼统办法

1)、笼统类便是不能实例化的类,经过 abstract 关键字声明

2)、笼统办法便是没有完结的办法,Dart 中的笼统办法不能用 abstract 声明,Dart 中没有办法体的办法就称为笼统办法

3)、承继笼统类,子类有必要要完结一切笼统办法,不然会报错

// 运用 abstract 关键字润饰的类,便是笼统类
abstract class Doer{
 // 笼统类跟一般类相同,可以界说成员变量,成员办法。
 String name = "";
 // 界说个笼统办法,这个办法咱们没有完结详细的功用
 void doSomething();
}
​
// 承继笼统类 Doer
class EffectiveDoer extends Doer{
 // 完结笼统类的笼统办法
 @override
 void doSomething() {
  print('doSomething');
  }
}
​
void main(){
 var doer = EffectiveDoer();
 doer.doSomething();
 doer.name = "erdai";
 print(doer.name);
}
​
//打印成果
doSomething
erdai

5.5、接口

1)、Dart 中的接口没有运用 interface 关键字界说,而是一般类和笼统类都可以作为接口被完结。可是一般都是用笼统类来界说接口

2)、子类经过 implements 来完结接口

3)、默许状况每一个类都隐含一个包含一切公有成员(特点和办法)的接口界说

abstract class Fruit{
 // 包含在隐式接口里边
 String name = "";
 
 // 结构办法不包含在隐式接口里边
 Fruit(this.name);
 
 // 包含在隐式接口里边
 void eat();
}
​
class Apple implements Fruit{
 @override
 String name = "苹果";
​
 @override
 void eat() {
  print('吃$name');
  }
}
​
void main(){
 var fruit = Apple();
 fruit.eat();
}
​
//打印成果
吃苹果

留意:尽管一般类也可以作为接口完结,可是仍然需求完结一般类里边一切的公有成员(特点和办法),因而主张咱们运用笼统类来作为接口完结,由于笼统类原本便是用来界说给子类完结的

六、空安全查看

1)、Dart 在 2.12 版别和 Flutter 2.0 中引入了空安全的新特性,在空安全版别下,运转时的 NPE (NullPointer Exception) 反常被提前到了编译期

2)、在空安全推出之前,静态类型体系答应一切的类型值为 null,由于 Null 是一切类型的子类。而在空安全推出后,一切类型默许为不行空类型,Null 不再是一切类的子类,它变成了和其他类型并行的类

3)、Dart 新增了一些关键字用于空安全,如下:

关键字 意义 示例
? 可空 int a?;
! 非空 int b = a!;
late 延迟初始化 late int a;
required 可选参数的不行空 {required int a}

6.1、空类型声明符 ?

1)、在类型后边加上 ?,表明可空类型

2)、运用 var 关键字界说的变量也是可空类型

3)、可空类型变量的调用,运用 ?. 操作符,它表明假如当时目标不为 null 则调用,为 null 则什么都不做

//1、在类型后边加上 ?,表明可空类型
void main() {
 //界说一个 String 的可空类型,默许值为 null
 String? str;
 //编译报红,由于可空类型需求运用 ?. 调用
 //提示:The property 'length' can't be unconditionally accessed because the receiver can be 'null'.
 print(str.length); 
}
​
//2、运用 var 关键字界说的变量也是可空类型
void main() {
 //界说一个动态的可空类型 str,默许值为 null
 var str;
 //下面这句代码会绕过编译器查看,但运转时会报错:NoSuchMethodError: 'length'
 print(str.length);
}
​
//3、可空类型变量的调用,运用 ?. 操作符,它表明假如当时目标不为 null 则调用,为 null 则什么都不做
void main() {
 String? str1;
 var str2;
 print(str1?.length);
 print(str2?.length);
}
//打印成果
null
null

6.2、非空断语 !

1)、运用 ! 关键字表明告诉编译器这是一个不行能为空的变量。假如为空,你就抛反常

String? getName() => "erdai";
​
void main() {
 String? str = getName();
 //此刻会编译报红,由于编译器无法智能判空
 //print(str.length);
 //因而咱们需求运用 ! 关键字
 print(str!.length);
}
//打印成果
5

6.3、late 延迟初始化

1)、late 关键字会告诉编译器:这是个非空变量,我稍后会初始化

//此刻会编译报红,由于编译器会告诉咱们非空变量有必要先初始化
//String str;
//因而咱们需求运用 late 关键字
late String str;
​
void main() {
 str = "erdai";
 print(str);
}
//打印成果
erdai

6.4、required 关键字

1)、required 关键字首要是用来符号命名参数,在运用时一定要给他们赋值,使得他们不为空

2)、运用 required 润饰的参数不能供给默许值

void optionFunction(var value1,{required var value2,var value3 = 3}){
 print('$value1 $value2 $value3');
} 
​
void main() {
 optionFunction(1, value2: 100);
}
​
//打印成果
1 100 3

七、有趣的运算符重载

与 Kotlin 相似,Dart 的运算符重载答应咱们让恣意两个目标进行相加,或许是进行其他更多的运算操作

1)、运算符重载运用的是 operator 关键字,咱们只需求在指定运算符前面加上 operator 关键字,就可以完结运算符重载的功用了,Dart 支撑的重载运算符如下:

<   +   |   []
>   /   ^   []=
<=  ~/  &   ~
>=  *   <<  ==
–   %   >>

2)、重载运算符的语法格局如下:

函数回来值 operator 运算符(运算符参数) {
  // 完结运算符重载逻辑
}

下面咱们就来实践一下:

class Money{
 int value = 0;
​
 Money(this.value);
​
 //重载 + 号运算符, 两个 Money 目标相加,然后回来一个新的 Money 目标
 Money operator +(Money money){
  var sum = value + money.value;
  return Money(sum);
  }
}
​
void main() {
 var money1 = Money(100);
 var money2 = Money(200);
 //两个目标相加
 var money3 = money1 + money2;
 print(money3.value);
}

八、 mixin 混入

1)、前面提到 Dart 言语的类是单承继的,假如咱们想要完结相似多承继的作用可以运用 mixin 机制,又叫混入机制,例如把类 A 混入到类 B 中,那么类 B 就具有了类 A 的成员,跟承继的特性十分相似

2)、界说一个可以被 mixin 的类,运用 mixin 关键字替代 class 关键字即可

3)、承继被 mixin 的类,运用 with 关键字,假如有多个,中心用 , 隔开

4)、被 mixin 的类只能承继自 Object,不能承继其他类,且不能有结构办法

5)、父类束缚:当声明一个 mixin 时, on 后边的类便是这个 mixin 的父类束缚。一个类若是要 with 这个 mixin,则这个类有必要承继或完结这个 mixin 的父类束缚

6)、就远射中准则:当 with 多个 mixin,多个 mixin 具有同一个办法,则调用办法时会射中最终一个 mixin 类的办法

//1、界说一个可以被 mixin 的类,运用 mixin 关键字替代 class 关键字即可
//2、承继被 mixin 的类,运用 with 关键字,假如有多个,中心用 , 隔开
mixin A{
 void getA(){
  print('A');
  }
}
​
mixin B{
 void getB(){
  print('B');
  }
}
​
class C{
 void getC(){
  print('C');
  }
}
​
class CC extends C with A,B{}
​
void main() {
 var cc = CC();
 cc.getA();
 cc.getB();
 cc.getC();
 print(cc is A);
 print(cc is B);
 print(cc is C);
}
//打印成果
A
B
C
true
true
true
 
//3、被 mixin 的类只能承继自 Object,不能承继其他类,且不能有结构办法
class D {}
​
//编译报错,mixin 类不能承继其他类,只能承继自 Object
mixin E extends D{
  //编译报错,mixin 类不能有结构办法
  E();
}
​
//4、父类束缚:当声明一个 mixin 时, on 后边的类便是这个 mixin 的父类束缚。一个类若是要 with 这个 mixin,则这个类有必要承继
//或完结这个 mixin 的父类束缚
class F{}
mixin G on F{}
//class I with G{} //编译报错:class I 没有承继 mixin 的父类束缚
class I extends F with G{} //编译经过,class I 承继了 mixin 的父类束缚
//5、就远射中准则:当 with 多个 mixin,多个 mixin 具有同一个办法,则调用办法时会射中最终一个 mixin 类的办法
mixin Test1{
  void testMethod(){
    print('Test1 testMethod');
  }
}
mixin Test2{
  void testMethod(){
    print('Test2 testMethod');
  }
}
class Test with Test1,Test2{
}
void main() {
  var test = Test();
  test.testMethod();
}
//打印成果
Test2 testMethod

九、Dart 泛型

泛型编程机制最首要的意图是为了代码复用,避免类型转化反常。假如你对 Java ,Kotlin 泛型很了解,你会觉得 Dart 泛型十分简略。对 Java ,Kotlin 泛型还不了解的,看我这篇文章传送门

1)、Dart 中泛型首要有以下四种运用:

1、泛型类

2、泛型接口

3、泛型办法

4、约束泛型类型

9.1、泛型类,泛型接口,泛型办法

1)、咱们界说一个类,或许接口的时分,在类名后边添加泛型参数,便是为这个类或接口添加了一个泛型

2)、咱们界说一个办法时,在办法名后边添加泛型参数,便是为这个办法添加了一个泛型

3)、泛型语法格局:<T> ,多个泛型之间用 , 隔开:<T,K>

4)、泛型参数的命名可以随意取,可是咱们一般习气运用大写字母代表泛型参数

//一、泛型类
//1、界说泛型类
class GenericClass<T>{
​
 T? name;
​
 GenericClass(this.name);
​
 void setName(T? value){
  name = value;
  }
​
 T? getName(){
  return name;
  }
}
​
//2、泛型类运用
void main() {
 var genericClass = GenericClass<String>("");
 genericClass.setName("erdai");
 print(genericClass.getName());
}
​
//3、打印成果
erdai
 
//二、泛型接口
//1、界说泛型接口
abstract class GenericInterface<K,V>{
 void setKeyValue(K key,V value);
}
​
//2、界说泛型接口完结类
class GenericInterfaceImpl<K,V> implements GenericInterface<K,V>{
​
 var map = {};
​
 @override
 void setKeyValue(K key, V value) {
  map[key] = value;
  }
}
​
//3、泛型接口运用
void main() {
 var impl = GenericInterfaceImpl<String,int>();
 impl.setKeyValue("erdai", 666);
 impl.map.forEach((key, value) {
  print('$key $value');
  });
}
​
//4、打印成果
erdai 666
//三、泛型办法:类比 Java,Kotlin 中的写法
//Java 中的写法
public <T> void genericMethod(T param){
}
//Kotlin 中的写法
fun <T> genericMethod(param: T){
}
//Dart 中的写法
void genericMethod<T>(T param){
}

上述界说泛型类,泛型接口和 Java,Kotlin 没啥差异,却是界说泛型办法,咱们需求留意:

1、Java 中办法的泛型界说在回来值的前面

2、Kotlin 中的办法泛型界说在办法名的前面

3、Dart 中的泛型界说在办法名的后边

9.2、约束泛型类型

1)、约束泛型参数类型语法格局:<泛型参数 extends 父类>

class BaseClass{
​
 void baseMethod(){
  print('BaseClass baseMethod...');
  }
}
​
class Child extends BaseClass{
 @override
 void baseMethod() {
  print('Child baseMethod');
  }
}
​
//T 类型有必要是 BaseClass 或许其子类
class Foo<T extends BaseClass>{
 T? t;
 
 Foo(this.t);
​
 void fooTest(){
  t?.baseMethod();
  }
}
​
void main(){
 //1、运用父类 BaseClass 作为泛型参数是答应的
 var baseClass = BaseClass();
 var foo1 = Foo<BaseClass>(baseClass);
 foo1.fooTest();
 
 //2、运用子类 Child 作为泛型参数
 var childClass = Child();
 var foo2 = Foo<Child>(childClass);
 foo2.fooTest();
​
 //3、假如不传入任何泛型参数,默许运用父类 BaseClass 作为泛型参数
 var foo3 = Foo(baseClass);
 foo3.fooTest();
}
​
//打印成果
BaseClass baseMethod...
Child baseMethod
BaseClass baseMethod...

十、Dart Import 导入包

在日常开发中,咱们常常需求导入咱们的本地模块或许第三方开源包。Dart 中首要经过 import 指令导入包

10.1、导入内置包

1)、Dart 内置了一些常用的包,这些内置的包会随着 Dart sdk 一同安装在本地

2)、导入内置包运用 dart: 作为途径前缀

// 导入内置 math 包,运用 dart: 作为前缀。
// math 包首要供给一些数学相关的函数,例如,正弦函数、求最大值函数等等
import 'dart:math';
​
void main() {
 // 调用 math 包中的 max 函数,求两个数中的最大值。
 var a = max(1,100);
 print(a); //打印 100
}

10.2、包的别号

默许状况调用包中的函数或许类,不需求包名作为前缀,上面调用了 math 包中的 max 函数,直接运用包中的函数名。可是这样会存在命名抵触的可能性,假如导入的两个包,包含了同名的类或许函数,就会出现命名抵触,因而供给别号机制

1)、运用 as 关键字指定包的别号

//运用 as 关键字,指定包的别号
import 'dart:math' as math;
​
void main() {
 // 运用别号,引证包中的函数 。
 var a = math.max(1,100);
 print(a); //打印 100
}

10.3、导入包的部分内容

1)、有时分咱们不想导入整个包,只想导入包里边的某个类或许某个函数。Dart 供给了show 和 hide 关键字处理导入包的部分内容

//1、仅导入 max 函数, 导入多个内容运用逗号分隔,例如 show max,sin
import 'dart:math' show max;
​
//2、除了max函数,导入 math 中的一切内容。
import 'dart:math' hide max;

10.4、导入本地模块

在日常开发中,咱们会常常会导入本地的模块,一般项目中会有多个 dart 脚本文件,每个 dart 脚本完结不同模块的代码,在需求的时分直接导入 dart 脚本文件即可

//例如我有个本地模块:libs/stack.dart//导入本地模块
//直接经过本地文件途径导入 dart 脚本即可
import 'libs/stack.dart';

10.5、导入第三方开源包

10.5.1、查找第三方开源包

pub.dev/ :这个是 pub 的中央仓库, 上面有很多的第三方开源包,可以到这儿找到自己想要的包

10.5.2、装备依靠包

在项目根目录 pubspec.yaml 中装备 dependencies 特点,结构如下:

dependencies:

包名: 版别号

dependencies:
 http: ^0.13.5
 cupertino_icons: ^1.0.2

关于版别号阐明,如下:

^1.2.1 代表的更新版别规模为 >=1.2.1 && < 2.0.0

^0.2.1 代表的更新版别规模为 >=0.2.1 && < 0.3.0

^0.0.2 代表的更新版别规模为 0.0.2(相当于确定为了 0.0.2 版别)

规律: 实则便是把 ^ 后边非 0 的数字 +1 ,然后把其他位变为 0 便是它的最大版别。其他假如最终一位非 0 ,其他位为 0 ,就相当于锁版别。如:

最大版别:^1.2.1 => 2.2.1 => 2.0.0 规模:1.2.1-2.0.0
最大版别:^0.2.1 => 0.3.1 => 0.3.0 规模:0.2.1-0.3.0
^0.0.2:固定版别:0.0.2

10.5.3、下载依靠包

翻开命令行,输入如下命令:

flutter pub get

或许直接运用开发工具的可视化界面操作

10.5.4、导入第三方开源包

依靠包下载安装后,咱们就可以运用 import 导入第三方包,第三方包前缀为 package:

// 这儿导入 http 包,别号为 http
import 'package:http/http.dart' as http;

十一、Dart 反常处理

相似 Java,Dart 供给了 Exception 和 Error 两种类型的反常以及一些子类

1)、运用 throw 关键字抛出自界说类型反常,也可以将任何非 null 目标作为反常抛出

 throw Exception('这是一个反常');
 throw '这是一个反常';

小主张:一般主张抛出 Exception 和 Error , 或许他们的子类

2)、运用 try/on catch 配合捕获反常

void main() {
 try {
  var s;
  print(s.length);
  } on NoSuchMethodError catch (e) {
  //捕获反常并打印
  print(e);
  } catch (e, s) {
  //兜底处理 e:抛出的反常目标 s:栈信息,此参数可写可不写
  print(e);
  print(s);
  }
}

上述代码:

1、运用 on 和 catch 来捕获反常:on 用来指定反常的类型,catch 则用来捕获目标

2、当抛出的错误并不是 on 指定的反常类型时,则走最终面的 catch 兜底

3、兜底 catch 办法有两个参数,榜首个参数是抛出的反常目标,第二个参数是栈信息

3)、运用 rethrow 再次抛出反常

void exceptionMethod(){
 try {
  dynamic b = true;
  print(b++); //NoSuchMethodError
  } catch (e) {
  rethrow; //将上述反常再次抛出
  }
}
​
​
void main() {
 try {
  exceptionMethod();
  } catch (e) {
  //捕获反常并打印
  print(e);
  }
}

十二、Dart 异步处理

Dart 是单线程模型的言语,假如咱们在程序中做耗时操作:恳求 Api 接口,文件 IO 等,就可能导致点击事情没有响应,程序卡顿之类的状况。为了处理这种状况,Dart 引入了异步操作机制:

1、Dart 异步处理不会堵塞线程,其他使命可以持续运转

2、由于 Dart 的异步机制并不涉及线程的切换,仅仅是由咱们的编程言语去操控,所以它的履行功率十分高

12.1、Dart 异步处理的用法

1)、Dart 言语中,有很多库的函数回来 Future 或许 Stream 目标,这些目标都是 Dart 对异步编程支撑的完结

Future – 代表一个异步核算使命,可以获取使命的核算成果

Stream – 代表一个异步的数据序列,一般用于读取接连的数据或许事情

12.1.1、Future

1)、Future代表的是一个异步的核算使命,假如使命还没履行完结,咱们是拿不到异步使命的成果

import 'package:http/http.dart' as http;
​
void main() {
 var url = "https://www.baidu.com/";
 //调用 get 函数恳求 url, 回来一个封装了 http 恳求使命的 future 目标
 Future fTask = http.get(Uri.parse(url));
 //打印 future 目标
 print(fTask);
 
 // 向 future 目标注册回调函数,处理恳求成果
 fTask.then((response) => {
  print('Response status: ${response.statusCode}')
  });
 // 打印 main 函数完毕符号
 print('main end...');
}
​
//打印成果
Instance of 'Future<Response>'
main end...
Response status: 200
​
Process finished with exit code 0

上述代码:

1、首要打印了 fTask ,输出表明 fTask 是一个 Future 目标,将来会回来一个叫 Response 的成果目标

2、接下来打印了 main end… ,而不是先输出 http 的恳求状况码

3、最终打印了 http 的恳求状况码:Response status: 200 ,然后进程也退出了

上面这段程序在打印了 main end… 时进程并没有退出,而是比及打印了 http 的恳求状况码:Response status: 200 才退出,这也验证了咱们前面一个观念: Dart 的异步机制并不涉及线程的切换,仅仅是由咱们的编程言语去操控,所以它的履行功率十分高

12.1.2、await 和 async

上述这个比如存在一个问题:

1、需求注册回调函数,假如我有多层回调,可读性就会变得很差

此刻咱们可以运用 await 和 async 机制来处理这个问题,而且它还能让咱们运用同步的办法写出异步的代码

void main() async{
 var url = "https://www.baidu.com/";
 //恳求 url, 经过 await,等候 future 异步核算使命的成果,履行成功就直接回来成果
 var response = await http.get(Uri.parse(url));
 print('Response status: ${response.statusCode}');
 print('main end...');
}
​
//打印成果
Response status: 200
main end...

上述代码:

1、输出成果的次序,跟咱们书写代码的次序共同

2、经过符号 async 和 await 关键字,咱们的异步代码,看起来跟同步代码没什么差异:

1、async 关键字的作用便是符号一个函数是异步函数

2、await 关键字的作用是等候异步使命的成果

留意: await 关键字只能在符号了async 的异步函数中运用,不然会报错

12.1.3、Stream

1)、Stream 代表一个异步的数据序列,是一种异步读取流式数据的办法,运用格局如下:

await for (数据类型 变量 in stream类型变量) {

// 处理数据

}

Future<int> sumStream(Stream<int> stream) async {
 var sum = 0;
 await for (final value in stream) {
  sum += value;
  }
 return sum;
}
​
//async* 表明这是一个需回来 Stream 类型参数的异步函数
Stream<int> countStream(int to) async* {
 for (int i = 1; i <= to; i++) {
  //yield 在这儿表明暂时让出资源让其他代码履行
  yield i;
  }
}
​
void main() async{
 var stream = countStream(10);
 //经过 await 等候 sumStream 核算回来成果
 var sum = await sumStream(stream);
 print(sum);
}
//打印成果
55

上述代码咱们运用 await 符号 for in 循环句子,循环读取 stream 类型变量中的数据,代码书写也很直观,跟同步代码的书写办法共同

十三、Dart Isolate 并发

咱们知道一般常用的并发机制首要包含进程,线程以及后边的协程。可是 Dart 不一般 ,Dart 中的并发机制首要是由 Isolate 去完结的。所谓 Isolate ,你可以简略的了解是一种特别的线程

Isolate 的特点:

1、Isolate 之间不能同享内存

2、Isolate 之间只能经过音讯通讯

不能同享内存,意味着你不能像线程那样经过变量同享状况,每个 Isolate 都有自己独立的内存,这样设计的好处便是你不用加锁,也能安全的操作自己的数据

这儿你是否会有一个疑问️:前面咱们经过 Dart 异步机制处理了接口恳求之类的异步使命,不是也有相似并发的作用吗?那为什么还要引入 Isolate 并发机制呢?

答:前面咱们讲的异步机制你可以了解为一种假异步,由于它实际仍是在一个线程中去处理各种网络 IO,这些网络 IO 并不怎么消耗 CPU 资源,只是需求很多的等候恳求响应的时刻,因而咱们可以运用等候的闲暇时刻去处理其他使命,这便是异步机制可以进步功能的原因。这种机制其实和 Android Handler 机制有点相似。而现在假如你有一个核算量十分大的使命,例如:你需求对视频进行格局化处理,这个时分这些 CPU 密集型核算就会堵塞你的线程,导致其他使命都履行不了。因而针对这种比较耗 CPU 资源的使命,最好创立一个 Isolate 去处理,避免堵塞主 Isolate (也便是主线程),这样也可以运用设备的多核特性

13.1、Isolate 根本用法

// 导入 isolate 包
import 'dart:isolate';
​
void main() {
 // 经过 Isolate.spawn 静态函数,创立一个新的 Isolate
 // spawn 是一个泛型函数,承受一个泛型参数,表明 Isolate 进口函数承受的参数类型
 // 这儿 spawn 的泛型参数是 String,subTask 是进口函数
 // 第二个参数跟泛型参数类型共同,表明传递给进口函数的参数,这儿传入的是字符串
 Isolate.spawn<String>(subTask, "my task");
 print("main func end.");
}
​
// Isolate 进口函数界说,承受一个 String 参数
// 进口函数的参数类型由上面的 spawn 的泛型参数决定
void subTask(String msg){
 print("subTask receive: $msg ");
}
​
//打印成果
main func end.
subTask receive: my task

经过输出,咱们发现先打印了 main func end,然后,履行新建 Isolate 的进口函数。 假如咱们想让代码履行次序,跟咱们书写次序共同的话,可以运用 await 关键字等候 Isolate 履行完毕:

// 导入 isolate 包
import 'dart:isolate';
​
// 运用 async 关键字将 main 函数符号为一个异步函数,这样才干运用 await 关键字
void main() async{
 // 运用 await 关键字等候使命履行完结
 await Isolate.spawn<String>(subTask, "my task");
 print("main func end.");
}
​
// Isolate 进口函数界说,承受一个 String 参数
void subTask(String msg){
 print("subTask receive: $msg ");
}
​
//打印成果
subTask receive: my task 
main func end.

13.2、Isolate 音讯通讯

多个 Isolate 之间只能经过音讯进行通讯,那么咱们如何去获取一个 Isolate 回来的成果呢?

答:首要经过 ReceivePort 和 SendPort 两个类处理音讯通讯

1)、ReceivePort 负责接收 SendPort 发送的音讯, SendPort 和 ReceivePort 是捆绑联系, SendPort 是由 ReceivePort 创立的

void main() async{
  // 创立一个 ReceivePort 用于接收音讯
  var recv = ReceivePort();
​
  // 创立一个 Isolate,泛型参数为 SendPort,进口函数为 subTask
  // subTask 进口函数的参数为 SendPort 类型,因而 spawn 第二个参数,传入 recv 的 sendPort 目标
  Isolate.spawn<SendPort>(subTask, recv.sendPort);
​
  // 运用 await 等候 recv 的榜首条音讯
  var result = await recv.first;
  print("receive:$result");
}
​
// Isolate 进口函数界说,接收一个 SendPort 目标作为参数
void subTask(SendPort port){
 // 运用 SendPort 发送一条字符串音讯
 port.send("subTask Result");
}
​
//打印成果
receive:subTask Result

十四、总结

本篇估计是我写过最长的文章了,比之前写 Kotlin 入门那一篇还要长。总的来说,这篇文章几乎涵盖了 Dart 的一切语法常识,假如你可以耐性看到这儿,并手敲里边的示例,相信你一定收成很大。假如觉得我写的还不错,请给我点个赞吧

感谢你阅览这篇文章

下篇预告

根底打好了,下篇文章咱们就正式进入到 Flutter 的学习了,敬请期待吧

参阅和引荐

一文搞定Dart语法

Dart言语教程

Flutter 根底 | Dart 语法

Dart 官方教程

你的点赞,评论,是对我巨大的鼓舞!

欢迎关注我的大众号: sweetying ,文章更新可榜首时刻收到

假如有问题,大众号内有加我微信的进口,在技能学习、个人生长的道路上,咱们一同前进!