Flutter高級面試題&答案

1、Dart是值傳遞還是引用傳遞?

dart是值傳遞。

2、描述Flutter的核心渲染模塊三棵樹

WidgetTree:存放渲染內(nèi)容、它只是一個配置數(shù)據(jù)結(jié)構(gòu),創(chuàng)建是非常輕量的,在頁面刷新的過程中隨時會重建

Element 是分離 WidgetTree 和真正的渲染對象的中間層, WidgetTree 用來描述對應的Element 屬性,同時持有Widget和RenderObject,存放上下文信息,通過它來遍歷視圖樹,支撐UI結(jié)構(gòu)。

RenderObject (渲染樹)用于應用界面的布局和繪制,負責真正的渲染,保存了元素的大小,布局等信息,實例化一個 RenderObject 是非常耗能的

當應用啟動時 Flutter 會遍歷并創(chuàng)建所有的 Widget 形成 Widget Tree,通過調(diào)用 Widget 上的 createElement() 方法創(chuàng)建每個 Element 對象,形成 Element Tree。最后調(diào)用 Element 的 createRenderObject() 方法創(chuàng)建每個渲染對象,形成一個 Render Tree。

3、flutter 中Widget的分類

1、組合類:StatelessWidget和StatefulWidget

2、代理類:inheritedwidget、ParentDataWidget
inheritedwidget一般用于狀態(tài)共享,如Theme 、Localizations 、 MediaQuery 等,都是通過它實現(xiàn)共享狀態(tài),這樣我們可以通過 context 去獲取共享的狀態(tài),比如 ThemeData theme = Theme.of(context);

3、繪制類:RenderObjectWidget
RenderObject 的布局相關(guān)方法調(diào)用順序是 : layout -> performResize -> performLayout -> markNeedsPaint

4、mixin extends implement 之間的關(guān)系?

繼承(關(guān)鍵字 extends)、混入 mixins (關(guān)鍵字 with)、接口實現(xiàn)(關(guān)鍵字 implements)。

這三者可以同時存在,前后順序是extends -> mixins -> implements。

Flutter中的繼承是單繼承,子類重寫超類的方法要用@Override,子類調(diào)用超類的方法要用super。

在Flutter中,Mixins是一種在多個類層次結(jié)構(gòu)中復用類代碼的方法。mixins的對象是類,mixins絕不是繼承,也不是接口,而是一種全新的特性,可以mixins多個類,mixins的使用需要滿足一定條件。

使用mixins的條件:

mixins類只能繼承自object
mixins類不能有構(gòu)造函數(shù)
一個類可以mixins多個mixins類
可以mixins多個類,不破壞Flutter的單繼承

5、簡述Dart語音特性

在Dart中,一切都是對象,所有的對象都是繼承自O(shè)bject

Dart是強類型語言,但可以用var或 dynamic來聲明一個變量,Dart會自動推斷其數(shù)據(jù)類型,dynamic類似c#

沒有賦初值的變量都會有默認值null

Dart支持頂層方法,如main方法,可以在方法內(nèi)部創(chuàng)建方法

Dart支持頂層變量,也支持類變量或?qū)ο笞兞?/p>

Dart沒有public protected private等關(guān)鍵字,如果某個變量以下劃線(_)開頭,代表這個變量在庫中是私有的

6、Dart 中的級聯(lián)操作符

Dart 當中的 「..」意思是 「級聯(lián)操作符」,為了方便配置而使用。「..」和「.」不同的是 調(diào)用「..」后返回的相當于是 this,而「.」返回的則是該方法返回的值 。

7、Dart 的單線程模型是如何運行的?

Dart 在單線程中是以消息循環(huán)機制來運行的,包含兩個任務隊列,一個是“微任務隊列” microtask queue,另一個叫做“事件隊列” event queue。

