版權聲明:本文為作者原創(chuàng)書籍戈锻。轉載請注明作者和出處歼跟,未經授權,嚴禁私自轉載格遭,侵權必究9帧!拒迅!
情感語錄:很多人都覺得時間是最好的治療方案骚秦,但其實治好的全是皮外傷!
很高興再次遇到你她倘! 上期已經介紹完Dart中的基礎語法, 本期將繼續(xù)深入介紹Dart作箍, 相信在看這期內容的同學已經把上期的內容大部分掌握了硬梁。回顧上期內容:戳這里? Flutter大戰(zhàn)前夜之Dart語言(上)
Dart面向對象編程
面向對象編程(OOP)的三個基本特征是:封裝胞得、繼承荧止、多態(tài)。
封裝:封裝是對象和類概念的主要特性阶剑。封裝跃巡,把客觀事物封裝成抽象的類,并且把自己的部分屬性和方法提供給其他對象調用, 而一部分屬性和方法則隱藏牧愁。
繼承:面向對象編程 (OOP) 語言的一個主要功能就是“繼承”素邪。繼承是指這樣一種能力:它可以使用現有類的功能,并在無需重新編寫原來的類的情況下對這些功能進行擴展递宅。
多態(tài):允許將子類類型的指針賦值給父類類型的指針, 同一個函數調用會有不同的執(zhí)行效果 。
[溫馨小貼士:]
1苍狰、 Dart所有的東西都是對象办龄,所有的對象都繼承自Object類。
2淋昭、 Dart是一門使用類和單繼承 的面向對象語言俐填,所有的對象都是類的實例,并且所有的類都是Object的子類
3翔忽、一個類通常由屬性和方法組成英融。
什么是類?
1歇式、類的實質是一種數據類型驶悟,類似于int、double等基本類型材失,不同的是它是一種復雜的數據類型痕鳍。因為它的本質是類型,而不是數據龙巨,所以不存在于內存中笼呆,不能被直接操作,只有被實例化為對象時旨别, 才會變得可操作诗赌。
2、類是對現實生活中一類具有共同特征的事物的抽象秸弛。
類的定義和實例化:
1铭若、類的定義:class + 類名
(類名首字母大寫)
2洪碳、類的實例化:通過關鍵字 new 類名() 或者 類名()
創(chuàng)建實例化對象,new
并非必寫
//創(chuàng)建一個Persion 類
class Person{
//類的屬性
String name="再紅";
String dis="你好";
//類的方法
String getInfo(){
return "${this.name}-${this.dis}";
}
void setInfo(String dis){
this.dis=dis;
}
}
void main(){
//實例化Persion
var persion = Person();
//調用類中的方法
var info= persion.getInfo();
print(info); // 輸出:再紅-你好
}
什么是構造函數奥喻?
1偶宫、構造函數,是一種特殊的方法。主要用來在創(chuàng)建對象時初始化對象,即為對象成員變量賦初始值,總與new運算符一起使用在創(chuàng)建對象的語句中环鲤。
2纯趋、構造函數的命名必須和類名完全相同。
3冷离、構造函數的功能主要用于在類的對象創(chuàng)建時定義初始化的狀態(tài)吵冒。
4、Dart語言中的構造函數分為默認構造函數和命名構造函數西剥。
Dart和Java中構造函數的區(qū)別:
Dart:
1.Dart的默認構造函數: 類名(參數列表) 如:Persion(String name)
2.Dart的命名構造函數: 類名.構造函數名(參數列表) 如:Persion.useInfo(String name)
Java:
1.Java中構造函數: 類名(參數列表)
2.Java中的構造函數是根據 參數列表進行區(qū)分,多個構造函數的參數列表一定不同
總結:在Dart中默認構造函數有且只有一個,命名構造函數可以有任意個,但構造函數名不能相同痹栖。在Java中可以有多個構造函數,構造函數名一定相同瞭空,但構造函數的參數列表一定不同揪阿,否則報錯。
實例代碼:
//創(chuàng)建一個Persion 類
class Person{
//類的屬性
String name="再紅";
String dis="你好";
//Person(this.name,this.dis); // 構造函數簡寫,等同于下面的構造函數
Person(String name,String dis){
this.name = name;
this.dis = dis;
}
//定義一個無參命名構造函數
Person.UserInfo(){
}
Person.UserInfo1(String name){
this.name = name;
}
//類的方法
String getInfo(){
return "${this.name}-${this.dis}";
}
void setInfo(String dis){
this.dis=dis;
}
}
void main(){
//實例化Persion 并初始化成員變量
var persion = Person("小鄭","好帥");
//調用類中的方法
var info= persion.getInfo();
print(info); // 輸出:小鄭-好帥
//通過命名構造函數實例化對象
var persion1 = Person.UserInfo();
var info1 = persion1.getInfo();
print(info1); // 輸出:再紅-你好
//通過命名構造函數實例化對象
var persion2 = Person.UserInfo1("瑤瑤");
var info2 = persion2.getInfo();
print(info2); // 輸出:瑤瑤-你好
}
Dart中的getter和setter
getter和setter方法的定義:
1咆畏、getter的定義方式 get+方法名
2南捂、setter的定義方式 set+方法名(參數)
注意:
getter方法是沒有( )
的。返回值類型可以不用聲明旧找。setter方法有且只能接收一個參數
class Rect{
num height;
num width;
Rect(this.height,this.width);
// getter
get area{
return this.height*this.width;
}
//setter
set areaHeight(value){
this.height=value;
}
}
void main(){
Rect r=new Rect(10,2);
r.areaHeight=6; //修改高的值
print(r.area); //輸出:12
}
Dart中方法和屬性的私有化
Dart和其他面向對象語言不一樣溺健,Dart 中沒有 public private protected這些訪問修飾符。在Dart中使用 _ (下劃線)
把一個屬性或者方法定義成私有钮蛛。
class Persion{
//定義私有屬性
String _name ="小哥";
}
void main(){
Persion persion=new Persion();
print( persion._name);
}
程序一執(zhí)行Logcat上你會發(fā)現驚奇的一行字 "小哥"鞭缭。納尼?魏颓?岭辣? 按照要求書寫不是已經私有化了么?咋還能訪問該屬性呢甸饱? 你可能會說:"老鐵別怕易结,我刀很鋒利,感覺不到痛柜候!" 我只能說客官別急搞动,這是我們下面要講的內容。
Dart類的抽離
類抽離好處: 既把類單獨分離出去渣刷,既可以解決代碼臃腫的問題鹦肿,也能提高代碼的復用性,你可以簡單理解成模塊化吧!
繼續(xù)上面講的例子,我們把Persion類抽離出去辅柴,新建一個文件夾(bean)然后把Persion類寫入該文件夾下箩溃,如圖:
在Test類中通過 import 關鍵字進行導包引入瞭吃,這與Java和Kotlin中是一直的。然后我們在Test中去調用Persion中的方法和屬性看能不能訪問;林肌歪架!
import 'bean/Persion.dart';
void main() {
Persion persion = new Persion();
print(persion._name); // 嘗試訪問使用屬性
print(persion.getName()); //嘗試調用私有方法
}
如果你編譯器足夠智能,你會發(fā)現在打印輸出這行就已經提示錯誤信息了,不能訪問該屬性和方法霹陡,強行運行很顯然是會報錯的 eg: Error: The getter '_name' isn't defined for the class 'Persion'.
【總結:】
1和蚪、在Dart中是支持方法和屬性私有化的,私有化沒有關鍵字烹棉,而是通過 “_ 下劃線
”開頭進行命名的方法或屬性攒霹。
2、在一個類要引用另一個類使用關鍵字 import
進行類的導包浆洗。
思考:方法和屬性能進行私有化催束,那么類能進行私有化么?
Dart中的靜態(tài)成員
1伏社、使用 static 關鍵字來實現類級別的變量和函數
2抠刺、靜態(tài)方法不能訪問非靜態(tài)成員,非靜態(tài)方法可以訪問靜態(tài)成員
對于一些公共常用的方法和屬性我們常用 static進行定義摘昌,這樣的好處可避免頻繁的創(chuàng)建對象和銷毀速妖,這和Java 是如出一轍,做Java 或Android開發(fā)的這個在熟悉不過了第焰。
class Person {
static String name = '瑤瑤';
int age=20;
static void show() {
print(name);
}
void printInfo(){ /*非靜態(tài)方法可以訪問靜態(tài)成員以及非靜態(tài)成員*/
print(name); //訪問靜態(tài)屬性
print(this.age); //訪問非靜態(tài)屬性
show(); //調用靜態(tài)方法
}
static void printUserInfo(){//靜態(tài)方法
print(name); //靜態(tài)屬性
show(); //靜態(tài)方法
// print(this.age); //靜態(tài)方法沒法訪問非靜態(tài)的屬性
// this.printInfo(); //靜態(tài)方法沒法訪問非靜態(tài)的方法
// printInfo();
}
}
main(){
print(Person.name);
Person.show();
Person p=new Person();
p.printInfo();
Person.printUserInfo();
}
Dart中的對象操作符:
1买优、?
條件運算符
2妨马、as
類型轉換
3挺举、is
類型判斷
4、..
級聯操作
class Person {
String name;
num age;
Person(this.name,this.age);
void printInfo() {
print("${this.name}---${this.age}");
}
}
main(){
// 此時的 p 并未實例化烘跺,既空,p? 表示:如果p為空則不執(zhí)行.后面的方法湘纵,
// 否則執(zhí)行,喜歡kotlin開發(fā)的同學對此操作符再熟不過了!
Person p;
p?.printInfo(); //不會執(zhí)行printInfo()方法
Person p1=new Person('張三', 20);
p1?.printInfo(); //張三---20
Person p2=new Person('張三', 20);
if(p2 is Person){ //true
p2.name="李四";
}
p2.printInfo(); // 李四---20
print(p2 is Object); // 還記得上面講到的嗎滤淳?所有的類都是Object 的子類
num value = 123.2;
print(value as double); // 將num 強轉成double類型
Person p4=new Person('張三1', 20);
p4..name="李四"
..age=30
..printInfo();
// 效果同下
// Person p4=new Person('張三1', 20);
// p4.name='李四';
// p4.age=30;
// p4.printInfo();
}
Dart中的類的繼承(面向對象的三大特性之一):
1梧喷、子類使用 extends
關鍵詞來繼承父類
2、子類會繼承父類里面可見的屬性和方法 但是不會繼承構造函數
3脖咐、子類能復寫父類的方法 getter和setter
4铺敌、子類調用父類方法或屬性使用 super
關鍵字。
5屁擅、 @override
可以寫也可以不寫偿凭,建議在覆寫父類方法的時候加上 @override
class Parent {
String name='紅紅';
num age=18;
void userInfo() {
print("${this.name}---${this.age}");
}
}
class Child extends Parent{
}
class Child1 extends Parent{
//復寫父類方法
@override
void userInfo() {
name = "淇淇";
print("${this.name}---${this.age}");
}
}
class Child2 extends Parent{
//復寫父類方法
@override
void userInfo() {
//調用父類方法
super.userInfo();
//調用父類屬性
super.name = "瑤瑤";
super.userInfo();
}
}
main(){
Child child=new Child();
//訪問父類的屬性
print(child.name); //輸出:紅紅
//調用父類的方法
child.userInfo(); //輸出:紅紅---18
Child1 child1=new Child1();
child1.userInfo(); //輸出: 淇淇---18
Child2 child2=new Child2();
child2.userInfo(); //輸出: 紅紅---18 然后輸出:瑤瑤---18
}
Dart中抽象類
Dart抽象類主要用于定義標準,子類可以繼承抽象類派歌,也可以實現抽象類接口弯囊。
1痰哨、抽象類通過abstract
關鍵字來定義
2、Dart中的抽象方法不能用abstract聲明匾嘱,Dart中沒有方法體的方法我們稱為抽象方法斤斧。
3、如果子類繼承抽象類必須得實現里面的抽象方法
4、如果把抽象類當做接口實現的話必須得實現抽象類里面定義的所有屬性和方法昵济。
5审葬、抽象類不能被實例化,只有繼承它的子類可以
extends抽象類 和 implements的區(qū)別:
1锐秦、如果要復用抽象類里面的方法,并且要用抽象方法約束自類的話我們就用extends繼承抽象類
2盗忱、如果只是把抽象類當做標準的話我們就用implements實現抽象類
abstract class Animal{
eat(); //抽象方法
run(); //抽象方法
printInfo(){
print('抽象類里面的普通方法');
}
}
class Persion extends Animal{
@override
eat() {
print('人在吃飯');
}
@override
run() {
print('人在走路');
}
}
class Cat extends Animal{
@override
eat() {
print('小貓在吃老鼠');
}
@override
run() {
print('小貓在跑');
}
}
main(){
Persion persion = new Persion();
persion.eat(); // 輸出:人在吃飯
persion.run(); // 輸出:人在走路
persion.printInfo(); // 調用父類的普通方法
Cat c=new Cat();
c.eat(); // 輸出:小貓在吃老鼠
c.run(); // 輸出:小貓在跑
//Animal a=new Animal(); //錯誤操作酱床;抽象類沒法直接被實例化
}
Datr中的多態(tài):
1、允許將子類類型的指針賦值給父類類型的指針, 同一個函數調用會有不同的執(zhí)行效果 趟佃。
2扇谣、子類的實例賦值給父類的引用。
3闲昭、 多態(tài)就是父類定義一個方法不去實現罐寨,讓繼承他的子類去實現,每個子類有不同的表現序矩。
abstract class Animal{
eat(); //抽象方法
}
class Persion extends Animal{
@override
eat() {
print('人在吃飯');
}
}
class Cat extends Animal{
@override
eat() {
print('小貓在吃老鼠');
}
}
main(){
Animal persion = new Persion();
persion.eat(); // 輸出:人在吃飯
Animal cat =new Cat();
cat.eat(); // 輸出:小貓在吃老鼠
//Animal a=new Animal(); //錯誤操作鸯绿;抽象類沒法直接被實例化
}
Datr中的接口:
Dart 的接口沒有像Java中的 interface 關鍵字定義接口,而是普通類或抽象類都可以作為接口被實現簸淀。同樣使用 implements 關鍵字進行實現瓶蝴。如果實現的類是普通類,會將普通類和抽象中的屬性的方法全部需要覆寫一遍租幕。而因為抽象類可以定義抽象方法舷手,普通類不可以,所以一般要實現像Java接口那樣的方式劲绪,一般會使用抽象類男窟。
接口的優(yōu)勢:
一、規(guī)范性
接口就是約定 贾富、規(guī)范,讓實現者按著規(guī)范去實現自己的業(yè)務邏輯歉眷,在整個系統(tǒng)設計中,涉及到很多層颤枪,為了使各個層之間調用透明話汗捡,你只需要知道接口,按照這個接口做你具體做的事情汇鞭,就可以融合到整個系統(tǒng)中了凉唐。
二庸追、高內聚低耦合
一個好的程序一定符合高內聚低耦合的特征,那么實現低耦合台囱,定義接口是一個很好的方法淡溯,能夠讓系統(tǒng)的功能較好地實現,而不涉及任何具體的實現細節(jié)簿训。這樣就比較安全咱娶、嚴密一些。
三强品、擴展性
在項目開發(fā)過程中膘侮,由于客戶的需求經常變化,如果不采用接口的榛,那么我們必須不停改寫現有的業(yè)務代碼琼了。改寫代碼可能產生新的BUG,而且改寫代碼還會影響到調用該業(yè)務的類夫晌,可能全都需要修改雕薪,影響系統(tǒng)本身的穩(wěn)定性。到最后晓淀,可能會出現代碼凌亂所袁,不易讀懂,后接手的人無法讀懂代碼凶掰,系統(tǒng)的維護工作越來越重燥爷,最終可能導致項目失敗。
實例: 定義一個DB庫 支持 Mysql 懦窘、SQLserver的接口
//定義一個添加刪改查的接口
abstract class DbController {
//數據庫的鏈接地址
String uri;
add(String data);
delete();
update();
query();
}
//使用MySql去實現自己的CRUD
class Mysql implements DbController {
@override
String uri;
Mysql(this.uri);
add(String data) {
}
@override
delete() {
}
@override
query() {
}
@override
update() {
}
}
//使用SqlServer去實現自己的CRUD
class SqlServer implements DbController {
@override
String uri;
SqlServer(this.uri);
add(String data) {
}
@override
delete() {
}
@override
query() {
}
@override
update() {
}
}
main() {
Mysql mysql = new Mysql('連接地址');
mysql.add('張三');
SqlServer server = new SqlServer('連接地址');
server.query();
}
【溫馨提示:】一個類實現一個接口使用 implements
關鍵字,實現多個接口使用 ,
進行分割開前翎。
Dart中的泛型
泛型就是解決類、 接口奶赠、 方法的復用性鱼填、以及對不特定數據類型的支持药有。
一毅戈、 泛型的定義:泛型的本質是參數化類型,也就是說所操作的數據類型被指定為一個參數愤惰。這種參數類型可以用在類苇经、接口和方法
的創(chuàng)建中,分別稱為泛型類宦言、泛型接口扇单、泛型方法。
二奠旺、 泛型的好處:泛型的好處是在編譯的時候檢查類型安全蜘澜,并且所有的強制轉換都是自動和隱式的施流,提高代碼的重用率。
實例 (泛型方法):
假設別人需要你提供一個方法給開發(fā)者使用鄙信,前期開發(fā)者會告訴你他會傳一個什么樣類型的數據給你瞪醋,比如是String 類型的年齡 如:二十歲,并讓你處理后返回給他一個結果装诡。你可能說很簡單银受,咔咔幾下就寫出了如下代碼:
String getAge(String value){
return "紅紅今年:"+value+"歲";
}
嗯,滿足需求鸦采,沒有任何毛病宾巍。就在你很滿意的時候,開發(fā)者告訴你我給不了你中文的數字"二十"渔伯,只能給你一個阿拉伯數字顶霞,讓你改改。
此時你可能咔咔幾下又寫出了如下代碼:
String getAge(String value){
return "紅紅今年:"+value+"歲";
}
String getAge1(int value){
return "紅紅今年:${value}歲";
}
完美锣吼,同時支持中文和阿拉伯數字的方法了确丢,但是這樣就出現一個問題(代碼冗余) 實際開發(fā)中肯定不是這樣一個簡單的需求。那怎么辦吐限,要是我能接收一個任意類型的參數就好了鲜侥,靈機一動你可能想到,啊哈,前面不是講到在Dart中方法中參數類型和返回類型可以不用指定么诸典! 然后你可能Duang寫出了下面代碼:
// 同時返回 string類型 和number類型
getAge(value){
return "紅紅今年:${value}歲";
}
但是C韬!:弧Rㄔⅰ!肌蜻,不指定類型放棄了類型檢查,使得程序變的極不安全互墓。使用Object 做參數類型和返回類型,看起來很理想哈,在Dart中所有的對象都是Object的子類蒋搜,通過對類型Object的引用來實現參數的“任意化”篡撵,“任意化”帶來的缺點是要做顯式的強制類型轉換,而這種轉換是要求開發(fā)者對實際參數類型可以預知的情況下進行的豆挽。對于強制類型轉換錯誤的情況育谬,編譯器可能不提示錯誤,在運行的時候才出現異常帮哈,這也是一個安全隱患膛檀。
我們現在想實現的是傳入什么 返回什么。比如:傳入number 類型必須返回number類型 傳入 Persion 類型必須返回Persion類型。使用泛型進行參數任意化
T getUserInfo<T>(T value) {
return value;
}
void main() {
print(getUserInfo(21));
print(getUserInfo('曉曉'));
print(getUserInfo<String>('你好'));
}
實例 (泛型方類):
上面講了泛型方法即在方法中使用 "T" 聲明的參數類型,這個"T"可以是任意字母咖刃,但通常是一個大寫的字母表示即可泳炉。這個作用在方法是泛型方法,作用在類上就是泛型類了嚎杨。
class PrintClass<T>{
List list=new List<T>();
void add(T value){
this.list.add(value);
}
void printInfo(){
for(var i=0;i<this.list.length;i++){
print(this.list[i]);
}
}
}
main() {
PrintClass p = new PrintClass<String>();
p.add('你好');
p.add('哈哈');
p.printInfo(); //分別輸出: 你好; 哈哈
PrintClass p1=new PrintClass<int>();
p1.add(12);
p1.add(23);
p1.printInfo(); //分別輸出: 12; 23
}
實例 (泛型接口):
實現數據緩存的功能:有文件緩存胡桃、和內存緩存。內存緩存和文件緩存按照接口約束實現磕潮。
1翠胰、定義一個泛型接口 約束實現它的子類必須有getByKey(key) 和 setByKey(key,value)
2、要求setByKey的時候的value的類型和實例化子類的時候指定的類型一致
//定義接口
abstract class Cache<T>{
getByKey(String key);
void setByKey(String key, T value);
}
class FileCache<T> implements Cache<T>{
@override
getByKey(String key) {
return null;
}
@override
void setByKey(String key, T value) {
print("我是文件緩存 把key=${key} value=${value}的數據寫入到了文件中"); // 我是文件緩存 把key=文件緩存 value=這是文件緩存的數據寫入到了文件中
}
}
class MemoryCache<T> implements Cache<T>{
@override
getByKey(String key) {
return null;
}
@override
void setByKey(String key, T value) {
print("我是內存緩存 把key=${key} value=${value} -寫入到了內存中");//我是內存緩存 把key=內存緩存 value={name: 張三, age: 20} -寫入到了內存中
}
}
void main(){
FileCache fileCache=new FileCache<String>();
fileCache.setByKey('文件緩存', '這是文件緩存');
MemoryCache m=new MemoryCache<Map>();
m.setByKey('內存緩存', {"name":"張三","age":20});
}
Dart中的異步
在Dart中異步是用async
關鍵字標識自脯,async使用在方法時則在調用該方法時必須使用await
關鍵字之景,async是讓方法變成異步,await是等待異步方法執(zhí)行完成膏潮。
實例 (異步):
import 'dart:io';
import 'dart:convert';
void main() async{
//等待異步方法返回結果,在main方法中使用了 await锻狗,所以main 方法也需要async 標識
var result = await request();
print(result);
}
//api接口: http://news-at.zhihu.com/api/3/stories/latest
//創(chuàng)建一個異步方法
request() async{
//1、創(chuàng)建HttpClient對象
var httpClient = new HttpClient();
//2焕参、創(chuàng)建Uri對象
var uri = new Uri.http('news-at.zhihu.com','/api/3/stories/latest');
//3轻纪、發(fā)起請求,等待請求
var request = await httpClient.getUrl(uri);
//4叠纷、關閉請求刻帚,等待響應
var response = await request.close();
//5、解碼響應的內容
return await response.transform(utf8.decoder).join();
}
Dart中的懶加載
什么是懶加載涩嚣?既在需要的時候再進行加載崇众。 懶加載的最大好處是可以減少APP的啟動時間。懶加載使用deferred as
關鍵字來指定航厚。
import 'hello.dart' deferred as hello;
greet() async {
//加載hello.dart
await hello.loadLibrary();
//調用hello中的方法
hello.printf();
}
void main() async{
}
Dart中庫以及導包
1顷歌、關于庫的使用
Dart中的庫主要有三種:
1、我們自定義的庫
import 'lib/xxx.dart';
2幔睬、系統(tǒng)內置庫
import 'dart:math';
import 'dart:io';
import 'dart:convert';
3眯漩、Pub包管理系統(tǒng)中的庫
https://pub.dev/packages
https://pub.flutter-io.cn/packages
https://pub.dartlang.org/flutter/
1、需要在自己想項目根目錄新建一個pubspec.yaml
2麻顶、在pubspec.yaml文件 然后配置名稱 赦抖、描述、依賴等信息
3澈蚌、然后運行 pub get 獲取包下載到本地
4摹芙、項目中引入庫
如下:
name: 庫名
description: 庫的描述
dependencies:
http: ^0.12.0+2 // 依賴信息 如這個 http 版本0.12
2灼狰、關于導包的使用
在Dart中當引入兩個庫中有相同名稱標識符的時候宛瞄,如果是java通常我們通過寫上完整的包名路徑來指定使用的具體標識符,甚至不用import都可以,但是Dart里面是必須import
的份汗。當沖突的時候盈电,可以使用as關鍵字來指定庫的前綴。如下例子所示:
import 'package:lib1/User.dart';
import 'package:lib2/User.dart' as User2;
Element element1 = new Element(); // Uses Element from lib1.
User2.Element element2 = new User2.Element(); // Uses Element from lib2.
如果只需要導入庫的一部分杯活,有兩種模式:
模式一:只導入需要的部分匆帚,使用show關鍵字,如下例子所示:
import 'package:lib1/User.dart' show getAge;
模式二:隱藏不需要的部分旁钧,使用hide關鍵字吸重,如下例子所示:
import 'package:lib2/User.dart' hide getAge;
好了,關于Dart的基礎知識到這里就基本講完了,更多知識點請關注下期內容 一舉拿下Android和IOS《Flutter專欄》,謝謝大家觀看歪今,下期再會:啃摇!<男伞嫉晶!