flutter開發之Dart[必讀篇]?

這樣就好╮發表於2020-07-25
?

?Flutter應用程式使用Dart語言開發,Dart是物件導向的、類定義的、單繼承的語言。它的語法類似C語言,可以轉譯為JavaScript,支援介面(interfaces)、混入(mixins)、抽象類(abstract classes)、具體化泛型、可選型別 。

我相信本篇文章肯定會給你帶來一定的收穫?

前言

下面這一段是筆者從Dart官網摘抄過來的,筆者認為在學習Dart之前,先要了解以下Dart相關概念:

  • 任何儲存在變數中的都是一個 物件 , 並且所有的物件都是對應一個 類 的例項。 無論是數字,函式和 null 都是物件。所有物件繼承自 Object 類。

  • 儘管 Dart 是強型別的,但是 Dart 可以推斷型別,所以型別註釋是可選的。 如果要明確說明不需要任何型別, 需要使用特殊型別 dynamic 。

  • Dart 支援泛型,如 List (整數列表)或 List (任何型別的物件列表)。

  • Dart 支援頂級函式(例如 main() ), 同樣函式繫結在類或物件上(分別是 靜態函式 和 例項函式 )。 以及支援函式內建立函式 ( 巢狀 或 區域性函式 ) 。

  • Dart 支援頂級 變數 , 同樣變數繫結在類或物件上(靜態變數和例項變數)。 例項變數有時稱為欄位或屬性。

  • 與 Java 不同,Dart 沒有關鍵字 “public” , “protected” 和 “private” 。 如果識別符號以下劃線(_)開頭,則它相對於庫是私有的。

  • 識別符號 以字母或下劃線(_)開頭,後跟任意字母和數字組合。

  • Dart 語法中包含 表示式( expressions )(有執行時值)和 語句( statements )(沒有執行時值)。 例如,條件表示式 condition ? expr1 : expr2 的值可能是 expr1 或 expr2 。 將其與 if-else 語句 相比較,if-else 語句沒有值。 一條語句通常包含一個或多個表示式,相反表示式不能直接包含語句。

  • Dart 工具提示兩種型別問題:警告_和_錯誤。 警告只是表明程式碼可能無法正常工作,但不會阻止程式的執行。 錯誤可能是編譯時錯誤或者執行時錯誤。 編譯時錯誤會阻止程式碼的執行; 執行時錯誤會導致程式碼在執行過程中引發 [異常](#exception)。

Dart 命名規則

main() 程式開始執行函式,該函式是特定的、必須的、頂級函式。

Dart 變數

Dart本身是一個強型別語言,任何變數都是有確定型別的。

1. 使用var建立一個變數並初始化

var name = 'lisa'; //name 變數的型別被推斷為 String
複製程式碼

在Dart中,當用var宣告一個變數後,Dart在編譯時會根據第一次賦值資料的型別來推斷其型別,編譯結束後其型別就已經被確定。比如上面的name已經被確定為String型別,不能在複製其他的型別,否則會報錯。但是也可以通過指定型別的方式,來改變變數型別。比如:

name = 123; //報錯
name = '張三' // 不報錯
複製程式碼

2. 顯式宣告可以推斷出的型別:

比如: 使用String ,name被確定為String型別了。

String name = '張三';
複製程式碼

3. dynamic 它的型別可以隨意改變。

如果要明確說明不需要任何型別, 需要使用特殊型別 dynamic。

4. 預設值——未初始化的變數預設值是 null。

即使變數是數字 型別預設值也是 null,因為在 Dart 中一切都是物件,數字型別 也不例外。

Dart 常量:

使用過程中從來不會被修改的變數, 可以使用 final 或 const, 而不是 var 或者其他型別.

使用final或者const修飾的變數,變數型別可以省略.

final

final name = 'Bob';
final String nickname = 'Bobby';
複製程式碼

const

const bar = 1000000; 
const double atm = 1.01325 * bar; 
複製程式碼

final和const的區別:

final a=new DateTime.now();
print(a);   //2019-05-10 15:59:02.966122
//const a=new DateTime.now();   //報錯了
複製程式碼

兩者區別在於:const 變數是一個編譯時常量,final變數在第一次使用時被初始化。

Dart的命名規則:

  1. 變數名稱必須由數字、字母、下劃線和美元符($)組成。

  2. 注意:識別符號開頭不能是數字

  3. 識別符號不能是保留字和關鍵字。

  4. 變數的名字是區分大小寫的如: age和Age是不同的變數。在實際的運用中,也建議,不要用一個單詞大小寫區分兩個變數。

  5. 識別符號(變數名稱)一定要見名思意 :變數名稱建議用名詞,方法名稱建議用動詞 。

Dart 資料型別

字串型別

1、字串定義的幾種方式

var str1='this is str1';
String str1='this is str1';
複製程式碼

2、字串的拼接

String str1='你好';

String str2='Dart';

print("$str1 $str2"); //你好Dart
  
//或者使用 +運算子
print(str1+str2); //你好Dart
複製程式碼
  1. 使用連續三個單引號或者三個雙引號實現多行字串物件的建立:

數值型別

Number型別分為int型別和double型別。

int 和 double 都是 num. 的亞型別。 num 型別包括基本運算 +, -, /, 和 *, 以及 abs(), ceil(), 和 floor(), 等函式方法.

1、int 必須是整型

int a=123;

a=45;

print(a);
複製程式碼

2、double 既可以是整型 也可是浮點型

double b=23.5;

b=24;

print(b);
複製程式碼

布林型別

Dart 使用 bool 型別表示布林值。 Dart 只有字面量 true and false 是布林型別.

bool flag1=true;

print(flag1);

bool flag2=false;

print(flag2);
複製程式碼
 bool flag=true;

if(flag){
  print('真');
}else{
  print('假');
}
複製程式碼

List集合型別

在 Dart 中的 Array 就是 List 物件, 通常稱之為 List 。

1、第一種定義List的方式

var arr1=['aaa','bbbb','cccc'];

print(arr1);

print(arr1.length);

print(arr1[1]);
複製程式碼

2、第二種定義List的方式

元素的型別不確定。

var arr2=new List();
arr2.add('張三');
arr2.add('李四');
arr2.add('王五');

print(arr2);
print(arr2[2]);
  
//也可以使用下面這種方法 
  
var arr3=new List<dynamic>();

arr3.add('張三');

arr3.add(123);

print(arr3);
複製程式碼

3、定義List指定型別 指定List的每一個元素都是String型別。

main() {
  var arr3=new List<String>();

  arr3.add('張三');

  arr3.add('lisa');

  print(arr3);

}

複製程式碼

Map 型別

通常來說, Map 是用來關聯 keys 和 values 的物件。 keys 和 values 可以是任何型別的物件。在一個 Map 物件中一個 key 只能出現一次。 但是 value 可以出現多次。 Dart 中 Map 通過 Map 字面量 和 Map 型別來實現。

第一種定義 Map的方式

var person={
"name":"張三",
"age":20,
"work":["程式設計師","送外賣"]
};

print(person);

print(person["name"]);

print(person["age"]);

print(person["work"]);
複製程式碼

第二種定義 Map的方式

var p=new Map();

p["name"]="李四";
p["age"]=22;
p["work"]=["程式設計師","送外賣"];
print(p);

print(p["age"]);
複製程式碼

Set型別

Set是沒有順序且不能重複的集合,所以不能通過索引去獲取值

var s=new Set();
s.add('香蕉');
s.add('蘋果');
s.add('蘋果');

print(s);   //{香蕉, 蘋果}
print(s.toList()); //[香蕉, 蘋果] 


  List myList=['香蕉','蘋果','西瓜','香蕉','蘋果','香蕉','蘋果'];

  var s=new Set();
複製程式碼

可以用來陣列去重

 List myList=['香蕉','蘋果','西瓜','香蕉','蘋果','香蕉','蘋果'];

  var s=new Set();

  s.addAll(myList);

  print(s); //{香蕉, 蘋果, 西瓜}

  print(s.toList());  //[香蕉, 蘋果, 西瓜]
複製程式碼

其他型別

Runes

Rune是UTF-32編碼的字串。它可以通過文字轉換成符號表情或者代表特定的文字。

Symbols

Symbol物件表示在Dart程式中宣告的運算子或識別符號。

Dart 常用操作符

算數運算子

int a=13;
int b=5;

print(a+b);   //加
print(a-b);   //減
print(a*b);   //乘
print(a/b);   //除
print(a%b);   //取餘
print(a~/b);  //取整
複製程式碼

關係運算子

  int a=5;
  int b=3;

  print(a==b);   //判斷是否相等
  print(a!=b);   //判斷是否不等
  print(a>b);   //判斷是否大於
  print(a<b);   //判斷是否小於
  print(a>=b);   //判斷是否大於等於
  print(a<=b);   //判斷是否小於等於
複製程式碼

賦值運算子

使用 = 為變數賦值。 使用 ??= 運算子時,只有當被賦值的變數為 null 時才會賦值給它。


int b=6;
b??=23;
print(b); //6


int b;
b??=23;
print(b); //23
複製程式碼

如果a為空,則b=10;

var a=22;
var b= a ?? 10;

print(b); //22
複製程式碼

型別轉換

Number型別轉換成String型別 toString()

String型別轉成Number型別 int.parse()

字串轉化為數值

String str='123';

var myNum=int.parse(str);

print(myNum is int);



String str='123.1';

var myNum=double.parse(str);

print(myNum is double);


String price='12';

var myNum=double.parse(price);

print(myNum);

print(myNum is double);     
複製程式碼

可以通過try ...catch做判斷

String price='';
try{
  var myNum=double.parse(price);

  print(myNum);

}catch(err){
     print(0);
} 

複製程式碼

數值轉化為字串

var myNum=12;

var str=myNum.toString();

print(str is String);
複製程式碼

其他型別轉換成Booleans型別

//isEmpty:判斷字串是否為空  

var str='';
if(str.isEmpty){
  print('str空');
}else{
  print('str不為空');
}


var myNum=123;

if(myNum==0){
   print('0');
}else{
  print('非0');
}


var myNum;

if(myNum==0){
   print('0');
}else{
  print('非0');
}



var myNum;
if(myNum==null){
   print('空');
}else{
  print('非空');
}


複製程式碼

Dart 集合型別詳解

List裡面常用的屬性和方法:

List裡面的屬性:

List myList=['香蕉','蘋果','西瓜'];
    print(myList.length);
    print(myList.isEmpty);
    print(myList.isNotEmpty);
    print(myList.reversed);  //對列表倒序排序
    var newMyList=myList.reversed.toList();
    print(newMyList);
複製程式碼

List裡面的方法:

List myList=['香蕉','蘋果','西瓜'];
myList.add('桃子');   //增加資料  增加一個

myList.addAll(['桃子','葡萄']);  //拼接陣列

print(myList);

print(myList.indexOf('蘋x果'));    //indexOf查詢資料 查詢不到返回-1  查詢到返回索引值


myList.remove('西瓜'); //刪除  傳入具體值

myList.removeAt(1);  //刪除  傳入索引值

print(myList);

myList.fillRange(1, 2,'aaa');  //修改

myList.fillRange(1, 3,'aaa');  


myList.insert(1,'aaa');      // 指定位置插入  插入一個

myList.insertAll(1, ['aaa','bbb']);  //插入 多個 指定位置插入List

var str=myList.join('-');   //list轉換成字串

print(str);

print(str is String);  //true
var list=str.split('-'); // 字串轉化成List

var arr = (11,22,33);
var arr2 = arr.toList(); //其他型別轉換成List 
複製程式碼

遍歷List的方法:forEach map where any every,用法和ES6一樣,此處不再贅述。

Set:

用它最主要的功能就是去除陣列重複內容; Set是沒有順序且不能重複的集合,所以不能通過索引去獲取值;

var s=new Set();
s.add('香蕉');
s.add('蘋果');
s.add('蘋果');

print(s);   //{香蕉, 蘋果}

print(s.toList()); 
複製程式碼

List myList=['香蕉','蘋果','西瓜','香蕉','蘋果','香蕉','蘋果'];

var s=new Set();

s.addAll(myList);

print(s);

print(s.toList());
複製程式碼
s.forEach((value)=>print(value));
複製程式碼

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

常用屬性:

Map person={
"name":"張三",
"age":20,
"sex":"男"
};

print(person.keys.toList());
print(person.values.toList());
print(person.isEmpty); //是否為空
print(person.isNotEmpty); //是否不為空
複製程式碼

常用方法:

Map person={
  "name":"張三",
  "age":20,
  "sex":"男"
};

person.addAll({ //合併對映  給對映內增加屬性
  "work":['敲程式碼','送外賣'],
  "height":160
});

print(person);

person.remove("sex"); //刪除指定key的資料
print(person);
 
print(person.containsValue('張三')); //檢視對映內的值  返回true/false

person.forEach((key,value){            
    print("$key---$value");
});
複製程式碼

Dart 函式

Dart 是一門真正物件導向的語言, 甚至其中的函式也是物件,並且有它的型別 Function 。

自定義方法

無返回值 前面加void關鍵字表示改方法無返回值。

void printInfo(){
  print('我是一個自定義方法');
}
複製程式碼

返回一個int型別的數值

int getNum(){
  var myNum=123;
  return myNum;
}
複製程式碼

返回字串型別

String printUserInfo(){
  return 'this is str';
}
複製程式碼

返回List型別

List getList(){
  return ['111','2222','333'];
}
複製程式碼

main() 函式

任何應用都必須有一個頂級 main() 函式,作為應用服務的入口。 main() 函式返回值為空,引數為一個可選的 List 。

void main(){

  print('呼叫系統內建的方法');

  printInfo();
  var n=getNum();
  print(n);


  print(printUserInfo());


  print(getList());

  print(getList());
}
複製程式碼

方法的作用域

  void get(){

      aaa(){

          print(getList());
          print('aaa');
      }
      aaa();
  }

  // aaa();  錯誤寫法 

  get();  //呼叫方法
}
複製程式碼