當Flutter應用啟動后,消息循環(huán)機制便啟動了。
按照先進先出的順序逐個執(zhí)行 微任務隊列 中的任務,當所有 微任務隊列 執(zhí)行完后便開始執(zhí)行 事件隊列 中的任務,事件任務執(zhí)行完畢后再去執(zhí)行微任務,如此循環(huán)往復;

8、await for 與 stream流

Stream<String> stream = new Stream<String>.fromIterable(['1', '2', '3', '4']);
main() async{
 print('start');
 await for(String s in stream){
   print(s);
 }
 print('end..');
}

輸出

start
1
2
3
4
end..

await for一般用在直到Stream什么時候完成,并且必須等待傳遞完成之后才能使用,不然就會一直阻塞。

9、Stream 與 Future是什么關(guān)系?

1、Future 表示稍后獲得的一個數(shù)據(jù),所有異步的操作的返回值都用 Future 來表示。
2、Future 只能表示一次異步獲得的數(shù)據(jù)。
3、Stream 表示多次異步獲得的數(shù)據(jù)。比如界面上的按鈕可能會被用戶點擊多次,按鈕上的點擊事件(onClick)就是一個 Stream 。
4、Future將返回一個值,而Stream將返回多次值。
5、Dart 中統(tǒng)一使用 Stream 處理異步事件流。

10、Stream 有哪兩種訂閱模式?分別是怎么調(diào)用的?

Stream有兩種訂閱模式:單訂閱(single) 和 多訂閱(broadcast)。單訂閱就是只能有一個訂閱者,而廣播是可以有多個訂閱者。這就有點類似于消息服務(Message Service)的處理模式。單訂閱類似于點對點,在訂閱者出現(xiàn)之前會持有數(shù)據(jù),在訂閱者出現(xiàn)之后就才轉(zhuǎn)交給它。而廣播類似于發(fā)布訂閱模式,可以同時有多個訂閱者,當有數(shù)據(jù)時就會傳遞給所有的訂閱者,而不管當前是否已有訂閱者存在。

Stream 默認處于單訂閱模式,所以同一個 stream 上的 listen 和其它大多數(shù)方法只能調(diào)用一次,調(diào)用第二次就會報錯。但 Stream 可以通過 transform() 方法(返回另一個 Stream)進行連續(xù)調(diào)用。通過 Stream.asBroadcastStream() 可以將一個單訂閱模式的 Stream 轉(zhuǎn)換成一個多訂閱模式的 Stream,isBroadcast 屬性可以判斷當前 Stream 所處的模式。

11、Flutter中的Widget、State、Context 的核心概念?是為了解決什么問題?

主要是為了解決多個部件之間的交互和部件自身狀態(tài)的維護。

1、Widget: 在Flutter中,幾乎所有東西都是Widget。將一個Widget想象為一個可視化的組件(或與應用可視化方面交互的組件),當你需要構(gòu)建與布局直接或間接相關(guān)的任何內(nèi)容時,你正在使用Widget。

2、Widget樹: Widget以樹結(jié)構(gòu)進行組織。包含其他Widget的widget被稱為父Widget(或widget容器)。包含在父widget中的widget被稱為子Widget。

3、Context: 僅僅是已創(chuàng)建的所有Widget樹結(jié)構(gòu)中的某個Widget的位置引用。簡而言之,將context作為widget樹的一部分,其中context所對應的widget被添加到此樹中。一個context只從屬于一個widget,它和widget一樣是鏈接在一起的,并且會形成一個context樹。

4、State: 定義了StatefulWidget實例的行為,它包含了用于”交互/干預“Widget信息的行為和布局。應用于State的任何更改都會強制重建Widget。

12、Dart異步編程中的 Future關(guān)鍵字?

Dart中,執(zhí)行一個異步任務使用Future來處理。在 Dart 的每一個 Isolate 當中,執(zhí)行的優(yōu)先級為 :Main > MicroTask > EventQueue。

13、Flutter 中的生命周期

