用前端 最舒服的躺姿 “搞定” Flutter (元件篇)

ryan730發表於1970-01-01

前言

要說2018年最火的跨端技術,當屬於 Flutter 莫屬,應該沒人質疑吧。一個新的技術的趨勢,最明顯的特徵,就是它一定想把“前浪”拍死在沙灘上。這個前浪,就是”react Native”,”weex”。目前隨便在搜尋引擎上 搜尋”Flutter reactNative”,就全是這兩個技術的對比,評測。

image.png

一股股濃濃 : 不服來 “掰” 啊 !!!的味道。

是的,錯過了react Native, weex 這些 “炸” 翻前端的技術,不能在錯過 Flutter 了,這年頭,你不會一門,跨端技術,怎麼好意思說自己是【前端】。

timg.gif

Flutter是谷歌的移動UI框架,可以快速在iOS和Android上構建高質量的原生使用者介面。 Flutter可以與現有的程式碼一起工作。在全世界… …

image.png

好了, 這些,大家早就知道了,來點實在的!!!

話說隔壁師兄,“閒魚” 是最早一批與谷歌展開合作,並在重要的商品詳情頁中使用flutter技術上線的BU。一路走來,積累了大量的開發經驗。“閒魚” flutter 相關文章的都很有深度,足見功力。

都是一篇篇的深度好文,讀完收益匪淺,但是對於剛接觸 Flutter 的web前端同學來說還是

image.png
image.png
2E803E03-8FE3-4F5B-9943-51F9C54B3BF1.png

好了,回到標題,筆者作為一名傳統 web前端,想從前端最熟悉的視角 “躺” 著把 Flutter 瞭解一遍,不要敬仰,平視它!!!先從興趣開始。

用前端 最舒服的躺姿 “搞定” Flutter (元件篇)

正文

Flutter 環境的搭建,其實有很多資源可以參考。這裡就不累述了(知道有很多坑,在後續文章中,有機會把個人遇到的坑彙總一下 )。

有興趣可以參考 flutter安裝環境的搭建 , 在這裡建議各位,一定要自己親自搭一下環境,跑一下官方demo, 小馬過河,焉知深淺,自己定的位才是最準確的。

有了環境,先配置編輯器

然後 建立一個Flutter專案

專案建立完成,可以先用 flutter run 跑一下。

flutter run 複製程式碼

好了,跑起來了吧,你會看到一個計數的官方示例,點選加號圖片可以做加運算。

這時候我們看專案的project 目錄裡 有一個入口檔案叫 main.dart。然後開啟 main.dart 就像下面這樣:

image.png

( 為什麼你們看到程式碼比我的長,因為我摺疊了!!! ) 這不是重點,重點是每個類繼承的都是一個尾號為Widget 的字元。

聰明的你一定會覺得 WidgetFlutter 有著某種神祕的聯絡。

“Binggo!”,是的,Flutter 有兩個重型武器,一個叫 Dart ,另一個就是 Widget 了。

Dart 一切皆來自 Object, Flutter 的元件皆來自 Widget

關於強大的Dart,今天暫且不表,後面有時間可以獨立篇幅來聊聊Dart。

先祭出一張 Flutter 的架構老圖。

image.png

Flutter 的世界裡,包括views,view controllers,layouts等在內的概念都建立在Widget之上。

WidgetFlutter 元件的抽象描述。所以掌握Flutter的基礎就是學會使用 Widget開始。

在Flutter介面渲染過程分為三個階段:佈局、繪製、合成,佈局和繪製在Flutter框架中完成,合成則交由引擎負責:

640.jpeg

Flutter 通過組合、巢狀不同型別的控制元件,就可以構建出任意功能、任意複雜度的介面。

  • 它包含的最主要的幾個類有:
class WidgetsFlutterBinding extends BindingBase with GestureBinding, ServicesBinding, SchedulerBinding,PaintingBinding, RendererBinding, WidgetsBinding { 
...
}abstract class Widget extends DiagnosticableTree {
...
}abstract class StatelessWidget extends Widget {
...
}abstract class StatefulWidget extends Widget {
...
}abstract class RenderObjectWidget extends Widget {
...
}abstract class Element extends DiagnosticableTree implements BuildContext {
...
}abstract class RenderObjectElement extends Element {
...
}class StatelessElement extends ComponentElement {
...
}class StatefulElement extends ComponentElement {
...
}複製程式碼

上面這些類的主要作用如下:

  • 基於Flutter控制元件系統開發的程式都需要使用WidgetsFlutterBinding,它是Flutter的控制元件框架和Flutter引擎的膠水層。
  • Widget就是所有控制元件的基類,它本身所有的屬性都是隻讀的。
  • RenderObjectWidget所有的實現類則負責提供配置資訊並建立具體的RenderObjectElement。
  • Element是Flutter用來分離控制元件樹和真正的渲染物件的中間層,控制元件用來描述對應的element屬性,控制元件重建後可能會複用同一個element。
  • RenderObjectElement持有真正負責佈局、繪製和碰撞測試(hit test)的RenderObject物件。
  • StatelessWidget和StatefulWidget並不會直接影響RenderObject建立,只負責建立對應的RenderObjectWidget
  • StatelessElement和StatefulElement也是類似的功能。

很複雜是吧,先不用管,簡單表述Widget是這樣的:

