要說2018年最火的跨端技術,當屬於 Flutter
莫屬,應該沒人質疑吧。一個新的技術的趨勢,最明顯的特徵,就是它必定想把「前浪」拍死在沙灘上。這個前浪,就是"react Native","weex"。目前隨便在搜索引擎上 搜索"Flutter reactNative",就全是這兩個技術的對比,評測。javascript
是的,錯過了react Native
, weex
這些 「炸」 翻前端的技術,不能在錯過 Flutter
了,這年頭,你不會一門,跨端技術,怎麼好意思說本身是【前端】。css
Flutter是谷歌的移動UI框架,能夠快速在iOS和Android上構建高質量的原生用戶界面。 Flutter能夠與現有的代碼一塊兒工做。在全世界... ... html
話說隔壁師兄,「閒魚」 是最先一批與谷歌展開合做,並在重要的商品詳情頁中使用flutter技術上線的BU。一路走來,積累了大量的開發經驗。「閒魚」 flutter 相關文章的都頗有深度,足見功力。前端
Flutter
的web前端同窗來講仍是好了,回到標題,筆者做爲一名傳統 web前端,想從前端最熟悉的視角 「躺」 着把 Flutter
瞭解一遍,不要敬仰,平視它!!!先從興趣開始。java
Flutter 環境的搭建,其實有不少資源能夠參考。這裏就不累述了(知道有不少坑,在後續文章中,有機會把我的遇到的坑彙總一下 )。react
有興趣能夠參考 flutter安裝環境的搭建 , 在這裏建議各位,必定要本身親自搭一下環境,跑一下官方demo, 小馬過河,焉知深淺,本身定的位纔是最準確的。git
有了環境,先配置編輯器。github
而後 建立一個Flutter項目。web
項目建立完成,能夠先用 flutter run 跑一下。segmentfault
flutter run
複製代碼
好了,跑起來了吧,你會看到一個計數的官方示例,點擊加號圖片能夠作加運算。
這時候咱們看項目的project 目錄裏 有一個入口文件叫 main.dart。而後打開 main.dart 就像下面這樣:
( 爲何大家看到代碼比個人長,由於我摺疊了!!! ) 這不是重點,重點是每一個類繼承的都是一個尾號爲Widget
的字符。
聰明的你必定會以爲 Widget
和 Flutter
有着某種神祕的聯繫。
「Binggo!",是的,Flutter
有兩個重型武器,一個叫 Dart
,另外一個就是 Widget
了。
Dart
一切皆來自 Object
, Flutter
的組件皆來自 Widget
。關於強大的Dart,今天暫且不表,後面有時間能夠獨立篇幅來聊聊Dart。
先祭出一張 Flutter
的架構老圖。
在 Flutter
的世界裏,包括views,view controllers,layouts等在內的概念都創建在Widget之上。
Widget
是 Flutter
組件的抽象描述。因此掌握Flutter的基礎就是學會使用 Widget
開始。
在Flutter界面渲染過程分爲三個階段:佈局、繪製、合成,佈局和繪製在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 { ... }
複製代碼
上面這些類的主要做用以下:
Widget = 樣式(css) + 標記語義(標籤) + 組件化(官方) + 數據綁定(props)
「什麼?這不就是 react 嗎?"
對, React 的概念和
Flutter
的Widget
是有相通性的。
「既然有react的概念,難道還有state,setState嗎?「
又對, Flutter 還真有 相似 state 狀態機制的概念,並且也確實有 setState 的方法。
「dome裏有兩個基類,StatelessWidget 和 StatefulWidget 是作什麼用的?」
StatelessWidget 和 StatefulWidget,這裏兩個類特別重要,幾乎全部的組件都是基於他們建立的。
StatelessWidget 是狀態不可變的widget,稱爲
無狀態widget
。初始狀態設置之後就不可再變化。若是須要變化須要從新建立。
StatefulWidget 能夠保存本身的狀態,稱爲
有狀態widget
。Flutter
首先保存了初始化時建立的State,狀態是經過改變State,來從新構建Widget
樹來進行UI變化。改變狀態的方法,就是咱們用的最多的神器"setState",而單純改變數據是不會引起UI改變的,這個概念和咱們的 React 同樣同樣的。
Flutter
能夠不用記憶這麼多組件基類,只用記住如下式子就能夠, 不誇張的說,熟悉這個式子就能夠開發 Flutter 項目了:爲何不稱爲 「模版」,「標籤」,「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 這麼類比多少有些牽強,但能夠我的總結一些方法,方便理解:
Flutter
項目後,自動追加上去的,像 jsx 語言的標籤封閉,方便發現標註的起始節點。對於 Flutter
樣式的理解,能夠查看官方的這篇文檔,也是一樣用類比的方式,很直觀的瞭解,HTML、css樣式和 flutter
之間的聯繫. 能夠參考這裏: flutter.io/docs/get-st… flutterchina.club/web-analogs…
筆者摘選其中樣例的重點部分,對比展現來講明( 因爲篇幅問題, 父子關係css 結構,用tab方式來表示 ):
左邊是css 寫法,右邊是 flutter-dart寫法
.demo1 { background-color: #e0e0e0; width: 320px; height: 240px; font: 900 24px Georgia; letter-spacing: 4px; text-transform: uppercase; }
複製代碼複製代碼
var demo1 = new Container(
child: new Text(
"Lorem ipsum".toUpperCase(), // 對應 左邊的文本轉換大小寫
style: new TextStyle( // 對應 左邊的 font
fontSize: 24.0
fontWeight: FontWeight.w900,
fontFamily: "Georgia",
letterSpacing: 4.0,
),
),
width: 320.0, // 對應 左邊的 width
height: 240.0, // 對應 左邊的 height
color: Colors.grey[300], // 對應 左邊的 background-color
);
複製代碼
.demo2 { display: flex; align-items: center; justify-content: center; }
複製代碼複製代碼
var demo2 = new Container(
child: new Center( // 對應左邊的整個 flex 屬性
child: new Text("Lorem ipsum")
)
);
);
複製代碼
.container{ width:300px .demo3 { width: 100%; max-width: 240px; } }
複製代碼複製代碼
// 對於嵌套容器,若是父級的寬度小於子級寬度,則子級容器將自行調整大小以匹配父級。
var container = new Container(
child: new Center(
child: new Text("Lorem ipsum"),
decoration: new BoxDecoration( ... ), // constraints屬性,建立一個新的BoxConstraints來設置minWidth或maxWidth
width: 240.0, // 對應左邊的 max-width
),
width:300.0
),
複製代碼
.box { transform: rotate(15deg); }
複製代碼複製代碼
var container = new Container( // gray box
child: new Transform( // 對應左邊的 transform
child: new Container( ... ),
alignment: Alignment.center, // 對應左邊的 transform
transform: new Matrix4.identity() // 對應左邊的 transform
..rotateZ(15 * 3.1415927 / 180),
),
)
);
複製代碼
.box { transform: scale(1.5); } 複製代碼複製代碼
var container = new Container( // gray box child: new Transform( // 對應左邊的 transform child: new Container( ... ), alignment: Alignment.center, // 對應左邊的 transform的中心 transform: new Matrix4.identity() // 對應左邊的 transform ..scale(1.5), ), ) ); 複製代碼複製代碼
.greybox {
position: relative;
.redbox {
position: absolute;
top: 24px;
left: 24px;
}
}
複製代碼
var container = new Container( // grey box child: new Stack( // 相對跟容器位置 relative children: [ new Positioned( // 相對父容器位置 absolute child: new Container( ... ), left: 24.0, top: 24.0, )], ) ); 複製代碼複製代碼
.redbox { background: linear-gradient(180deg, #ef5350, rgba(0, 0, 0, 0) 80%); }
複製代碼複製代碼
var container = new Container( // grey box child: new Center( child: new Container( // red box child: new Text( ... ), decoration: new BoxDecoration( gradient: new LinearGradient( // 對應左邊的 background: linear-gradient begin: const Alignment(0.0, -1.0), end: const Alignment(0.0, 0.6), colors: [ const Color(0xffef5350), const Color(0x00ef5350) ], ), ) ), ) ); 複製代碼複製代碼
.box { border-radius: 8px; } 複製代碼複製代碼
var container = new Container( // grey box child: new Center( child: new Container( // red circle child: new Text( ... ), decoration: new BoxDecoration( borderRadius: new BorderRadius.all( const Radius.circular(8.0), ), ) ), ) ); 複製代碼複製代碼
.box { box-shadow: 0 2px 4px rgba(0, 0, 0, 0.8), 0 6px 20px rgba(0, 0, 0, 0.5); } 複製代碼複製代碼
var container = new Container( // grey box child: new Center( child: new Container( // red box child: new Text( ... ), decoration: new BoxDecoration( boxShadow: [ // 對應左邊的 box-shadow new BoxShadow ( color: const Color(0xcc000000), offset: new Offset(0.0, 2.0), blurRadius: 4.0, ), new BoxShadow ( color: const Color(0x80000000), offset: new Offset(0.0, 6.0), blurRadius: 20.0, ), ], ) ), ) ); 複製代碼複製代碼
.circle { text-align: center; width: 160px; height: 160px; border-radius: 50%; } 複製代碼複製代碼
var container = new Container( // grey box child: new Center( child: new Container( // red circle child: new Text( ... ), decoration: new BoxDecoration( color: Colors.red[400], shape: BoxShape.circle, // 畫圓和圓角不太同樣,用的是BoxShape繪製圖像能力 ), width: 160.0, height: 160.0, ), ) ); 複製代碼複製代碼
// css 的內聯結構 .greybox { font: 900 24px Roboto; .redbox { em { font: 300 48px Roboto; font-style: italic; } } } 複製代碼複製代碼
var container = new Container( // grey box child: new Center( child: new Container( // red box child: new RichText( text: new TextSpan( style: bold24Roboto, children: [ new TextSpan(text: "Lorem "), // 繼承內聯樣式 new TextSpan( text: "ipsum", style: new TextStyle( // 具備自定義樣式的單獨樣式 fontWeight: FontWeight.w300, fontStyle: FontStyle.italic, fontSize: 48.0, ), ), ], ), ), ), ) ); 複製代碼複製代碼
官方的widgets目錄
點擊每個card後,裏面還有子card, 以一個展開的緯度來看,是這樣的:
這真是一個龐大的組件系統,這不是社區提供的,而是官方的。 flutter 團隊事無鉅細的實現了目前市面上基本上能見到的組件方式和類型。 我的認爲這樣作優缺點並存的。 先說缺點:
1.學習成本增長,曲線也仍是比較陡峭的。
2.組件直接的繼承關係路徑比較零亂,好比 繼承自 Widget 是這些大類道還清晰: PreferredSizeWidget ProxyWidget RenderObjectWidget StatefulWidget StatelessWidget。 可是,StatelessWidget和StatefulWidget的子類就過於平行化了,名稱上晦澀,沒有抽象架構化,分層或者塔型級別。
StatelessWidget:
![]()
StatefulWidget
![]()
Flutter
官方就不建議使用第三方組件,這個也未有定論。再說優勢:
1.能夠阻止之後輪子氾濫,在團隊僵持不下使用哪一個輪子庫時,最好的理由就是「官方」二字,由於使用官方,能夠弱化和規避一些問題,好比: 版本迭代不一樣步,性能瓶頸,規範不統一等問題,也能快速支持官方的輔助工具。
2.正是因爲官方的標準劃一,爲自動化編譯,自動化搭建,測試調優,可視化帶來便利,甚至爲Ai前端模型化業務場景,提供支撐,都說前端的組件像搭樂高
,Flutter
就是顆粒標準統一的樂高
。
3.天下之勢,分久必合,合久必分。
前端在經歷了 flash
一統pc頁面的富媒體時代,後被喬布斯和H5瓦解;
以後又有H5
的繁盛和框架、語法、構建模式的亂戰;
再到有「別再更新,老子學不動"的呼聲下,但願有個一統江湖的跨平臺系統出現;
讓開發者更專一於,提升業務內容,創造 「新, 酷,炫」 的展示形勢上下功夫。
也許,真有一個前端江湖的王者的誕生。
實際的 Flutter Widget
要複雜的更多的多,在眼花繚亂的 Widget
組件中,筆者想用本身的一些理解,去粗取精,來逐步理解 Flutter
這個新傢伙 ,文中有理解不到位的地方,歡迎你們指正。 筆者團隊也正在開發一套《Flutter GO》的APP,幫助你們熟悉複雜的 Flutter Widget
。
更多學習 Flutter的小夥伴,歡迎入QQ羣 Flutter Go :679476515
《Flutter GO》項目地址 alibaba/flutter-go