flutter基礎 dart語言學習筆記

2020-08-11 09:50:30

1.JIT(Just-In-Time 動態編譯) 即時編譯爲什麼能大幅度提升效能

JIT,即Just-in-time,動態(即時)編譯,邊執行邊編譯;

https://book.flutterchina.club/chapter1/dart.html flutter中文網

或者讓演員表演兩分鐘,再看兩分鐘指令碼,思考一下,再表演兩分鐘,再看一會指令碼,思考一下。。。恩。。。

這就是 JIT 的原理。批次的把一件事情先整理成爲目標能夠直接接受的方式,然後直接執行。

2.AOT (Ahead-Of-Time-預先編譯 靜態編譯)

AOT,Ahead Of Time,指執行前編譯,是兩種程式的編譯方式

假定你是個導演,寫了個指令碼,讓演員表演。

先讓演員把整個指令碼都背下來,吃透,到腦子裏,然後連續的表演一個小時。

3.這兩種編譯方式的主要區別在於是否在「執行時」進行編譯

4.跨平臺自繪引擎

Flutter與用於構建移動應用程式的其它大多數框架不同,因爲Flutter既不使用WebView,也不使用操作系統的原生控制元件。 相反,Flutter使用自己的高效能渲染引擎來繪製widget。這樣不僅可以保證在Android和iOS上UI的一致性,而且也可以避免對原生控制元件依賴而帶來的限制及高昂的維護成本。

Flutter使用Skia作爲其2D渲染引擎,Skia是Google的一個2D圖形處理常式庫,包含字型、座標轉換,以及點陣圖都有高效能且簡潔的表現,Skia是跨平臺的,並提供了非常友好的API,目前Google Chrome瀏覽器和Android均採用Skia作爲其繪圖引擎。

目前Flutter預設支援iOS、Android、Fuchsia(Google新的自研操作系統)三個移動平臺。但Flutter亦可支援Web開發(Flutter for web)和PC開發,本書的範例和介紹主要是基於iOS和Android平臺的,其它平臺讀者可以自行瞭解。

5.高效能

Flutter高效能主要靠兩點來保證,首先,Flutter APP採用Dart語言開發。Dart在 JIT(即時編譯)模式下,速度與 JavaScript基本持平。但是 Dart支援 AOT,當以 AOT模式執行時,JavaScript便遠遠追不上了。速度的提升對高影格率下的檢視數據計算很有幫助。其次,Flutter使用自己的渲染引擎來繪製UI,佈局數據等由Dart語言直接控制,所以在佈局過程中不需要像RN那樣要在JavaScript和Native之間通訊,這在一些滑動和拖動的場景下具有明顯優勢,因爲在滑動和拖動過程往往都會引起佈局發生變化,所以JavaScript需要和Native之間不停的同步佈局資訊,這和在瀏覽器中要JavaScript頻繁操作DOM所帶來的問題是相同的,都會帶來比較可觀的效能開銷。

6.採用Dart語言開發

這是一個很有意思,但也很有爭議的問題,在瞭解Flutter爲什麼選擇了 Dart而不是 JavaScript之前我們先來介紹兩個概念:JIT和AOT。

目前,程式主要有兩種執行方式:靜態編譯與動態解釋。靜態編譯的程式在執行前全部被翻譯爲機器碼,通常將這種型別稱爲AOT (Ahead of time)即 「提前編譯」;而解釋執行的則是一句一句邊翻譯邊執行,通常將這種型別稱爲JIT(Just-in-time)即「即時編譯」。AOT程式的典型代表是用C/C++開發的應用,它們必須在執行前編譯成機器碼,而JIT的代表則非常多,如JavaScript、python等,事實上,所有指令碼語言都支援JIT模式。但需要注意的是JIT和AOT指的是程式執行方式,和程式語言並非強關聯的,有些語言既可以以JIT方式執行也可以以AOT方式執行,如Java、Python,它們可以在第一次執行時編譯成中間位元組碼、然後在之後執行時可以直接執行位元組碼,也許有人會說,中間位元組碼並非機器碼,在程式執行時仍然需要動態將位元組碼轉爲機器碼,是的,這沒有錯,不過通常我們區分是否爲AOT的標準就是看程式碼在執行之前是否需要編譯,只要需要編譯,無論其編譯產物是位元組碼還是機器碼,都屬於AOT。在此,讀者不必糾結於概念,概念就是爲了傳達精神而發明的,只要讀者能夠理解其原理即可,得其神忘其形。

現在我們看看Flutter爲什麼選擇Dart語言?筆者根據官方解釋以及自己對Flutter的理解總結了以下幾條(由於其它跨平臺框架都將JavaScript作爲其開發語言,所以主要將Dart和JavaScript做一個對比):

  1. 開發效率高

    Dart執行時和編譯器支援Flutter的兩個關鍵特性的組合:

    基於JIT的快速開發週期:Flutter在開發階段採用,採用JIT模式,這樣就避免了每次改動都要進行編譯,極大的節省了開發時間;

    基於AOT的發佈包: Flutter在發佈時可以通過AOT生成高效的ARM程式碼以保證應用效能。而JavaScript則不具有這個能力。

  2. 高效能

    Flutter旨在提供流暢、高保真的的UI體驗。爲了實現這一點,Flutter中需要能夠在每個動畫幀中執行大量的程式碼。這意味着需要一種既能提供高效能的語言,而不會出現會丟幀的週期性暫停,而Dart支援AOT,在這一點上可以做的比JavaScript更好。

  3. 快速記憶體分配

    Flutter框架使用函數式流,這使得它在很大程度上依賴於底層的記憶體分配器。因此,擁有一個能夠有效地處理瑣碎任務的記憶體分配器將顯得十分重要,在缺乏此功能的語言中,Flutter將無法有效地工作。當然Chrome V8的JavaScript引擎在記憶體分配上也已經做的很好,事實上Dart開發團隊的很多成員都是來自Chrome團隊的,所以在記憶體分配上Dart並不能作爲超越JavaScript的優勢,而對於Flutter來說,它需要這樣的特性,而Dart也正好滿足而已。

  4. 型別安全

    由於Dart是型別安全的語言,支援*靜態型別檢測,*所以可以在編譯前發現一些型別的錯誤,並排除潛在問題,這一點對於前端開發者來說可能會更具有吸引力。與之不同的,JavaScript是一個弱型別語言,也因此前端社羣出現了很多給JavaScript程式碼新增靜態型別檢測的擴充套件語言和工具,如:微軟的TypeScript以及Facebook的Flow。相比之下,Dart本身就支援靜態型別,這是它的一個重要優勢。

  5. Dart團隊就在你身邊

    看似不起眼,實則舉足輕重。由於有Dart團隊的積極投入,Flutter團隊可以獲得更多、更方便的支援,正如Flutter官網所述「我們正與Dart社羣進行密切合作,以改進Dart在Flutter中的使用。例如,當我們最初採用Dart時,該語言並沒有提供生成原生二進制檔案的工具鏈(這對於實現可預測的高效能具有很大的幫助),但是現在它實現了,因爲Dart團隊專門爲Flutter構建了它。同樣,Dart VM之前已經針對吞吐量進行了優化,但團隊現在正在優化VM的延遲時間,這對於Flutter的工作負載更爲重要。」