方法傳參

定義一個方法 求1到這個數的所有數的和 ,返回值是一個int型別。

int sumNum(int n){ //接收的引數必須事int型別,否則報錯
  var sum=0;
  for(var i=1;i<=n;i++)
  {
    sum+=i;
  }
  return sum;
} 

var sum=sumNum(5);

複製程式碼

定義一個方法然後列印使用者資訊,返回值是String型別, 穿的引數是String和int型別。

String printUserInfo(String username,int age){  //行參
    return "姓名:$username---年齡:$age";
}

print(printUserInfo('張三',20)); //實參
複製程式碼

定義一個帶可選引數的方法

包裝一組函式引數,用[]標記為可選的位置引數,並放在引數列表的最後面:

String printUserInfo(String username,[int age]){  //行參

  if(age!=null){
    return "姓名:$username---年齡:$age";
  }
  return "姓名:$username---年齡保密";

}

print(printUserInfo('張三',21)); //實參

print(printUserInfo('張三'));
複製程式碼

定義一個帶預設引數的方法

如果呼叫該方法時,不傳可選引數,可以使用預設引數。

String printUserInfo(String username,[String sex='男',int age]){  //行參

  if(age!=null){
    return "姓名:$username---性別:$sex--年齡:$age";
  }
  return "姓名:$username---性別:$sex--年齡保密";

}

