[Flutter]flutter基礎(chǔ)之Dart語言基礎(chǔ)(一)

一、概述

Dart 是 Google 公司在2011年10月發(fā)布的一種編程語言,主要是將其作為一種結(jié)構(gòu)化的 Web 開發(fā)語言??梢杂迷赪eb、服務(wù)器、移動(dòng)應(yīng)用和物聯(lián)網(wǎng)等領(lǐng)域的開發(fā),是寬松開源許可證(修改的BSD證書)下的開源軟件。官方網(wǎng)站:https://dart.dev/

在 Dart 中,所有東西都是對(duì)象,變量,數(shù)字,函數(shù)等都是對(duì)象類型,都是類的實(shí)例,所有對(duì)象都繼承自 Object 類。

在進(jìn)行 Dart 開發(fā)前,需要安裝 Dart SDK,安裝方法官網(wǎng)網(wǎng)站有介紹,地址為:https://dart.dev/get-dart 。如直接進(jìn)行 Flutter 開發(fā),也可不用下載此 SDK ,直接安裝 Flutter 即可。Flutter開發(fā)工具包中包含 Dart。

二、語法基礎(chǔ)

1. 入口方法

Dart 語言文件格式為 .dart ,并以 main 方法作為程序的入口點(diǎn),我們使用 VSCode 創(chuàng)建 demo1.dart 文件,代碼如下:

main(){
  print('hello dart!');  //輸出結(jié)果:hello dart!
}

Dart 語言有很多簡寫形式,以上的 main 方法就是簡寫,完整的寫法如下:

void main(List<String> args){
  print('hello dart!');
}

參數(shù)為泛型的寫法,List 為列表,也就是其他語言中的數(shù)組。 main 方法可以接收由命令行輸入的參數(shù),可以通過 args 參數(shù)在程序中獲取。如下代碼:

void main(List<String> args){
  print('hello dart!');
  print(args);
}

通過命令行的方式運(yùn)行 dart 文件,首先 cd 到目標(biāo)文件夾,運(yùn)行如下命令: dart demo1.dart arg1,arg2 ,輸出結(jié)果如下:

hello dart!
[arg1,arg2]

一般情況下,使用省略方式即可。

在 Dart 中,print 方法不支持多參數(shù)打印,其函數(shù)原型如下:

void print(Object object);

只支持單參數(shù)打印,但是可以利用字符串插值的方式進(jìn)行多參數(shù)打印,使用 $ 美元符號(hào),比如打印 a, b兩個(gè)變量,如下:

print("$a $b");

以上是利用字符串插值進(jìn)行多參數(shù)打印,但實(shí)質(zhì)上依然是單參數(shù),只不過利用了字符串的插值特性輸出多個(gè)參數(shù)而已。當(dāng)然依然可以利用多個(gè) print 打印多個(gè)參數(shù)。

2. 數(shù)據(jù)類型

Dart 中所有類型都是對(duì)象,基礎(chǔ)數(shù)據(jù)類型也是對(duì)象,都有自己的方法和屬性。Dart 支持自動(dòng)類型推斷,所以在定義數(shù)據(jù)類型的時(shí)候,可以通過兩種方式進(jìn)行定義,直接使用具體數(shù)據(jù)類型定義和使用 var 關(guān)鍵字進(jìn)行定義。當(dāng)使用 var 定義時(shí),編譯器會(huì)根據(jù)具體的賦值數(shù)據(jù)類型推斷出定義的變量或常量的數(shù)據(jù)類型。

可以使用 runtimeType 來查看對(duì)象的運(yùn)行時(shí)類型。

因?yàn)?Dart 中所有的類型都是對(duì)象,所以定義一個(gè)數(shù)據(jù)類型以后,如果未對(duì)其進(jìn)行初始化操作,則其默認(rèn)值為null

Dart 中,變量是區(qū)分大小寫的。

**2.1.1 數(shù)值類型 (Numbers) **

Dart 中數(shù)值類型有兩種,整數(shù)( int )和小數(shù)( double),他們繼承自 num 類。使用直接數(shù)據(jù)類型定義方式如下:

