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