# 簡介

Flutter 是 google 爸爸資助的一款原生跨平台框架。使用一種程式語言 dart 架構的 app 可同時提供 ios 與 android 運行。

在 Flutter 中,所有的元件都是 widget 。整個視圖就是一個 root widget。

# Flutter v.s. React Native.

網路上諸多人對於 flutter 與 react native 到底該選誰吵得不可開交。

我在網路上找尋了許多大大小小的不同評論,以下節錄一些我同意的觀點。

先上結論

如果你已經會 React 且只有前端開發經驗,但缺乏 client side 開發經驗。也許 React Native 會更適合你。 - 節錄《知乎 - # 2022 年了,现在学习选择 Flutter 还是 ReactNative ?》

這邊就僅記錄一些我所查詢的資料,有興趣的可以再轉去細究。

  • 2022 年了,现在学习选择 Flutter 还是 ReactNative ?
  • 2021 年,Flutter 与 React Native 该如何选择?
  • 选 Flutter 还是 React Native?跨平台开发框架推荐及优劣势比较

本篇主要會是以記錄 Dart 語法為核心。

# Dart

由於開發 Flutter 所使用的語言是 Dart, 因此我們有必要瞭解一下如何撰寫 Dart 語法的程式。

Dart 也是 google 開發的程式語言,主要用於前端界面的描述。其屬於物件導向的語言,但是同時結合了 javascript, java, c#, python 等特性。因此如果我們有學過其中任何一種語言,想要上手這門程式語言應該不會是太困難的事情。

首先 dart 擁有一個 online playground: DartPad 我們可以在這上面嘗試我們不確定的語法。算是蠻平易近人的。

# Type 類型

Dart 是個強型別的程式語言,我們需要在變數定義時就給與其型別。

例如常見的 int, string, boolean...etc.

以下使用 table 列舉我們可能會用到的幾種資料形態。

Data Type (資料型態) Keyword (關鍵字) Description (描述)
Number num, int, double 數值型別,整數,浮點數
Strings String 字串
Boolean bool true/false
Lists List Array
Maps Map Key-Value pairs
var 自行推斷數值型別的宣告,一定要給予初始值。 動態宣告。值的型別利用初始值自動推斷
dynamic 動態宣告變數,可以接受變數在執行階段改變型別。 動態宣告。值的型別可以動態改變
# 一般數值宣告
String name = "John Smith"; // String
int age = 18; // Number
bool isMale = true; // Boolean

使用 var 宣告

var name = "John Smith"; // String
var age = 18; // Number
var isMale = true; // Boolean
# 字串串接

dart 的字串串接可以使用 + 或是使用「空格」即可。當然,其也有所謂的版模字串,使用 ${} 直接將變數插入字串之中。

var name = "John " + "Smith"; // John Smith
var desc = "He " "is " "a " "kind " "person."; // He is a kind person.
var text = "lorem ${name} dolor sit ${desc}"; // lorem John Smith dolor sit He is a kind person.

# List

Dart 允許 list 中的 element 型別不同。

與 js 不同的地方是 Dart 的 List 不可以使用 string 作為 index, 也不可以直接 assign 超過 List 長度的 index 給變數.

List list = [
  "123",
  'chara'
  345,
  '''
  multi line string 1
  multi line string 2
  multi line string 3
  '''
];
// 註解
list[10] = "test"; // Uncaught Error: RangeError (index): Index out of range: index should be less than 5: 10
// 巡行
list.forEach((ele) => print("${ele}"));
// (不同於 Maps, list 的 forEach 僅能 literal element 無法獲得 index.)
// 如果需要獲得 index, 我們可以使用 `asMap` 先轉換成 Map 再進行 literal.
list.asMap().forEach((index, value) => print("${ele}"));
// add element.
list.add(element);
// 或是使用 spread operator.
var value = [...list, element];
// remove by condition
list.removeWhere( (item) => item["id"] == 3 );
// remove by index.
list.removeIndex(3);
// filter
list.where((ele) => ele).toList();

# Maps

Dart 的 Maps 有三種

  • HashMap
    • 是無序的 Map,存放在 HashMap 內的元素,在迭代取出時,不會依照順序取出。
  • LinkedHashMap
    • 預設 Map 的實作。 將元素存進 LinkedHashMap,LinkedHashMap 會自動將前後的元素串起來,所以在迭代的時候就可以按照原先加入的順序取出。
  • SplayTreeMap
    • 存放在 SplayTreeMap 的元素,可以按照字母順序排列。
// 定義空 Map
var emptyMap = {}; //_InternalLinkedHashMap<dynamic, dynamic>
var emptyMap = <String, String>{}; //_InternalLinkedHashMap<String, String>
// 用 LinkedHashMap 儲存 Map 的元素,會依照順序頭尾串接起來,是有序的 Map。
var keyValuePair = <String, String>{ // Map
  'Taiwan':'Taipei',
	'Japan':'Tokyo',
	'USA':'Washington'
};
// 使用
print(keyValuePair["Taiwan"]);
// 新增資料
keyValuePair["Tokyo"] = "Hot";
print(keyValuePair);
// 刪除資料
keyValuePair.remove('USA');
print(keyValuePair); // {Taiwan: Taipei, Japan: Tokyo, Tokyo: Hot}
// 檢查是否有 key
keyValuePair.containsKey("Taiwan"); // true
// 檢查是否有 value
keyValuePair.containsValue("Tokyo"); // true
// 巡行
keyValuePair.forEach((key, value) => {
	print("$key's capital is $value.")
});