總結

本節主要介紹了一下Flutter的特點,如果你感到有些點還不是很好理解,不用着急,隨着日後對Flutter細節的瞭解,再回過頭來看,相信你會有更深的體會。

1.2.2 Flutter框架結構

本節我們先對Flutter的框架做一個整體介紹,旨在讓讀者心中有一個整體的印象,這對初學者來說非常重要。如果一下子便深入到Flutter中,就會像是一個在沙漠中沒有地圖的人,即使可以找到一個綠洲,但是他也不會知道下一個綠洲在哪。因此,無論學什麼技術,都要先有一張清晰的「地圖」,而我們的學習過程就是「按圖索驥」,這樣我們纔不會陷於細節而「目無全牛」。言歸正傳,我們看一下Flutter官方提供的Flutter框架圖,如圖1-1所示:

图1-1

Flutter Framework

這是一個純 Dart實現的 SDK,它實現了一套基礎庫,自底向上,我們來簡單介紹一下:

  • 底下兩層(Foundation和Animation、Painting、Gestures)在Google的一些視訊中被合併爲一個dart UI層,對應的是Flutter中的dart:ui包,它是Flutter引擎暴露的底層UI庫,提供動畫、手勢及繪製能力
  • Rendering層,這一層是一個抽象的佈局層,它依賴於dart UI層,**Rendering層會構建一個UI樹,當UI樹有變化時,會計算出有變化的部分,然後更新UI樹,最終將UI樹繪製到螢幕上,這個過程類似於React中的虛擬DOM。**Rendering層可以說是Flutter UI框架最核心的部分,它除了確定每個UI元素的位置、大小之外還要進行座標變換、繪製(呼叫底層dart:ui)。
  • Widgets層是Flutter提供的的一套基礎元件庫,在基礎元件庫之上,Flutter還提供了 Material 和Cupertino兩種視覺風格的元件庫。而我們Flutter開發的大多數場景,只是和這兩層打交道

Flutter Engine

這是一個純 C++實現的 SDK,其中包括了 Skia引擎、Dart執行時、文字排版引擎等。在程式碼呼叫 dart:ui庫時,呼叫最終會走到Engine層,然後實現真正的繪製邏輯。

總結

Flutter框架本身有着良好的分層設計,本節旨在讓讀者對Flutter整體框架有個大概的印象,相信到現在爲止,讀者已經對Flutter有一個初始印象,在我們正式動手之前,我們還需要瞭解一下Flutter的開發語言Dart

1.4.1 變數宣告

1.var

類似javascript裏面的 var 但是 dart裏面的 一旦使用 型別就會固定不能改變 否則會報錯

var t;
t = "hi world";
// 下面 下麪程式碼在dart中會報錯,因爲變數t的型別已經確定爲String,
// 型別一旦確定後則不能再更改其型別。
t = 1000;

2.dynamic和Object

是Dart所有物件的根基礎類別,也就是說所有型別都是Object的子類(包括Function和Null),所以任何型別的數據都可以賦值給Object宣告的物件. dynamicvar一樣都是關鍵詞,宣告的變數可以賦值任意物件。 而dynamicObject相同之處在於,他們宣告的變數可以在後期改變賦值型別。

 dynamic t;
 Object x;
 t = "hi world";
 x = 'Hello Object';
 //下面 下麪程式碼沒有問題
 t = 1000;
 x = 1000;

dynamicObject不同的是,dynamic宣告的物件編譯器會提供所有可能的組合, 而Object宣告的物件只能使用Object的屬性與方法, 否則編譯器會報錯。如:

 dynamic a;
 Object b;
 main() {
     a = "";
     b = "";
     printLengths();
 }   

 printLengths() {
     // no warning
     print(a.length);
     // warning:
     // The getter 'length' is not defined for the class 'Object'
     print(b.length);
 }

變數a不會報錯, 變數b編譯器會報錯

dynamic的這個特性與Objective-C中的id作用很像. dynamic的這個特點使得我們在使用它時需要格外注意,這很容易引入一個執行時錯誤.

3.final和const

如果您從未打算更改一個變數,那麼使用 finalconst,不是var,也不是一個型別。 一個 final 變數只能被設定一次,兩者區別在於:const 變數是一個編譯時常數,final變數在第一次使用時被初始化。被final或者const修飾的變數,變數型別可以省略,如:

常數的值可以賦值給變數 變數的值可以被修改 常數的值不可以被更改

final name = "Bob";

main() {
  var t = name;
  t = '123';
  name = "123" //報錯
  print(t);
}
//可以省略String這個型別宣告
final str = "hi world";
//final String str = "hi world"; 
const str1 = "hi world";
//const String str1 = "hi world";

重要的概念