Widget = 樣式(css) + 標記語義(標籤) + 元件化(官方) + 資料繫結(props)

“什麼?這不就是 react 嗎?”

對, React 的概念和 FlutterWidget 是有相通性的。

“既然有react的概念,難道還有state,setState嗎?“

又對, Flutter 還真有 類似 state 狀態機制的概念,而且也確實有 setState 的方法。

“dome裡有兩個基類,StatelessWidget 和 StatefulWidget 是做什麼用的?”

StatelessWidget 和 StatefulWidget,這裡兩個類特別重要,幾乎所有的元件都是基於他們建立的。

StatelessWidget 是狀態不可變的widget,稱為 無狀態widget。初始狀態設定以後就不可再變化。如果需要變化需要重新建立。

StatefulWidget 可以儲存自己的狀態,稱為 有狀態widgetFlutter 首先儲存了初始化時建立的State,狀態是通過改變State,來重新構建 Widget 樹來進行UI變化。改變狀態的方法,就是我們用的最多的神器”setState”,而單純改變資料是不會引發UI改變的,這個概念和我們的 React 一樣一樣的。

如果你是初次接觸 Flutter 可以不用記憶這麼多元件基類,只用記住以下式子就可以, 不誇張的說,熟悉這個式子就可以開發 Flutter 專案了:

c4d850e3c04dd51db6cad9112f885e6b.png

拆解

圍繞著widget的構成,我們來拆解分析一下,標記語義,樣式,元件化。

標記語義

為什麼不稱為 “模版”,“標籤”,“element” ,而叫”標記語義”,是因為flutter的 widget 結構並不只是 “模版”,“標籤”,“element”。widget 描述結構更像是 React 的虛擬dom階段,濃縮了相關上下文,以物件化的結構展示。

我們先來看看,React 建立出來的虛擬dom結構( 虛擬碼 ):

var newTree = el('div', {'id': 'container'
}, [ el('h1', {style: 'color: red'
}, ['simple virtal dom']), el('p', ['Hello, virtual-dom']), el('ul', [el('li'), el('li')])])複製程式碼

再來看看,flutter 用Dart 建立的 widget 程式碼結構:

Widget build(BuildContext context) { 
return new Column( children: <
Widget>
[ new Container( padding: new EdgeInsets.only(top:100.0), child: new Text('這是一個元件') ), new Container( decoration: new BoxDecoration(border: new Border.all(width:1.0,color: Colors.blue)), padding: new EdgeInsets.all(20.0), child: new Text('來自輸入框:'+active) ) ], );

}
複製程式碼

是不是這樣看就熟悉很多了。

注:很多前端er 會不習慣這中書寫方式,目前有開發者在社群推動,在編譯前,使用jsx標籤,編譯後再解析成標記樹,比如這個DSX設計的提案。

雖然jsx->
標記樹 還只是提案,但其實可以幫助我們更容易理解,此提案想表達的樣式像這樣:

class MyScaffold extends StatelessWidget { 
build(context) {
return <
Material>
<
Column>
<
MyAppBar title={<
Text text='Example title' style={Theme.of(context).primaryTextTheme.title
}, />

} />
<
Expanded>
<
Center>
<
Text text='Hello, world!'/>
<
/Center>
<
/Expanded>
<
/Column>
<
/Material>
;

}
}複製程式碼

上面這段 jsx 要是用 Dart 來寫是什麼樣的?如下:

class MyScaffold extends StatelessWidget { 
@override Widget build(BuildContext context) {
return Material( child: Column( children: <
Widget>
[ MyAppBar( title: Text( 'Example title', style: Theme.of(context).primaryTextTheme.title, ), // Text ), // MyAppBar Expanded( child: Center( child: Text('Hello, world!'), ), // Center ), // Expanded ], // <
Widget>
[] ), // Column );
// Material
}
}複製程式碼

雖然 Flutter 與 react 這麼類比多少有些牽強,但可以個人總結一些方法,方便理解:

  • 開頭大寫的類名 相當於 jsx 的標籤名
  • child 相當於 jsx 的 子標籤+佈局
  • children 相當於 jsx 的 群組子標籤(和child還是有區別的)
  • 其他屬性相當於 jsx 的 props
  • 大家有沒有注意, 第二段dart 語法結尾都會帶上 “ // ” 註釋符號,這個是編輯器IDE在識別是 Flutter 專案後,自動追加上去的,像 jsx 語言的標籤封閉,方便發現標註的起始節點。

樣式

對於 Flutter 樣式的理解,可以檢視官方的這篇文件,也是同樣用類比的方式,很直觀的瞭解,HTML、css樣式和 flutter 之間的聯絡.可以參考這裡:flutter.io/docs/get-st…flutterchina.club/web-analogs…

筆者摘選其中樣例的重點部分,對比展示來說明( 由於篇幅問題, 父子關係css 結構,用tab方式來表示 ):

左邊是css 寫法,右邊是 flutter-dart寫法

文字樣式:

padding: 10px;
border-radius: 5px;
color: black;
background: beige;
width:48%;
“>

.demo1 {background-color: #e0e0e0;
width: 320px;
height: 240px;
font: 900 24px Georgia;
letter-spacing: 4px;
text-transform: uppercase;

}

複製程式碼

複製程式碼

相關文章