print(printUserInfo('張三'));

print(printUserInfo('小李','女'));

print(printUserInfo('小李','女',30));
複製程式碼

定義一個命名引數的方法

定義函式時,使用{param1, param2, …},放在引數列表的最後面,用於指定命名引數。例如:

呼叫函式時,可以使用指定命名引數。例如:paramName: value

String printUserInfo(String username,{int age,String sex='男'}){  //行參

  if(age!=null){
    return "姓名:$username---性別:$sex--年齡:$age";
  }
  return "姓名:$username---性別:$sex--年齡保密";
}

print(printUserInfo('張三',age:20,sex:'未知'));
複製程式碼

命名引數的方法和可選引數的方法區別:

  1. 命名引數方法使用的是{};
  2. 可選引數使用的是[];
  3. 命名引數方法必須根據形參的欄位名字傳值;

可選引數可以是命名引數或者位置引數,但一個引數只能選擇其中一種方式修飾。

函式是一等物件

一個函式可以作為另一個函式的引數。例如:

//方法
fn1(){
  print('fn1');
}

//方法
fn2(fn){

  fn();
}

//呼叫fn2這個方法 把fn1這個方法當做引數傳入
fn2(fn1);
複製程式碼

匿名方法

多數函式是有名字的, 比如 main() 和 getInfo()。 也可以建立沒有名字的函式,這種函式被稱為 匿名函式.

