Flutter小白教程系列(三) --- Dart語言快速入門

##轉載請註明出處: learnandfish.com/html

入門簡單的Dart程序

// 定義一個函數
printNumber(int number) {
  print('The number is $number.'); // 打印到控制檯。
}

// 入口函數
void main() {
  var number = 42; // 聲明並初始化一個變量。
  printNumber(number); // 調用函數。
}
複製代碼
  • // 代碼註釋。
  • $variableName (或 ${expression}) 獲取變量值

重要概念

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

  • 萬物皆對象, 不管是數字,函數和null都是對象。全部對象繼承自Object類。
  • 儘管Dart語言是強類型的, 可是Dart能夠推斷類型, 上面的var number等價於int number。
  • 針對於不肯定的數據類型,請使用dynamic。
  • Dart支持泛型, 如List(整數列表)和List(任何類型的對象列表)。
  • 與Java不一樣, Dart中沒有權限關鍵字"public", "protected", "private"。 若是標識符如下劃線(_number)開頭, 則認爲是私有屬性。
  • Dart語言定義的變量,若是不賦值,默認爲空null。

變量

Dart內建數據類型以下:編程

  • Number 經過num聲明
  • int 整數值
  • double 雙精度浮點值
void main() {
  var number = 1; // Dart會自動推斷爲int類型
  int value = 1; // 顯示聲明爲int類型
  print(number == value); // 該值應爲true, 由於上述兩個變量的值和類型相同

  var pointNumber = 1.0; // Dart會自動推斷爲double類型
  double pointValue = 1.0; // 顯示聲明爲double類型
  double pointValueCast = 1; // Dart2.1以後會自動轉換爲爲double類型
  print(pointNumber == pointValue); // 該值應爲true, 由於上述兩個變量的值和類型相同
  print(pointNumber == pointValueCast); // 該值應爲true, 由於上述兩個變量的值和類型相同
}
複製代碼
  • String
  • 以單引號或者雙引號包裹。
  • 能夠經過"+"鏈接。
  • 能夠經過$variableName或者${expression}獲取取值。
  • 使用r前綴建立原始字符串。
  • 對象的經常使用方法請查閱String經常使用方法
void main() {
  var s1 = "我是字符串1";
  String s2 = "我是字符串2";
  // s3雖然手動換行,可是輸出的時候仍是在一行
  var s3 = '我是'
      '字符串'
      '3';
  // s4和s5爲多行字符串
  var s4 = """ 我是 字符串 4 哈哈哈 """;
  var s5 = ''' 我是 字符串 4 哈哈哈 ''';
  // s6會換行
  var s6 = "In a raw string, even \n isn't special.";
  // s7保持原樣輸出
  var s7 = r"In a raw string, even \n isn't special.";
  print(s1);
  print(s2);
  print(s3);
  print(s4);
  print(s5);
  print(s6);
  print(s7);
}

/* console 我是字符串1 我是字符串2 我是字符串3 我是 字符串 4 哈哈哈 我是 字符串 4 哈哈哈 In a raw string, even isn't special. In a raw string, even \n isn't special. */
複製代碼
  • Boolean
  • Dart 使用 bool 類型表示布爾值。 Dart 只有字面量 true and false 是布爾類型, 這兩個對象都是編譯時常量。
void main() {
  var b = true;
  var c = false;
  bool d; // 聲明變量不賦值的狀況下,默認爲null
  bool e  = true;
  print(b);
  print(c);
  print(d);
  print(e);
}
/* console true false null true */
複製代碼
  • List
  • 幾乎每種編程語言中最多見的集合多是array或有序的對象集合。在Dart中的Array就是List對象, 一般稱之爲List。
void main() {
  var list = [1, 2, 3];
  List list1 = [1, 2, 3];
  assert(list.length == 3);
  assert(list[1] == 2);

  list[1] = 1;
  assert(list[1] == 1);
}
複製代碼
  • Set
  • 在Dart中Set是一個元素惟一且無序的集合。
void main() {
  var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};
  // Dart 推斷 halogens 類型爲 Set<String> 。若是嘗試爲它添加一個 錯誤類型的值,分析器或執行時會拋出錯誤。
  halogens.add('haha'); // 添加成功
