前言一:接下來一段時間我會陸續更新一些列Flutter文字教程更新進度: 每週至少兩篇;html
更新地點: 首發於公衆號,次日更新於掘金、思否等地方;vue
更多交流: 能夠添加個人微信 372623326,關注個人微博:coderwhy算法
但願你們能夠 幫忙轉發,點擊在看,給我更多的創做動力。微信
這裏,我只列出來相對其餘語言比較特殊的運算符,由於某些運算符太簡單了,不浪費時間,好比+、-、+=、==。你可能會疑惑,Dart爲何要搞出這麼多特殊的運算符呢?數據結構
你要堅信一點:全部這些特殊的運算符都是爲了讓咱們在開發中能夠更加方便的操做,而不是讓咱們的編碼變得更加複雜。app
咱們來看一下除法、整除、取模運算ide
var num = 7; print(num / 3); // 除法操做, 結果2.3333.. print(num ~/ 3); // 整除操做, 結果2; print(num % 3); // 取模操做, 結果1;
dart有一個不少語言都不具有的賦值運算符:函數
main(List<String> args) { var name1 = 'coderwhy'; print(name1); // var name2 = 'kobe'; var name2 = null; name2 ??= 'james'; print(name2); // 當name2初始化爲kobe時,結果爲kobe,當初始化爲null時,賦值了james }
Dart中包含一直比較特殊的條件運算符:expr1 ?? expr2工具
var temp = 'why'; var temp = null; var name = temp ?? 'kobe'; print(name);
class Person { String name; void run() { print("${name} is running"); } void eat() { print("${name} is eating"); } void swim() { print("${name} is swimming"); } } main(List<String> args) { final p1 = Person(); p1.name = 'why'; p1.run(); p1.eat(); p1.swim(); final p2 = Person() ..name = "why" ..run() ..eat() ..swim(); }
和大部分語言的特性比較類似,這裏就再也不詳細贅述,看一下便可。
和其餘語言用法同樣學習
這裏有一個注意點:不支持非空即真或者非0即真,必須有明確的bool類型
基本的for循環
for (var i = 0; i < 5; i++) { print(i); }
for in遍歷List和Set類型
var names = ['why', 'kobe', 'curry']; for (var name in names) { print(name); }
while和do-while和其餘語言一致
break和continue用法也是一致
普通的switch使用
main(List<String> args) { var direction = 'east'; switch (direction) { case 'east': print('東面'); break; case 'south': print('南面'); break; case 'west': print('西面'); break; case 'north': print('北面'); break; default: print('其餘方向'); } }
Dart是一個面向對象的語言,面向對象中很是重要的概念就是類,類產生了對象。這一節,咱們就具體來學習類和對象,可是Dart對類進行了不少其餘語言沒有的特性,因此,這裏我會花比較長的篇幅來說解。
在Dart中,定義類用class關鍵字
。
類一般有兩部分組成:成員(member)和方法(method)。
定義類的僞代碼以下:
class 類名 { 類型 成員名; 返回值類型 方法名(參數列表) { 方法體 } }
編寫一個簡單的Person類:
並無加this
;省略this
,可是有命名衝突
時,this不能省略
;class Person { String name; eat() { print('$name在吃東西'); } }
咱們來使用這個類,建立對應的對象:
main(List<String> args) { // 1.建立類的對象 var p = new Person(); // 直接使用Person()也能夠建立 // 2.給對象的屬性賦值 p.name = 'why'; // 3.調用對象的方法 p.eat(); }
咱們知道, 當經過類建立一個對象時,會調用這個類的構造方法。
沒有明確指定構造方法
時,將默認擁有一個無參的構造方法
。咱們也能夠根據本身的需求,定義本身的構造方法:
注意一:當有了本身的構造方法時,默認的構造方法將會失效
,不能使用
不支持函數的重載
(名稱相同, 參數不一樣的方式)。class Person { String name; int age; Person(String name, int age) { this.name = name; this.age = age; } @override String toString() { return 'name=$name age=$age'; } }
另外,在實現構造方法時,一般作的事情就是經過參數
給屬性
賦值
爲了簡化這一過程, Dart提供了一種更加簡潔的語法糖形式
.
上面的構造方法能夠優化成下面的寫法:
Person(String name, int age) { this.name = name; this.age = age; } // 等同於 Person(this.name, this.age);
可是在開發中, 咱們確實但願實現更多的構造方法,怎麼辦呢?
咱們須要使用命名構造方法:
class Person { String name; int age; Person() { name = ''; age = 0; } // 命名構造方法 Person.withArgments(String name, int age) { this.name = name; this.age = age; } @override String toString() { return 'name=$name age=$age'; } } // 建立對象 var p1 = new Person(); print(p1); var p2 = new Person.withArgments('why', 18); print(p2);
在以後的開發中, 咱們也能夠利用命名構造方法,提供更加便捷的建立對象方式:
// 新的構造方法 Person.fromMap(Map<String, Object> map) { this.name = map['name']; this.age = map['age']; } // 經過上面的構造方法建立對象 var p3 = new Person.fromMap({'name': 'kobe', 'age': 30}); print(p3);
咱們來從新定義一個類Point, 傳入x/y,能夠獲得它們的距離distance:
class Point { final num x; final num y; final num distance; // 錯誤寫法 // Point(this.x, this.y) { // distance = sqrt(x * x + y * y); // } // 正確的寫法 Point(this.x, this.y) : distance = sqrt(x * x + y * y); }
上面這種初始化變量的方法, 咱們稱之爲初始化列表(Initializer list)
在某些狀況下, 咱們但願在一個構造方法中去調用另一個構造方法, 這個時候可使用重定向構造方法
:
class Person { String name; int age; Person(this.name, this.age); Person.fromName(String name) : this(name, 0); }
在某些狀況下,傳入相同值時
,咱們但願返回同一個對象
,這個時候,可使用常量構造方法.
默認狀況下,建立對象時,即便傳入相同的參數,建立出來的也不是同一個對象,看下面代碼:
identical(對象1, 對象2)
函數來判斷兩個對象是不是同一個對象:main(List<String> args) { var p1 = Person('why'); var p2 = Person('why'); print(identical(p1, p2)); // false } class Person { String name; Person(this.name); }
可是, 若是將構造方法前加const進行修飾
,那麼能夠保證同一個參數,建立出來的對象是相同的
常量構造方法
。main(List<String> args) { var p1 = const Person('why'); var p2 = const Person('why'); print(identical(p1, p2)); // true } class Person { final String name; const Person(this.name); }
常量構造方法有一些注意點:
注意二: 爲了能夠經過常量構造方法,建立出相同的對象,再也不使用 new關鍵字,而是使用const關鍵字
Dart提供了factory關鍵字, 用於經過工廠去獲取對象
main(List<String> args) { var p1 = Person('why'); var p2 = Person('why'); print(identical(p1, p2)); // true } class Person { String name; static final Map<String, Person> _cache = <String, Person>{}; factory Person(String name) { if (_cache.containsKey(name)) { return _cache[name]; } else { final p = Person._internal(name); _cache[name] = p; return p; } } Person._internal(this.name); }
默認狀況下,Dart中類定義的屬性是能夠直接被外界訪問的。
可是某些狀況下,咱們但願監控這個類的屬性
被訪問的過程,這個時候就可使用setter和getter
了
main(List<String> args) { final d = Dog("黃色"); d.setColor = "黑色"; print(d.getColor); } class Dog { String color; String get getColor { return color; } set setColor(String color) { this.color = color; } Dog(this.color); }
面向對象的其中一大特性就是繼承,繼承不只僅能夠減小咱們的代碼量
,也是多態的使用前提
。
Dart中的繼承使用extends關鍵字
,子類中使用super來訪問父類。
父類中的全部成員變量和方法都會被繼承,,可是構造方法除外。
main(List<String> args) { var p = new Person(); p.age = 18; p.run(); print(p.age); } class Animal { int age; run() { print('在奔跑ing'); } } class Person extends Animal { }
子類能夠擁有本身的成員變量,
而且能夠對父類的方法進行重寫
:
class Person extends Animal { String name; @override run() { print('$name在奔跑ing'); } }
子類中能夠調用父類的構造方法,對某些屬性進行初始化:
無參默認構造方法
(沒有參數且與類同名的構造方法)。無參默認構造方法
,則子類的構造方法必須在初始化列表中經過super
顯式調用父類的某個構造方法。class Animal { int age; Animal(this.age); run() { print('在奔跑ing'); } } class Person extends Animal { String name; Person(String name, int age) : name=name, super(age); @override run() { print('$name在奔跑ing'); } @override String toString() { return 'name=$name, age=$age'; } }
咱們知道,繼承是多態使用的前提。
因此在定義不少通用的調用接口
時, 咱們一般會讓調用者傳入父類
,經過多態來實現更加靈活的調用方式。
可是,父類自己可能並不須要對某些方法進行具體的實現,因此父類中定義的方法,,咱們能夠定義爲抽象方法。
什麼是 抽象方法? 在Dart中沒有具體實現的方法(沒有方法體),就是抽象方法。
abstract
聲明的類。下面的代碼中, Shape類就是一個抽象類, 其中包含一個抽象方法.
abstract class Shape { getArea(); } class Circle extends Shape { double r; Circle(this.r); @override getArea() { return r * r * 3.14; } } class Reactangle extends Shape { double w; double h; Reactangle(this.w, this.h); @override getArea() { return w * h; } }
注意事項:
Dart中的接口比較特殊, 沒有一個專門的關鍵字來聲明接口.
默認狀況下,定義的每一個類都至關於默認也聲明瞭一個接口,能夠由其餘的類來實現(由於Dart不支持多繼承)
在開發中,咱們一般將用於給別人實現的類聲明爲抽象類:
abstract class Runner { run(); } abstract class Flyer { fly(); } class SuperMan implements Runner, Flyer { @override run() { print('超人在奔跑'); } @override fly() { print('超人在飛'); } }
在經過implements實現某個類時,類中全部的方法都必須被從新實現
(不管這個類原來是否已經實現過該方法)。
可是某些狀況下,一個類可能但願直接複用以前類的原有實現方案,怎麼作呢?
Dart提供了另一種方案: Mixin混入的方式
main(List<String> args) { var superMan = SuperMain(); superMan.run(); superMan.fly(); } mixin Runner { run() { print('在奔跑'); } } mixin Flyer { fly() { print('在飛翔'); } } // implements的方式要求必須對其中的方法進行從新實現 // class SuperMan implements Runner, Flyer {} class SuperMain with Runner, Flyer { }
前面咱們在類中定義的成員和方法都屬於對象級別的, 在開發中, 咱們有時候也須要定義類級別的成員和方法
在Dart中咱們使用static關鍵字來定義:
main(List<String> args) { var stu = Student(); stu.name = 'why'; stu.sno = 110; stu.study(); Student.time = '早上8點'; // stu.time = '早上9點'; 錯誤作法, 實例對象不能訪問類成員 Student.attendClass(); // stu.attendClass(); 錯誤作法, 實現對象補鞥呢訪問類方法 } class Student { String name; int sno; static String time; study() { print('$name在學習'); } static attendClass() { print('去上課'); } }
枚舉在開發中也很是常見, 枚舉也是一種特殊的類, 一般用於表示固定數量的常量值。
枚舉使用enum關鍵字來進行定義:
main(List<String> args) { print(Colors.red); } enum Colors { red, green, blue }
枚舉類型中有兩個比較常見的屬性:
main(List<String> args) { print(Colors.red.index); print(Colors.green.index); print(Colors.blue.index); print(Colors.values); } enum Colors { red, green, blue }
枚舉類型的注意事項:
對於有基礎的同窗, 這部分再也不解釋
List使用時的泛型寫法:
// 建立List的方式 var names1 = ['why', 'kobe', 'james', 111]; print(names1.runtimeType); // List<Object> // 限制類型 var names2 = <String>['why', 'kobe', 'james', 111]; // 最後一個報錯 List<String> names3 = ['why', 'kobe', 'james', 111]; // 最後一個報錯
Map使用時的泛型寫法:
// 建立Map的方式 var infos1 = {1: 'one', 'name': 'why', 'age': 18}; print(infos1.runtimeType); // _InternalLinkedHashMap<Object, Object> // 對類型進行顯示 Map<String, String> infos2 = {'name': 'why', 'age': 18}; // 18不能放在value中 var infos3 = <String, String>{'name': 'why', 'age': 18}; // 18不能放在value中
若是咱們須要定義一個類, 用於存儲位置信息Location, 可是並不肯定使用者但願使用的是int類型,仍是double類型, 甚至是一個字符串, 這個時候如何定義呢?
Location類的定義: Object方式
main(List<String> args) { Location l1 = Location(10, 20); print(l1.x.runtimeType); // Object } class Location { Object x; Object y; Location(this.x, this.y); }
Location類的定義: 泛型方式
main(List<String> args) { Location l2 = Location<int>(10, 20); print(l2.x.runtimeType); // int Location l3 = Location<String>('aaa', 'bbb'); print(l3.x.runtimeType); // String } } class Location<T> { T x; T y; Location(this.x, this.y); }
若是咱們但願類型只能是num類型, 怎麼作呢?
main(List<String> args) { Location l2 = Location<int>(10, 20); print(l2.x.runtimeType); // 錯誤的寫法, 類型必須繼承自num Location l3 = Location<String>('aaa', 'bbb'); print(l3.x.runtimeType); } class Location<T extends num> { T x; T y; Location(this.x, this.y); }
最初,Dart僅僅在類中支持泛型。後來一種稱爲泛型方法的新語法容許在方法和函數中使用類型參數。
main(List<String> args) { var names = ['why', 'kobe']; var first = getFirst(names); print('$first ${first.runtimeType}'); // why String } T getFirst<T>(List<T> ts) { return ts[0]; }
在Dart中,你能夠導入一個庫來使用它所提供的功能。庫的使用可使代碼的重用性獲得提升,而且能夠更好的組合代碼。
Dart中任何一個dart文件都是一個庫,即便你沒有用關鍵字
library
聲明
import語句用來導入一個庫,後面跟一個字符串形式的Uri來指定表示要引用的庫,語法以下:
import '庫所在的uri';
常見的庫URI有三種不一樣的形式
//dart:前綴表示Dart的標準庫,如dart:io、dart:html、dart:math import 'dart:io';
//固然,你也能夠用相對路徑或絕對路徑的dart文件來引用 import 'lib/student/student.dart';
//Pub包管理系統中有不少功能強大、實用的庫,可使用前綴 package: import 'package:flutter/material.dart';
庫文件中內容的顯示和隱藏
若是但願只導入庫中某些內容
,或者刻意隱藏庫裏面某些內容
,可使用show
和hide
關鍵字
import 'lib/student/student.dart' show Student, Person; import 'lib/student/student.dart' hide Person;
庫中內容和當前文件中的名字衝突
當各個庫有命名衝突的時候,可使用as關鍵字
來使用命名空間
import 'lib/student/student.dart' as Stu; Stu.Student s = new Stu.Student();
library關鍵字
一般在定義庫時,咱們可使用library關鍵字給庫起一個名字。
但目前我發現,庫的名字並不影響導入,由於import語句用的是字符串URI
library math;
part關鍵字
在以前咱們使用student.dart做爲演練的時候,只是將該文件做爲一個庫。
在開發中,若是一個庫文件太大,將全部內容保存到一個文件夾是不太合理的,咱們有可能但願將這個庫進行拆分,這個時候就可使用part
關鍵字了
不過官方已經不建議使用這種方式了:
mathUtils.dart
文件
part of "utils.dart"; int sum(int num1, int num2) { return num1 + num2; }
dateUtils.dart
文件
part of "utils.dart"; String dateFormat(DateTime date) { return "2020-12-12"; }
utils.dart
文件
part "mathUtils.dart"; part "dateUtils.dart";
test_libary.dart
文件
import "lib/utils.dart"; main(List<String> args) { print(sum(10, 20)); print(dateFormat(DateTime.now())); }
export關鍵字
官方不推薦使用part關鍵字
,那若是庫很是大,如何進行管理呢?
mathUtils.dart
文件
int sum(int num1, int num2) { return num1 + num2; }
dateUtils.dart
文件
String dateFormat(DateTime date) { return "2020-12-12"; }
utils.dart
文件
library utils; export "mathUtils.dart"; export "dateUtils.dart";
test_libary.dart
文件
import "lib/utils.dart"; main(List<String> args) { print(sum(10, 20)); print(dateFormat(DateTime.now())); }
最後,也能夠經過Pub管理本身的庫本身的庫,在項目開發中我的以爲不是很是有必要,因此暫時不講解這種方式。
備註:全部內容首發於公衆號,以後除了Flutter也會更新其餘技術文章,TypeScript、React、Node、uniapp、mpvue、數據結構與算法等等,也會更新一些本身的學習心得等,歡迎你們關注