image.png

14、Widget 唯一標識Key

GlobalKey:確保生成的Key在整個應用中唯一,是很昂貴的,允許element在樹周圍移動或變更父節(jié)點而不會丟失狀態(tài);
LocalKey
UniqueKey
ObjectKey

15、Flutter是怎么完成組件渲染的?

在計算機系統(tǒng)中,圖像的顯示需要CPU、GPU和顯示器一起配合完成CPU負責圖像數(shù)據(jù)計算,GPU負責圖像數(shù)據(jù)渲染,而顯示器則負責最終圖像顯示。CPU把計算好的、需要顯示的內(nèi)容交給GPU,由GPU完成渲染后放入幀緩沖區(qū),隨后視頻控制器根據(jù)垂直同步信號以每秒60次的速度,從幀緩沖區(qū)讀取幀數(shù)據(jù)交由顯示器完成圖像顯示。操作系統(tǒng)在呈現(xiàn)圖像時遵循了這種機制。

而Flutter作為跨平臺開發(fā)框架也采用了這種底層方案,UI線程使用Dart語言來構(gòu)建視圖結(jié)構(gòu)數(shù)據(jù),這些數(shù)據(jù)會在GPU線程進行圖層合成,隨后交給圖像渲染引擎Skia加工成GPU數(shù)據(jù),而這些數(shù)據(jù)會通過OpenGL最終提供給GPU渲染。

可以看到Flutter用了計算機最基本的圖像渲染技術(shù),摒棄其他一些通道和過程,用最直接的方式完成了圖形顯示,自然性能也就得到了保障。

16、PlatformView 以及其原理

Flutter 中通過 PlatformView 可以嵌套原生 View 到 Flutter UI 中,這里面其實是使用了 Presentation + VirtualDisplay + Surface 等實現(xiàn)的,
大致原理:
使用了類似副屏顯示的技術(shù),VirtualDisplay 類代表一個虛擬顯示器,調(diào)用 DisplayManager 的 createVirtualDisplay() 方法,將虛擬顯示器的內(nèi)容渲染在一個 Surface 控件上,然后將 Surface 的 id 通知給 Dart,讓 engine 繪制時,在內(nèi)存中找到對應的 Surface 畫面內(nèi)存數(shù)據(jù),然后繪制出來,實時控件截圖渲染顯示技術(shù)。

17、Flutter 線程管理模型

Flutter Engine層會創(chuàng)建一個Isolate,并且Dart代碼默認就運行在這個主Isolate上。必要時可以使用spawnUri和spawn兩種方式來創(chuàng)建新的Isolate,在Flutter中,新創(chuàng)建的Isolate由Flutter進行統(tǒng)一的管理。
事實上,F(xiàn)lutter Engine自己不創(chuàng)建和管理線程,F(xiàn)lutter Engine線程的創(chuàng)建和管理是Embeder負責的,Embeder指的是將引擎移植到平臺的中間層代碼。

Flutter 中存在的四大線程:分別為 UI Runner、GPU Runner、IO Runner, Platform Runner (原生主線程) ,

在 Flutter 中可以通過 isolate 或者 compute 執(zhí)行真正的跨線程異步操作。

18、Flutter狀態(tài)管理

Flutter的狀態(tài)可以分為全局狀態(tài)和局部狀態(tài)兩種。常用的狀態(tài)管理有ScopedModel、BLoC、Redux / FishRedux和Provider。

狀態(tài)管理基本都是基于InheritedWidget封裝的用于Widget樹的數(shù)據(jù)傳遞與共享的的一套框架

Provider是繼承于InheritProvider,而InheritProvider本質(zhì)上是一個InheritWidget,所以Provider本質(zhì)上是依托于InheritProvider的機制來實現(xiàn)的widget樹的狀態(tài)共享。

Flutter中Provider和Redux狀態(tài)管理簡述

19、isolate是怎么進行通信和實例化的?

