Flutter 是 Google 開源的 UI 工具包,幫助開發者經過一套代碼庫高效構建多平臺精美應用,Flutter 開源、免費,擁有寬鬆的開源協議,支持移動、Web、桌面和嵌入式平臺。html
Flutter是使用Dart語言開發的跨平臺移動UI框架,經過自建繪製引擎,能高性能、高保真地進行Android和IOS開發。Flutter採用Dart語言進行開發,而並不是Java,Javascript這類熱門語言,這是Flutter團隊對當前熱門的10多種語言慎重評估後的選擇。由於Dart囊括了多數編程語言的優勢,它更符合Flutter構建界面的方式。express
本文主要就是簡單梳理一下Dart語言的一些基礎知識和語法。關於編程語言的基本語法無外乎那麼些內容,註釋、變量、數據類型、運算符、流程控制、函數、類、異常、文件、異步、經常使用庫等內容,相信大部分讀者都是有必定編程基礎的,因此本文就簡單地進行一個梳理,不作詳細的講解。你們也能夠參考 Dart編程語言中文網。編程
Dart基本語法是指編寫dart代碼最基本的一些內容、規範,主要包括註釋、變量、數據類型和運算符等內容。api
Dart 支持單行註釋、多行註釋和文檔註釋。數組
//
開始。 全部在 //
和改行結尾之間的內容被編譯器忽略。
void main() { // TODO: refactor into an AbstractLlamaGreetingFactory? print('Welcome to my Llama farm!'); }
/*
開始, 以 */
結尾。 全部在 /*
和 */
之間的內容被編譯器忽略 (不會忽略文檔註釋)。 多行註釋能夠嵌套。
void main() { /* * This is a lot of work. Consider raising chickens. Llama larry = Llama(); larry.feed(); larry.exercise(); larry.clean(); */ }
///
或者 /**
開始。 在連續行上使用 ///
與多行文檔註釋具備相同的效果。在文檔註釋中,除非用中括號括起來,不然Dart 編譯器會忽略全部文本。 使用中括號能夠引用類、 方法、 字段、 頂級變量、 函數、 和參數。 括號中的符號會在已記錄的程序元素的詞法域中進行解析。下面是一個引用其餘類和成員的文檔註釋,在生成的文檔中,[Food]
會成爲一個連接, 指向 Food 類的 API 文檔。
/// A domesticated South American camelid (Lama glama). /// /// 自從西班牙時代以來, /// 安第斯文化就將駱駝當作肉食類和運輸類動物。 class Llama { String name; /// 餵養駱駝 [Food]. /// /// 典型的美洲駝每週吃一捆乾草。 void feed(Food food) { // ... } /// 使用 [activity] 訓練駱駝 /// [timeLimit] 分鐘。 void exercise(Activity activity, int timeLimit) { // ... } }
任何保存在變量中的都是一個 對象 , 而且全部的對象都是對應一個 類 的實例。 不管是數字,函數和 null
都是對象。全部對象繼承自Object 類。儘管 Dart 是強類型的,可是 Dart 能夠推斷類型,因此類型註釋是可選的。 若是要明確說明不須要任何類型, 須要使用特殊類型 dynamic
。安全
var name = 'Bob';
變量僅存儲對象引用,這裏的變量是 name
存儲了一個 String
類型的對象引用。 「Bob」 是這個 String
類型對象的值。app
name
變量的類型被推斷爲 String
。 可是也能夠經過指定類型的方式,來改變變量類型。 若是對象不限定爲單個類型,能夠指定爲 對象類型
或 動態類型
。框架
//指定爲動態類型 dynamic name = 'Bob'; //顯示指定爲字符串類型 String name = 'Bob';
未初始化的變量默認值是 null
。即便變量是數字 類型默認值也是 null,由於在 Dart 中一切都是對象,數字類型 也不例外。dom
int lineCount; assert(lineCount == null); //結果爲true
提示: 在生產環境代碼中
assert()
函數會被忽略,不會被調用。 在開發過程當中,assert(condition)
會在非true
的條件下拋出異常。異步
使用過程當中歷來不會被修改的值,咱們成爲常量,可使用 final
或 const
, 而不是 var
或者其餘類型。 Final 變量的值只能被設置一次; Const 變量在編譯時就已經固定 (Const 變量 是隱式 Final 的類型.) 。最高級 final 變量或類變量在第一次使用時被初始化。
提示: 實例變量能夠是
final
類型但不能是const
類型。 必須在構造函數體執行以前初始化 final 實例變量 —— 在變量聲明中,參數構造函數中或構造函數的初始化列表中進行初始化。
final name = 'Bob'; // Without a type annotation final String nickname = 'Bobby'; //final 不能被修改: name = 'Alice'; // Error: 一個 final 變量只能被設置一次。
若是須要在編譯時就固定變量的值,可使用 const
類型變量。 若是 Const 變量是類級別的,須要標記爲 static const
。 在這些地方可使用在編譯時就已經固定不變的值,字面量的數字和字符串, 固定的變量,或者是用於計算的固定數字:
const bar = 1000000; // 壓力單位 (dynes/cm2) const double atm = 1.01325 * bar; // 標準氣壓 // Const 關鍵字不只能夠用於聲明常量變量。 還能夠用來建立常量值,以及聲明建立常量值的構造函數。 任何變量均可以擁有常量值。 var foo = const []; final bar = const []; const baz = []; // 聲明 const 的初始化表達式中 const 能夠被省略。 好比上面的 baz。 Equivalent to `const []` //Const 變量的值不能夠修改: baz = [42]; // Error: 常量變量不能賦值修改。 //非 Final , 非 const 的變量是能夠被修改的,即便這些變量 曾經引用過 const 值。 foo = [1, 2, 3]; // 曾經引用過 const [] 常量值。
1.聲明時必需要賦值
2.只能在初始化賦值一次,以後不能從新賦值
3.後面都不能接var關鍵字
4.類型聲明能夠忽略,相似 var,能夠根據初始化的值推斷出變量類型
一、final變量的初始值能夠在編譯時肯定,也能夠在運行時肯定,cosnt變量的初始值只能是編譯時肯定的值,好比當前時間
2.const變量的不可變性是嵌套的,final不是
const a = {'c': 1}; a['c'] = 2; // 運行結果 Unsupported operation: Cannot set value in unmodifiable Map
3.內存中的建立:相同的值,final變量會重複建立,const會引用同一份值
const a = {'c': 1}; const b = {'c': 1}; print(a == b);//true final c = {'c': 1}; final d = {'c': 1}; print(c == d);//false
Dart 語言支持如下內建類型:
這些類型均可以被初始化爲字面量。 例如, 'this is a string'
是一個字符串的字面量, true
是一個布爾的字面量。由於在 Dart 全部的變量終究是一個對象(一個類的實例), 因此變量可使用 構造涵數 進行初始化。 一些內建類型擁有本身的構造函數。 例如, 經過 Map()
來構造一個 map 變量。
Dart 語言的 Number 有兩種類型:
int
和 double
都是 num
. 的亞類型。 num 類型包括基本運算 +, -, /, 和 *, 以及 abs()
, ceil()
, 和 floor()
, 等函數方法。 (按位運算符,例如»,定義在 int 類中。) 若是 num 及其亞類型找不到你想要的方法, 嘗試查找使用 dart:math 庫。
// 整數類型不包含小數點。 下面是定義整數類型字面量的例子: var x = 1; var hex = 0xDEADBEEF;
// 若是一個數字包含小數點,那麼就是小數類型。 下面是定義小數類型字面量的例子: var y = 1.1; var exponents = 1.42e5;
// 從 Dart 2.1 開始,必要的時候 int 字面量會自動轉換成 double 類型。 double z = 1; // 至關於 double z = 1.0. //版本提示: 在dart 2.1 以前,在 double 上下文中使用 int 字面量是錯誤的。 //如下是將字符串轉換爲數字的方法,反之亦然: // String -> int var one = int.parse('1'); assert(one == 1); // String -> double var onePointOne = double.parse('1.1'); assert(onePointOne == 1.1); // int -> String String oneAsString = 1.toString(); assert(oneAsString == '1'); // double -> String String piAsString = 3.14159.toStringAsFixed(2); assert(piAsString == '3.14');
//int 特有的傳統按位運算操做,移位(<<, >>),按位與(&)以及 按位或(|)。 例如: assert((3 << 1) == 6); // 0011 << 1 == 0110 assert((3 >> 1) == 1); // 0011 >> 1 == 0001 assert((3 | 4) == 7); // 0011 | 0100 == 0111
//數字類型字面量是編譯時常量。 在算術表達式中,只要參與計算的因子是編譯時常量, 那麼算術表達式的結果也是編譯時常量。 const msPerSecond = 1000; const secondsUntilRetry = 5; const msUntilRetry = secondsUntilRetry * msPerSecond;
Dart 字符串是一組 UTF-16 單元序列。 字符串經過單引號或者雙引號建立。
var s1 = 'Single quotes work well for string literals.'; var s2 = "Double quotes work just as well."; var s3 = 'It\'s easy to escape the string delimiter.'; var s4 = "It's even easier to use the other delimiter.";
字符串能夠經過 ${
expression
}
的方式內嵌表達式。 若是表達式是一個標識符,則 {} 能夠省略。 在 Dart 中經過調用就對象的 toString()
方法來獲得對象相應的字符串。
var s = 'string interpolation'; assert('Dart has $s, which is very handy.' == 'Dart has string interpolation, ' + 'which is very handy.'); assert('That deserves all caps. ' + '${s.toUpperCase()} is very handy!' == 'That deserves all caps. ' + 'STRING INTERPOLATION is very handy!');
提示:
==
運算符用來測試兩個對象是否相等。 在字符串中,若是兩個字符串包含了相同的編碼序列,那麼這兩個字符串相等。
此外,還有字符串的拼接和多行字符串等用法
// 用 + 運算符來把多個字符串鏈接爲一個,也能夠把多個字面量字符串寫在一塊兒來實現字符串鏈接: var s1 = 'String ' 'concatenation' " works even over line breaks."; assert(s1 == 'String concatenation works even over ' 'line breaks.'); var s2 = 'The + operator ' + 'works, as well.'; assert(s2 == 'The + operator works, as well.');
// 使用連續三個單引號或者三個雙引號實現多行字符串對象的建立: var s1 = ''' You can create multi-line strings like this one. '''; var s2 = """This is also a multi-line string.""";
// 使用 r 前綴,能夠建立 「原始 raw」 字符串: var s = r"In a raw string, even \n isn't special.";
**一個編譯時常量的字面量字符串中,若是存在插值表達式,表達式內容也是編譯時常量, 那麼該字符串依舊是編譯時常量。 插入的常量值類型能夠是 null,數值,字符串或布爾值
// const 類型數據 const aConstNum = 0; const aConstBool = true; const aConstString = 'a constant string'; // 非 const 類型數據 var aNum = 0; var aBool = true; var aString = 'a string'; const aConstList = [1, 2, 3]; const validConstString = '$aConstNum $aConstBool $aConstString'; //const 類型數據 // const invalidConstString = '$aNum $aBool $aString $aConstList'; //非 const 類型數據,error Not a constant expression.
Dart 使用 bool
類型表示布爾值。 Dart 只有字面量 true、
false
是布爾類型, 這兩個對象都是編譯時常量。
Dart 的類型安全意味着不能使用 if (nonbooleanValue)
或者 assert (nonbooleanValue)
。 而是應該像下面這樣,明確的進行值檢查:
// 檢查空字符串。 var fullName = ''; assert(fullName.isEmpty); // 檢查 0 值。 var hitPoints = 0; assert(hitPoints <= 0); // 檢查 null 值。 var unicorn; assert(unicorn == null); // 檢查 NaN 。 var iMeantToDoThis = 0 / 0; assert(iMeantToDoThis.isNaN);
幾乎每種編程語言中最多見的集合多是 array 或有序的對象集合。 在 Dart 中的 Array 就是 List 對象, 一般稱之爲 List 。 下面是一個 Dart List 的示例:
var list = [1, 2, 3];
提示: Dart 推斷 list
的類型爲 List<int>
。 若是嘗試將非整數對象添加到此 List 中, 則分析器或運行時會引起錯誤。
Lists 的下標索引從 0 開始,第一個元素的索引是 0。 list.length - 1 是最後一個元素的索引。
var list = [1, 2, 3]; assert(list.length == 3); assert(list[1] == 2); list[1] = 1; assert(list[1] == 1); //在 List 字面量以前添加 const 關鍵字,能夠定義 List 類型的編譯時常量 var constantList = const [1, 2, 3]; // constantList[1] = 1; // 取消註釋會引發錯誤。
List 類型包含了不少 List 的操做函數。 更多信息參考 泛型 和 集合.
在 Dart 中 Set 是一個元素惟一且無序的集合。 Dart 爲 Set 提供了 Set 字面量和 Set 類型。
版本提示: 雖然 Set 類型 一直是 Dart 的核心部分, 但在 Dart2.2 中才引入了 Set 字面量 。
下面是經過字面量建立 Set 的一個簡單示例:
var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};
Note: Dart 推斷 halogens
類型爲 Set<String>
。若是嘗試爲它添加一個 錯誤類型的值,分析器或執行時會拋出錯誤。
要建立一個空集,使用前面帶有類型參數的 {}
,或者將 {}
賦值給 Set
類型的變量:
var names = <String>{}; // Set<String> names = {}; // 這樣也是能夠的。 // var names = {}; // 這樣會建立一個 Map ,而不是 Set 。
是 Set 仍是 Map ? Map 字面量語法同 Set 字面量語法很是類似。 由於先有的 Map 字母量語法,因此 {}
默認是 Map
類型。 若是忘記在 {}
上註釋類型或賦值到一個未聲明類型的變量上, 那麼 Dart 會建立一個類型爲 Map<dynamic, dynamic>
的對象。
// 使用 add() 或 addAll() 爲已有的 Set 添加元素: var elements = <String>{}; elements.add('fluorine'); elements.addAll(halogens); // 使用 .length 來獲取 Set 中元素的個數: var elements = <String>{}; elements.add('fluorine'); elements.addAll(halogens); assert(elements.length == 5); // 在 Set 字面量前增長 const ,來建立一個編譯時 Set 常量: final constantSet = const { 'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine', }; // constantSet.add('helium'); // Uncommenting this causes an error.
更多關於 Set 的內容,參閱 Generic 及 Set。
一般來講, Map 是用來關聯 keys 和 values 的對象。 keys 和 values 能夠是任何類型的對象。在一個 Map 對象中一個 key 只能出現一次。 可是 value 能夠出現屢次。 Dart 中 Map 經過 Map 字面量 和 Map 類型來實現。下面是使用 Map 字面量的兩個簡單例子:
var gifts = { // Key: Value 'first': 'partridge', 'second': 'turtledoves', 'fifth': 'golden rings' }; var nobleGases = { 2: 'helium', 10: 'neon', 18: 'argon', };
提示: Dart 會將 gifts
的類型推斷爲 Map<String, String>
, nobleGases
的類型推斷爲 Map<int, String>
。 若是嘗試在上面的 map 中添加錯誤類型,那麼分析器或者運行時會引起錯誤。
以上 Map 對象也可使用 Map 構造函數建立:
var gifts = Map(); gifts['first'] = 'partridge'; gifts['second'] = 'turtledoves'; gifts['fifth'] = 'golden rings'; var nobleGases = Map(); nobleGases[2] = 'helium'; nobleGases[10] = 'neon'; nobleGases[18] = 'argon';
提示: 這裏爲何只有
Map()
,而不是使用new Map()
。 由於在 Dart 2 中,new
關鍵字是可選的。
// 添加 key-value 對到已有的 Map 中: var gifts = {'first': 'partridge'}; gifts['fourth'] = 'calling birds'; // Add a key-value pair // 從一個 Map 中獲取一個 value: var gifts = {'first': 'partridge'}; assert(gifts['first'] == 'partridge'); // 若是 Map 中不包含所要查找的 key,那麼 Map 返回 null: var gifts = {'first': 'partridge'}; assert(gifts['fifth'] == null); // 使用 .length 函數獲取當前 Map 中的 key-value 對數量: var gifts = {'first': 'partridge'}; gifts['fourth'] = 'calling birds'; assert(gifts.length == 2); // 建立 Map 類型運行時常量,要在 Map 字面量前加上關鍵字 const。 final constantMap = const { 2: 'helium', 10: 'neon', 18: 'argon', }; // constantMap[2] = 'Helium'; // 取消註釋會引發錯誤。
改名多關於 Map 的內容,參考 Generics and Maps.
在 Dart 中, Rune 用來表示字符串中的 UTF-32 編碼字符。
Unicode 定義了一個全球的書寫系統編碼, 系統中使用的全部字母,數字和符號都對應惟一的數值編碼。 因爲 Dart 字符串是一系列 UTF-16 編碼單元, 所以要在字符串中表示32位 Unicode 值須要特殊語法支持。
表示 Unicode 編碼的經常使用方法是, \uXXXX
, 這裏 XXXX 是一個4位的16進制數。 例如,心形符號 (♥) 是 \u2665
。 對於特殊的非 4 個數值的狀況, 把編碼值放到大括號中便可。 例如,emoji 的笑臉 (�) 是 \u{1f600}
。
String 類有一些屬性能夠得到 rune 數據。 屬性 codeUnitAt
和 codeUnit
返回16位編碼數據。 屬性 runes
獲取字符串中的 Rune 。
下面是示例演示了 Rune 、 16-bit code units、 和 32-bit code points 之間的關係。
main() { var clapping = '\u{1f44f}'; print(clapping); print(clapping.codeUnits); print(clapping.runes.toList()); Runes input = new Runes( '\u2665 \u{1f605} \u{1f60e} \u{1f47b} \u{1f596} \u{1f44d}'); print(new String.fromCharCodes(input)); }
提示: 謹慎使用 list 方式操做 Rune 。 這種方法很容易引起崩潰, 具體緣由取決於特定的語言,字符集和操做。
一個 Symbol 對象表示 Dart 程序中聲明的運算符或者標識符。 你也許永遠都不須要使用 Symbol ,但要按名稱引用標識符的 API 時, Symbol 就很是有用了。 由於代碼壓縮後會改變標識符的名稱,但不會改變標識符的符號。 經過字面量 Symbol ,也就是標識符前面添加一個 #
號,來獲取標識符的 Symbol 。
#radix
#bar
Symbol 字面量是編譯時常量。
下表是 Dart中定義的運算符,描述的運算符優先級近似於Dart 解析器實際行爲。
描述 | 運算符 |
---|---|
前綴運算符 | expr++ expr-- () [] . ?. |
後綴運算符 | -expr !expr ~expr ++expr --expr |
倍數運算符 | * / % ~/ |
加減運算符 | + - |
移位運算符 | << >> >>> |
位與 | & |
位異或 | ^ |
位或 | | |
關係運算符和測試運算符 | >= > <= < as is is! |
相等判斷 | == != |
邏輯與 | && |
邏輯或 | || |
判空運算符 | ?? |
條件運算符 | expr1 ? expr2 : expr3 |
級聯運算符 | .. |
賦值運算符 | = *= /= += -= &= ^= etc. |
建立表達式的時候會用到運算符。 下面是一些運算符表達式的實例:
a++ a + b a = b a == b c ? a : b a is T
在 運算符表 中, 每一行的運算符優先級,由上到下依次排列,第一行優先級最高,最後一行優先級最低。 例如 %
運算符優先級高於 ==
, 而 ==
高於 &&
。 根據優先級規則,那麼意味着如下兩行代碼執行的方式相同:
// 括號能夠提升可讀性。 if ((n % i == 0) && (d % i == 0)) ... // 可讀性差,可是是等效的。 if (n % i == 0 && d % i == 0) ...
警告: 對於有兩個操做數的運算符,運算符的功能由左邊的操做數決定。 例如, 若是有兩個操做數 Vector 和 Point, aVector + aPoint
使用的是 Vector 中定義的 + 運算符。
下面就對dart中的運算符進行常規意義的分類簡單列舉一下:
assert(2 + 3 == 5); assert(2 - 3 == -1); assert(2 * 3 == 6); assert(5 / 2 == 2.5); // 結果是雙浮點型 assert(5 ~/ 2 == 2); // 結果是整型 assert(5 % 2 == 1); // 餘數 assert('5/2 = ${5 ~/ 2} r ${5 % 2}' == '5/2 = 2 r 1');
assert(2 == 2); assert(2 != 3); assert(3 > 2); assert(2 < 3); assert(3 >= 3); assert(2 <= 3);
//短路運算,即當左邊的表達式結果能肯定最終結果時,右邊的表達式再也不進行運算 var a = 10; var b = a > 9 || a++ > 10; //a>9成立,又是或運算,因此b的結果爲true,右邊的 a++ > 10不會進行計算,因此a的值不會加1 print(a); //10
var a = 2; // 使用 = 複製 a *= 3; // 複製並作乘法運算: a = a * 3 assert(a == 6);
final value = 0x22; final bitmask = 0x0f; assert((value & bitmask) == 0x02); // AND assert((value & ~bitmask) == 0x20); // AND NOT assert((value | bitmask) == 0x2f); // OR assert((value ^ bitmask) == 0x2d); // XOR assert((value << 4) == 0x220); // Shift left assert((value >> 4) == 0x02); // Shift right
// 第一句調用函數 querySelector() , 返回獲取到的對象。 獲取的對象依次執行級聯運算符後面的代碼, 代碼執行後的返回值會被忽略。 querySelector('#confirm') // 獲取對象。 ..text = 'Confirm' // 調用成員變量。 ..classes.add('important') ..onClick.listen((e) => window.alert('Confirmed!')); // 上面的代碼等價於: var button = querySelector('#confirm'); button.text = 'Confirm'; button.classes.add('important'); button.onClick.listen((e) => window.alert('Confirmed!')); // 級聯運算符能夠嵌套,例如: final addressBook = (AddressBookBuilder() ..name = 'jenny' ..email = 'jenny@example.com' ..phone = (PhoneNumberBuilder() ..number = '415-555-0100' ..label = 'home') .build()) .build(); // 在返回對象的函數中謹慎使用級聯操做符。 例如,下面的代碼是錯誤的: var sb = StringBuffer(); sb.write('foo') ..write('bar'); // Error: 'void' 沒喲定義 'write' 函數。 // sb.write() 函數調用返回 void, 不能在 void 對象上建立級聯操做。
as
運算符將對象強制轉換爲特定類型。 一般,能夠認爲是 is
類型斷定後,被斷定對象調用函數的一種縮寫形式。 請考慮如下代碼: if (emp is Person) { // 類型判斷 // emp.firstName = 'Bob'; // 下面這種寫法通常是沒問題的,進行類型強轉 (emp as Person).firstName = 'Bob'; }