做爲忠實與較資深的Android汪, 最近抽出了一些時間研究了一下Google的親兒子Flutter, 尚屬皮毛, 只能算是個簡單的記錄吧.html
Google自2017年第一次提出Flutter, 到2018年Beta, 再加之RN的各類風波與問題, 使得Flutter的熱度不斷上升, 國內很多公司都公佈Flutter在其產品中的應用, 如美團, 閒魚等.android
PS歡迎你們加入Android技術開發交流羣:653583088
本羣提供免費的學習指導以及免費的解答不懂得問題均可以在本羣提出來 以後還會有職業生涯規劃以及面試指導進羣修改羣備註:開發年限-地區-經驗方便解答問題程序員
Flutter做爲跨平臺框架, 經常被人拿出來與React Native, 以及Xamarin進行對比, 除了你們都是跨平臺框架以外且能達到近乎Native的體驗以外, Flutter與這二者的原理大不相同.面試
讓咱們來看看這三者的結構圖吧.canvas
可能有一些複雜, 咱大體解釋一下.網絡
React Native跟Xamarin都是基於mapping native代碼來實現所謂的Native體驗的框架, 只是RN基於JS引擎 + Bridge與native打交道, 而且在運行時進行綁定, 而Xamarin是基於微軟的基於Linux的C#虛擬機mono + JNI與native進行通訊.app
這裏Android與iOS仍是有差異的, 如RN在iOS上JS引擎不支持JIT, 會必定程度影響效率, Xamarin在iOS上能夠直接編譯成iOS平臺能夠執行的程序, 因此在實際運行起來的性能是同樣的, 惟一的差異就是微軟得更快的支持API同步.框架
對於Flutter來講, 因爲他的渲染引擎使用了Skia直繪, 加上基於C++的Dart引擎, 因此在不一樣平臺上沒有差異, 加之其實現了Android Material Design與iOS Cupertino兩套UI組件, 因此即使是自繪組件, 看起來仍是跟原生的一個樣子.less
經過對三種跨平臺引擎的大體瞭解, 咱們能夠看出來, 他們都達到了必定程度的Native體驗, 然則各自都有必定的性能損耗, 好比RN的JS引擎加載JS, 以及Bridge通訊的損耗, Xamarin Mono虛擬機與Java通訊的損耗, 以及Flutter Skia渲染與Native Android渲染的差別等.異步
Android須要在Manfest裏面指定帶有MAIN action與LAUNCHER category的Activity聲明, 而Flutter只須要一行.
void main() => runApp(MyApp());
其中MyApp就是一個普通的Widgets(View).
Flutter沒有View, 與之對應的是Widget, 而且分爲StatelessWidgets與StatefulWidgets, 前者是個靜態View, 後者是動態經過Data來更新的View.
Text( 'I like Flutter!', );
class StatefulText extends StatefulWidget { @override State<StatefulWidget> createState() => _TextState(); } class _TextState extends State<StatefulText> { // Default placeholder text String textToShow = "I Like Flutter"; void _updateText() { setState(() { // update the text textToShow = "Flutter is Awesome!"; }); } @override Widget build(BuildContext context) { ...invoke _updateText } }
其實是由於StatefulWidgets經過調用State
的setState
方法來觸發整個Widgets樹的重繪, 而且在重繪以前會調用傳進去的(){ ... }
block.
實際上Flutter沒有xml了, 而且是經過Widgets的嵌套來實現一個佈局的.
如:
Center
是一個能夠把子View放置在中央的容器.Row
對應的就是LinearLayout + Horizontal, Column
對應的就是LinearLayout + Vertical, 他們都具有一個屬性叫作crossAxisAlignment
, 有點相似gravity
, 來控制子View相對於父View的位置.Expanded
支持一個相似weight的屬性, 叫flex
.Container
是一個具備decoration
屬性的容器, 能夠用來控制背景色, border, margin等等.Stack
有點像是一個特殊的RelatetiveLayout或者ConstraintLayout, children
屬性指定了它的子View, 第一個是Base View, alignment
屬性指定了後面的子View相對於BaseView的位置, 如alignment: const Alignment(0.6, 0.6)
指定了位於BaseView右下角的位置.ListTile
是一個特殊的ListItem, 有三個屬性, 分別是左邊的Icon (leading), 文字 (title), 以及右邊的Icon (trailing).ListView
, GridView
, Card
等等比較熟悉的Widgets.另外有一個相似於咱們Activity的Widgets:
MaterialApp
, 能夠指定theme
, title
, 以及子View home
, 還有更重要的頁面跳轉routes
.MaterialApp( title: 'Welcome to Flutter', home: ..., routes: <String, WidgetBuilder> ..., theme: ThemeData( primaryColor: Colors.white ), )
還有一個相似於Fragment的:
Scaffold
, 中文意思是腳手架
, 它包含一個appBar (ActionBar)與一個body, appBar能夠指定title與actions (相似於action button的點擊事件).Scaffold( appBar: AppBar( title: Text(widget.title), actions: <Widget>[...], ), body: ..., )
答案是沒有... 由於在Flutter看來, Widgets的樹結構是不能夠被更改的, 可是若是想更改, 則是經過StatefulWidgets的方法, 經過setState來更改Data, 觸發Widgets重繪, 從而替換掉以前的Widgets.
Flutter一樣支持, CustomPaint
做爲一個 Widgets就支持傳入一個實現CustomPainter
抽象類的參數, 而CustomPainter
的抽象方法也相似於Android View的onDraw
.
void paint(Canvas canvas, Size size) bool shouldRepaint(CustomPainter oldDelegate)
不用繼承, 而使用相似Android ViewGroup的辦法, 經過組合(composing)與封裝的方法來實現, 經過小Widgets組合成須要的新Widgets.
貌似在講相似於Activity的MaterialApp
的時候劇透了...
就是使用Navigator
與Routes
來實現界面跳轉, 其實是整個Widgets的替換.
routes: <String, WidgetBuilder> { '/a': (BuildContext context) => MyPage(title: 'page A'), '/b': (BuildContext context) => MyPage(title: 'page B'), '/c': (BuildContext context) => MyPage(title: 'page C'), } Navigator.of(context).pushNamed('/b');
實際上仍是須要在Flutter App的Android殼子中註冊這個filter, 而後在FlutterActivity中拿到存下來,
FlutterView初始化後再經過Bridge, 官方叫MethodChannel
從Java裏獲取,進行下一步邏輯.
能夠看個簡單的例子.
new MethodChannel(getFlutterView(), "app.channel.shared.data").setMethodCallHandler( new MethodCallHandler() { @Override public void onMethodCall(MethodCall call, MethodChannel.Result result) { if (call.method.contentEquals("getSharedText")) { result.success(sharedText); sharedText = null; } } }); getSharedText() async { var sharedData = await platform.invokeMethod("getSharedText"); if (sharedData != null) { setState(() { dataShared = sharedData; }); } }
這個Flutter有徹底對應的辦法, 並且用起來很方便, 結合以前說的頁面跳轉:
Map xxx = await Navigator.of(context).pushNamed('/xxx'); Navigator.of(context).pop({xxx});
Flutter有點像JS, 是一個單線程模式, 因此只是經過模擬來構建簡單的異步, 關鍵字就是相似於kotlin coroutines同樣, 經過await
+async
來處理.
如:
loadData() async { response = await http.get(xxx); setState(() {xxx}); }
可是因爲它的單線程, 因此沒法作很長的阻塞操做, 像http請求的延遲正常狀況可能都是毫秒級的, 可是數據的處理等, 可能就得秒級了.
這也是RN在線程方面的作android程序的一個痛點, Flutter採用了比較容易想到的曲線救國的辦法, 提供了一個叫Isolate
的對象, 它實際是一個基於socket的數據通道, 至關於把數據放在一個獨立的進程進行處理, 而後再經過socket發送回程序進程, 還記得進程間通訊辦法之一的管道
嗎...
自帶了http庫, 直接http.get(url)
, 在線程部分的代碼實例裏也有涉及.
經過相似gradle的文件pubspec.yaml
引入.
dependencies: ... http: ^0.12
^
表示不升大版本, 並取最新版本, 比gradle的+要範圍更小.
Flutter有一個widget叫作ProgressIndicator
, 好比咱們指望有一個轉圈圈的Loading界面在數據加載出來以前.
咱們就能夠經過StatefulWidgets, 根據數據, 或者List Widgets的個數 (若是是顯示一個List的話)來判斷是否顯示Loading, 使用子類CircularProgressIndicator
, 來替換頁面的Widgets.
固然也是經過setState(() {...})來觸發界面刷新的, 能夠在initState()內觸發加載數據的異步操做.
這個有點像iOS了, 即有1x,2x,3x:
images/my_icon.png // Base: 1.0x image images/2.0x/my_icon.png // 2.0x image images/3.0x/my_icon.png // 3.0x image
不同的一點還須要添加到相似gradle的文件pubspec.yaml
裏.
assets: - images/my_icon.jpeg
Flutter沒有像Android的string.xml
的東西, 目前來講最好的就就是存成靜態字符串.
class Strings { static String welcomeMessage = "Welcome To Flutter"; } Text(Strings.welcomeMessage)
前面說網絡庫, 圖片資源的時候提到過, 提供了一個叫pubspec.yaml
的文件
以前作過類比, 如MaterialApp
有點相似於Activity, 而Scaffold
都點相似Fragment, 實際上他們兩個都是Flutter的Widgets, 也就是說其實只有View的概念了.
Flutter有一個叫作WidgetsBinding
的能夠提供相似生命週期的回調.
四種狀態inactive
(iOS專用), paused
(至關於onPause, 退後臺), resumed
(至關於onPostResume, 到前臺), suspending
(android專用, 至關於onStop).
通常在StatefulWidgets的State中註冊與反註冊.
@override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); } @override void dispose() { WidgetsBinding.instance.removeObserver(this); super.dispose(); }
Flutter沒有ScrollView, 合併到了ListView, 經過ListView.builder建立的ListView提供了View複用的邏輯.
ListView.builder( itemCount: widgets.length, itemBuilder: (BuildContext context, int position) { return Text(xxx); }))
其中itemBuilder有點像Android ListView的getView, 官方文檔說它會自動回收Element給你, 可是事實上每次你都須要根據position生成新的Widgets, 因此呢應該是Flutter在內部回收了以前的Widgets並在你從新建立的時候又用上了.
BTW, 經過ListView構造來顯示就不具有這種特性, 因此大量數據須要用Builder.
由於它實際上仍是藉助了Android程序的殼子, 因此若是AndroidManifect定義了android:configChanges="orientation|screenSize"
, 則Flutter會本身hanlde.
Flutter提供了GestureDetector
, 它至關於一個Container, 將咱們指望接收手勢的Widgets放進去, 再實現事件回調就好了.
GestureDetector( child: FlutterLogo( size: 200.0, ), onTap: () { print("tap"); }, )
它一樣支持其餘的手勢, 如onDoubleTap
等等等.
首先須要在pubspec.yaml
裏面配置須要的字體庫:
fonts: - family: MyCustomFont fonts: - asset: fonts/MyCustomFont.ttf - style: italic
而後在Text的style
屬性進行配置.
Text( 'This is a custom font text', style: TextStyle(fontFamily: 'MyCustomFont'), )
對於輸入框的Hint基本一致, 可能就是換了個名字, 一看便知.
TextField( decoration: InputDecoration(hintText: "This is a hint", errorText: _getErrorText()), )
Flutter在視圖渲染上另闢蹊徑, 性能優點凸顯, 在跨平臺框架屬於一匹黑馬, 又有Google撐腰, 值得在Mobile勤耕多年的同窗入手.
因爲做者曾經從事過2年的Webkit開發工做, 拜讀了Flutter的渲染模式, 很像是Webkit/Chrome/Blink的思路, 經過查證, 起草者確實有大批一樣的人, 若是你尚未入坑RN, 或許Flutter能夠做爲跨平臺方案學習的首選哦.
一樣Google本身也有不少Plugin去支持更多擴展功能, 如GPS, Camera, SharePreference, Database. 還例如Firebase這種親兒子級的服務也是全面支持Flutter.
固然也能夠本身去開發須要的Plugin來適配須要的功能, 基於的技術就是上面有提的MethodChannel
, NDK的支持也是一樣的道理.
PS歡迎你們加入Android技術開發交流羣:653583088 本羣提供免費的學習指導以及免費的解答不懂得問題均可以在本羣提出來 以後還會有職業生涯規劃以及面試指導進羣修改羣備註:開發年限-地區-經驗方便解答問題