匿名函式和命名函式看起來類似 - 在括號裡面可以傳參,引數使用都好分割。

var printNum=(int n, int m){

  print(m*n);
};

printNum(12,20); //240
複製程式碼

自執行方法

((int n){
  print(n);
  print('我是自執行方法');
})(12);
複製程式碼

方法的遞迴

請求100以內數值的和

var sum=0;
fn(int n){

    sum+=n;

    if(n==0){
      return;
    }
    fn(n-1);
}

fn(100);
print(sum);

複製程式碼

閉包

閉包 即一個函式物件,即使函式物件的呼叫在它原始作用域之外, 依然能夠訪問在它詞法作用域內的變數。

/// 返回一個函式,返回的函式引數與 [addBy] 相加。
Function makeAdder(num addBy) {
  return (num i) => addBy + i;
}

void main() {
  // 建立一個加 2 的函式。
  var add2 = makeAdder(2);

  // 建立一個加 4 的函式。
  var add4 = makeAdder(4);

  print(add2(3) == 5); //true
  print(add4(3) == 7); //true
}
複製程式碼

Dart 中的類

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

Dart是一門使用類和單繼承的面嚮物件語言,所有的物件都是類的例項,並且所有的類都是Object的子類。

Dart 中定義類 使用類


class Person{
  String name="張三";
  int age=23;
  void getInfo(){
      // print("$name----$age");
      //等同於下面
      print("${this.name}----${this.age}");
  }
  void setInfo(int age){
    this.age=age;
  }
}
  
void main() {
  //例項化類
  Person p1=new Person();
  print(p1.name); //張三

  p1.setInfo(28);
  p1.getInfo(); //張三----28
}
複製程式碼

Dart 中類的預設建構函式

定義一個預設的建構函式


class Person{
  String name='張三';
  int age=20; 
  
  //預設建構函式
  Person(){
    print('這是建構函式裡面的內容  這個方法在例項化的時候觸發');
  }
  
  void printInfo(){   
    print("${this.name}----${this.age}");
  }
}
void main() {
  //例項化類
  Person p1=new Person();
  print(p1.name);

  p1.printInfo();
}
複製程式碼

給建構函式傳參


class Person{
  String name;
  int age; 
  
  //預設建構函式
  Person(String name,int age){
      this.name=name;
      this.age=age;
  }
  
  void printInfo(){   
    print("${this.name}----${this.age}");
  }
}

void main() {
  //例項化類
  Person p1=new Person('張三',20);
  print(p1.name);  //張三

  p1.printInfo(); //張三----20
}
複製程式碼

建構函式的簡寫方法:


class Person{
  String name;
  int age;
  
  //預設建構函式的簡寫
  Person(this.name,this.age);
  
  void printInfo(){   
    print("${this.name}----${this.age}");
  }
}

void main() {
  //例項化類
  Person p1=new Person('張三',20);
  print(p1.name);  //張三

  p1.printInfo(); //張三----20
}
複製程式碼

Dart中的命名建構函式

dart裡面預設建構函式只有一個,但是命名建構函式可以有多個。


class Person{
  String name;
  int age; 
  
  //預設建構函式的簡寫
  Person(this.name, this.age);
  
  //我是命名建構函式
  Person.now(){
    print('我是命名建構函式');
  }

  //給命名建構函式傳參
  Person.setInfo(String name, int age){
    this.name=name;
    this.age=age;
  }

  void printInfo(){   
    print("${this.name}----${this.age}");
  }
}

void main() {
  Person p1=new Person('張三', 20);   //預設例項化類的時候呼叫的是 預設建構函式

  Person p2=new Person.now();   //命名建構函式
  
  Person p3=new Person.setInfo('李四',30);
  p1.printInfo(); //張三----20
  p3.printInfo(); //李四----30

}
複製程式碼