1、isolate實際就是一個隔離的Dart執(zhí)行的上下文環(huán)境(或者容器)
2、isolate是有自己的內(nèi)存和單線程控制的事件循環(huán)
3、isolate之間的內(nèi)存在邏輯上是隔離的,不像Java一樣是共享內(nèi)存的
4、任何Dart程序的并發(fā)都是運行多個isolate的結(jié)果。Dart沒有共享內(nèi)存的并發(fā);

isolate線程之間的通信主要通過port來進行,這個port消息傳遞過程是異步的。

//實現(xiàn)newIsolate與rootIsolate互相通信
import 'dart:io';
import 'dart:isolate';
//定義一個newIsolate
late Isolate newIsolate;
//定義一個newIsolateSendPort, 該newIsolateSendPort需要讓rootIsolate持有,
//這樣在rootIsolate中就能利用newIsolateSendPort向newIsolate發(fā)送消息
late SendPort newIsolateSendPort;

void main() {
  establishConn(); //建立連接
}

//特別需要注意:establishConn執(zhí)行環(huán)境是rootIsolate
void establishConn() async {
  //第1步: 默認執(zhí)行環(huán)境下是rootIsolate,所以創(chuàng)建的是一個rootIsolateReceivePort
  ReceivePort rootIsolateReceivePort = ReceivePort();
  //第2步: 獲取rootIsolateSendPort
  SendPort rootIsolateSendPort = rootIsolateReceivePort.sendPort;
  //第3步: 創(chuàng)建一個newIsolate實例,并把rootIsolateSendPort作為參數(shù)傳入到newIsolate中,為的是讓newIsolate中持有rootIsolateSendPort, 這樣在newIsolate中就能向rootIsolate發(fā)送消息了
  newIsolate = await Isolate.spawn(createNewIsolateContext, rootIsolateSendPort); //注意createNewIsolateContext這個函數(shù)執(zhí)行環(huán)境就會變?yōu)閚ewIsolate, rootIsolateSendPort就是createNewIsolateContext回調(diào)函數(shù)的參數(shù)
  //第7步: 通過rootIsolateReceivePort接收到來自newIsolate的消息,所以可以注意到這里是await 因為是異步消息
  //只不過這個接收到的消息是newIsolateSendPort, 最后賦值給全局newIsolateSendPort,這樣rootIsolate就持有newIsolate的SendPort
  newIsolateSendPort = await rootIsolateReceivePort.first;
  //第8步: 建立連接后,在rootIsolate環(huán)境下就能向newIsolate發(fā)送消息了
  sendMessageToNewIsolate(newIsolateSendPort);
}

//特別需要注意:sendMessageToNewIsolate執(zhí)行環(huán)境是rootIsolate
void sendMessageToNewIsolate(SendPort newIsolateSendPort) async {
  ReceivePort rootIsolateReceivePort = ReceivePort(); //創(chuàng)建專門應答消息rootIsolateReceivePort
  SendPort rootIsolateSendPort = rootIsolateReceivePort.sendPort;
  newIsolateSendPort.send(['this is from root isolate: hello new isolate!', rootIsolateSendPort]);//注意: 為了能接收到newIsolate回復消息需要帶上rootIsolateSendPort
  //第11步: 監(jiān)聽來自newIsolate的消息
  print(await rootIsolateReceivePort.first);
}