// halogens.add(1); // 類型錯誤,編譯時提示
 // 要建立一個空集,使用前面帶有類型參數的 {} ,或者將 {} 賦值給 Set 類型的變量
  var names = <String>{};
  Set<String> names1 = {}; // 這樣也是能夠的。
// var names = {}; // 這樣會建立一個 Map ,而不是 Set 。
  var elements = <String>{};
  // 使用 add() 或 addAll() 爲已有的 Set 添加元素
  elements.add('fluorine');
  elements.addAll(halogens);
  // 使用 .length 來獲取 Set 中元素的個數
  assert(elements.length == 5);
}
複製代碼
  • Map
  • Map是用來關聯keys和values的對象。keys和values能夠是任何類型的對象。
  • 在一個Map對象中一個key只能出現一次。可是value能夠出現屢次。
void main() {
  var gifts = {
    // Key: Value
    'first': 'partridge',
    'second': 'turtledoves',
    'fifth': 'golden rings'
  };

  var nobleGases = {
    2: 'helium',
    10: 'neon',
    18: 'argon',
  };
  // 以上 Map 對象也可使用 Map 構造函數建立
  var gifts1 = Map();
  gifts['first'] = 'partridge';
  gifts['second'] = 'turtledoves';
  gifts['fifth'] = 'golden rings';

  var nobleGases2 = Map();
  nobleGases[2] = 'helium';
  nobleGases[10] = 'neon';
  nobleGases[18] = 'argon';

  // 賦值
  gifts['fourth'] = 'calling birds';
  // 從一個 Map 中獲取一個 value
  var first = gifts['first'];
  // 獲取Map的長度
  print(gifts.length);
}
複製代碼
  • Rune和Symbol
  • 不經常使用,有興趣能夠自行學習。
  • 經過Symbol聲明的變量, 經過#variableName調用

final和const

  • 使用過程當中歷來不會被修改的變量, 可使用final或const, 而不是var或者其餘類型。
  • final變量的值只能被設置一次; const變量在編譯時就已經固定(const變量是隱式final類型)。
void main() {
  final name = 'Bob';
  final String nickname = 'Bobby';
// name = 'Jack'; // Error: 一個 final 變量只能被設置一次。
  // const是編譯時就固定值
  const bar = 1000000; // 壓力單位 (dynes/cm2)
  const double atm = 1.01325 * bar; // 標準氣壓
  // Const 關鍵字不只能夠用於聲明常量變量。 還能夠用來建立常量值。
  var foo = const []; // 等價於 var foo = [];
  final bar1 = const [];
  const baz = []; // Equivalent to `const []`
  // 非 Final , 非 const 的變量是能夠被修改的,即便這些變量 曾經引用過 const 值。
  foo = [1, 2, 3]; // 曾經引用過 const [] 常量值。
  // final和const修飾的不能修改
// bar1 = [1]; // 編譯時報錯, final修飾的不可修改
// baz = [2]; // 編譯時報錯, const修飾的不可修改
}
複製代碼

函數

  • Dart 是一門真正面向對象的語言, 甚至其中的函數也是對象, 而且有它的類型Function。
  • 這也意味着函數能夠被賦值給變量或者做爲參數傳遞給其餘函數。也能夠把Dart類的實例當作方法來調用。
// 有返回值的函數
bool showPrice() {
   return true;
}

// 返回值爲void
void printVoidNumber() {
  print('return void');
}

// 返回值爲void, 此時void的能夠省略
printVoidNumber1() {
  print('return void');
}

// 若是一個函數函數體只有一行,能夠用箭頭代替大括號
// => expr 語法是 { return expr; } 的簡寫。 => 符號 有時也被稱爲 箭頭 語法。
showBooleanValue() => true;

// 將函數賦值給一個變量
var a = showBooleanValue();


void main() {
  print(a);
}

複製代碼
  • 可選參數
  • 命名可選參數, 放在大括號內{}
void enableFlags({bool bold, bool hidden}) {}
複製代碼
  • 位置可選參數, 放在中括號內[]
String say(String from, String msg, [String device]) {
  var result = '$from says $msg';
  if (device != null) {
    result = '$result with a $device';
  }
  return result;
}