main(){
  int a = 10;
  double b = 20.1;
  print(a);  //輸出10
  print(b);  //輸出20.1
  print(a+b);////輸出30.1
}

也可以使用如下方式定義:

main(){
  var a = 10;
  var b = 20.1;
  print(a);
  print(b);
  print(a+b);
  print(a.runtimeType);  //輸出int
}

輸出結(jié)果與上面相同,下面的方式編譯器會(huì)自動(dòng)推斷出具體的數(shù)據(jù)類型。

Dart 中可以使用十進(jìn)制和十六進(jìn)制進(jìn)行整數(shù)的賦值,不能使用其他進(jìn)制。

2.1.2 字符串類型 (Strings)

使用關(guān)鍵字 String 來聲明字符串類型,字符串使用單引號(hào)或雙引號(hào)包起來,Dart 中字符串是UTF-16編碼,如下:

main(){
  String str1 = 'hello dart!';
  var str2 = "hello world!";
  print('$str1 $str2');  //輸出 hello dart! hello world!
}

也可以在定義一個(gè)變量的同時(shí)直接賦值多個(gè)字符串,如下:

main(){  
  String str = 'hello dart! ' 'this is'
   " a string";
  print(str);  //輸出 hello dart! this is a string
}

可以看到上面的代碼,我們定義了一個(gè)變量,但是賦值了三個(gè)字符串,編譯器會(huì)將三個(gè)字符串合并為一個(gè)字符串進(jìn)行處理。但是雖然我們分兩行書寫的字符串,但是在輸出時(shí)并沒有分行,也就是定義與輸出的格式并不一樣,如果需要定義與輸出一樣,可以使用如下方式:

main(){  
  //1.通過轉(zhuǎn)義字符\n進(jìn)行換行操作
  String str = "hello dart! \nthis is a string ";
  print(str);

  //2.通過```或"""將字符串包裹起來為多行字符串,使用此方式時(shí),需注意字符串的對(duì)齊方式,字符的前面的空格會(huì)包含在內(nèi),左邊界為編譯器的行頭
  var str1 = '''hello dart! 
  this is a string''';
  print(str1);
}

輸出結(jié)果如下:

hello dart! 
this is a string 
hello dart! 
  this is a string

可以使用加號(hào)和插值的方式進(jìn)行拼接,如下:

main(){  
  String a = "hello";
  String b = " dart!";
  print(a + b);  //輸出 hello dart!
  print("$a$b"); //輸出 hello dart!
}

使用字符串插值的方式進(jìn)行拼接時(shí),如果 $ 后為一個(gè)標(biāo)識(shí)符則可直接使用,如果為表達(dá)式則需要使用 {} 將表達(dá)式包含起來,如下:

main(){  
  String a = "hello";
  String b = " dart!";
  print(a + b);
  print("${a.runtimeType} ${b.runtimeType}"); //此處需要使用{}將表達(dá)式包含起來作為一個(gè)標(biāo)識(shí)符使用
}

在 Dart 中,字符串也是一種集合類型,可以通過如下方式獲取字符串中的單個(gè)字符:

main(){  
  String str = "hello dart!";
  print(str[0]);  //輸出 h
  print(str[4]);  //輸出 o
}

下標(biāo)從0開始。

也可以通過 * 運(yùn)算符對(duì)字符串進(jìn)行拷貝操作,如下:

main(){  
  String str = "hello dart!";
  print(str*2);  //輸出 hello dart!hello dart!
}

使用如下方式原樣輸出字符串,包括里面的轉(zhuǎn)義字符:

main(){  
  var str = r"this is a \n string!";
  print(str);  //輸出 this is a \n string!
}

2.1.3 布爾類型 (Booleans)

Dart 中,布爾類型只有兩個(gè)字面量值:truefalse 。

main(){  
  bool a = true;
  var b = false;
  print("$a $b");  //輸出 true false
  print(b.runtimeType);  //輸出 bool
}

上面說過,Dart中所有的類型都是對(duì)象,如果在定義 bool 類型時(shí),沒有進(jìn)行初始化,則其默認(rèn)值為 null,在進(jìn)行條件判斷時(shí)會(huì)報(bào)異常,如下:

