[譯] Flutter 組件究竟是個啥?

Flutter 組件究竟是什麼?

如下對 Flutter 組件的解釋是個人我的觀點,並非 Flutter 框架的組件或相關領域的官方解釋。想要了解有關 Flutter 團隊對其見解,請參閱 Flutter 的官方文檔。前端

Flutter 是一個移動端的 UI 框架,它使 UI 開發變得更有趣、快速和簡單。但從傳統的 Android 和 iOS 到 Flutter,感受真的難以想象。之因此感到驚訝是由於咱們從可變的、生命週期長的 ViewUIView,變成了這些不可改變的、生命週期短的 Widget。它們究竟是什麼呢,它們又爲何高效呢?android

最近發表了一篇關於 Widget 與 Element 和 RenderObjects 的關係的文章。我很是推薦這篇文章,建議你繼續深刻研究,直到你能夠徹底理解其內容爲止。但對於那些已經迷失在 Widget 的人,請容許我提供一些可能有幫助的解釋。ios

傳統的 View 和視圖模型

我一直支持在移動 UI 開發中使用視圖模型。git

不管你是在 Android 仍是 iOS 上工做,都要考慮自定義的 ViewUIView,稱爲ListItemView。這個 ListItemView 在左邊顯示一個圖標,而後在圖標右邊顯示字幕上方的標題,最後在右側顯示一個可選附件:github

在定義這個自定義 View 的時候,你能夠將每一個對 View 的描述設爲獨立屬性:編程

myListItemView.icon = blah;  
myListItemView.title = 「blah」;  
myListItemView.subtitle = 「blah」;  
myListItemView.accessory = blah;
複製代碼

從技術上來講,這沒什麼問題,可是帶來了架構成本。經過獨立定義每一個配置,你對其描述的 Object 須要引用你的 View,以便它能夠配置每一個屬性。可是,若是你使用視圖模型,那麼你的描述 Object 能夠在不引用 View 的狀況下運行,這意味着描述 Object 能夠進行單元測試,而且它避免了對具體 View 的編譯時依賴性:後端

class ListItem {  
  final Icon icon;  
  final String title;  
  final String subtitle;  
  final Icon accessory;  
  ...  
}

// 使用 Presenter 建立一個新的視圖模型。
myListItem = myPresenter.present();

// 傳遞視圖模型到 View 來渲染新的視圖外觀。 
myListItemView.update(myListItem);
複製代碼

這種使用視圖模型的基本原理,與 Flutter 無關,但與傳統的 View 相比,理解視圖模型是很是重要的。視圖模型是一個不可變的配置,須要應用於生命週期長的,可變的 Viewbash

傳統 Android 和 iOS 中的依賴關係以下:架構

MyAndroidView -> MyAndroidViewModel

MyiOSUIView -> MyiOSViewModel
複製代碼

換句話說,在傳統的 Android 和 iOS 中,咱們主要使用可變的,生命週期長的 View(和 UIView)。咱們經過使用那些生命週期長的 View(和UIViewObject來定義佈局 XML,Storyboard 和可編程的佈局。而後,咱們不按期會傳遞新的視圖模型來改變它們的界面。框架

如今,讓咱們來談談 Flutter。

Flutter 顛覆了這種依賴關係

與其使用可變的、生命週期長,且會不按期接收新的視圖模型的 View,不如咱們只使用不可變視圖模型,來配置可變的、生命週期長的 View

之前是:

MyView -> MyViewModel
複製代碼

如今改成:

MyViewModel -> MyView
複製代碼

就像這樣,簡單來講,咱們剛剛發明了 Flutter 的組件系統:

MyWidget -> MyElement  
MyWidget -> MyRenderObject
複製代碼

這些組件都是不可變的,其中包含了許多用於配置渲染內容方式的屬性:

// 這個組件看起來確定很像一個視圖模型,不是嗎?
new Container(  
  width: 50.0,  
  height: 50.0,  
  padding: EdgeInsets.all(16.0),  
  color: Colors.black,  
);

// Flutter 組件和傳統視圖有一個很大的區別  
// 就是這些組件一樣能夠  
// 實例化生命週期長的視圖 
final mutableSubtree = myContainer.createElement();  
final mutableRender = myContainer.createRenderObject();
複製代碼

但爲何這些組件能建立這兩樣東西呢?我認爲,組件應該只能建立一個生命週期長的視圖?

在 Flutter 中,父/子的概念獨立於渲染而存在。在 iOS 和 Android 中,父/子關係與繪製到屏幕的概念是一致的。

例如,在 Android 中,ViewGroup 須要負責:

// 父/子關係  
myViewGroup.addView(...);  
myViewGroup.removeView(...);

// 以及

// 佈局和繪製  
myViewGroup.measure(...);  
myViewGroup.layout(...);  
myViewGroup.draw(...);
複製代碼

在 Flutter,咱們有

// Element 來管理父/子關係  
myElement.mount(); // 這建立並添加子級組件 
myElement.forgetChild(...); // 移除子級組件

// 用 RenderObjects 來佈局和繪製:
myRenderObject.layout();  
myRenderObject.paint();
複製代碼

因此說,儘管 Flutter 中的組件負責建立一個 Element 和一個 RenderObject,但這兩個 Object的組合等同於 Android 單個 ViewGroup 相同的功能。

所以,在 Flutter 中,咱們使用能夠配置 View 的視圖模型,而不是使用視圖模型配置的 View。這種關係是顛倒的。

爲何說這種顛倒關係是個大問題

顛倒視圖模型的關係,以及若是一個視圖模型知道如何實例化相對應的一個長生命週期的視圖,你可能會感到特別奇怪。但 Flutter 向咱們展現的是,經過顛倒這種關係,咱們實現了以聲明方式組合用戶界面的能力。

在我看來,Flutter 正在作的事情,從根本上說,像是正在接近繪製像素的特定領域語言。

特定領域的語言是幾乎全部開發人員的終極目標。若是你正在爲航空業開發應用,那麼你將花費大量時間構建行業特定術語的實現,如航班清單、登機牌、座位分配和會員身份。你在利用較低級別的語言語義,來表示這些術語在你的行業中的含義。然而,理想狀況下,在某些時候,開發人員將中止使用這種較低級別的構造方式,他們將開始使用像 FlightManifestBoardingPassSeatAssignment 這樣的 Object 來實現整個應用。

但並不是每一個問題都是商業問題。一些問題是技術問題,例如渲染。渲染用戶界面自己就是一個問題範疇。Flutter 正經過設計出用於渲染用戶界面的一種特定領域的語言來解決此問題。就像 SQL 是用於搜索信息聲明式的領域特定語言同樣,Flutter 的組件系統正在成爲用於組合用戶界面的聲明式的領域特定語言。這能夠經過在外部放置不可變視圖模型,同時將可變視圖限制在內部來實現。

但願這個視角,能夠幫助你理解和欣賞 Flutter 中的組件。可是若是沒有,只要你繼續使用 Flutter 的 API,最終你也會體驗出箇中的妙處。

若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章
相關標籤/搜索