在學習 Dart 語言時, 應該基於以下事實和概念:

  • 任何儲存在變數中的都是一個 物件 , 並且所有的物件都是對應一個 的範例。 無論是數位,函數和 null 都是物件。所有物件繼承自 Object 類。
  • 儘管 Dart 是強型別的,但是 Dart 可以推斷型別,所以型別註釋是可選的。 在上面的程式碼中, number 被推斷爲 int 型別。 如果要明確說明不需要任何型別, 需要使用特殊型別 dynamic
  • Dart 支援泛型,如 List <int> (整數列表)或 List <dynamic> (任何型別的物件列表)。
  • Dart 支援頂級函數(例如 main() ), 同樣函數系結在類或物件上(分別是 靜態函數範例函數 )。 以及支援函數內建立函數 ( 巢狀區域性函數 ) 。
  • 類似地, Dart 支援頂級 變數 , 同樣變數系結在類或物件上(靜態變數和範例變數)。 範例變數有時稱爲欄位或屬性。
  • 與 Java 不同,Dart 沒有關鍵字 「public」 , 「protected」 和 「private」 。 如果識別符號以下劃線(_)開頭,則它相對於庫是私有的。 有關更多資訊,參考 庫和可見性
  • 識別符號 以字母或下劃線(_)開頭,後跟任意字母和數位組合。
  • Dart 語法中包含 表達式( expressions )(有執行時值)和 語句( statements )(沒有執行時值)。 例如,條件表達式 condition ? expr1 : expr2 的值可能是 expr1expr2 。 將其與 if-else 語句 相比較,if-else 語句沒有值。 一條語句通常包含一個或多個表達式,相反表達式不能直接包含語句。
  • Dart 工具提示兩種型別問題:警告_和_錯誤。 警告只是表明程式碼可能無法正常工作,但不會阻止程式的執行。 錯誤可能是編譯時錯誤或者執行時錯誤。 編譯時錯誤會阻止程式碼的執行; 執行時錯誤會導致程式碼在執行過程中引發 [異常](#exception)。

void 函數沒有返回值

void main(){
print('123')
}

變數 會型別自動推斷 var int String 定義變數 值可以改變

var int String 定義變數 會自動推斷型別 推斷型別不能更改 否則報錯;

var str= ‘’;
str = 123  //報錯

String str = '';
str=1234  //報錯 必須爲字串
int str = 123;
str='1234'  //報錯 必須爲數位

變數名

  • 識別符號不能以數位開頭

  • 變數名必須由數位,字母,下劃線和美元($)組成;

  • 識別符號不能是保留字關鍵字;

  • 變數名的名字是區分大小寫的: age和Age是不同變數名 不建議使用;

  • 識別符號(變數名)一點要見名思意:變數名建議用名詞 ,方法名建議用動詞;

常數 const 和 final

const PI = 3.14159;
PI = 123; // 常數的值不可以修改
final PI = 3.14159;
PI = 123; //常數的值不可以修改


兩者區別在於:const 變數是一個編譯時常數,final變數在第一次使用時被初始化。被final或者const修飾的變數,變數型別可以省略,如:

final a = new DateTime.now();
print(a) // 2020-7-22 16:00

const a = new DateTime.now();
print(a) // 2020-7-22 16:00 //報錯

數據型別

1.常用數據型別

String 字元型別 字串拼接 可以‘+’拼接 也可以 ${} $a 拼接。

String a = 'a' 
String b= ''' asda
asd
asd''';//字串可以換行

String a = 'abc';
String c = 'aaa${abc}' // aaaabc  字串拼接 

2**.int 和double數位型別**

double既可以是整形也可以是浮點型

int只能是整形 整形不能賦值爲浮點型 小數

int a = 123;

double b = 1.1; 
double c = 2;

3.bool 布爾值 true和false

bool a = false
bool b = true

條件判斷 ‘123’ 不等於 123 不會型別隱式轉換

bool flag = true;
if(flag) {
print('真')
}else {
print('假')
}

4.list 定義方式 (array)

var a = [1,2,3]; //第一種 var a = [];
a.length;  //3
a[0]; //1
var l = new List();  //第二種建立list方式 new List();
l.add('張三');
l.add('李四');
l.add('王五');

定義List 指定型別

var a = new List<String>();  //List裏面只能是String型別
var a = new List<int>();  //List裏面只能是int型別

5.Maps 型別 類似 js物件

//第一種定義Maps的方式
var person = {
	"name":"張三",
	"age":20
}
print(person) //{"name":"張三","age":20} 
print(person["name"]) //張三 
print(person["age"]) //20
//第二種定義maps的方式
var p = new Map();
p["name"] = "張三";
p["age"] = 20;
print(p) //{"name":"張三","age":20} 
print(p["age"]) // 20

6.is 關鍵詞來判斷型別

var str = "123";
if( str == String) {
	print (String型別)
} else if ( str == int ){
print(int型別)
}else {
print(’其他‘)
}

運算子 條件判斷 關係執行符 邏輯運算子

  • 算數運算子
int a= 13;
int b = 5;

print(a+b)//18
print(a-b)//8
print(a*b)//65
print(a/b)//2.6 
print(a%b)//3 餘
print(a~/b)//2 取整
  • 關係運算符
int a= 13;
int b = 5;

print(a>b)//true
print(a<b)//false
print(a>=b)//true
print(a<=b)// false
print(a==b)// fasle
  • 邏輯運算子 !非 &&與|| 或
bool a = !fasle //true 
&& 兩個都爲真 才爲真
|| 一個爲真 就爲真
  • 賦值 運算 = ??=
int b = 10或'';
b??=23;  //如果 b爲空 b = 23 如果 b 不爲空 b = 10;
print(b) //23
  • 複合運算子 += ,-= ,*=,/=
int a = a+ 
  • if else switch case
if(a>0) {
print('a大於零')
}else {
print('小於零')
}
  String sex = "123";
  switch (sex) {
    case "男":
      print(sex);
      break;
    case "女":
      print(sex);
      break;
    default:
      print('參數錯誤');
  }
}

bool a = true;
String c = a?"我是true":"我是false";

??用法

var a;
b = a || 20;//20
var a = 10;
b = a || 20;//10

型別轉換 字串轉數值型 int.parse 或double.pares 數值轉字串 toString(); isEmpty 是否爲空;

int 型別 只能轉換 int 型別 不能轉換 double 型別 會報錯

double 可以轉換 int型別 和double型別 但是不能轉換 「」型別 會報錯

字串轉數值型 int.parse  或double.pares
String a = '123';
var num = int.parse(a); //true
String a = '123.1';
var num = int.parse(a); //會報錯

String a = '123.1';
var num = double.parse(a); //true
String a = '123.1';
var num = int.parse(a); //true 

假如後臺傳過來一個空 需要做判斷  不會報錯 try 轉換失敗 拋出異常
String price= '';
try{
	var num = double.parse(price);
	print(num)
}carch(err) {
print("傳過來的爲空");
}
數值轉字串  toString();
var a = 10;
var b = a.toString(); //數值轉字串

print(b is String) //true
isEmpty 是否爲空; 只能判斷String型別 不能判斷int double 型別
var a = "";
if(a.isEmpty) {
	print('是爲空')
}else {
print('不爲空');
}
0除0返回NaN
var myNum = 0/0;
if(myNum.isNaN) {
print('Nan');
}


回圈語句 for