// 方法調用
void main() {
  say('Bob', 'Howdy'); //不使用可選參數
  say('Bob', 'Howdy', 'smoke signal'); //使用可選參數
}
複製代碼
  • 默認參數值
/// 設置 [bold] 和 [hidden] 標誌 ...
void enableFlags({bool bold = false, bool hidden = false}) {}

void main(){
  // bold 值爲 true; hidden 值爲 false.
  enableFlags(bold: true);
}
複製代碼
  • 函數是一等公民
  • 一個函數能夠做爲另外一個函數的參數。
  • 一樣能夠將一個函數賦值給一個變量。
printElement(int element) => print(element);

void mian(){
  var list = [1, 2, 3];
  
  // 將 printElement 函數做爲參數傳遞。
  list.forEach(printElement);
}
複製代碼
  • 匿名函數
void main() {
  var list = ['apples', 'bananas', 'oranges'];
  list.forEach((item) {
    print('${list.indexOf(item)}: $item');
  });
}
複製代碼
  • 閉包:其實閉包就是在一個函數中能夠調用其餘的函數,這樣其餘的函數可使用本函數的參數及其餘內容。

運算符

  • 類型判斷運算符
  • as 類型轉換
  • is 判斷是否爲某類型
  • is! 判斷不是某類型
  • 賦值運算符
  • a = value; 將value賦值給便里昂a
  • b ??= value; 若是b爲空時, 將value賦值給b, 不然b的值保持不變。
  • 條件表達式
  • condition ? expr1 : expr2 若是條件爲 true, 執行 expr1 (並返回它的值): 不然, 執行並返回 expr2 的值。
  • expr1 ?? expr2 若是 expr1 是 non-null, 返回 expr1 的值; 不然, 執行並返回 expr2 的值。
  • 級聯運算符
  • 能夠實現對同一個對象進行一系列的操做。
  • 除了調用函數, 還能夠訪問同一對象上的字段屬性。這一般能夠節省建立臨時變量的步驟, 同時編寫出更流暢的代碼。
  • 相似與build模式。
class Person{
  int age;
  String name;
  
  Person({this.name, this.age});
  
  void say(String words) {
    print(name + words);
  }
}
void main(){
  Person() // 獲取對象。
    ..name = 'Bob' // 調用成員變量。
    ..say('important');
  // 等價於下面的代碼
  var person = Person(name: "Bob");
  person.say('important');
  // 級聯運算符能夠嵌套
  /*final addressBook = (AddressBookBuilder() ..name = 'jenny' ..email = 'jenny@example.com' ..phone = (PhoneNumberBuilder() ..number = '415-555-0100' ..label = 'home') .build()) .build();*/
  // 在返回值爲void的函數以後不能繼續調用級聯操做符
  var sb = StringBuffer();
  sb.write('foo') // write函數返回值是void, 不能再繼續調用
    ..write('bar');  // 運行會報錯
}
複製代碼

控制流程語句

// if elseif else
weatherStatus(){
  if (isRaining()) {
    you.bringRainCoat();
  } else if (isSnowing()) {
    you.wearJacket();
  } else {
    car.putTopDown();
  }
}

// for 循環
text(){
  var message = StringBuffer('Dart is fun');
  for (var i = 0; i < 5; i++) {
    message.write('---$i!');
  }
  // for ... in ...
  var collection = [0, 1, 2];
  for (var x in collection) {
    print(x); // 0 1 2
  }
  // forEach
  collection.forEach((item) => print(item));
}

// while 和 do-while
printSomethings() {
  while (!isDone()) {
    doSomething();
  }
  do {
    printLine();
  } while (!atEndOfPage());
}

// break 和 continue
pauseSomethings(){
  while (true) {
    if (shutDownRequested()) break;
    processIncomingRequests();
  }
  // 執行yearsExperience大於等於5的元素
  for (int i = 0; i < candidates.length; i++) {
    var candidate = candidates[i];
    if (candidate.yearsExperience < 5) {
      continue;
    }
    candidate.interview();
  }
  // 若是candidate對象實現了Iterable接口, 能夠調用以下方式
  candidates
      .where((c) => c.yearsExperience >= 5)
      .forEach((c) => c.interview());
}