main(){  
 bool c;
 if(c) {   
   print(c);
 }
}

上面的代碼編譯不會(huì)報(bào)錯(cuò),運(yùn)行時(shí)會(huì)報(bào)如下異常:

Unhandled exception:
Failed assertion: boolean expression must not be null

所以在使用 bool 類型進(jìn)行條件判斷時(shí),如果有為空的可能應(yīng)先進(jìn)行是否為 null 的判斷,并且 Dart 中的條件判斷必須使用 bool 類型,其他類型會(huì)編譯錯(cuò)誤。

2.1.4 集合類型

列表 (Lists)

列表用來存放一組數(shù)據(jù),在很多其他編程語言中,列表也被稱為數(shù)組,所以與其他語言一樣,下標(biāo)從0開始,最后一個(gè)數(shù)據(jù)元素的下標(biāo)為 lenght-1 。列表的具體數(shù)據(jù)類型由其中的元素類型決定,使用方式如下:

main(){  
  var arr  = [1, 2, 3];
  var arr1 = [1, 2, 3, "string"];
  List arr2 = [1, 2, 3];
  List arr3 = [1, 2, 3, "string"];

  print("$arr \t\t\t ${arr.runtimeType}");
  print("$arr1 \t ${arr1.runtimeType}");
  print("$arr2 \t\t\t ${arr2.runtimeType}");
  print("$arr3 \t ${arr3.runtimeType}");
}

輸出結(jié)果如下:

[1, 2, 3]                    List<int>
[1, 2, 3, string]    List<Object>
[1, 2, 3]                    List<dynamic>
[1, 2, 3, string]    List<dynamic>

以上方式直接創(chuàng)建 List 并進(jìn)行初始化操作,定義空列表,使用 var arr = []; 即可。需要注意如果不進(jìn)行初始化,其數(shù)據(jù)類型為 null ,即便使用 List arr; 定義,其 runtimeType 類型也為 Null 。

也可使用構(gòu)造方法創(chuàng)建數(shù)組,如下:

main(){  
  var arr = new List();   //創(chuàng)建空列表
  var arr1 = new List(3); //創(chuàng)建長度為3的列表,使用null填充
  var arr2 = new List.filled(3, 1); //創(chuàng)建長度為3的列表,并使用整形數(shù)據(jù)1填充
  var arr3 = new List.filled(3, "a"); //創(chuàng)建長度為3的列表,并使用字符串a(chǎn)填充

  print("$arr \t ${arr.runtimeType}");
  print("$arr1 \t ${arr1.runtimeType}");
  print("$arr2 \t ${arr2.runtimeType}");
  print("$arr3 \t ${arr3.runtimeType}");
}

輸出如下:

[]                                      List<dynamic>
[null, null, null]    List<dynamic>
[1, 1, 1]                     List<int>
[a, a, a]                     List<String>

在 Dart 中,可是省略 new 關(guān)鍵字,如下:

main(){  
  var arr = List(2);
  arr[0] = 10;
  arr[1] = "str";
  print("$arr \t ${arr.runtimeType}");   //輸出 [10, str]      List<dynamic>
  print(arr.length);  //輸出 2
}

以上創(chuàng)建的列表都是可存放任意類型的數(shù)據(jù)的,如果想存放固定類型的數(shù)據(jù),可以使用泛型進(jìn)行數(shù)據(jù)類型約束,使用方式如下:

main(){  
  List<String> arr = ["str1", "str2"];
  print(arr);  //輸出 [str1, str2]

  var arr1 = List<int>();
  arr1.add(1);
  arr1.add(2);
  print(arr1);  //輸出 [1, 2]
}

創(chuàng)建固定數(shù)據(jù)類型的列表,如果添加其他數(shù)據(jù)類型數(shù)據(jù)編譯器會(huì)報(bào)錯(cuò)。

集合 (Sets)

SetList 類似,但是 Set 為無序列表,所以不能通過下標(biāo)的方式進(jìn)行訪問。 Set 中不能有重復(fù)數(shù)據(jù),如果存在重復(fù)數(shù)據(jù),只會(huì)保留一份數(shù)據(jù)。依靠此特性,一般使用 Set 來對(duì)列表進(jìn)行去重處理。