如果++ --寫在前面 先運算 在賦值 如果++ – 先賦值 後運算

++ 自增寫在前面和後面的區別 
var b = 10; // 11
var a = ++b; //11 寫在前面 先 自增加一
print(b) //11
print(a) //11;
var a = b++; //10 寫在後面 先 賦值 給b 所以b =10 再 b+1;
print(10) //11;
for (int i=1;i<=10;i++){
print(i); //1 -10;
}
//100求和
var sum =0;
for(int i=0;i<=100;i++){
sun +=i;
}
print(sum)//5050
//5的階剩 1*2*3*4*5
var sum =1;
for(int i=1;i<=5;i++){
sun *=i;
}
print(sum)//120

1.列印陣列 List 
var list = new List();
List list = ['張三','李四','王二'];
for(int i = 0;i<lsit.length-1;i++){
print(list[i]); //張三 李四 王二
}

2.列印陣列 List;
var List = [
{"name":'馮上進'},
{"name":'張三'},
{"name":"李四"}
]


for(int i=0;i<List.length;i++){
 print(List[i]["name"]) //張三 李四 王二
}
3.列印陣列List  雙層for回圈  list 必須是 爲陣列 字面量  List
List list = [
    {
      "title": '標題',
      "name": [
        {"age": 20},
        {"age": 20},
        {"age": 20}
      ]
    },
    {
      "title": '標題',
      "name": [
        {"age": 20},
        {"age": 20},
        {"age": 20}
      ]
    },
    {
      "title": '標題',
      "name": [
        {"age": 20},
        {"age": 20},
        {"age": 20}
      ]
    }
  ];
  for (int i = 0; i < list.length; i++) {
    print(list[i]["title"]);
    for (int j = 0; j < list[i]["name"].length; j++) {
      print(list[i]["name"][j]["age"]);
    }
  }

回圈 while 和 do …while

語法格式

while(表達式/回圈條件) {
	裏面的語句會一直回圈
	防止死回圈
}
//先判斷語句 再回圈
do{
語句/回圈體
} while('表達式/回圈條件');
//先回圈語句 在判斷條件
//列印1-10
var i=1;
while(i<10){
	print(i);
	i++;
}
//while 計算 1-100 的和
var i=1;
var sum = 0;
while(i<=100) {
print(sun+=i);
i++;
}
//do...while 計算 1-100 的和
  var i = 1;
  var sum = 0;
  do {
    sum += i;
    i++;
  } while (i <= 100);
  print(sum);

while 和 do…while 的 區別 :

如果 條件不成立;

wihile 先判斷條件 在執行 ;

do…while 先執行至少一次 再 判斷條件;

break語句的使用continue

break可以在switch case 中 使用 也可以在for 回圈 while中使用

for(var i=1;i<=10;i++){
if(i==4) {
	continue; // 跳出當前回圈體 回圈會繼續執行 
}
print(i); //123 56789
}
for(var i=1;i<=10;i++){
if(i==4) {
	break; // 跳出當前循結束 只能跳出一層for回圈
}
print(i);//123
}



List 屬性和放法

常用屬性 :

  • length 長度
  • reversed 翻轉
  • isEmpty 是否爲空
  • isNotEmpty 是否不爲空

常用放法:

  • add 新增陣列
  • addAll 拼接陣列
  • indexOf 查詢 傳入具體值 找不到返回-1
  • remove 刪除 傳入具體值
  • removeAt 刪除 傳入索引值
  • fillRange 修改
  • insert(index,value); 指定位置插入
  • insertAll(index,list) 指定位置插入List
  • toList() 其他型別轉換成List
  • join() List轉換成字串
  • split() 字串轉化成List
  • new Set 去重 set 新增重複的 只返回一個 類似去重後的 List
  • forEach
List list = ["蘋果","香蕉","西瓜"];
  List list = ['蘋果', '香蕉', '水果'];
  print(list.length); //3
  print(list.reversed.toList()); // [水果, 香蕉, 蘋果]
  print(list.isEmpty); //false
  print(list.isNotEmpty); //true
  list.add("123"); 
  list.fillRange(1, 2, '111');
  list.remove('111');
  print(list);//[蘋果, 水果, 123]
  var a = list.indexOf("1");
  print(a);//-1
  list.removeAt(1);
  print(list);//[蘋果, 123]
  list.insert(1, "馮上進");
  print(list);//[蘋果, 馮上進, 123]
  list.insertAll(1, ["我", "是", "馮"]);
  print(list);//[蘋果, 我, 是, 馮, 馮上進, 123]
  var s = list.join();
  print(s);//蘋果我是馮馮上進123
  var m = list.join(',');
  print(m);//蘋果,我,是,馮,馮上進,123
  
  //new Set()陣列去重 返回物件 .toList 轉成List型別 
  var l = new Set();
  l.add("蘋果");
  l.add("香蕉");
  l.add("蘋果");
  print(l);{"蘋果","香蕉"}
  print(l.toList());//["蘋果","香蕉"];

 

maps(對映) 是無序的鍵值對:

常用屬性:

  • keys 獲取所有的key值
  • values 獲取所有的value值
  • isEmpty 是否爲空
  • isNotEmpty 是否不爲空

常用放法:

  • remove(key) 刪除指定key的數據
  • addAll({…}) 合併對映 給對映內新增屬性
  • containsValue 檢視對映內的值 返回True/False
  • forEach 回圈遍歷 返回每一項
  • map 回圈遍歷 返回陣列
  • where 條件判斷回圈遍歷 返回陣列
  • any 只要集合裏面有滿足條件的就返回true
  • every 集閤中每一個都滿足條件 返回true
var person = {
"name" :'張三';
"age" :'李四';
}

var person = new Map();
perosn["name"] = "張三"


var person = {"name": "張三", "age": 20};
  print(person.keys.toList()); [name, age]
  print(person.values.toList());[張三, 20]
  print(person.isEmpty);false
  print(person.isNotEmpty);true
  person.addAll({"work": '敲程式碼', "身高": '160'});
  print(person); {name: 張三, age: 20, work: 敲程式碼, 身高: 160}
  person.remove("name");
  print(person);  {age: 20, work: 敲程式碼, 身高: 160}
  var s = person.containsValue("張三");檢視value 是否存在
  print(s); //false  