// switch 和 case
judgeType() {
  var command = 'OPEN';
  switch (command) {
    case 'CLOSED':
      executeClosed();
      break;
    case 'PENDING':
      executePending();
      break;
    case 'APPROVED':
      executeApproved();
      break;
    case 'DENIED': // 當command爲該種狀況時,因爲該分支沒有break語句會繼續執行下一個分支,直到遇到break中止執行。
      executeDenied();
    case 'OPEN':
      executeOpen();
      break;
    default:
      executeUnknown();
  }
}
// assert 判斷是否成立
judgeEquals() {
  // 確認變量值不爲空。
  assert(text != null);
  
  // 確認變量值小於100。
  assert(number < 100);
  
  // 確認 URL 是不是 https 類型。
  assert(urlString.startsWith('https'));
  // assert 的第二個參數能夠爲其添加一個字符串消息。
  assert(urlString.startsWith('https'), 'URL ($urlString) should start with "https".');
}
複製代碼

異常處理

  • Dart 代碼能夠拋出和捕獲異常。
  • 異常表示一些未知的錯誤狀況。若是異常沒有被捕獲, 則異常會拋出, 致使拋出異常的代碼終止執行。
  • throw 能夠拋出Dart定義的異常, 也能夠拋出任意對象
// 高質量的生產環境代碼一般會實現 Error 或 Exception 類型的異常拋出。
throw FormatException('Expected at least 1 section');
throw 'Out of llamas!';
// 完整的try catch finally代碼
try {
  // 代碼塊
} on Exception catch (e) {
  // 捕獲到異常走這裏
} finally {
  // 不管是否捕獲異常, 最終都會走這裏
}
複製代碼

類和構造函數

  • 經過class關鍵字聲明。
  • 在沒有聲明構造函數的狀況下, Dart會提供一個默認的構造函數。默認構造函數沒有參數並會調用父類的無參構造函數。
class Point {
  num x, y;

  // 構造函數的第一種方法
  Point(num x, num y) {
    // 還有更好的方式來實現下面代碼,敬請關注。
    this.x = x;
    this.y = y;
  }
  
  // 構造函數的第二種方法
  Point(this.x, this.y)
  
  // 構造函數的第三種方法--命名構造函數
  Point.origin(){
    x = 0;
    y = 0;
  }
}
複製代碼
  • 類的繼承
  • 子類經過關鍵字extends繼承父類。
  • 默認狀況下, 子類的構造函數會自動調用父類的默認構造函數(匿名, 無參數)。
  • 若是父類沒有匿名無參的構造函數, 則在子類中須要手動調用父類的構造函數。
class Person {
  String firstName;
  
  Person.fromJson(Map data){
    print("I'm $firstName");
  }
}

class Employee extends Person {
  
  Employee.fromJson(Map data) : super.fromJson(data){
    print("I'm Employee");
  }
  
}

main() {
  var emp = new Employee.fromJson({});

  if (emp is Person) {
    // Type check
    emp.firstName = 'Bob';
  }
  (emp as Person).firstName = 'Bob';
}
複製代碼
  • 構造函數不只能夠調用父類構造函數, 還能夠在構造函數體執行以前初始化成員變量。
import 'dart:math';

class Point {
  final num x;
  final num y;
  final num distance;

  Point(x, y)
      : x = x,
        y = y,
        distance = sqrt(x * x + y * y);
}
複製代碼
  • 重定向構造函數
class Point {
  num x, y;

  // 類的主構造函數。
  Point(this.x, this.y);

  // 指向主構造函數
  Point.alongXAxis(num x) : this(x, 0);
}
複製代碼
  • 常量構造函數
  • 若是該類生成的對象是固定不變的, 那麼就能夠把這些對象定義爲編譯時常量。
  • 爲此須要定義一個const構造函數, 而且聲明全部實例變量爲final。
class ImmutablePoint {
  static final ImmutablePoint instance =
      const ImmutablePoint(0, 0);

  final num x, y;

  const ImmutablePoint(this.x, this.y);
}
複製代碼
  • 工廠構造函數
  • 當執行構造函數並不老是建立這個類的一個新實例時, 則使用 factory 關鍵字。
  • 一個工廠構造函數可能返回一個cache中的實例。
