# 簡介
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 |