List list = ["蘋果","香蕉","西瓜"];
list.forEach((value){
	print(value); //蘋果香蕉水果
})

 List list = [1, 2, 3];
  list.forEach((element) {
    print(element * 2); //2 4 6
  });
  
 List list = [1, 2, 3];
 var newList = list.map((value){
 return value*2;
 })
 print(newList); //(2,4,6);
 
  List list = [1, 2, 3,4,5,6,7,8,9];
  var newList = list.where((value){
  return value>5
  })
  print(newList); (6,7,8,9);


var s = new Set();
a.addAll([1,222,333,333]);
s.forEach((value){
print(value); //1,222,333 去重後的陣列 List
})

var person = {
"name":'馮上進',
"age":18
}
person.forEach((keys,value){
print("$key---$value")
});

內建方法/函數

print() 內建方法

自定義方法:
	自定義方法的基本格式:
	返回型別 方法名稱 (參數1,參數2,...){
	方法體;
	return 返回值;
	}
	void 表示函數沒有返回值 函數寫在入口方法外面 是全域性的方法 
	void main(){
		String sum (a,b){
		c = a+b;
		print(c); //5
		};
		sum(2,3)
	}
	
	函數封裝 1-n的和
	
	int sumNum(int n){
		int sum = 0;
		for(int i =0;i<=n;i++){
			sum+=i;
		}
		return sum;
	}
	int s = sumNum(60);
	print(s);
	
	1.定義一個方法 然後列印使用者資訊 
	String printUserInfo(String username,int age){
	return "姓名:$username ---年齡:$age";
	}
	String s = printUserInfo('馮上進',20);
	print(s);// 姓名:馮上進---年齡:20 
	
	2.//[int age,String sex] 可選參數 可傳 可不傳
	String printUserInfo(String username,[int age]){
	
	if(age!==null){
	return "姓名:$username ---年齡:$age";
	
	}
	return "姓名:$username ---年齡保密";
	
	}
	String s = printUserInfo('馮上進');//不傳參數 
	print(s);// 姓名:馮上進---年齡保密
		String s = printUserInfo('馮上進',20);//傳參數
	print(s);// 姓名:馮上進---年齡:20 
	
	3.預設參數 [int age,String sex="男"]
	String printUserInfo(String username,[int age,String sex="男"]){
	
	if(age!==null){
	return "姓名:$username ---年齡:$age--性別:$sex";
	
	}
	return "姓名:$username ---年齡保密---性別:$sex";
	
	}
	 String l = printUserInfo('馮上進', 20);
  	print(l); // 姓名:馮上進 ---年齡:20--性別:男
  	
  	4.命名參數  {int age,String sex="男"}  printUserInfo('馮上進', age:20,sex:'未知')
	String printUserInfo(String username,{int age,String sex="男"}){
	
	if(age!==null){
	return "姓名:$username ---年齡:$age--性別:$sex";
	
	}
	return "姓名:$username ---年齡保密---性別:$sex";
	
	}
	 String l = printUserInfo('馮上進', age:20,sex:'未知'); //命名參數 傳參
  	print(l); // 姓名:馮上進 ---年齡:20--性別:未知
	
	5.實現一個把方法當做參數的方法
	fn1(){
	print('fn1');
	}
	fn2(fn){
	fn();
	}
	fn2(fn1); //'fn1'

箭頭函數 程式碼只能寫一行

List list =['香蕉','蘋果','橘子'];
list.forEach((value)=>{print(value)});

1.匿名放法 把方法賦值給變數 printNum
var printNum = (){
	print(123);
}
2.自執行方法
((n){
print(n);
})(12) //12
3.方法的遞回
int sum = 1;
fn(n){
	sum*=n;
	if(n== 1){
	return;
	}
	fn(n-1);
}
print(sum);
3.通過方法的遞回 求1-100的和;
int sum = 0;
  fn(n) {
    sum += n;
    if (n == 0) {
      return;
    }
    fn(n - 1);
  }

  fn(100);
  print(sum);
}

閉包

1.全域性變數特點:全域性變數常駐記憶體,全域性變數污染全域性。

2.區域性變數的特點:不常駐在記憶體 會被垃圾回收機制 機製回收 ,不會污染全域性。

想實現的功能:

1.常駐記憶體。

2.不污染全域性。

​ 產生閉包,閉包可以解決這個問題…

​ 閉包 :函數巢狀函數,內部函數會呼叫外部函數的變數或參數,變數或參數不會被系統回收。

​ 閉包的寫法:函數巢狀函數,並return裏面的函數,這樣就形成了閉包。

fn() {
    var a = 123; //不會全域性污染 常駐記憶體;
    return () {
      a++;
      print(a);
    };
  }

  var b = fn();
  b(); //124
  b();//125
  b();//126
  b();//127
  b();//128

物件導向(oop)的三個基本特徵 :封裝 ,多型,繼承;

  • 封裝 :封裝是物件和類概唸的主要特性。封裝,把客觀事物封裝成抽象的類,並且把自己的部分屬性和放法提供給其他物件。
  • 繼承 :物件導向程式設計(oop)語言的主要功能是"繼承"。繼承是指這樣的一種能力:他可以使用現有類的功能,
  • 多型 :允許將子類型別的指針賦值給父類別型別的指針,同一個函數呼叫會有不同的執行效果。

dart所有的東西都是物件,所有的物件都繼承自Object類。

dart是一門使用類和單繼承的物件導向語言,所有的物件都是類的範例,並且所有的類都是Object的子類

一個類通常是由屬性和放法組成。

系統的類
void main (){
List list = new List();
list.isEmpty;
list.add('香蕉');
list.add('蘋果');

Map m = new Map();
m["userName"] = '馮上進';
m.addAll({"age":20});
}
自定義類 定義裏面的屬性和放法 預設建構函式 範例化的時候呼叫
class Person{
String name = "張三";
int age = 20;
Person(){
print('這是建構函式的內容,這個方法在範例化時候被觸發 預設建構函式');
};
void getInfo(){
	print("${this.name} --- ${this.age}");
	}
}
Person n = new Person();
print(n.name);//張三
n.getInfo(); // 張三--- 20

class Person{
String name ;
int age;
//預設建構函式
Person(String name,int age){
print('這是建構函式的內容,這個方法在範例化時候被觸發 預設建構函式');
this.name = name;
this.age = age;
}; // 預設建構函式的簡寫Person(this.name,this.age);



void getInfo(){
	print("${this.name} --- ${this.age}");
	}
}
Person n = new Person('張1',20);
print(n.name);//張1
print(n.age);//20
Person n1 = new Person('李四',25); //類 可以範例化多次 