main(){  
  Set set = {1, 2, 3, 4, 2};
  print(set);   //輸出 {1, 2, 3, 4}

  var set1 = {"str1", "str2", "str1", 1, 2, 3, 2};
  print(set1);  //輸出 {str1, str2, 1, 2, 3}

  var set2 = Set();
  set2.add(1);
  set2.add(2);
  set2.add(2);
  set2.add("str");
  print(set2);  //輸出 {1, 2, str}

  Set<int> set3 = Set();
  set3.add(1);
  // set3.add("str");  //錯(cuò)誤
  print(set3);  //輸出 {1}

  Set set4 = Set<String>();
  print(set4);  //輸出 {}
}

數(shù)組去重處理,如下:

main(){  
  var arr = [1, 2, 3, 4, 5, 6, 3, 4];
  Set set = Set.from(arr);
  var arr1 = List.from(set);
  print(arr1);  //輸出 [1, 2, 3, 4, 5, 6]
}

字典 (Maps)

字典是一組鍵值對(duì)的集合,可以通過鍵完成對(duì)值的修改、查找、添加或刪除操作,其中,鍵( key ) 必須是唯一的。創(chuàng)建 Map 類型,鍵 (key) 與值 (value) 要成對(duì)出現(xiàn)。一般情況下,鍵都是字符串類型,但是 Dart 中并沒有嚴(yán)格約束鍵的數(shù)據(jù)類型,所以鍵可以為任意類型,值也可以為任意類型。如果需要?jiǎng)?chuàng)建固定類型的鍵值,可通過泛型進(jìn)行約束。

main(){  
  var map1 = {
    "name" : "zhangsan",
    "age" : 20
  };

  var map2 = {
    1 : 20,
    2 : 30.2
  };

  Map map3 = {
    "name" : "lisi",
    1 : [1, 2, 3]
  };

  Map map4 = new Map();

  Map map5 = Map.from(map1);

  Map<String, int> map6 = {
    "number" : 1,
    "age" : 20
  };
  
  Map map7 = Map<String, dynamic>();
  map7["name"] = "wanger";
  map7["age"] = 20;

  print(map1);  //輸出 {name: zhangsan, age: 20}
  print(map2);  //輸出 {1: 20, 2: 30.2}
  print(map3);  //輸出 {name: lisi, 1: [1, 2, 3]}
  print(map4);  //輸出 {}
  print(map5);  //輸出 {name: zhangsan, age: 20}
  print(map6);  //輸出 {number: 1, age: 20}
  print(map7);  //輸出 {name: wanger, age: 20}
}

2.1.5 泛型

泛型,即通用類型,使 Dart 中的類型更加動(dòng)態(tài),提高了代碼的重用率。Dart 中使用 <T> 的方式來定義泛型,T 為標(biāo)識(shí)符或關(guān)鍵字,一般用 T 表示。上面的例子中也使用了泛型來約束數(shù)據(jù)類型,例如列表:

main(){  
  var arr = List<String>();
  arr.addAll(["str1", "str2", "str3"]);
  print(arr);  //輸出 [str1, str2, str3]
}

2.1.6 符文 (Runes)

符文(runes)是字符串的UTF-32編碼點(diǎn),要在字符串中表示32位的Unicode值,可使用 \uxxxx 形式,xxxx 為四位十六進(jìn)制值,如多于或少于四位,則需要使用 {} 包裝。

main(){  
  var value = '\u{1F362}';
  print(value);  //輸出 
}

2.1.7 符號(hào)類型 (Symbols)

Symbol 表示運(yùn)算符或標(biāo)識(shí)符,使用相同名稱創(chuàng)建的符號(hào)是相等的,有如下兩種方式創(chuàng)建符號(hào):

main(){  
  var symb = Symbol("s");  //通過構(gòu)造方法創(chuàng)建符號(hào)
  var symb1 = #s;                //通過#創(chuàng)建符號(hào)
  print(symb.runtimeType);  //輸出 Symbol
  print(symb1.runtimeType); //輸出 Symbol

  if(symb == symb1) {
    print("相同");
  }
}

