跟Java等不少語言不一樣的是,Dart沒有public protected private等關鍵字,若是某個變量如下劃線 _ 開頭,表明這個變量在庫中是私有的。Dart中變量能夠以字母或下劃線開頭,後面跟着任意組合的字符或數字html
如下代碼是Dart中定義變量的方法:編程
main() { var a = 1; int b = 10; String s = "hello"; dynamic c = 0.5; } 複製代碼
你能夠明確指定某個變量的類型,如int bool String,也能夠用var或 dynamic來聲明一個變量,Dart會自動推斷其數據類型。網絡
注意:沒有賦初值的變量都會有默認值nullapp
若是你毫不想改變一個變量,使用final或const,不要使用var或其餘類型,一個被final修飾的變量只能被賦值一次,一個被const修飾的變量是一個編譯時常量(const常量毫無疑問也是final常量)。能夠這麼理解:final修飾的變量是不可改變的,而const修飾的表示一個常量。異步
注意:實例變量能夠是final的但不能是const的async
下面用代碼說明:編程語言
var count = 10; final Num = count; // final 只能賦值一次const Num1 = 10; // const賦值必須是編譯時常量複製代碼
區別一:final 要求變量只能初始化一次,並不要求賦的值必定是編譯時常量,能夠是常量也能夠不是。而 const 要求在聲明時初始化,而且賦值必需爲編譯時常量。ide
區別二:final 是惰性初始化,即在運行時第一次使用前才初始化。而 const 是在編譯時就肯定值了。函數
Dart有以下幾種內建的數據類型:ui
numbers
strings
booleans
lists(或者是arrays)
maps
runes(UTF-32字符集的字符)
symbols 下面用一段代碼來演示以上各種數據類型:
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複製代碼
類除了有跟類名相同的構造方法外,還能夠添加命名的構造方法,以下代碼所示:
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.dartimport './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關鍵字來導入某個包中的部分功能,好比下面的代碼:
// 只導入fooimport '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); }); }