自定義類命名建構函式 命名建構函式可以寫多個 預設建構函式只能寫一個

class Person{
String name = "張三";
int age = 20;
Person.now(){
	print('命名建構函式');
}
Person(){
print('這是建構函式的內容,這個方法在範例化時候被觸發 預設建構函式');
};
void getInfo(){
	print("${this.name} --- ${this.age}");
	}
}
Person n = new Person.now(); //命名建構函式   var time = new Datetime.now();  也屬於命名建構函式

總結:1.預設建構函式 和命名建構函式 都在範例化的時候被觸發。傳參和預設建構函式一樣

​ 2.建構函式封裝 單獨抽離 使用 import 引入

import '檔名/類資料夾';
class Person {
  String name = "馮上進";
  int age = 20;
  //預設建構函式
  Person(String name, int age) {
    this.name = name;
    this.age = age;
  }
  //命名建構函式
  Person.son(String name, int age) {
    this.name = name;
    this.age = age;
  }
}
封裝好的類
import 'person.dart';  //最前面不用加點

void main() {
  var n = new Person('李四', 100);
  print(n.name);
  print(n.age);
  var n1 = new Person("馮大大", 1000);
  print(n1.name);
  print(n1.age);

  var n2 = new Person.son("江西", 100);
  print(n2.name);
  print(n2.age);
}
呼叫上面的類

dart中的是有方法和是有屬性

Dart個其他物件導向不一樣,Dart中沒有public private protected 這些存取修飾符合。但是我們可以使用 " - " (下劃線)把一個屬性或方法定義成私有(必須在一個檔案裏面)。

class Person {
  String _name = "馮上進";//是有私屬性
    getName(){//公有方法
  return this._name; //私有屬性  通過公有方法可存取
  }
  _run (){
  print('123');
  }//是私有方法
  
  getrun(){//公有方法
  this._run(); 
  }
  int age = 20;
  //預設建構函式
  Person(String name, int age) {
    this.name = name;
    this.age = age;
  }
  //命名建構函式
  Person.son(String name, int age) {
    this.name = name;
    this.age = age;
  }

}
封裝好的類
import 'person.dart';  //最前面不用加點

void main() {
 	var n = new Person();
 	n._name; //存取不到 _name 因爲 _name 爲上面 抽離類的是有方法。
 	n.getName(); // 通過公有的方法可以存取上面類的是有方法,獲取私有屬性。
 	n._run(); //存取不到上面類的私有方法。 
 	n.getrun();//通過公有方法 可以存取到 上面的私有方法。
 	
}

總結:通過公有方法間接呼叫私有屬性和私有方法、

類中的getter 和setter修飾符用法。

class Per {
  int width;
  int height;

  sum(int width, int height) {
    return width * height;
  }
}

void main() {
  var n = new Per();
  var s = n.sum(100, 200);
  print(s); //40000
}// 第一種方法 非get存取類裏面的方法
class Per {
  int width;
  int height;
   Per(this.width, this.height);

  get sum { //通過花括號計算屬性得到的 sum的值
    return width * height;
  }
  set areaHeight(value){
  	this.height = value;
  }
  
}

void main() {
 var n = new Per(20,20);
 n.sum // 400  使用get的時候 存取類裏面的方法是用帶你的形式存取。  注意呼叫直接通過存取屬性的方式存取 sum 類似計算屬性;
 n.areaHeight = 10;  //set的計算屬性 呼叫修改高度後 再計算面積
 print(n.sum); //呼叫修改高度後 再計算面積

}

dart中我們也可以在建構函式體執行之前初始化範例變數

class Rect(){
int height;
int width;
Rect():height=2,width=10{
print("${this.height}---${this.width}"); //2---10初始化範例變數
}  //dart中我們也可以在建構函式體執行之前初始化範例變數
getArea(){
return width*height;
}
}
void main(){
Reat r = new Reat();
print(r.getArea());
}

dart類中的靜態成員靜態方法

  • 使用Static 關鍵字來實現類級別的變數和函數。
  • 靜態方法不能存取非靜態成員,非靜態方法可以存取靜態成員。
  • 靜態方法和靜態屬性直接通過類進行存取 Rect.height
class Rect(){
static int height = 10;
static int width = 20;
int age = 20;  //靜態方法

static getArea(){
return width*height;  

}
static prints(){
	return age;  //報錯 靜態方法不可訪非靜態屬性。
	printWdith(); //報錯 靜態方法不可訪非靜態方法。
}
//非靜態方法
void printWidth(){
print(width); //非靜態方法 可以存取靜態成員和非靜態成員。
print(this.age); //存取非靜態屬性
prints() //呼叫靜態方法
 }
}
void main(){

print(Rect.height); //10
print(Rect.getArea());//200 //通過類存取靜態屬性和靜態方法

Reat r= new Reat();
r.printWidth();//20 20

}

dart中的物件操作符

? 條件運算子(瞭解)

as 型別轉換

is 型別判斷

… 級聯操作(連綴)(記住)

class Person {
  String name = "張三";
  int age = 20;
  printName() {
    print(this.name);
  }
}

void main() {
  Person p = new Person();
  if (p is Person) { //is的用法
    p.name = "李四";
  }
  p.printName(); //李四
  
  
  var p1;
  p1 ='';
  p1 = new Person('張三',20);
  p1.printInfo();  //老版本報錯 新版本不報錯
  (p1 as Person).printInfo(); //型別轉換 as 字串轉換成Person 類
}
class Person {  //級聯..的用法
  String name;
  int age;
  Person(this.name, this.age);
  printName() {
    print("${this.name}---${this.age}");
  }
}

void main() {
  Person p = new Person('張三', 20);
  p.printName();
  p.name = '李四'; //20
  p.age = 200;
  p.printName(); //200
//上面的簡寫下面 下麪的樣子
  //用級聯 ..
  p
    ..name = "李四222"
    ..age = 100
    ..printName(); //李四222---100
}

dart中類的繼承:

  • 子類使用extends關鍵詞來繼承父類別。
  • 子類會繼承父類別課件的屬性和方法 但是不會繼承建構函式。
  • 子類能複寫父類別的方法getter 和setter。
class Father {
  String name = '爸爸';
  int age = 20;
  void printName() {
    print(this.name);
  }
}

class Son extends Father {}

void main() {
  Son n = new Son();
  print(n.name); //爸爸 
}
//類的繼承
//建構函式繼承