2.1.8 常量

以上聲明的變量都是可變量,也就是變量,有時(shí)候需要一些數(shù)據(jù)是不可變的,稱為常量。在 Dart 中,定義常量可以通過關(guān)鍵字 constfinal 來定義,如下:

main(){
  const a = 10;  //聲明具體數(shù)據(jù)類型和var關(guān)鍵字
  final String b = 'hello dart!';
  print("$a $b");  //輸出 hello world! hello dart!
  print("${a.runtimeType} ${b.runtimeType}");!  //輸出 String String
}

可以看出,在聲明常量時(shí),可以省略其數(shù)據(jù)類型,編譯器會(huì)自動(dòng)進(jìn)行推斷。無論使用 const 還是 final 聲明的常量都是不可以改變的,并且在定義常量時(shí),在定義的同時(shí)就需要對(duì)其進(jìn)行初始化操作,否則會(huì)報(bào)錯(cuò)。如果試圖對(duì)常量進(jìn)行重新復(fù)制,也會(huì)報(bào)錯(cuò)。

上面的代碼中,使用 constfinal 聲明的常量,其輸出的最終數(shù)據(jù)類型都為 String ,那么具體他們之間的區(qū)別是什么呢?

const 定義的常量在定義時(shí)必須賦值為一個(gè)常量值,不可以通過表達(dá)式或函數(shù)進(jìn)行賦值操作(常量表達(dá)式可以),而 final 定義的常量則可以通過表達(dá)式或函數(shù)賦值,也就是說 const 常量為編譯時(shí)常量,在編譯階段就必須是可知的,而 final 常量是只能對(duì)其進(jìn)行一次賦值,但是可以在運(yùn)行時(shí)進(jìn)行設(shè)置。 如下:

main(){  
  const a = 10;
  final b = 20.5;
  const c = a ;
  final d = a;
  // const e = a+b;  //錯(cuò)誤
  final f = a + b;
    const g = a * 10;  //正確,因?yàn)閍為常量,常量表達(dá)式在編譯期就可以確定值
  
  print(a);  //輸出 10
  print(b);  //輸出 20.5
  print(c);  //輸出 10
  print(d);  //輸出 10
  print(f);  //輸出30.5
  print(g);  //輸出 100
}

const 還可以用來創(chuàng)建常量值以及聲明創(chuàng)建常量值的構(gòu)造函數(shù)(后期文章會(huì)介紹到),創(chuàng)建常量值如下:

main(){  
  List a = const [1, 2, 3];
  List b = const [1, 2, 3];

  if(a == b) {
    print("相同");   //會(huì)輸出 相同
  }
}

如果不用 const 修飾,則a與b為不同,如下:

main(){  
  List a = [1, 2, 3];
  List b = [1, 2, 3];

  if(a == b) {
    print("相同");
  }else print("不同");  //輸出 不同
}

并且 使用如下方式聲明創(chuàng)建的常量值是可以改變的:

main(){  
  List a = const [1, 2, 3]; //可改變
  const b = [1, 2, 3]; //不可改變

  a = [4, 5, 6];
  print(a);
}

但是對(duì)于 const [1, 2, 3] 中的單個(gè)數(shù)據(jù)元素是不可修改的,修改會(huì)拋出異常。對(duì)于 a 變量來說是可變的,因?yàn)槲词褂?const 修飾,所以對(duì) a 重新賦值 [4, 5, 6] 以后,a 對(duì)象相當(dāng)于如下定義:

var a = [4, 5, 6];

通過以下代碼驗(yàn)證:

main(){  
  var a = const [1, 2, 3];
  var b = const [4, 5, 6];
  a = [4, 5, 6];
  if(a == b) {
    print("相同");
  }else{
    print("不同");   //輸出不同
  }
}

以下代碼為對(duì) const [1, 2, 3] 單個(gè)元素修改拋出異常的示例:

main(){  
  var a = const [1, 2, 3];
  a[0] = 10;   //錯(cuò)誤,不可修改
}