Dart中的私有方法 和私有屬性

Dart和其他面嚮物件語言不一樣,Data中沒有 public private protected這些訪問修飾符合

但是我們可以使用_把一個屬性或者方法定義成私有。

新建一個單獨的 Animal類

class Animal{

  String _name;   //私有屬性
  int age; 
  
  //預設建構函式的簡寫
  Animal(this._name,this.age);

  void printInfo(){   
    print("${this._name}----${this.age}");
  }

  String getName(){ 
    return this._name;
  } 
  void _run(){
    print('這是一個私有方法');
  }

  execRun(){
    this._run();  //類裡面方法的相互呼叫
  }
}
複製程式碼

//呼叫私有方法

import 'lib/Animal.dart';

void main(){
 
 Animal a=new Animal('小狗', 3);

 print(a.getName());

  a.execRun();   //間接的呼叫私有方法
 

}
複製程式碼

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

class Rect{
  num height;
  num width; 
  
  Rect(this.height,this.width);
  get area{
    return this.height*this.width;
  }
  set areaHeight(value){
    this.height=value;
  }
}

void main(){
  Rect r=new Rect(10,4);
  // print("面積:${r.area()}");   
  r.areaHeight=6;

  print(r.area); //24

}
複製程式碼

Dart中 初始化例項變數

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

class Rect{

  int height;
  int width;
  Rect():height=2,width=10{
    
    print("${this.height}---${this.width}"); //2---10
  }
  getArea(){
    return this.height*this.width;
  } 
}

void main(){
  Rect r=new Rect();
  print(r.getArea());  //10
   
}
複製程式碼

Dart 一個類實現多個介面

定義2個抽象類A和B,在抽象類中定義的屬性和方法,在介面中必須實現。

abstract class A{
  String name;
  printA();
}

abstract class B{
  printB();
}

class C implements A,B{  
  @override
  String name;  
  
  @override
  printA() {
    print('printA');
  }
  
  @override
  printB() {
    // TODO: implement printB
    return null;
  }

  
}

複製程式碼
void main(){

  C c=new C();
  
  c.printA();  //printA

}
複製程式碼

Dart中的mixins

在Dart中可以使用mixins實現類似多繼承的功能。

因為mixins使用的條件[Dart2.x版本]:

1、作為mixins的類只能繼承自Object,不能繼承其他類;

2、作為mixins的類不能有建構函式;

3、一個類可以mixins多個mixins類;

4、mixins絕不是繼承,也不是介面,而是一種全新的特性

class A {
  String info="this is A";
  void printA(){
    print("A");
  }
}

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

class C with A,B{
  
}

void main(){
  
  var c=new C();  
  c.printA();
  c.printB();
  print(c.info);


}
複製程式碼

Dart中類的 靜態成員 操作符 類的繼承

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

Dart中的靜態成員:

1、使用static 關鍵字來實現類級別的變數和函式

2、靜態方法不能訪問非靜態成員,非靜態方法可以訪問靜態成員

定義一個有靜態屬性和靜態方法的類

class Person {
  static String name = '張三';
  static void show() {
    print(name);
  }
}

main(){
  print(Person.name);
  Person.show();  
}
複製程式碼

Dart中的物件操作符

  1. ? 條件運算子 (瞭解)
  2. as 型別轉換
  3. is 型別判斷
  4. .. 級聯操作
class Person {
  String name;
  num age;
  Person(this.name,this.age);
  void printInfo() {
    print("${this.name}---${this.age}");  
  }
  
}
複製程式碼

? 條件運算子

如果p存在,就呼叫printInfo方法。

Person p=new Person('張三', 20);
   p?.printInfo();
複製程式碼

is 判斷型別

Person p=new Person('張三', 20);

if(p is Person){
    p.name="李四";
} 
p.printInfo();
print(p is Object);
複製程式碼

as 型別轉換

 p1=new Person('張三1', 20);

// p1.printInfo();

(p1 as Person).printInfo();
複製程式碼

.. 級聯操作 (連綴)

 Person p1=new Person('張三1', 20);

   p1.printInfo();

   p1.name='張三222';
   p1.age=40;
   p1.printInfo();
複製程式碼

等同於

Person p1=new Person('張三1', 20);

p1.printInfo();

p1..name="李四"
..age=30
..printInfo();
複製程式碼

Dart 類的繼承-簡單繼承

Dart中的類的繼承:
1、子類使用extends關鍵詞來繼承父類

2、子類會繼承父類裡面可見的屬性和方法 但是不會繼承建構函式

3、子類能複寫父類的方法 getter和setter

class Person {
  String name='張三';
  num age=20; 
  void printInfo() {
    print("${this.name}---${this.age}");  
  } 
}
class Web extends Person{

}

main(){   

  Web w=new Web();
  print(w.name);
  w.printInfo();
 
}
複製程式碼

給父類建構函式傳參

class Person {
  String name;
  num age; 
  Person(this.name,this.age);
  void printInfo() {
    print("${this.name}---${this.age}");  
  }
}


class Web extends Person{
  Web(String name, num age) : super(name, age){

  }
  
}

main(){ 
  Web w=new Web('張三', 12);

  w.printInfo();


}
複製程式碼