class Father {
  String name = '爸爸';
  int age = 20;
  Father(this.name, this.age);
  void printName(String name, int age) {
    print("${this.name}---${this.age}");
  }
}

class Son extends Father {
  Son(String name, int age) : super(name, age) {}  //關鍵 處 繼承預設建構函式
}

void main() {
  Son n = new Son('張三', 20);
  n.printName('馮上進', 20);
}
//建構函式繼承 子類可以傳自己的參數 定義自己的方法

class Father {
  String name = '爸爸';
  int age = 20;
  Father(this.name, this.age);
  Father.xxx(this.name, this.age);
  void printName(String name, int age) {
    print("${this.name}---${this.age}");
  }
  void age(){
  print(this.age);
  }
}

class Son extends Father {
  String sex;
  //繼承父類別的預設建構函式 和命名建構函式 和傳參
  Son(String name, int age, String sex) : super.xxx(name, age) { //命名建構函式傳參 
    this.sex = sex;
  }
  run() { //自己的方法
    print("${this.name}---${this.age}---${this.sex}");
    super.age(); // 在run方法裏面呼叫父類別的方法  age
  }
  @override  //複寫父類別的方法  @override可寫 可不寫
    void printName(String name, int age) {
    print("${this.name}-----${this.age}");
  }
}

void main() {
  Son n = new Son('張三', 20, '男');
  n.printName('馮上進', 20); //'馮上進'-----20
  n.run(); // 張三 --- 20 --- 男  20
 
}

總結: 1.類的繼承 extends 繼承父類別的屬性方法 繼承不了 建構函式 2, 3爲繼承建構函式的方法

​ 2.預設建構函式的繼承 Son(String name, int age) : super(name, age) {}

​ 3.命名建構函式的繼承 Son(String name, int age) : super.xxx(name, age){}

​ 4.子類可以傳自己的參數到建構函式中 Son(String name, int age, String sex) : super.xxx(name, age)

​ 5.複寫父類別的 方法 @override(可以省略) 父類別的方法名

​ 6.子類方法中可以呼叫父類別的方法 super.age(); // 在run方法裏面呼叫父類別的方法 age

dart中的抽象類 多型 和介面

Dart中抽象類:Dart抽象類主要用於定義標準,子類可以繼承抽象類,也可以實現抽象類介面

  1. 抽象類通過 abstract 關鍵字來定義
  2. Dart 中的抽象方法不能用abstract宣告,Dart中沒有方法體的方法我們稱之爲抽象方法
  3. 如果子類繼承抽象類必須得實現裏面的抽象方法
  4. 如果把抽象類當做介面實現的話必須實現抽象類裏面定義的所有屬性和方法。
  5. 抽象類不能範例化,只有繼承它的子類可以。

extends抽象類 和 implements的區別:

  1. 如果要複用抽象類裏面的方法,並且要用抽象方法約束子類的話我們就用 extends繼承抽象類。(只是複用抽象類裏面的方法 並且使用抽象類約束方法);
  2. 如果 只是把抽象類當做標準的話 我們就用implements 實現抽象類。(只是作爲約束的標準 作爲介面)

案例:定義一個Animal 類 要求它的子類必須包含eat方法。

//定義一個 Animal的抽象類 

abstract class Animal {
  eat(); // 抽象方法
  run(); // 抽象方法
  printInfo() {
    //抽象類中的普通方法  子類的都可以繼承
    print("抽象類中的普通方法"); 
  }
}

class Dog extends Animal {
  @override
  eat() {
    print('小狗吃骨頭');
  }

  @override
  run() {
    print('小狗在跑');
  }
}

class Cat extends Animal {
  @override
  eat() {
    print('小貓在吃老鼠');
  }

  @override
  run() {
    print('貓在跑');
  }
}

void main() {
  Dog l = new Dog(); //抽象類的 子類可以範例化
  l.eat();
  l.run();
  l.printInfo();
  Cat c = new Cat();//抽象類的 子類可以範例化
  c.eat();
  c.run();
  c.printInfo();

  // Animal a = new Animal();  //抽象類不能被範例化 只有繼承它的子類可以範例化
}

Dart中的多型
  1. 允許將子類的型別的指針賦值給父類別型別的指針,同一個函數呼叫會有不同的執行結果。上面的程式碼就是 多型。
  2. 子類的範例賦值給父類別的參照。
  3. 多型就是父類別定義一個方法不去實現,讓繼承他的子類去實現,每個子類有不同的表現。

子類的範例賦值給父類別的參照

//定義一個 Animal的抽象類

abstract class Animal {
  eat(); // 抽象方法

  printInfo() {
    //抽象類中的普通方法
    print("抽象類中的普通方法");
  }
}

class Dog extends Animal {
  @override
  eat() {
    print('小狗吃骨頭');
  }

  run() {
    print('小狗在跑');
  }
}

class Cat extends Animal {
  @override
  eat() {
    print('小貓在吃老鼠');
  }

  run() {
    print('貓在跑');
  }
}

void main() {
  Animal d = new Dog(); //子類的範例賦值給父類別的參照 多型
  d.eat(); //小狗在跑
  // d.run(); //會報錯
  Animal c = new Cat();
  c.eat(); //貓在跑
  // c.run(); //會報錯
}

Dart介面

和Java 一樣,dart 也有介面 ,但是和java還是有區別的。

首先,dart的介面沒有interface關鍵字第一介面,而是普通類或抽象類都可以作爲介面被實現。

同樣使用implement 關鍵字進行實現。

但是dart的介面有點奇怪,如果實現的類是普通類,會將普通類和抽象類中的屬性的方法全部需要複寫一遍。

而因爲抽象類可以定義抽象方法,普通類不可以,所以一般如果要實現像java介面那樣的方式,一般會使用抽象類。

建議使用抽象類(定義標準)定義介面。

//定義一個DB庫 支援mysql mssql mongodb  mysql mssql mongodb三個類裏面有同樣的方法
abstract class Db {  //抽象類當做介面 定義標準 
  //介面
  String uri; //數據庫的鏈接地址
  add(String data);
  save();
  delete();
}

//實現Mysql介面 類似繼承標準
class Mysql implements Db {
  @override
  String uri;

  Mysql(this.uri);
  @override
  add(data) {
    print('這是mysql的add方法' + data);
  }

  @override
  delete() {
    // TODO: implement delete
    throw UnimplementedError();
  }

  @override
  save() {
    // TODO: implement save
    throw UnimplementedError();
  }
}

