一、初识 Dart
1.1 什么是 Dart
Dart 是 Google 于 2011 年发布的一门编程语言,其属性应用层的语言。Dart 是一门面向对象语言,集成了各家语言之所长,所以它的用法有时类似于 java ,有时类似于 js ,连 c++、swift 等语言的身影都有
1.2 安装 Dart
无论是 window/mac 还是 linux 安装 Dart 都有两种方式:命令行安装和下载 SDK 安装,具体可以查看官网
官网地址:https://dart.dev/get-dart,具体安装官网介绍非常详细
SDK 下载地址:https://dart.dev/tools/sdk/archive
PS:如果安装了 Flutter 可以不用再单独下载 dart 安装了「Flutter 依赖 Dart」,完全可以把相应的 flutter 安装目录下的 Dart 配置到环境变量即可
环境变量配置好以后,在命令行中输入 dart 来验证环境是否配置好
1.3 开发工具
分别安装 Dart 插件即可,以 vscode 为例
我们使用 vscode 来编写 dart 代码
二、永不过时的 HelloWorld
2.1 HelloWorld
我们来编写永不过时的 HelloWorld,无论什么语言都要有程序的入口,比如 java
public static void main(String args[]){ // 程序主入口
}
在 Dart 中也有程序入口,新建一个 hello.dart 文件,输入以下内容
main(List<String> args) {
// 函数体
print('HelloWorld') ; // 输出 HelloWorld 语句以 ; 结尾 、
//print("HelloWorld") ; // 其中字符串以 '' 或 " " 括起来都行
}
看起来和 java 非常相似 Dart 就去掉了一些无关的关键字而已
2.2 运行程序
运行 HelloWorld 程序有两种方法
- 在命令行使用
dart hello.dart
「要在程序在的目录下输出」
- 点击 debug 左边的 run 查看结果
2.3 小结
- Dart 有一个程序入口也就是 main 函数,并且此函数没有返回值,参数是一个 String 类型 list 集合,如果我们想传递参数给 args 直接使用 dart hello.dart 参数1,… 参数 n 即可
- 我们可以使用 print() 语句向控制台输出,类似于 js 的 console.log/java 的 System.out.println() 等
- 运行程序可以使用命令行 dart xxx.dart 也可以直接在 vscode 中使用调度的 run 按钮输出到调试控制台
- 每条语句结束以后以 ; 结束
三、变量
3.1 变量初始化
变量初始化基本格式:
类型 名称 = 值
在 Dart 中有两种方式初始化变量:
- 静态初始化:使用具体类型来初始化就是
- 动态初始化:使用 var 初始化,让系统自动推导出类型,其在运行时确定变量的类型
静态初始化
int age = 18 ;
String name = 'tigerchain' ;
动态初始化举例「类型自动推导」
var age = 12 ; // 自动推导 int 类型
var name = 'tigerchain' ; // 自动推导字符串类型
var love = ['读书','看电影','侃大山'] ; // 自动推导为数组类型
ps: var 和具体类型不能一块使用,并且 var 赋值以后类型就确定了,不能修改,以下使用是错误的
var int age = 18 ;
var String name = "tigerchain"
var age = 18 ;
age = "tigerchain" // 类型确定是 int 型,就不能修改成 String 类型
小结
- var 可以接受任何变量的类型,在运行时确定变量类型
- 使用 var 声明的变量一旦赋值类型就确定了,不能再修改其类型
3.2 final 和 const
如果想让一个变量从来都不被修改,那么可以使用 final 和 const 来声明「const 是编译时就要确定值得,而 final 是运行时确定值」
final
在 java 中 final 是变量不能修改,方法不能重写,类不能被继承,而 Dart 中的 final 只能修饰变量,不能修饰方法和类
格式:
final 类型 变量名 = 值 其中类型可以不写,会自动推导
正确用法
final name = 'tigerchain' ; // 等价于 final String name = 'tigerchain' ;
final int age = 18 ;
来一个错误例子
// 定义一个 final 变量
final name ;
运行报错
由此可知 final 修饰的变量必须要初始化,否则报错
错误使用如下:
final name ; // 未初始化变量
final var age = 18 ;// final 不能和 var 同时使用
final age = 18 ;
age = 24 ; // 一旦赋值不能修改
const
const 和 final 类似我们来看例子
const name ; // 错误必须初始化
const var age = 18 ; // 错误 const 不能和 var 同时使用
const age = 18 ; // 等价于 const int age = 18 ;
final 和 const 相同点总结
- final 和 const 声明的变量必须初始化「不初始化就相当于后面可以修改,和 final/const 设置违背」,并且初始化后不允许修改
- final 和 const 声明变量时可以指定具体的类型,也可以省略具体类型 如:final int age = 18 ; int 可写可不写
- final 和 const 不能和 var 同时使用
再来看看 final 和 const 有什么不同点,示例如下:
// demo1
final finalTime = DateTime.now() ;
const constTime = DateTime.now() ; // The constructor being called isn't a const constructor
print(finalTime) ;
print(constTime) ;
//demo2
final finalList = [1,2,3] ;
finalList[0] = 3 ; // 可以修改 list 内部
print(finalList) ; // [3, 2, 3]
const constList = [4,5,6] ;
constList[0] = 6 ;
print(constList) ; // Unsupported operation: Cannot modify an unmodifiable list 不能修改 list 内部
// demo3
final finalArray = [1,2,3] ;
final finalArray1 = [1,2,3] ;
const constArray = [4,5,6] ;
const constArray1 = [4,5,6] ;
// identical 用来判断两个变量是否指向同一个对象
print(identical(finalArray, finalArray1)) ; // false
print(identical(constArray, constArray1)) ; // true
示例代码分析
先看看 final 和 const 的特点
(1)、运行时和编译时常量
- final 是在运行时就要确定变量的值,也叫
运行时常量
,其在运行时第一次使用前才被初始化 - const 在编译期间就在确定变量的值,也叫
编译时常量
demo1 中 DateTime.now() 是一个函数肯定是程序运行起来才能获得,所以使用 final 没毛病,使用 const 就出错了「除非构造方法改为 const 势必要修改字段为 final」
demo2 中可以看到 final 的 list 可以修改内部值,但 const 就不行,由此可知
(2)、final 的不可变性没有传递性,而 const 的不变可性有传递性
demo3 中可以看到使用 final 声明的两个值得相同的变量不指向同一个对象「结果为 false」,而使用 const 声明的两个值相同的变量指向的是同一个对象「在内存中只存储一份」,由此可行
(3)、使用 final 修饰的值相等变量指向对象会重复创建,而 const 则引用同一个对象
ps: 这里注意一点对于一些内置的字面量的数字、字符串、布尔等使用 final 修饰 identical 判断也为 true 如:
final name = "tigerchain" ; // 使用 var 结果也 true
final name1 = "tigerchain" ;
print(identical(name, name1)); // true
这一点官网有说明
示例分析完毕,final 和 const 的不同点也总结出来了即上述的 (1)(2)(3)
四、内置数据类型
在 Dart 中所有的变量最终都是一个对象,比如 int double String 都是一个类,其声明的变量都是一个对象
4.1 Number 类型
Number 分为两种类型:
- int
- double
4.2 String 类型
Dart 中的字符串是一组 UTF-16 单元序列。字符串可能通过单引号或双引号来创建
定义字符串
String name = 'tigerchain' ;
var str = "i like book" ;
多行字符串使用三个点 ‘’’
var demo = '''
我是
中国
人
''' ;
字符串类型可以使用 ${} 来嵌入表达式来输出结果
var str = "tigerchain" ;
print("i am ${str}") ;// 如果是一个变量不是表达式则可以省略 {} 即 "$str"
4.3 Bolean 类型
在 Dart 中 Boolean 类型使用 bool 来表示,其值可以为 true 或 false ,bool 声明的变量是编译时常量
var flag = true ; // 等价于 bool flag = true ;
ps: Dart 类型安全所以你不能使用 if (nonbooleanValue) 或者 assert (nonbooleanValue),也就是说你不能直接
使用非 0 或非空作判断,案例如下:
var name = "";
if(name){ // 使用错误
xxx
}
if(name.isEmpty){ // 正确使用
xxx
}
var num = 0 ;
if(num) { // 使用错误
xxx
}
if(num >= 0){ // 正确使用
xxx
}
var address ;
if(address){ // 使用错误
xxx
}
if(address == null){ // 正确使用
xxx
}
也就是说 if(表达式) 表达式的结果要是 true/false
4.4 Collections 类型
对于集合类型来说 Dart 内置了三种最常见的类型 List、Set 和 Map
4.4.1 List
在 Dart 中数组使用 List 表示,类似于 java 中的 ArrayList
List 的特点:是有序可以重复
,集合元素可以重复
List 的创建有二类–四种方式:
// 第一类:使用字面量方式
// 1、使用字面量方式创建
var list1 = [1,2,3] ;
print(list1) ;
// 2、明确指定类型
List<String> list2 = ["1","1","3"];
print(list2) ;
// 第二类:使用构造方法创建
// 1、自动推导
var list3 = List<int>() ;
list3.add(1) ;
print(list3) ;
// 2、明确指定类型
List<int> list4 = List<int>() ;
list4.add(5) ;
print(list4) ;
4.4.2 Set
Set 是无序且不能重复
的集合,创建方式如下:
// 字面量方式
var books = {"西游记","红楼梦","三国演义","水浒传"} ;
// 创建一个空 set,且只能装 String
var names = <String>{} ; // 等价于 Set<String> names = {} ; 但是不能是 var nams = {} ;
names.add("tigerchain") ;
print(names) ;
// 明确指定类型
Set<int> nums = {} ;
4.4.3 Map
Map 通常是用来存储键值对的 key-value,,key 和 value 可以是任意类型对象,但是 key 不能重复,value 可以重复
在 Dart 中声明 Map 也分为自动推导和明确类型两种定义方式:
// 1、字面量声明 map 类型自动推导
var userInfo = {
"name":"tigerchain",
"age":18,
"sex":"男"
};
print(userInfo) ;
// 2、明确类型 字面量 key 是 String value 可以是任意对象
Map<String,Object> myInfo= {
"name":"tigerchain",
"age":18,
"sex":"男",
"address":"china"
};
print(myInfo) ;
// 3、构造方法
var info = Map() ; // 没有泛型就根据添加类型推导 key 和 value 类型
info[1] = "one" ;
info[2] = "two" ;
info[3] = "three" ;
info["name"] = "tigerchain" ;
print(info) ;
// 4、构造方法明确类型
Map<String,String> books = Map<String,String>() ; // 等价于 var books = Map<String,String>() ;
books["name"] = "红楼梦";
books["sale"] = "18元" ;
print(books) ;
注意:我们来看看 Map 的源码
Map 是一个抽象类,但是为什么我们可以实例化呢?难道 Dart 中的抽象类是可以实例化?,其实不是的。这里我们看一下注释第一句其实 Map() 的默认实现是 LinkedHashMap,这里扩展一下
在 Dart 中抽象类不可以被直接实例化的,但是配合工厂(factory) 就能间接的实例化了,举个例子
// 定义一个抽象类
abstract class Person {
factory Person() = TigerChain ;
}
// 实现这个抽象类
class TigerChain implements Person{}
// 调用
var p = Person() ; // 没有问题可以实例化抽象方法
print(p.runtimeType) ; // 输出 TigerChain 类型
回过头来看 Map() 没有它的子类去实现呀「猜想肯定在某个地方实现了」,我们注意到 external 这个字段,external 就是方法的声明和实现分开,其只声明方法,而具体的实现由不同平台去实现 runtime ,我们在源码中找找
flutter/bin/cache/dart-sdk/lib/_internal/vm/lib/map_patch.dart
其中有句代码
factory Map() => new LinkedHashMap<K, V>();
可以看到 Map 的实现类是 LinkedHashMap ,这是 Map 可以实例化的根本原因。 List 和 Set 也类似,只不过 Set 中默认有一个实现类 factory Set() = LinkedHashSet<E>;
没有 external 而已
五、函数
5.1 函数的定义和基本使用
前面我们知道在 Dart 中 内置的一些如 int double 等都是类,其实函数也是一种类型即 Function
函数也叫方法,在 Dart 中函数的基本格式如下
[返回值] 函数名([参数1,参数1,...,参数n]) {
函数体
}
函数可以分为四种类型:
- 无参数无返回值
void fun1() {
print("this is fun1") ;
}
- 无参数有返回值
double getPI() {
return 3.141 ;
}
- 有参数无返回值
void userInfo(String name,int age){
print("i am $name i'm $age years old") ;
}
- 有参数有返回值
int add(int num1,int num2){
return num + num2 ;
}
注意: 在 Dart 中所有函数都有返回值,在 Dart 可以不写返回类型,也可以不写 return 语句默认返回 null,总结起来就是
- 无返回类型,无 return 语句,默认返回 null
- 无返回类型,有 return 语句,返回 Object,自动推导其对应的类型
// 无返回类型,默认返回 Object 这里自动推导成 int
defalutReturnObject() {
return 0 ;
}
// 无返回类型,无 return 语句打印出来还返回一个 null「返回值」
defultReturnNull() {
print("tigerchain") ; // tigerchain null 后面的 null 就是返回值
}
5.2 函数的参数
在 Dart 中函数的参数分为两类:
- 必选参数
- 可选参数
5.2.1 必选参数
必选参数即每个参数都是必须的不能省略「这没什么即说的」
5.2.2 可选参数
可选参数又分为位置可选参数和命名可选参数
基本格式如下:
位置可选参数--[参数1,参数2,...,参数n]
命名可选参数--{参数1,参数2,...,参数n}
位置可选参数顾名思义是要按参数的顺序去传递,如:
// 位置可选参数使用 [],参数传递要按顺序去传递,并且参数可以有默认值
void userInfo(name,[int age,String address]){
print("$name -- $age -- $address") ;
}
// 函数调用
userInfo('tigerchain') ; // tigerchain -- null -- null
userInfo('tigerchain',18) ;//tigerchain -- 18 -- null
userInfo('tigerchain',18,"china") ;//tigerchain -- 18 -- china
userInfo('tigerchain',"china",18) ; // The argument type 'String' can't be assigned to the parameter type 'int' 没有按顺序传递参数报错
命名可选参数可以不按顺序传传递参数,但是必须要指定参数名字,如下:
// 命名可选参数使用 {} ,参数传递是无序的,并且参数可以有默认值
void userInfo2(name,{int age,String address}) {
print("$name -- $age -- $address") ;
}
// 函数调用
userInfo2('zhang') ; // zhang -- null -- null
userInfo2('wang',age: 18) ; // wang -- 18 -- null
userInfo2('li',address: 'china',age: 18) ;// li -- 18 -- china
在 Flutter 中我到处可见命名可选参数,基本每个 Widget 的构造函数都有使用,如 Flutter 的 Text 组件
5.2.3 参数默认值
只有可选参数才可以设定默认值
// 可选参数才可设定默认值
void setDefaultParams({String name="tigerchain",int age = 18}) {
print("i am $name and $age") ;
} // i am tigerchain and 18
5.3 函数是一等对象
我们知道在 Dart 中函数也是一种类型,即函数也是对象这和 js 有点类似,既然函数是一种类型那么函数就可以使用一个变量去接收,也可以将函数作为别一个函数的参数或者返回值
// 定义一个参数为函数的函数
void funMethod(Function myFun){
myFun() ;
}
// 一个函数可以作为别一个函数的参数
void calledFun0() {
print('我是被调用的函数0') ;
}
// 调用
// 传递的时候传递参数的声明
funMethod(calledFun0) ;
下面看一个有返回值的函数
// 定义一个有返回值的函数
int calledFun1(int a,int b) {
print("我是被调用的方法") ;
return a +b ;
}
// 定义一个函数「其中一个参数也是函数」
int funMethod1(Function myFun,int a,int b) {
return myFun(a,b) ;
}
// 调用
int result = funMethod1(calledFun1,1,2) ;
print(result) ;
5.4 匿名函数
匿名函数如其名是没有名字的函数也叫 lambda 或 closure,其格式如下
([类型] 参数1[,...参数n]){
xxx // 函数体
}
看一个简单例子
// 定义一个函数
void funMethod(Function myFun){
myFun() ;
}
// 调用
funMethod((){
print('我是匿名函数') ; // 我是匿名函数
}) ;
// 匿名函数赋给一个变量
var method = (){
print("匿名函数给一个变量") ;
} ;
// 调用
funMethod(method) ; // 匿名函数给一个变量
带参数的匿名函数
// 定义一个参数是函数的函数
int funMethod1(Function myFun,int a,int b) {
return myFun(a,b) ;
}
//调用
// 带参数匿名函数 其中 int 可以省略
funMethod1((int a,int b){
print(a+b) ;
}, 1, 2) ;