繼承的時候也可以給自己的類傳參

class Web extends Person{
  String sex;
  Web(String name, num age,String sex) : super(name, age){
    this.sex=sex;
  }
  run(){
   print("${this.name}---${this.age}--${this.sex}");  
  }
  
}

Web w=new Web('張三', 12,"男");

w.printInfo();
  
複製程式碼

給命名建構函式傳參

class Person {
  String name;
  num age; 
  Person(this.name,this.age);
  Person.xxx(this.name,this.age);
  void printInfo() {
    print("${this.name}---${this.age}");  
  }
}


class Web extends Person{
  String sex;
  Web(String name, num age,String sex) : super.xxx(name, age){
    this.sex=sex;
  }
  run(){
   print("${this.name}---${this.age}--${this.sex}");  
  }
  
}

main(){ 
  Web w=new Web('張三', 12,"男");

  w.printInfo();

  w.run();

}
複製程式碼

Dart 類的繼承 覆寫父類的方法

class Person {
  String name;
  num age; 
  Person(this.name,this.age);
  void printInfo() {
    print("${this.name}---${this.age}");  
  }
  work(){
    print("${this.name}在工作...");
  }
}

class Web extends Person{
  Web(String name, num age) : super(name, age);

  run(){
    print('run');
  }
  //覆寫父類的方法
  @override       //可以寫也可以不寫  建議在覆寫父類方法的時候加上 @override 
  void printInfo(){
     print("姓名:${this.name}---年齡:${this.age}"); 
  }
  @override
  work(){
    print("${this.name}的工作是寫程式碼");
  }

}
main(){ 

  Web w=new Web('李四',20);

  w.printInfo();

  w.work();
 
}
複製程式碼

Dart 自類裡面呼叫父類的方法

class Person {
  String name;
  num age; 
  Person(this.name,this.age);
  void printInfo() {
    print("${this.name}---${this.age}");  
  }
  work(){
    print("${this.name}在工作...");
  }
}

class Web extends Person{
  Web(String name, num age) : super(name, age);

  run(){
    print('run');
    super.work();  //自類呼叫父類的方法
  }
  //覆寫父類的方法
  @override       //可以寫也可以不寫  建議在覆寫父類方法的時候加上 @override 
  void printInfo(){
     print("姓名:${this.name}---年齡:${this.age}"); 
  }

}


main(){ 

  Web w=new Web('李四',20);

  // w.printInfo();

  w.run();
 
}
複製程式碼

Dart中的抽象類 多型 以及介面

extends抽象類 和 implements的區別:

1、如果要複用抽象類裡面的方法,並且要用抽象方法約束自類的話我們就用extends繼承抽象類

2、如果只是把抽象類當做標準的話我們就用implements實現抽象類

Dart中抽象類

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

1、抽象類通過abstract 關鍵字來定義

2、Dart中的抽象方法不能用abstract宣告,Dart中沒有方法體的方法我們稱為抽象方法。

3、如果子類繼承抽象類必須得實現裡面的抽象方法

4、如果把抽象類當做介面實現的話必須得實現抽象類裡面定義的所有屬性和方法。

5、抽象類不能被例項化,只有繼承它的子類可以

abstract class Animal{
  eat();   //抽象方法
  run();  //抽象方法  
  printInfo(){
    print('我是一個抽象類裡面的普通方法');
  }
}

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

  @override
  run() {
    // TODO: implement run
    print('小狗在跑');
  }  
}
class Cat extends Animal{
  @override
  eat() {
    // TODO: implement eat
    print('小貓在吃老鼠');
  }

  @override
  run() {
    // TODO: implement run
    print('小貓在跑');
  }

}

main(){

  Dog d=new Dog();
  d.eat();
  d.printInfo();

   Cat c=new Cat();
  c.eat();

  c.printInfo();


  // Animal a=new Animal();   //抽象類沒法直接被例項化

}
複製程式碼

Dart中多型

  1. 允許將子類型別的指標賦值給父類型別的指標, 同一個函式呼叫會有不同的執行效果 。

  2. 子類的例項賦值給父類的引用。

  3. 多型就是父類定義一個方法不去實現,讓繼承他的子類去實現,每個子類有不同的表現

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

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

main(){
  Animal d=new Dog();

  d.eat(); 

  Animal c=new Cat();

  c.eat();

}
複製程式碼

Dart中的介面

dart的介面沒有interface關鍵字定義介面,而是普通類或抽象類都可以作為介面被實現。

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

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

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

建議使用抽象類定義介面。

定義一個DB庫 支援 mysql mssql mongodb , mysql mssql mongodb三個類裡面都有同樣的方法

abstract class Db{   //當做介面   介面:就是約定 、規範
    String uri;      //資料庫的連結地址
    add(String data);
    save();
    delete();
}

class Mysql implements Db{
  
  @override
  String uri;

  Mysql(this.uri);

  @override
  add(data) {
    // TODO: implement add
    print('這是mysql的add方法'+data);
  }

  @override
  delete() {
    // TODO: implement delete
    return null;
  }

  @override
  save() {
    // TODO: implement save
    return null;
  }

  remove(){
      
  }

  
}

