本文首發於微信公衆號——世界上有意思的事,搬運轉載請註明出處,不然將追究版權責任。交流qq羣:859640274java
你們很久不見,又有一個多月沒有發文章了,因此今天發一篇來刷刷存在感。最近 Flutter 很是火,我這一個月也不斷的找資料來學習 Flutter。通過一段時間的摸索,我發現如今不少資料都很是」水「。各類 Dart 入門、Flutter 入門、Flutter 資料收集,徹底沒有任何有趣的東西。我不想去寫重複而無聊的文章,因此本篇文章會拋轉引玉的探討一些在學習和開發 Flutter 的過程當中碰見的問題和解決方案。android
閱讀須知:ios
本文分爲如下章節,讀者可按需閱讀:git
天下事有難易乎?爲之,則難者亦易已!程序員
Q:Flutter 怎麼學?github
A:這是老生常談的問題了。隨便打開一個 Flutter 系列文章,都會爲你鋪平接下來幾周的路。可是幾周以後呢?彷佛不多文章會接着寫下去,**畢竟大腦最喜歡簡單的東西(我也不例外),一件事情的難度與受歡迎程度成反比。**因此 Flutter 怎麼學?所謂:取乎其上,得乎其中。我只有一句話:以讓 Flutter 成爲你最拿手技能爲目標去學。web
Q:能給一些 Flutter 的學習資料嗎?編程
A:我列舉一下我學習 Flutter 過程當中用到的資料:後端
1.Dart官網,啃完官方文檔,Dart 你就入門了。api
2.Flutter實戰,這本開源書的例子不少,所有敲一遍 flutter 你就入門了。特別是最後的 Flutter 原理分析能夠仔細看看。
3.Flutter github 倉庫,如今網絡上 Flutter 原理分析的文章真的很是少,因此真想要成爲 Flutter 專家,你必須做爲開拓者去閱讀 Flutter 在各類層級下的源碼。
Q:Flutter 會幹掉 Native?
A:Flutter 是 Native 的子集。在手機被」革命「以前,但凡業務比較複雜的公司,只會要求 Native 工程師掌握 Flutter。而不會出現拋棄 Native 只作 Flutter 的工程師,由於 Flutter 說一千道一萬隻是一個 ui 框架。畢竟它自身的複雜度很難支撐起比它還複雜的業務。以上只是我的觀點,有分歧能夠在評論區探討。
Q:Flutter 哪些地方作的比 Native 好?
A:下面是我總結出來的 Flutter 比 Native 好的地方:
常常讀個人文章的讀者應該看過我上一篇文章:抖音、ins、微信功能大比拼——Story的貼紙文字,這篇文章中詳細比較了各家 Story 的貼紙文字的功能,而後在 Android 端實現了一個貼紙框架。而這一章我就打算將這個貼紙框架移植到 Flutter,相信最後的還原度會超過你的想象。接下來建議配合源碼閱讀文章。注意這一章的大部份內容和上一篇文章中講解 Android 端實現控件的章節是差很少的。
使用方式:sticker_framework: ^0.0.1
咱們第一節先講講文字貼紙控件的架構實現,我會基於下面的 圖1 和 github 上的代碼進行講解。建議你們把代碼 clone 下來,固然別忘了給個 star。
咱們先來根據圖1來說講整個控件的架構
我在開發整個控件的時候遇到過比較多的技術實現上的難點,因此這一節就選一些來說講,讓讀者在看源碼的時候不會特別困惑。
-----代碼塊1----- ws_element.dart
int mZIndex = -1; // 圖像的層級
double mMoveX = 0.0; // 初始化後相對 ElementContainerWidget 中心的移動距離
double mMoveY = 0.0; // 初始化後相對 ElementContainerWidget 中心的移動距離
double mOriginWidth; // 初始化時內容的寬度
double mOriginHeight; // 初始化時內容的高度
Rect mEditRect; // 可繪製的區域
double mRotate = 0.0; // 圖像順時針旋轉的角度,以 π 爲基準
double mScale = 1.0; // 圖像縮放的大小
double mAlpha = 1.0; // 圖像的透明度
bool mIsSelected = false; // 是否處於選中狀態
bool mIsSingeFingerMove = false; // 是否處於單指移動的狀態
bool mIsDoubleFingerScaleAndRotate = false; // 是否處於雙指旋轉縮放的狀態
Widget mElementShowingWidget; // 展現內容的 widget
Offset mOffset; // ElementContainerWidget 相對屏幕的位移
複製代碼
函數未動數據先行,數據結構是一個框架很是核心的東西,定義了一個好的數據結構能夠省去不少沒必要要的代碼。因此這一小節咱們來根據代碼塊1定義一下數據結構和 Widget 繪製座標系
1.咱們將 WE 所在的 ECWS 做爲 WE 中 view 的可繪製區域,代碼塊1中的 mEditRect 就是這個區域表明的矩形。因此 mEditRect 通常爲**[0, 0, ECWS.getWidth, ECWS.getHeight],mEditRect 的單位爲px**。
2.咱們定義的座標系原點在 mEditRect 的中心點,也就是 ECWS 的中心點。mMoveX、mMoveY 分別表示 view 距離座標系原點的距離。由於它們倆默認爲 0,因此通常 view 被添加到 ECWS 中的時候默認位置就在 ECWS 的中心。這兩個參數的單位爲px。
3.咱們的座標系具備 z 軸,mZIndex 就是 z 軸的座標,z 軸表示 view 的層疊關係,mZIndex 爲 0 時表示 view 在 ECWS 的頂層。mZindex 默認爲 -1,表示 view 沒有被添加到 ECWS 中。mZIndex 是整數。
4.咱們定義 mRotate 爲正時 view 順時針轉動,mRotate 的區間爲[-360,360]。
5.咱們定義 view 沒有縮放的時候 mScale 爲 1,mScale 爲 2 的時候表示 view 放大 2 倍,以此類推。
6.mOriginWidth 和 mOriginHeight 爲 view 的初始大小,單位是px。
7.mAlpha 爲 view 的透明度,默認爲 1 且小於等於1。
8.剩下的參數就不用解釋了,代碼裏面都有註釋。
-----代碼塊2----- ws_element.dart
add() {
mElementShowingWidget = initWidget();
}
Widget initWidget();
Widget buildTransform() {
Matrix4 matrix4 = Matrix4.translationValues(mMoveX, mMoveY, 0);
matrix4.rotateZ(mRotate);
matrix4.scale(mScale, mScale, 1);
return Transform(
alignment: Alignment.center,
transform: matrix4,
child: Opacity(
opacity: mAlpha,
child: mElementShowingWidget,
),
);
}
複製代碼
-----代碼塊2----- element_container_widget.dart
@override
Widget build(BuildContext context) {
RawGestureDetector gestureDetectorTwo = GestureDetector(
child: GestureDetector(
child: Stack(
alignment: AlignmentDirectional.center,
key: globalKey,
children: mElementList.map((e) {
return e.buildTransform();
})
.toList()
.reversed
.toList()
),
onPanUpdate: onMove,
behavior: HitTestBehavior.opaque,
),
).build(context);
gestureDetectorTwo.gestures[RotateScaleGestureRecognizer] =
GestureRecognizerFactoryWithHandlers<RotateScaleGestureRecognizer>(
() => RotateScaleGestureRecognizer(debugOwner: this),
(RotateScaleGestureRecognizer instance) {
instance
..onStart = onDoubleFingerScaleAndRotateStart
..onUpdate = onDoubleFingerScaleAndRotateProcess
..onEnd = onDoubleFingerScaleAndRotateEnd;
},
);
return Listener(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: double.infinity,
minWidth: double.infinity,
),
child: gestureDetectorTwo,
),
behavior: HitTestBehavior.opaque,
onPointerDown: onDown,
onPointerUp: onUp,
);
}
複製代碼
這一節我主要會對項目中的測試 demo 進行源碼流程分析,讓讀者對控件總體的運行方式有個簡單的瞭解。這一節主要是講解源碼,因此讀者必定要去 clone 源碼,跟隨文章的腳步前進。
元素手勢不像添加元素那樣須要外部調用,元素手勢是經過事件分發觸發的,咱們這裏不講 Flutter 的事件分發機制,只講咱們基於其上的邏輯。
這一章我會從一個 Android 工程師的角度來研究一下 Flutter,講一講我在移植控件時碰見的問題們。
先看看 Flutter 與 Android 寫的 App 實際的比較吧
以一個 Android 工程師的眼光來看 Flutter
1.LIstener 是手勢的基礎:GestureDetector 是基於 Listener 開發的。
2.事件自底向上,事件不可截斷
3.開發中咱們使用 GestureDetector 封裝 Widget,咱們定義的一個個手勢回調會讓 GestureDetector 生成多個 GestureRecognizer 附着在當前的 Widget 上以處理 Widget 接收到的事件。
4.每根手指的 down、move、up 都是一個事件流,當 down 事件自底向上確立了一個 Widget 鏈的時候,附着在鏈中各個 Widget 上的 GestureRecognizer 們就會去競爭這個事件流的歸屬。
5.一個事件流的勝出 GestureRecognizer 只有一個,勝出後整個事件流都屬於這個 GestureRecognizer 。
6.GestureRecognizer 的勝出機制,就是 Flutter 在事件不可截斷這個 feature 上的補充的靈活性,可使得某個 Widget 上的手勢被截斷,推薦優先使用 Gesture。
7.Gesture 的勝出機制是怎麼樣的?
啊!感受這篇文章有點有始無終的感受,文章從開始到結束跨了好幾周。中間又是加班又是搬家,把個人熱血都消磨了。原本多加一些 Flutter 的深刻探究的,可是感受會越寫越久,因此先就這樣。接下來我會寫一系列文章來分析 Flutter 的原理和 Flutter Sdk。因此更多內容敬請期待!ps:一氣呵成,再而竭,三而衰。真是完美的表現了我寫這篇文章的過程,但願讀者們不要學我。
不販賣焦慮,也不標題黨。分享一些這個世界上有意思的事情。題材包括且不限於:科幻、科學、科技、互聯網、程序員、計算機編程。下面是個人微信公衆號:世界上有意思的事,乾貨多多等你來看。