class Logger {
  final String name;
  bool mute = false;

  // 從命名的 _ 能夠知,
  // _cache 是私有屬性。
  static final Map<String, Logger> _cache =
      <String, Logger>{};

  factory Logger(String name) {
    if (_cache.containsKey(name)) {
      return _cache[name];
    } else {
      final logger = Logger._internal(name);
      _cache[name] = logger;
      return logger;
    }
  }

  // 私有命名構造函數 
  Logger._internal(this.name);

  void log(String msg) {
    if (!mute) print(msg);
  }
}
複製代碼
  • Getter 和 Setter方法
class Person {
  num age;
  final String name;
  final String hobby;
  
  // Getter
  String get hobby => "basketball";
  // Setter
  set hobby(String hobbyName) => hobby = hobbyName;
  
  Person(this.name, this.age)
}
複製代碼
  • abstract 和 implements
  • abstract抽象類, 類中的抽象方法經過分號結束, 不用實現函數體, 故能夠省略大括號。
  • implements實現接口。每一個類都隱式的定義了一個接口, 接口包含該類全部的成員及其實現的接口。
// 抽象類
abstract class Person {
    void walk(); // 抽象方法
}

class Animal { 
  void fly() => print("I can fly");
}

class Bird implements Animal {
  void fly() {
    // 具體實現
  }
}
複製代碼
  • 重寫運算符 經過operator重寫
class Vector {
  final int x, y;

  Vector(this.x, this.y);
  
  // 返回值 operator 運算符(參數) {具體實現}
  Vector operator +(Vector v) => Vector(x + v.x, y + v.y);
  Vector operator -(Vector v) => Vector(x - v.x, y - v.y);

}
複製代碼
  • noSuchMethod()
  • 當代碼嘗試調用類中不存在的方法時, 會拋出NoSuchMethodError異常, 能夠經過重寫該方法實現本身想要的結果。
class Animal {
  
  @override
  void noSuchMethod(Invocation invocation){
    // 具體實現
  }
}
複製代碼
  • 枚舉類型
enum Color {
  RED, GREEN, BLUE
}
複製代碼
  • Mixin爲類添加新功能
  • Mixin是複用類代碼的一種途徑, 複用的類能夠不存在繼承關係。
  • 經過with後面跟一個或者多個類進行復用。
  • 經過mixin定義一個Mixin類, Mixin類經過on複用其餘類
class Musician extends Performer with Musical {
  // ···
}

class Maestro extends Person with Musical, Aggressive, Demented {
  Maestro(String maestroName) {
    name = maestroName;
    canConduct = true;
  }
}

mixin MusicalPerformer on Musician {}
複製代碼
  • 靜態變量和靜態類型
class Animal {
  
  static const height = 180;
  
  static num distance(){
    return 100;
  }
  
}
複製代碼

// 經過import導入
import 'dart:html';
// 經過as指定別名, 調用時經過lib2調用
import 'package:lib2/lib2.dart' as lib2;
// 導入庫的一部分, 減小包體積
// Import only foo.
import 'package:lib1/lib1.dart' show foo;

// Import all names EXCEPT foo.
import 'package:lib2/lib2.dart' hide foo;
// 延遲加載庫, 當使用時進行加載
import 'package:greetings/hello.dart' deferred as hello;

Future greet() async {
  // 使用時使用loadLibrary()加載
  await hello.loadLibrary();
     hello.printGreeting();
   }
複製代碼

異步(Future和Stream)

  • Future 使用async 和 await 構造。
Future<bool> downLoad(String url) async {
  val result = await down();
  return result;
}
複製代碼
  • Stream可以屢次響應事件, 好比下載進度回調經常使用。
  • 有個博客介紹的比較詳細, 請查看Future使用Stream介紹

typedefs

  • 爲函數定義別名
typedefs Compare<T> = int Function(T a, T b);
複製代碼

註釋

// 單行註釋

/* 多行註釋 多行註釋 多行註釋 */

/// 文檔註釋
/// 文檔註釋

/** * 文檔註釋 */
複製代碼

若是你以爲本篇博客對你有用,能夠點個贊,評個論或者打個賞給博主增長動力哦。markdown

相關文章
相關標籤/搜索