class MsSql implements Db{
  @override
  String uri;
  @override
  add(String data) {
    print('這是mssql的add方法'+data);
  }

  @override
  delete() {
    // TODO: implement delete
    return null;
  }

  @override
  save() {
    // TODO: implement save
    return null;
  }

}

main() {

  Mysql mysql=new Mysql('xxxxxx');

  mysql.add('1243214');  
}
複製程式碼

Dart中一個類實現多個介面 以及Dart中的Mixins

Dart中一個類實現多個介面:

abstract class A{
  String name;
  printA();
}

abstract class B{
  printB();
}

class C implements A,B{  
  @override
  String name; 
  
  @override
  printA() {
    print('printA');
  }
  
  @override
  printB() {
    // TODO: implement printB
    return null;
  }
}

void main(){

  C c=new C();
  c.printA();
}
複製程式碼

Dart中的mixins

Mixin 是複用類程式碼的一種途徑, 複用的類可以在不同層級,之間可以不存在繼承關係。

通過 with 後面跟一個或多個混入的名稱,來 使用 Mixin 。

因為mixins使用的條件,隨著Dart版本一直在變,這裡講的是Dart2.x中使用mixins的條件:

1、作為mixins的類只能繼承自Object,不能繼承其他類;

2、作為mixins的類不能有建構函式;

3、一個類可以mixins多個mixins類;

4、mixins絕不是繼承,也不是介面,而是一種全新的特性;

class A {
  String info="this is A";
  void printA(){
    print("A");
  }
}

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

class C with A,B{
  
}

void main(){  
  var c=new C();  
  c.printA();
  c.printB();
  print(c.info);
}
複製程式碼

給父類傳參

class Person{
  String name;
  num age;
  Person(this.name,this.age);
  printInfo(){
    print('${this.name}----${this.age}');
  }
  void run(){
    print("Person Run");
  }
}

class A {
  String info="this is A";
  void printA(){
    print("A");
  }
  void run(){
    print("A Run");
  }
}

class B {  
  void printB(){
    print("B");
  }
  void run(){
    print("B Run");
  }
}

class C extends Person with B,A{

  //給父類傳參
  C(String name, num age) : super(name, age);
}

void main(){  
  var c=new C('張三',20);  
  c.printInfo();
  c.run();
}
複製程式碼

Dart中的mixins 的型別

mixins的型別就是其超類的子型別。

class A {
  String info="this is A";
  void printA(){
    print("A");
  }
}

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

class C with A,B{
  
}

void main(){  
  var c=new C();  
  print(c is C);    //true
  print(c is A);    //true
  print(c is B);   //true
}
複製程式碼

Dart 中的泛型

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

Dart中的泛型方法

只能返回string型別的資料

String getData(String value){
      return value;
  }
複製程式碼

同時支援返回 string型別 和int型別 (程式碼冗餘)

 String getData1(String value){
      return value;
  }

  int getData2(int value){
      return value;
  }
複製程式碼

同時返回 string型別 和number型別 不指定型別可以解決這個問題

getData(value){
      return value;
  }
複製程式碼

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

 getData<T>(T value){
      return value;
  }
  
print(getData<int>(12)); //傳入型別和返回型別一致
複製程式碼

Dart中的泛型類

可以給陣列新增int型別,也可以新增String型別的元素。

class PrintClass<T>{
      List list=new List<T>();
      void add(T value){
          this.list.add(value);
      }
      void printInfo(){          
          for(var i=0;i<this.list.length;i++){
            print(this.list[i]);
          }
      }
 }
 
 PrintClass p=new PrintClass<int>();

p.add(12);

p.add(23);

p.printInfo();
複製程式碼

Dart中的泛型介面

實現資料快取的功能:有檔案快取、和記憶體快取。記憶體快取和檔案快取按照介面約束實現。

1、定義一個泛型介面 約束實現它的子類必須有getByKey(key) 和 setByKey(key,value)

2、要求setByKey的時候的value的型別和例項化子類的時候指定的型別一致

abstract class Cache<T>{
  getByKey(String key);
  void setByKey(String key, T value);
}

class FlieCache<T> implements Cache<T>{
  @override
  getByKey(String key) {    
    return null;
  }

  @override
  void setByKey(String key, T value) {
   print("我是檔案快取 把key=${key}  value=${value}的資料寫入到了檔案中");
  }
}

class MemoryCache<T> implements Cache<T>{
  @override
  getByKey(String key) {   
    return null;
  }

  @override
  void setByKey(String key, T value) {
       print("我是記憶體快取 把key=${key}  value=${value} -寫入到了記憶體中");
  }
}


void main(){

 MemoryCache m=new MemoryCache<Map>();

 m.setByKey('index', {"name":"張三","age":20});
}
複製程式碼

Dart的非同步

Dart是基於單執行緒模型的語言。在Dart也有自己的程式(或者叫執行緒)機制,名叫isolate。APP的啟動入口main函式就是一個isolate。

Dart執行緒中有一個訊息迴圈機制(event loop)和兩個佇列(event queue和microtask queue)。

  • event queue包含所有外來的事件:I/O,mouse events,drawing events,timers,isolate之間的message等。任意isolate中新增的event(I/O,mouse events,drawing events,timers,isolate的message)都會放入event queue中排隊等待執行。

  • microtask queue只在當前isolate的任務佇列中排隊,優先順序高於event queue.