//特別需要注意:createNewIsolateContext執(zhí)行環(huán)境是newIsolate
void createNewIsolateContext(SendPort rootIsolateSendPort) async {
  //第4步: 注意callback這個函數(shù)執(zhí)行環(huán)境就會變?yōu)閚ewIsolate, 所以創(chuàng)建的是一個newIsolateReceivePort
  ReceivePort newIsolateReceivePort = ReceivePort();
  //第5步: 獲取newIsolateSendPort, 有人可能疑問這里為啥不是直接讓全局newIsolateSendPort賦值,注意這里執(zhí)行環(huán)境不是rootIsolate
  SendPort newIsolateSendPort = newIsolateReceivePort.sendPort;
  //第6步: 特別需要注意這里,這里是利用rootIsolateSendPort向rootIsolate發(fā)送消息,只不過發(fā)送消息是newIsolate的SendPort, 這樣rootIsolate就能拿到newIsolate的SendPort
  rootIsolateSendPort.send(newIsolateSendPort);
  //第9步: newIsolateReceivePort監(jiān)聽接收來自rootIsolate的消息
  receiveMsgFromRootIsolate(newIsolateReceivePort);
}

//特別需要注意:receiveMsgFromRootIsolate執(zhí)行環(huán)境是newIsolate
void receiveMsgFromRootIsolate(ReceivePort newIsolateReceivePort) async {
  var messageList = (await newIsolateReceivePort.first) as List;
  print('${messageList[0] as String}');
  final messageSendPort = messageList[1] as SendPort;
  //第10步: 收到消息后,立即向rootIsolate 發(fā)送一個回復消息
  messageSendPort.send('this is reply from new isolate: hello root isolate!');
}

代碼摘自https://zhuanlan.zhihu.com/p/355315785

20、Future還是isolate場景分析?

1、如果一段代碼不會被中斷,那么就直接使用正常的同步執(zhí)行就行。
2、如果代碼段可以獨立運行而不會影響應用程序的流暢性,建議使用 Future (需要花費幾毫秒時間)

3、如果繁重的處理可能要花一些時間才能完成,而且會影響應用程序的流暢性,建議使用 isolate (需要幾百毫秒)

下面列出一些使用 isolate 的具體場景:
1、JSON解析: 解碼JSON,這是HttpRequest的結(jié)果,可能需要一些時間,可以使用封裝好的 isolate 的 compute 頂層方法。
2、加解密: 加解密過程比較耗時
3、圖片處理: 比如裁剪圖片比較耗時
4、從網(wǎng)絡(luò)中加載大圖

21、Flutter 是如何與原生Android、iOS進行通信的?

Flutter 通過 PlatformChannel 與原生進行交互,其中 PlatformChannel 分為三種:

BasicMessageChannel :用于傳遞字符串和半結(jié)構(gòu)化的信息。
MethodChannel :用于傳遞方法調(diào)用(method invocation)。
EventChannel : 用于數(shù)據(jù)流(event streams)的通信。

22、Flutter 繪制流程

Flutter只關(guān)心向 GPU提供視圖數(shù)據(jù),GPU的 VSync信號同步到 UI線程,UI線程使用 Dart來構(gòu)建抽象的視圖結(jié)構(gòu),這份數(shù)據(jù)結(jié)構(gòu)在 GPU線程進行圖層合成,視圖數(shù)據(jù)提供給 Skia引擎渲染為 GPU數(shù)據(jù),這些數(shù)據(jù)通過 OpenGL或者 Vulkan提供給 GPU。

23、Flutter 的熱重載

Flutter 的熱重載是基于 JIT 編譯模式的代碼增量同步。由于 JIT 屬于動態(tài)編譯,能夠?qū)?Dart 代碼編譯成生成中間代碼,讓 Dart VM 在運行時解釋執(zhí)行,因此可以通過動態(tài)更新中間代碼實現(xiàn)增量同步。
熱重載的流程可以分為 5 步,包括:掃描工程改動、增量編譯、推送更新、代碼合并、Widget 重建。Flutter 在接收到代碼變更后,并不會讓 App 重新啟動執(zhí)行,而只會觸發(fā) Widget 樹的重新繪制,因此可以保持改動前的狀態(tài),大大縮短了從代碼修改到看到修改產(chǎn)生的變化之間所需要的時間。
另一方面,由于涉及到狀態(tài)的保存與恢復,涉及狀態(tài)兼容與狀態(tài)初始化的場景,熱重載是無法支持的,如改動前后 Widget 狀態(tài)無法兼容、全局變量與靜態(tài)屬性的更改、main 方法里的更改、initState 方法里的更改、枚舉和泛型的更改等。
可以發(fā)現(xiàn),熱重載提高了調(diào)試 UI 的效率,非常適合寫界面樣式這樣需要反復查看修改效果的場景。但由于其狀態(tài)保存的機制所限,熱重載本身也有一些無法支持的邊界。