//實現ms介面
class ms implements Db {
  @override
  String uri;

  @override
  add(data) {
    // TODO: implement add
    throw UnimplementedError();
  }

  @override
  delete() {
    // TODO: implement delete
    throw UnimplementedError();
  }

  @override
  save() {
    // TODO: implement save
    throw UnimplementedError();
  }
}

void main() {
  Mysql n = new Mysql('xxxxx');
  print(n.uri);
  n.add('馮上進');
}

Dart 中一個類實現多個介面 實現所有介面中的屬性和方法 (可以實現多介面);
abstract class A {
  String name;
  printA();
}

abstract class B {
  printB();
}

class C implements A, B { //一個類繼承多個介面
  @override
  String name = "張三";
  @override
  printA() {
    print(A);
  }

  @override
  printB() {
    print(B);
  }
}

void main() {
  C c = new C();
  print(c.name);//張三
  c.printA(); //A
  c.printB();//B
}

mixins 的中文意思是混入,就是在類中混入其他功能。

類是不可以繼承的,但是在Dart中可以使用mixins實現類似多繼承的功能

因爲mixins使用的條件,隨着Dart版本一直在變,這裏講的是Dart2.x中使用mixins的條件

  1. 作爲mixins的類只能繼承自Object,不能繼承其他類(A類和B類不能繼承其他類 否則會報錯)。
  2. 作爲mixins的類不能有建構函式
  3. 一個類可以mixins多個mixins類
  4. mixins絕不是繼承,也不是介面,而是一種全新的特性
class A {
  String info = 'this is info';
  A();//不能有建構函式 
  printA() {
    print(A);
  }
}

class B {
  printB() {
    print(B);
  }
}



class Person {
  String name;
  int age;
  Person(this.name, this.age);
  printP() {
    print('${this.name}---${this.age}');
  }
}

 //A和B兩個類 有相同的方法 那個類 ↓ 誰在後面 列印誰 
class C extends Person with A, B {  //繼承了 Person的 建構函式 又繼承了 Persond的屬性和方法 
  C(String name, int age) : super(name, age) {}
}

void main(){
C c = new C('張三',20);
c.printP(); //張三 ---20
}

class C with A, B {}//可以使用mixins實現類似多繼承的功能 通過 with繼承

void main() {
  C c = new C();
  print(c.info);//this is info
  c.printA(); //A
  c.printB();//B
  
  
  print(c is C); true
print(c is A);true
print(c is B);true   //c爲A和B的超類
A a = new A();
print(a is Object);true   //所有的類都繼承自Object
}




泛型 泛型方法 泛型類 泛型介面(解決程式碼沉冗問題)。

通俗理解:泛型就是解決 類 介面 方法的複用性,以及對不特定數據型別的支援(型別校驗)。

泛型方法

不指定型別放棄了型別檢查。我們在想實現的是傳入什麼 返回什麼。比如:傳入number型別必須返回 num

T getData<T>(T value){ //對傳入和返回的參數做校驗  去除第一個 T 就是 不對返回參數做校驗
	return value
}

void main(){
print(getData int(123))//123
print(getData String('xxx'))//xxx

}

泛型類

系統內建泛型類 List

void main() {
  List myList = new List<int>();  //<int>尖括號 裏面爲int型別新增的只能是數位  不能是別的型別 
  myList.add(123);
  myList.add('123');//錯誤寫法
  print(myList);
}
//泛型類
class Sum<T> {
  List list = new List<T>();

  void printS(T value) {
    this.list.add(value);
  }
}

void main() {
  Sum s1 = new Sum<int>(); //範例化子類的型別爲int  所以list不能add字串。
  s1.list.add(123); 
  s1.list.add("你好"); //報錯 因爲s1範例傳的;型別爲int型別。

  print(s1.list);
}
泛型介面(可以是普通類 也是可以是抽象類)

Dart中的泛型介面

實現數據快取的功能:有檔案快取,和記憶體快取。記憶體快取和檔案快取按照介面實現。

  1. 定義一個泛型介面 約束實現它的子類必須有getByKey(key) 和 setByKey(key,value)。
  2. 要求setByKey的時候的value的型別和範例化子類的時候指定的型別一致。
// 泛型介面
abstract class Cache<T>{
	getByKey( String key);
	void setByKey(String key,T value);
}



abstract class Person<T> {
  //泛型介面
  getByKey(String key);
  void setByKey(String key, T value);
}

class Son<T> implements Person<T> {
  @override
  getByKey(String key) {
    print('123');
  }

  @override
  void setByKey(String key, T value) {
    print("${key} ----${value}");
  }
}

void main() {
  Son s = new Son<int>(); //要求setByKey 中的value的型別 與子型別範例化參數一致
  s.setByKey('馮上進', 123);
  
}

dart中的庫 自定義庫 系統庫 第三方庫

在Dart中,庫的使用是通過import 關鍵字引入。

library指令可以建立一個庫,每個dart檔案都是一個庫,即使沒有使用library指令來指定。

Dart中的庫有三種:

  1. 我們自定義的庫 import ’lib/xxx.dart‘;

  2. 系統內建庫

    • import ‘dart:math’;
    • import ‘dart:io’;
    • import ‘dart:convert’;
  3. Pub包管理系統中的庫

    • http://pub.dev/packages
    • http://pub.flutter-io.cn/packages
    • http://pub.dartlang.org/flutter/
    1. 需要在自己專案根目錄新建一個pubspec.yal
    name:xxx
    dessciption:A new flutter module project.
    dependencies:
    	http:^0.12.0+2
    	date_format:^1.0.6
    
    1. 在pubspec.yaml檔案 然後設定名稱,描述,依賴等資訊。
    2. 然後執行pub get 獲取包下載到本地
    3. 專案中引入 import ‘package:http//http.dart’ as http; 看文件使用
void main() async {   //使用非同步方法 必須加 async await 纔可以使用非同步方法;
  var result = await testAsync();
  print(result);
}

//非同步方法
testAsync() async {  
  return '張三';
}

as 庫的重新命名

import 'lib/Person1.dart'
import 'lib/Person2.dart as lib'

main(List<String>args) {
Perosn p1 = new Person('張三',20);
p1.PrintInfo();
}
lib.Person p2 = new lib.Person('李四',20);
p2.PrintInfo();

部分匯入

import 'lib/myMath.dart' show getName;  //只能用庫裏面的getName();
import 'lib/myMath.dart' hide getName;  //隱藏庫裏面的getName();