上一篇介紹了跨平臺移動開發解決方案Flutter以及Flutter開發環境的搭建,因爲Flutter開發使用的是Dart語言,故本篇記錄的是Dart語言的語法基礎,但願跟小夥伴們一塊兒溫故知新。html
Dart是Google推出的一門編程語言,最初是但願取代Javascript運行在瀏覽器端,後來慢慢發展成能夠開發Android、iOS和Web端App的一門高質量的編程語言,目前Dart的版本是Dart2,官網是:www.dartlang.org/react
在Dart官方網站上,對於Dart的描述以下:git
Developers at Google and elsewhere use Dart to create high-quality, mission-critical apps for iOS, Android, and the web. With features aimed at client-side development, Dart is a great fit for both mobile and web apps.github
Google和其餘地方的一些開發者使用Dart語言爲Android、iOS和web構建高質量,關鍵任務的應用程序,針對客戶端開發的特色,Dart很是適合移動和Web應用程序。web
Dart’s syntax is clear and concise, its tooling simple yet powerful. Sound typing helps you to identify subtle errors early. Dart has battle-hardened core libraries and an ecosystem of thousands of packages.編程
Dart的語法清晰明瞭,工具簡單但功能強大。Sound typing有助於早期識別細微的錯誤。Dart擁有久經沙場的核心庫和數以千計的生態系統。segmentfault
Dart provides optimizing ahead-of-time compilation to get predictably high performance and fast startup across mobile devices and the web.瀏覽器
Dart提供提早優化編譯,以在移動設備和Web上得到可預測的高性能和快速啓動。bash
Dart compiles to ARM and x86 code, so that Dart mobile apps can run natively on iOS, Android, and beyond. For web apps, Dart transpiles to JavaScript.網絡
Dart可編譯成ARM和X86代碼,這樣Dart移動應用程序能夠在iOS、Android和其餘地方運行。對於Web應用程序,DART可編譯成JavaScript。
Dart is familiar to many existing developers, thanks to its unsurprising object orientation and syntax. If you already know C++, C#, or Java, you can be productive with Dart in just a few days.
Dart對於許多現有的開發人員來講是熟悉的,這得益於其使人驚訝的對象定位和語法。若是你已經知道C++,C語言,或者Java,你能夠在短短几天內用Dart來開發。
Dart is well-suited to reactive programming, with support for managing short-lived objects—such as UI widgets—through Dart’s fast object allocation and generational garbage collector. Dart supports asynchronous programming through language features and APIs that use Future and Stream objects.
Dart很是適合於反應式編程,支持經過Dart的快速對象分配和代垃圾收集器來管理諸如UI小部件之類的短命對象。Dart經過使用將來和流對象的語言特徵和API支持異步編程。
關於Dart的語法,若是你熟悉Java,應該很快能掌握Dart,官網上對於Dart的語法也有詳細介紹,不過是全英文的,若是對英文沒有什麼閱讀障礙,能夠直接移步官方文檔。
爲了瞭解Dart的語法基礎,這裏咱們使用Android Studio做爲開發工具(你也可使用dartpad來運行代碼,它是一個基於瀏覽器的dart運行時環境),若是你按照上一篇文章中搭建好了Flutter開發環境,那麼能夠直接在Android Studio中新建Flutter項目,以下圖所示:
lib/main.dart
文件中,因爲本篇主要是講Dart的語法,故暫時不看main.dart文件,在lib目錄下咱們建立一個新的
.dart
文件
demo.dart
,以下圖所示:
demo.dart
文件中,輸入以下代碼:
// Define a function.
printInteger(int aNumber) {
print('The number is $aNumber.'); // Print to console.
}
// This is where the app starts executing.
main() {
var number = 42; // Declare and initialize a variable.
printInteger(number); // Call a function.
}
複製代碼
而後在代碼編輯區域鼠標右鍵,選擇Run demo.dart
,便可運行一個最簡單的dart程序,以下圖所示:
Dart中單行註釋使用//
,Dart同時支持多行註釋和文檔註釋,能夠點擊這裏查看更多
int
是Dart中的一種數據類型,同時還有其餘的一些內置數據類型如String
List
bool
等
控制檯輸出使用print
語句
字符串使用單引號或雙引號都可,如'hello', "hello"
字符串插入可使用相似$name
或${name}
的語法,好比下面的代碼:
var name = 'zhangsan';
print("hello, I am $name");
int a = 10, b = 20;
print("$a + $b = ${a + b}");
複製代碼
若是使用${name}
這種方式,大括號中能夠是表達式
你可能已經注意到了,Dart的變量類型是可選的,你能夠爲某個變量指定類型,或者使用var
定義變量,Dart會自動推斷變量的類型
當你在學習Dart語言時,下面的這些事實和概念請牢記於心:
Object
List<int>
表示一個整型的數據列表,List<dynamic>
則是一個對象的列表,其中能夠裝任意對象main
方法),也支持類方法或對象方法,同時你也能夠在方法內部建立方法public
protected
private
等關鍵字,若是某個變量如下劃線(_
)開頭,表明這個變量在庫中是私有的,具體能夠看這裏如下代碼是Dart中定義變量的方法:
main() {
var a = 1;
int b = 10;
String s = "hello";
dynamic c = 0.5;
}
複製代碼
你能夠明確指定某個變量的類型,如int
bool
String
,也能夠用var
或 dynamic
來聲明一個變量,Dart會自動推斷其數據類型。
注意:沒有賦初值的變量都會有默認值null
若是你毫不想改變一個變量,使用final
或const
,不要使用var
或其餘類型,一個被final
修飾的變量只能被賦值一次,一個被const
修飾的變量是一個編譯時常量(const常量毫無疑問也是final常量)。能夠這麼理解:final
修飾的變量是不可改變的,而const
修飾的表示一個常量。
注意:實例變量能夠是final的但不能是const的
下面用代碼說明:
final String name = 'zhangsan';
name = 'lisi'; // 編譯不經過,被final修飾的是常量,不可從新賦值
const a = 0;
a = 1; // 錯誤
複製代碼
final
和const
的區別:
final
要求變量只能初始化一次,並不要求賦的值必定是編譯時常量,能夠是常量也能夠不是。而 const
要求在聲明時初始化,而且賦值必需爲編譯時常量。final
是惰性初始化,即在運行時第一次使用前才初始化。而 const
是在編譯時就肯定值了。Dart有以下幾種內建的數據類型:
下面用一段代碼來演示以上各種數據類型:
main() {
// numbers
var a = 0;
int b = 1;
double c = 0.1;
// strings
var s1 = 'hello';
String s2 = "world";
// booleans
var real = true;
bool isReal = false;
// lists
var arr = [1, 2, 3, 4, 5];
List<String> arr2 = ['hello', 'world', "123", "456"];
List<dynamic> arr3 = [1, true, 'haha', 1.0];
// maps
var map = new Map();
map['name'] = 'zhangsan';
map['age'] = 10;
Map m = new Map();
m['a'] = 'a';
//runes,Dart 中 使用runes 來獲取UTF-32字符集的字符。String的 codeUnitAt and codeUnit屬性能夠獲取UTF-16字符集的字符
var clapping = '\u{1f44f}';
print(clapping); // 打印的是拍手emoji的表情
// symbols
print(#s == new Symbol("s")); // true
}
複製代碼
Dart是一個面向對象的編程語言,因此即便是函數也是一個對象,也有一種類型Function
,這就意味着函數能夠賦值給某個變量或者做爲參數傳給另外的函數。雖然Dart推薦你給函數加上返回值,可是不加返回值的函數一樣能夠正常工做,另外你還能夠用=>
代替return
語句,好比下面的代碼:
// 聲明返回值
int add(int a, int b) {
return a + b;
}
// 不聲明返回值
add2(int a, int b) {
return a + b;
}
// =>是return語句的簡寫
add3(a, b) => a + b;
main() {
print(add(1, 2)); // 3
print(add2(2, 3)); // 5
print(add3(1, 2)); // 3
}
複製代碼
使用花括號將函數的參數括起來就是定義了命名參數,以下面的代碼所示:
sayHello({String name}) {
print("hello, my name is $name");
}
sayHello2({name: String}) {
print("hello, my name is $name");
}
main() {
// 打印 hello, my name is zhangsan
sayHello(name: 'zhangsan');
// 打印 hello, my name is wangwu
sayHello2(name: 'wangwu');
}
複製代碼
能夠看到,定義命名參數時,你能夠以{type paramName}
或者{paramName: type}
兩種方式聲明參數,而調用命名參數時,須要以funcName(paramName: paramValue)
的形式調用。
命名參數的參數並非必須的,因此上面的代碼中,若是調用sayHello()
不帶任何參數,也是能夠的,只不過最後打印出來的結果是:hello, my name is null
,在Flutter開發中,你可使用@required
註解來標識一個命名參數,這表明該參數是必須的,你不傳則會報錯,好比下面的代碼:
const Scrollbar({Key key, @required Widget child})
複製代碼
使用中括號[]
括起來的參數是函數的位置參數,表明該參數可傳可不傳,位置參數只能放在函數的參數列表的最後面,以下代碼所示:
sayHello(String name, int age, [String hobby]) { // 位置參數能夠有多個,好比[String a, int b]
StringBuffer sb = new StringBuffer();
sb.write("hello, this is $name and I am $age years old");
if (hobby != null) {
sb.write(", my hobby is $hobby");
}
print(sb.toString());
}
main() {
// hello, this is zhangsan and I am 20 years old
sayHello("zhangsan", 20);
// hello, this is zhangsan and I am 20 years old, my hobby is play football
sayHello("zhangsan", 20, "play football");
}
複製代碼
你能夠爲命名參數或者位置參數設置默認值,以下代碼所示:
// 命名參數的默認值
int add({int a, int b = 3}) { // 不能寫成:int add({a: int, b: int = 3})
return a + b;
}
// 位置參數的默認值
int sum(int a, int b, [int c = 3]) {
return a + b + c;
}
複製代碼
不論在Dart
仍是Flutter
中,必須都須要一個頂層的main()
函數,它是整個應用的入口函數,main()
函數的返回值是void
,還有一個可選的參數,參數類型是List<String>
。
你能夠將一個函數做爲參數傳給另外一個函數,好比下面的代碼:
printNum(int a) {
print("$a");
}
main() {
// 依次打印:
// 1
// 2
// 3
var arr = [1, 2, 3];
arr.forEach(printNum);
}
複製代碼
你也能夠將一個函數賦值給某個變量,好比下面的代碼:
printNum(int a) {
print("$a");
}
main() {
var f1 = printNum;
Function f2 = printNum;
var f3 = (int a) => print("a = $a");
f1(1);
f2(2);
f3(6);
}
複製代碼
大多數函數都是有名稱的,好比main()
printName()
等,可是你也能夠寫匿名函數,若是你對Java
比較熟悉,那下面的Dart
代碼你確定也不會陌生:
test(Function callback) {
callback("hello");
}
main() {
test((param) {
// 打印hello
print(param);
});
}
複製代碼
匿名函數相似於Java
中的接口,每每在某個函數的參數爲函數時使用到。
全部的函數都有返回值,若是沒有指定return
語句,那麼該函數的返回值爲null
。
Dart
中的運算符與Java
中的相似,好比++a
a == b
b ? a : b
,可是也有一些與Java
不太同樣的運算符,下面用代碼說明:
main() {
// 與Java相同的運算符操做
int a = 1;
++a;
a++;
var b = 1;
print(a == b); // false
print(a * b); // 3
bool real = false;
real ? print('real') : print('not real'); // not real
print(real && a == b); // false
print(real || a == 3); // true
print(a != 2); // true
print(a <= b); // false
var c = 9;
c += 10;
print("c = $c"); // c = 19
print(1<<2); // 4
// 與Java不太同樣的運算符操做
// is運算符用於判斷一個變量是否是某個類型的數據
// is!則是判斷變量不是某個類型的數據
var s = "hello";
print(s is String); // true
var num = 6;
print(num is! String); // true
// ~/纔是取整運算符,若是使用/則是除法運算,不取整
int k = 1;
int j = 2;
print(k / j); // 0.5
print(k ~/ j); // 0
// as運算符相似於Java中的cast操做,將一個對象強制類型轉換
(emp as Person).teach();
// ??=運算符 若是 ??= 運算符前面的變量爲null,則賦值,不然不賦值
var param1 = "hello", param2 = null;
param1 ??= "world";
param2 ??= "world";
print("param1 = $param1"); // param1 = hello
print("param2 = $param2"); // param2 = world
// ?.運算符
var str1 = "hello world";
var str2 = null;
print(str1?.length); // 11
print(str2?.length); // null
print(str2.length); // 報錯
}
複製代碼
若是你對Java中的建造者模式比較熟悉的話,Dart
中的..
運算符也很好理解,先看下面的代碼:
class Person {
eat() {
print("I am eating...");
}
sleep() {
print("I am sleeping...");
}
study() {
print("I am studying...");
}
}
main() {
// 依次打印
// I am eating...
// I am sleeping...
// I am studying...
new Person()..eat()
..sleep()
..study();
}
複製代碼
能夠看到,使用..
調用某個對象的方法(或者成員變量)時,返回值是這個對象自己,因此你能夠接着使用..
調用這個對象的其餘方法,這不就相似於Java
中的建造者模式,每次build某個屬性時,都返回一個this
對象嗎。
if / else
switch
for /while
try / catch
語句跟Java
中都相似,try / catch
語句可能稍有不一樣,下面用一段代碼說明:
main() {
// if else語句
int score = 80;
if (score < 60) {
print("so bad!");
} else if (score >= 60 && score < 80) {
print("just so so!");
} else if (score >= 80) {
print("good job!");
}
// switch語句
String a = "hello";
// case語句中的數據類型必須是跟switch中的類型一致
switch (a) {
case "hello":
print("haha");
break;
case "world":
print("heihei");
break;
default:
print("WTF");
}
// for語句
List<String> list = ["a", "b", "c"];
for (int i = 0; i < list.length; i++) {
print(list[i]);
}
for (var i in list) {
print(i);
}
// 這裏的箭頭函數參數必須用圓括號擴起來
list.forEach((item) => print(item));
// while語句
int start = 1;
int sum = 0;
while (start <= 100) {
sum += start;
start++;
}
print(sum);
// try catch語句
try {
print(1 ~/ 0);
} catch (e) {
// IntegerDivisionByZeroException
print(e);
}
try {
1 ~/ 0;
} on IntegerDivisionByZeroException { // 捕獲指定類型的異常
print("error"); // 打印出error
} finally {
print("over"); // 打印出over
}
}
複製代碼
Dart
中的類沒有訪問控制,因此你不須要用private
, protected
, public
等修飾成員變量或成員函數,一個簡單的類以下代碼所示:
class Person {
String name;
int age;
String gender;
Person(this.name, this.age, this.gender);
sayHello() {
print("hello, this is $name, I am $age years old, I am a $gender");
}
}
複製代碼
上面的Person
類中有3個成員變量,一個構造方法和一個成員方法,看起來比較奇怪的是Person
的構造方法,裏面傳入的3個參數都是this.xxx
,並且沒有大括號{}
包裹的方法體,這種語法是Dart比較獨特而簡潔的構造方法聲明方式,它等同於下面的代碼:
Person(String name, int age, String gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
複製代碼
要調用Person
類的成員變量或成員方法,能夠用下面的代碼:
var p = new Person("zhangsan", 20, "male");
p.sayHello(); // hello, this is zhangsan, I am 20 years old, I am a male
p.age = 50;
p.gender = "female";
p.sayHello(); // hello, this is zhangsan, I am 50 years old, I am a female
複製代碼
因爲Dart
中的類沒有訪問控制權限,因此你能夠直接用obj.var
的方式訪問一個對象的成員變量。
類除了有跟類名相同的構造方法外,還能夠添加命名的構造方法,以下代碼所示:
class Point {
num x, y;
Point(this.x, this.y);
// 類的命名構造方法
Point.origin() {
x = 0;
y = 0;
}
}
main() {
// 調用Point類的命名構造方法origin()
var p = new Point.origin();
var p2 = new Point(1, 2);
}
複製代碼
Dart
中使用extends
關鍵字作類的繼承,若是一個類只有命名的構造方法,在繼承時須要注意,以下代碼:
class Human {
String name;
Human.fromJson(Map data) {
print("Human's fromJson constructor");
}
}
class Man extends Human {
Man.fromJson(Map data) : super.fromJson(data) {
print("Man's fromJson constructor");
}
}
複製代碼
因爲Human
類沒有默認構造方法,只有一個命名構造方法fromJson
,因此在Man
類繼承Human
類時,須要調用父類的fromJson
方法作初始化,並且必須使用Man.fromJson(Map data) : super.fromJson(data)
這種寫法,而不是像Java
那樣將super
寫到花括號中。
有時候你僅僅只是在某個類的構造方法中,調用這個類的另外一個構造方法,你能夠這麼寫:
class Point {
num x, y;
Point(this.x, this.y);
// 命名構造方法調用了默認的構造方法
Point.alongXAxis(num x) : this(x, 0);
}
複製代碼
一個類的成員方法是一個函數,爲這個類提供某些行爲。上面的代碼中已經有了一些類的成員方法的定義,這些定義方式跟Java
很相似,你能夠爲某個類的成員變量提供getter/setter
方法,以下代碼:
class Rectangle {
num left, top, width, height;
// 構造方法傳入left, top, width, height幾個參數
Rectangle(this.left, this.top, this.width, this.height);
// right, bottom兩個成員變量提供getter/setter方法
num get right => left + width;
set right(num value) => left = value - width;
num get bottom => top + height;
set bottom(num value) => top = value - height;
}
複製代碼
使用abstract修飾一個類,則這個類是抽象類,抽象類中能夠有抽象方法和非抽象方法,抽象方法沒有方法體,須要子類去實現,以下代碼:
abstract class Doer {
// 抽象方法,沒有方法體,須要子類去實現
void doSomething();
// 普通的方法
void greet() {
print("hello world!");
}
}
class EffectiveDoer extends Doer {
// 實現了父類的抽象方法
void doSomething() {
print("I'm doing something...");
}
}
複製代碼
Dart中有相似於C++中的運算符重載語法,好比下面的代碼定義了一個向量類,重載了向量的+
-
運算:
class Vector {
num x, y;
Vector(this.x, this.y);
Vector operator +(Vector v) => new Vector(x + v.x, y + v.y);
Vector operator -(Vector v) => new Vector(x - v.x, y - v.y);
printVec() {
print("x: $x, y: $y");
}
}
main() {
Vector v1 = new Vector(1, 2);
Vector v2 = new Vector(3, 4);
(v1 - v2).printVec(); // -2, -2
(v1 + v2).printVec(); // 4, 6
}
複製代碼
使用enum
關鍵字定義一個枚舉類,這個語法跟Java
相似,以下代碼:
enum Color { red, green, blue }
複製代碼
mixins是一個重複使用類中代碼的方式,好比下面的代碼:
class A {
a() {
print("A's a()");
}
}
class B {
b() {
print("B's b()");
}
}
// 使用with關鍵字,表示類C是由類A和類B混合而構成
class C = A with B;
main() {
C c = new C();
c.a(); // A's a() c.b(); // B's b()
}
複製代碼
// 類的靜態成員變量和靜態成員方法
class Cons {
static const name = "zhangsan";
static sayHello() {
print("hello, this is ${Cons.name}");
}
}
main() {
Cons.sayHello(); // hello, this is zhangsan
print(Cons.name); // zhangsan
}
複製代碼
Java
和C++
語言都有泛型,Dart
語言也不例外,使用泛型有不少好處,好比:
Dart
內置的數據類型List
就是一個泛型數據類型,你能夠往List
中塞任何你想的數據類型好比整型、字符串、布爾值等
關於Dart
更多的泛型知識點,能夠查看這裏。
Dart
目前已經有不少的庫提供給開發者,許多功能不須要開發者本身去實現,只須要導入對應的包便可,使用import
語句來導入某個包,好比下面的代碼:
import 'dart:html';
複製代碼
若是你想導入本身寫的某個代碼文件,使用相對路徑便可,例如當前有一個demo.dart
文件,跟該文件同級目錄下有個util.dart
文件,文件代碼以下:
// util.dart文件內容
int add(int a, int b) {
return a + b;
}
複製代碼
在demo.dart
文件中若是要引用util.dart
文件,使用下面的方式導入:
// demo.dart
import './util.dart';
main() {
print(add(1, 2));
}
複製代碼
你可使用as
關鍵字爲導入的某個包設置一個前綴,或者說別名,好比下面的代碼:
import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;
// Uses Element from lib1.
Element element1 = Element();
// Uses Element from lib2.
lib2.Element element2 = lib2.Element();
複製代碼
你也能夠在導入包時使用show
hide
關鍵字來導入某個包中的部分功能,好比下面的代碼:
// 只導入foo
import 'package:lib1/lib1.dart' show foo;
// 導入除了foo的全部其餘部分
import 'package:lib2/lib2.dart' hide foo;
複製代碼
導入包時使用deferred as
可讓這個包懶加載,懶加載的包只會在該包被使用時獲得加載,而不是一開始就加載,好比下面的代碼:
import 'package:greetings/hello.dart' deferred as hello;
複製代碼
Dart
提供了相似ES7中的async
await
等異步操做,這種異步操做在Flutter開發中會常常遇到,好比網絡或其餘IO操做,文件選擇等都須要用到異步的知識。 async
和await
每每是成對出現的,若是一個方法中有耗時的操做,你須要將這個方法設置成async
,並給其中的耗時操做加上await
關鍵字,若是這個方法有返回值,你須要將返回值塞到Future
中並返回,以下代碼所示:
Future checkVersion() async {
var version = await lookUpVersion();
// Do something with version
}
複製代碼
下面的代碼使用Dart
從網絡獲取數據並打印出來:
import 'dart:async';
import 'package:http/http.dart' as http;
Future<String> getNetData() async{
http.Response res = await http.get("http://www.baidu.com");
return res.body;
}
main() {
getNetData().then((str) {
print(str);
});
}
複製代碼
關於Dart
異步操做,能夠查看這篇文章瞭解更多。
本篇博客較長,主要是對官方文檔的一個翻譯(大部分),若是你對英文閱讀沒有太大障礙,建議直接查看官方的英文文檔,但願各位都能愉快的學習Dart
和Flutter
。
上一篇 | 下一篇 |
---|---|
從0開始寫一個基於Flutter的開源中國客戶端(1) ——Flutter簡介及開發環境搭建 |
從0開始寫一個基於Flutter的開源中國客戶端(3)——初識Flutter & 經常使用的Widgets |