# 修飾字 const, final, dynamic

# const & final

const 與 final 都是用來宣告「常數」的關鍵字。

不同的地方在於 const 在 compile time 就要決定好 value, final 則是在 run time 才決定 value.

以下給一個 example 就會很容易明白了

const a = 1243; // 在 compile time a 就已經被賦值了
const time = DateTime.now(); //compile error. 因為在 compile time 無法獲得 now 的數值
final time = DateTime.now(); // 如果需要在 run time 才賦值的話,就要使用 final
# dynamic

dynamic 可以想成 javascript 的 var 。是一個動態變數。

意思是,使用這個關鍵字宣告的變數可以不管型別、任意賦值。

以下為一個例子

dynamic variable = "string";
print(variable); // string
variable = 123;
print(variable); // 123

# function

function 的命名需要先定義型別。

參數可以使用 = 給予 default value 但是需要使用 {} 包起來。呼叫的時候則是使用 : 來指定值

example.

String func({a=1, b="abc"}) {
  return "${a}, ${b}";
}
// 也可以對 arguments 定義型別
String func({int a=1, String b="abc"}) {
  return "${a}, ${b}";
}
print(func(a: 345, b: "hello world"})); // "345, hello world"

當 function statement 只有一行的時候,可以用 lambda function 的形式撰寫。

lambda function 的撰寫風格很像 javascript 的 arrow function.

//lambda 的方式宣告 function.
String func({int a=1, String b="abc"}) => "${a}, ${b}";
# anonymous function

dart 中的 function 也可以像 javascript 一樣在參數中傳遞。

我們可以使用 Function 這個型別來承接匿名函式。

var func1 = ({a=1, b="abc"}) {
    print("${a}, ${b}");
    print("${a}, ${b} = 123");
};
func1(a: 7893, b: "test code");
// Output:
// 7893, test code
// 7893, test code = 123

# class

Dart 中的 class 宣告方式比較接近 Java 使用與 class name 相同的名稱作為 constructor.

並且,Dart 並不支持 anonymous class.

class Video {
  String title; // 標題
  String description; // 影片資訊
  String publishTime; // 上傳時間
  String thumbnail; // 縮圖網址
  String url; // 影片網址
  Video(String title, String description, String publishTime, String thumbnail, String url) {
    this.title = title;
    this.description = description;
    this.publishTime = publishTime;
    this.thumbnail = thumbnail;
    this.url = url;
  }
}

另外 Dart 不支持 constructor 多型,若是我們需要依照不同情況給予不同參數的實例化,我們可以如下這樣寫。

class Video {
  String _title; // 標題
  String _description; // 影片資訊
  String _publishTime; // 上傳時間
  String _thumbnail; // 縮圖網址
  String _url; // 影片網址
  // 使用 縮寫,直接取代原本 assign 的寫法
  Video(this._title, this._description, this._publishTime, this._thumbnail, this._url);
  Video.onlyUrl(this._url);
  Video.onlyTitle(String title){
      this._title = title;
  }
  //getter
  String get title => this._title;
  String get url => this._url;
  //setter
  set title(String title) {
      this._title = title;
  }
}
var video1 = new Video('Dart Tutorial', 'Happy coding', '2019/9/14', 'https://123', 'https://456');
print(video1.title); //Output: Dart Tutorial
var video2 = new Video.onlyUrl("https://123.456");
print(video2.url); //Output: https://123.456
var video3 = new Video.onlyTitle("Only title");
print(video3.title); //Output: Only title

# 非同步語法

# async, await, Future

  • Future 是一個 class 用來表示非同步的結果,擁有兩個狀態: completed /uncompleted。可以類比為 javascript 的 promise.
  • async 是用於區分 function 是否為非同步,使用方式就如 javascript 一樣,在 function 前面加上關鍵字。
  • await 只能在 async function 中使用,用來等待非同步的工作完成。概念與 javascript 的 await 一樣。
// 載入 dart async 的 package.
import 'dart:async';
Future<void> introduction() {
  // Future.delayed 類似 js 的 Promise.delay. 模擬非同步情形。等待兩秒。
  return Future.delayed(Duration(seconds: 2), () => print("Dart tutorial"));
}
void main() {
  print("Before introduction");
  introduction();
  print("After introduction");
}
// Output:
// Before introduction
// After introduction
// Dart tutorial

使用 await 的效果

// 載入 dart async 的 package.
import 'dart:async';
Future<String> introduction() {
  // Future.delayed 類似 js 的 Promise.delay. 模擬非同步情形。等待兩秒。
  return Future.delayed(Duration(seconds: 2), () => "Dart tutorial");
}
void main() async {
  print("Before introduction");
  print(await introduction());
  print("After introduction");
}
// Output:
// Before introduction
// Dart tutorial
// After introduction