如果在event中插入microtask,當前event執行完畢即可插隊執行microtask。如果沒有microtask,就沒辦法插隊了,也就是說,microtask queue的存在為Dart提供了給任務佇列插隊的解決方案。

當main方法執行完畢退出後,event loop就會以FIFO(先進先出)的順序執行microtask,當所有microtask執行完後它會從event queue中取事件並執行。如此反覆,直到兩個佇列都為空,如下流程圖:

非同步執行

那麼在Dart中如何讓你的程式碼非同步執行呢?很簡單,把要非同步執行的程式碼放在微任務佇列或者事件佇列裡就行了。

  • 可以呼叫scheduleMicrotask來讓程式碼以微任務的方式非同步執行
 scheduleMicrotask((){
        print('a microtask');
    });
複製程式碼
  • 可以呼叫Timer.run來讓程式碼以Event的方式非同步執行
Timer.run((){
       print('a event');
   });
複製程式碼

和JS一樣,僅僅使用回撥函式來做非同步的話很容易陷入“回撥地獄(Callback hell)”,為了避免這樣的問題,JS引入了Promise。同樣的, Dart引入了Future。

Future

Future與JavaScript中的Promise非常相似,表示一個非同步操作的最終完成(或失敗)及其結果值的表示。簡單來說,它就是用於處理非同步操作的,非同步處理成功了就執行成功的操作,非同步處理失敗了就捕獲錯誤或者停止後續操作。一個Future只會對應一個結果,要麼成功,要麼失敗。

要使用Future的話需要引入dart.async

import 'dart:async';
複製程式碼

建立一個立刻在事件佇列裡執行的Future:

Future(() => print('立刻在Event queue中執行的Future'));
複製程式碼

Future.then

使用Future.delayed 建立了一個延時任務(實際專案可能是一個真正的耗時任務,比如一次網路請求),即2秒後返回結果字串"hi world!",然後我們在then中接收非同步結果並列印結果:

Future.delayed(new Duration(seconds: 2),(){
   return "hi world!";
}).then((data){
   print(data);
});
複製程式碼

Future.catchError

如果非同步任務發生錯誤,我們可以在catchError中捕獲錯誤,我們將上面示例改為:

Future.delayed(new Duration(seconds: 2),(){
   //return "hi world!";
   throw AssertionError("Error");  
}).then((data){
   //執行成功會走到這裡  
   print("success");
}).catchError((e){
   //執行失敗會走到這裡  
   print(e);
});
複製程式碼

then方法還有一個可選引數onError,我們也可以它來捕獲異常:

Future.delayed(new Duration(seconds: 2), () {
    //return "hi world!";
    throw AssertionError("Error");
}).then((data) {
    print("success");
}, onError: (e) {
    print(e);
});
複製程式碼

Future.whenComplete

有些時候,我們會遇到無論非同步任務執行成功或失敗都需要做一些事的場景。

Future.delayed(new Duration(seconds: 2),(){
   //return "hi world!";
   throw AssertionError("Error");
}).then((data){
   //執行成功會走到這裡 
   print(data);
}).catchError((e){
   //執行失敗會走到這裡   
   print(e);
}).whenComplete((){
   //無論成功或失敗都會走到這裡
});
複製程式碼

Future.wait

有些時候,我們需要等待多個非同步任務都執行結束後才進行一些操作。

我們通過模擬Future.delayed 來模擬兩個資料獲取的非同步任務,等兩個非同步任務都執行成功時,將兩個非同步任務的結果拼接列印出來。

Future.wait([
  // 2秒後返回結果  
  Future.delayed(new Duration(seconds: 2), () {
    return "hello";
  }),
  // 4秒後返回結果  
  Future.delayed(new Duration(seconds: 4), () {
    return " world";
  })
]).then((results){
  print(results[0]+results[1]);
}).catchError((e){
  print(e);
});
複製程式碼

Async/await

Dart中的async/await 和JavaScript中的async/await功能和用法是一模一樣的。

通過Future回撥中再返回Future的方式雖然能避免層層巢狀,但是還是有一層回撥,有沒有一種方式能夠讓我們可以像寫同步程式碼那樣來執行非同步任務而不使用回撥的方式?答案是肯定的,這就要使用async/await了,下面我們先直接看程式碼,然後再解釋,程式碼如下:

task() async {
   try{
    String id = await login("alice","******");
    String userInfo = await getUserInfo(id);
    await saveUserInfo(userInfo);
    //執行接下來的操作   
   } catch(e){
    //錯誤處理   
    print(e);   
   }  
}
複製程式碼
  • async用來表示函式是非同步的,定義的函式會返回一個Future物件,可以使用then方法新增回撥函式。
  • await 後面是一個Future,表示等待該非同步任務完成,非同步完成後才會往下走;await必須出現在 async 函式內部。

參考文件

Dart官方文件

Flutter文件中Dart語言簡介

希望看到這裡朋友可以動動手點個贊?哦,你們的支援就是對我最大的鼓勵?!!!

相關文章