24、Flutter 熱更新

Android:
利用原生框架更新,實際上就是更新Flutter框架相關(guān)的二進制。Flutter應用發(fā)布出來的產(chǎn)物主要包括 libflutter.so,libapp.so,flutterAssets,這樣,就可以通過Android端原生平臺網(wǎng)絡(luò)請求,動態(tài)下發(fā)并加載這些產(chǎn)物,從而實現(xiàn)熱更新。

iOS:蘋果商店不允許動態(tài)下發(fā)和加載二進制產(chǎn)物,包括動態(tài)庫之類的;

25、Flutter 動態(tài)化方案

基本思路:
通過定義統(tǒng)一的描述語言(JSON來表示結(jié)構(gòu)、樣式和行為),然后通過可視化平臺來拖拽出JSON模板,最后將JSON模板下發(fā)到Flutter App,F(xiàn)lutter App內(nèi)置了JS模板引擎以及DSL解析引擎,由它們將DSL解析映射為Flutter Widgets或者渲染對象。

Flutter動態(tài)化整體架構(gòu)
總體架構(gòu)上分為四大部分。
第一部分是可視化搭建平臺,負責開發(fā)DSL頁面和配置數(shù)據(jù)。
第二部分是低代碼服務中臺,提供組件保存、頁面發(fā)布和數(shù)據(jù)加工能力。
第三部分是面向端的接口服務,包括模板和數(shù)據(jù)接口。
第四部分是端,這塊是核心重點,端上需要支持一整套DSL的解析和渲染映射,并且要做好相應的優(yōu)化,以保證渲染性能和效率。

1、美團外賣Flutter動態(tài)化實踐
2、攜程App 首頁動態(tài)化探索
3、Flutter 動態(tài)化在最右 App 中的實踐
4、Flutter 動態(tài)化熱更新的思考與實踐
5、NOW直播Flutter動態(tài)搜索列表頁實現(xiàn)
6、Flutter動態(tài)化的方案對比及最佳實現(xiàn)-閑魚
7、基于JavaScript 的MXFlutter
8、Flutter App動態(tài)化與可視化搭建方案設(shè)計

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,527評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,687評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,640評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,957評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,682評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,011評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,009評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,183評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,714評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,435評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,665評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,148評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,838評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,251評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,588評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,379評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,627評論 2 380

推薦閱讀更多精彩內(nèi)容

  • Flutter是Google推出的一套開源跨平臺UI框架,可以快速地在Android、iOS和Web平臺上構(gòu)建高質(zhì)...
    GoldMask閱讀 15,565評論 2 48
  • 1. Dart 當中的 「..」表示什么意思? Dart 當中的 「..」意思是 「級聯(lián)操作符」,為了方便配置而使...
    馬修斯閱讀 13,762評論 0 22
  • 1、Dart中var 與 dynamic的區(qū)別 2、const和final的區(qū)別 3、Dart中 ?? 與 ??=...
    永不放棄_8eef閱讀 1,507評論 0 3
  • Dart 當中的 「..」表示什么意思? Dart 當中的 「..」意思是 「級聯(lián)操作符」,為了方便配置而使用。「...
    kadis閱讀 552評論 0 4
  • 概述 Flutter是Google推出的一套開源跨平臺UI框架,可以快速地在Android、iOS和Web平臺上構(gòu)...
    flutter開發(fā)精選閱讀 1,219評論 0 13