2.1.9 動(dòng)態(tài)數(shù)據(jù)類型

在確定數(shù)據(jù)類型以后再賦值其他數(shù)據(jù)類型則會(huì)報(bào)錯(cuò),如下:

main(){
  var a = 10;
  a = 20.1;   //錯(cuò)誤
}

上面的代碼會(huì)報(bào)錯(cuò)如下錯(cuò)誤:

A value of type 'double' can't be assigned to a variable of type 'int'.
Try changing the type of the variable, or casting the right-hand type to 'int'.dart(invalid_assignment)

如果希望變量在賦值一個(gè)數(shù)據(jù)類型以后再賦值其他數(shù)據(jù)類型,可以使用關(guān)鍵字 dynamic 定義,如下:

main(){
  dynamic a = 10;
  print(a.runtimeType);  //輸出int
  a = 20.1;
  print(a);  //輸出20.1
  print(a.runtimeType);  //輸出double
}

dynamic 的最終數(shù)據(jù)類型為最后賦值的數(shù)據(jù)的類型。

2.2.0 枚舉 (enum)

enum Week{
  Monday,
  Tuesday,
  Wednesday
}

main(){  
  var week = Week.Tuesday;
  if(week == Week.Monday){
    print("星期一");
  }else if(week == Week.Tuesday) {
    print("星期二");
  }else if(week == Week.Wednesday) {
    print("星期三");
  }
  
  print(Week.values); //輸出所有枚舉值
  print(Week.Monday.index);  //輸出索引值
  print(Week.Tuesday.index);
  print(Week.Wednesday.index);
}

輸出結(jié)果:

星期二
[Week.Monday, Week.Tuesday, Week.Wednesday]
0
1
2
3. 類型轉(zhuǎn)換
main(){  
  var a = 10;
  var b = 20.5;
  var c = a + b.toInt();  //浮點(diǎn)型轉(zhuǎn)整型
  var d = a.toDouble() + b;  //整型轉(zhuǎn)浮點(diǎn)型
  String e = b.toString();  //浮點(diǎn)型轉(zhuǎn)字符串
  double f = double.parse(e);  //字符串轉(zhuǎn)浮點(diǎn),使用此方法應(yīng)確定字符串可轉(zhuǎn)換為浮點(diǎn)型,否則會(huì)報(bào)異常,如不確定是否可以轉(zhuǎn)換應(yīng)使用tryParse方法,無法轉(zhuǎn)換會(huì)返回null

  print("$c $d $e $f");
}
4. Objectdynamic 的區(qū)別

因?yàn)樵?Dart 中,所有內(nèi)容都是從 Object 類擴(kuò)展而來,所以可以使用如下方式定義數(shù)據(jù):

Object a = 10;

dynamic 對(duì)比,他們都可以定義一種數(shù)據(jù)類型以后再次賦值其他數(shù)據(jù)類型,如下:

main(){  
  Object a = 10;
  a = "string";
  print(a);  //輸出 string

  dynamic b = 20;
  b = "string"; //輸出 string
  print(b);
}

但是 Object 定義的對(duì)象在調(diào)用方法或?qū)傩允?,可以在編譯時(shí)就判斷是否存在指定的方法或?qū)傩?,如不存在,編譯階段就會(huì)報(bào)錯(cuò)。而 dynamic 動(dòng)態(tài)類型,調(diào)用不存在的方法或?qū)傩?,在編譯階段不會(huì)報(bào)錯(cuò),運(yùn)行時(shí)才會(huì)拋出異常,如下:

main(){  
  Object a = 10;
  print(a);

  dynamic b = 20;
  print(b);

  // a.run; 調(diào)用不存在的屬性run,編譯時(shí)就會(huì)提示錯(cuò)誤
  b.run;  //編譯時(shí)不會(huì)提示錯(cuò)誤,運(yùn)行時(shí)才會(huì)拋出異常
}

PS:以上只是很小一部分內(nèi)容,因?yàn)橐磺薪詫?duì)象,所以每個(gè)類都有自己的很多屬性與方法,具體可以參看代碼文檔。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容