Flutter 與 iOS 功能比較

n以夢為馬 發表於 2020-05-11

此文件是學習過程中的總結,文章詳情:flutterchina.club/flutter-for…

Views

UIView 相當於 Flutter 中的什麼?

Widget 類似於 UIView,兩者區別如下:

  • 生存時間不同,widgets 一直存在且保持不變,直到當它們需要被改變,當 widgets 和它們的狀態被改變時,Flutter 會構建一顆新的 widgets 樹; views 在改變時並不會被重新建立。
  • Flutter 的 widgets 非常輕量。widgets 本身並不是什麼控制元件,也不會被直接繪製出什麼,而只是 UI 的描述。
  • iOS 上更新 views,只需要直接改變它們就可以了。在 Flutter 中,widgets 是不可變的,而且不能被直接更新。你需要去操縱 widget 的 state。
  • iOS 中可以通過約束或者 frame 來佈局,Flutter 中,可以通過編寫一個 widget 樹來宣告佈局(可以給任何的 widget 新增 padding),這裡是 Flutter 提供的佈局
  • 在 iOS 中,可以在父 view 中呼叫 addSubview() 或在子 view 中呼叫 removeFromSuperview() 來動態地新增或移除子 views。在 Flutter 中,由於 widget 不可變,所以沒有和 addSubview() 直接等價的東西。作為替代,可以向 parent 傳入一個返回 widget 的函式,並用一個布林值來控制子 widget 的建立。
  • 在 iOS 中,可以通過呼叫 animate(withDuration:animations:) 方法來給一個 view 建立動畫。在 Flutter 中,使用動畫庫來包裹 widgets,而不是建立一個動畫 widget。在 Flutter 中,使用 AnimationController。這是一個可以暫停、尋找、停止、反轉動畫的 Animation 型別。它需要一個 Ticker 當 vsync 發生時來傳送訊號,並且在每幀執行時建立一個介於 0 和 1 之間的線性插值(interpolation)。你可以建立一個或多個的 Animation 並附加給一個 controller。

繪圖

在 iOS 上,你通過 CoreGraphics 來在螢幕上繪製線條和形狀。Flutter 有一套基於 Canvas 類的不同的 API,還有 CustomPaint 和 CustomPainter 這兩個類來幫助你繪圖。後者實現你在 canvas 上的繪圖演算法。

怎麼建立自定義的 widgets

在 iOS 中,你編寫 UIView 的子類,或使用已經存在的 view 來過載並實現方法,以達到特定的功能。在 Flutter 中,你會組合(composing)多個小的 widgets 來構建一個自定義的 widget(而不是擴充套件它)。

導航

頁面之間跳轉

在 iOS 中,可以使用管理了 view controller 棧的 UINavigationController 來在不同的 view controller 之間跳轉。

Flutter 也有類似的實現,使用了 Navigator 和 Routes。一個路由是 App 中“螢幕”或“頁面”的抽象,而一個 Navigator 是管理多個路由的 widget 。可以粗略地把一個路由對應到一個 UIViewController。Navigator 的工作原理和 iOS 中 UINavigationController 非常相似,當你想跳轉到新頁面或者從新頁面返回時,它可以 push() 和 pop() 路由。

跳轉到其他 App

在 iOS 中,要跳轉到其他 App,需要一個特定的 URL Scheme。對系統級別的 App 來說,這個 scheme 取決於 App。為了在 Flutter 中實現這個功能,你可以建立一個原生平臺的整合層,或者使用現有的 plugin,例如 url_launcher

執行緒和非同步

怎麼編寫非同步的程式碼

Dart 是單執行緒執行模型,但是它支援 Isolate(一種讓 Dart 程式碼執行在其他執行緒的方式)、事件迴圈和非同步程式設計。除非你自己建立一個 Isolate ,否則你的 Dart 程式碼永遠執行在 UI 執行緒,並由 event loop 驅動。Flutter 的 event loop 和 iOS 中的 main loop 相似——Looper 是附加在主執行緒上的。

Dart 的單執行緒模型並不意味著你寫的程式碼一定是阻塞操作,從而卡住 UI。相反,使用 Dart 語言提供的非同步工具,例如 async / await ,來實現非同步操作。

把工作放到後臺執行緒

由於 Flutter 是單執行緒並且跑著一個 event loop 的(就像 Node.js 那樣),你不必為執行緒管理或是開啟後臺執行緒而操心。如果你正在做 I/O 操作,如訪問磁碟或網路請求,安全地使用 async / await 就完事了。如果,在另外的情況下,你需要做讓 CPU 執行繁忙的計算密集型任務,你需要使用 Isolate 來避免阻塞 event loop。

Isolates 是分離的執行執行緒,並且不和主執行緒的記憶體堆共享記憶體。這意味著你不能訪問主執行緒中的變數,或者使用 setState() 來更新 UI。正如它們的名字一樣,Isolates 不能共享記憶體。

發起網路請求

使用 http 做網路請求非常簡單,類似於 AFNetworking 或 Alamofire。

載入進度條

在 iOS 中,在後臺執行耗時任務時你會使用 UIProgressView。在 Flutter 中,使用一個 ProgressIndicator widget。通過一個布林 flag 來控制是否展示進度。在任務開始時,告訴 Flutter 更新狀態,並在結束後隱去。

工程結構、本地化、依賴和資源

怎麼在 Flutter 中引入 image assets?多解析度怎麼辦?

iOS 把 images 和 assets 作為不同的東西,而 Flutter 中只有 assets。Flutter 中的 assets 可以是任意型別的檔案,而不僅僅是圖片。例如,你可以把 json 檔案放置到 my-assets 資料夾中。

對於圖片,Flutter 像 iOS 一樣,遵循了一個簡單的基於畫素密度的格式。Image assets 可能是 1.0x 2.0x 3.0x 或是其他的任何倍數。

在哪裡放置字串?怎麼做本地化?

不像 iOS 擁有一個 Localizable.strings 檔案,Flutter 目前並沒有一個用於處理字串的系統。目前,最佳實踐是把你的文字拷貝到靜態區,並在這裡訪問。

預設情況下,Flutter 只支援美式英語字串。如果你要支援其他語言,請引入 flutter_localizations 包。你可能也要引入 intl 包來支援其他的 i10n 機制,比如日期/時間格式化。

Cocoapods 相當於什麼?如何新增依賴?

在 iOS 中,你把依賴新增到 Podfile 中。Flutter 使用 Dart 構建系統和 Pub 包管理器來處理依賴。這些工具將本機 Android 和 iOS 包裝應用程式的構建委派給相應的構建系統。

如果你的 Flutter 工程中的 iOS 資料夾中擁有 Podfile,請僅在你為每個平臺整合時使用它。總體來說,使用 pubspec.yaml 來在 Flutter 中宣告外部依賴。一個可以找到優秀 Flutter 包的地方是 Pub

ViewControllers

ViewController 相當於 Flutter 中的什麼?

在 iOS 中,一個 ViewController 代表了使用者介面的一部分,最常用於一個螢幕,或是其中一部分。它們被組合在一起用於構建複雜的使用者介面,並幫助你拆分 App 的 UI。在 Flutter 中,這一任務回落到了 widgets 中。就像在介面導航部分提到的一樣,一個螢幕也是被 widgets 來表示的,因為“萬物皆 widget!”。使用 Navigator 在 Route 之間跳轉,或者渲染相同資料的不同狀態。

怎麼監聽 iOS 中的生命週期事件?

在 iOS 中,你可以重寫 ViewController 中的方法來捕獲它的檢視的生命週期,或者在 AppDelegate 中註冊生命週期的回撥函式。在 Flutter 中沒有這兩個概念,但你可以通過 hook WidgetsBinding 觀察者來監聽生命週期事件,並監聽 didChangeAppLifecycleState() 的變化事件。

可觀察的生命週期事件有:

  • inactive - 應用處於不活躍的狀態,並且不會接受使用者的輸入。這個事件僅工作在 iOS 平臺,在 Android 上沒有等價的事件。
  • paused - 應用暫時對使用者不可見,雖然不接受使用者輸入,但是是在後臺執行的。
  • resumed - 應用可見,也響應使用者的輸入。
  • suspending - 應用暫時被掛起,在 iOS 上沒有這一事件。

更多細節:AppLifecycleState

佈局

UITableView 和 UICollectionView 相當於 Flutter 中的什麼?

在 iOS 中,你可能用 UITableView 或 UICollectionView 來展示一個列表。在 Flutter 中,你可以用 ListView 來達到相似的實現。在 iOS 中,你通過代理方法來確定行數,每一個 index path 的單元格,以及單元格的尺寸。由於 Flutter 中 widget 的不可變特性,你需要向 ListView 傳遞一個 widget 列表,Flutter 會確保滾動是快速且流暢的。

怎麼知道列表的哪個元素被點選了?

iOS 中,你通過 tableView:didSelectRowAtIndexPath: 代理方法來實現。在 Flutter 中,使用傳遞進來的 widget 的 touch handle。

怎麼動態地更新 ListView?

一個更新 ListView 的簡單方法是,在 setState() 中建立一個新的 list,並把舊 list 的資料拷貝給新的 list。雖然這樣很簡單,但當資料集很大時,並不推薦這樣做。

一個推薦的、高效的且有效的做法是,使用 ListView.Builder 來構建列表。這個方法在你想要構建動態列表,或是列表擁有大量資料時會非常好用。

ScrollView 相當於 Flutter 裡的什麼?

在 iOS 中,你給 view 包裹上 ScrollView 來允許使用者在需要時滾動你的內容。在 Flutter 中,最簡單的方法是使用 ListView widget。它表現得既和 iOS 中的 ScrollView 一致,也能和 TableView 一致,因為你可以給它的 widget 做垂直排布。

手勢檢測及觸控事件處理

怎麼給 Flutter 的 widget 新增一個點選監聽者?

在 iOS 中,你給一個 view 新增 GestureRecognizer 來處理點選事件。在 Flutter 中,有兩種方法來新增點選監聽者:

  • 如果 widget 本身支援事件監測,直接傳遞給它一個函式,並在這個函式裡實現響應方法。
  • 如果 widget 本身不支援事件監測,則在外面包裹一個 GestureDetector,並給它的 onTap 屬性傳遞一個函式。

怎麼處理 widget 上的其他手勢?

使用 GestureDetector 你可以監聽更廣闊範圍內的手勢,比如:

  • Tapping
    • onTapDown — 在特定位置輕觸手勢接觸了螢幕。
    • onTapUp — 在特定位置產生了一個輕觸手勢,並停止接觸螢幕。
    • onTap — 產生了一個輕觸手勢。
    • onTapCancel — 觸發了 onTapDown 但沒能觸發 tap。
  • Double tapping
    • onDoubleTap — 使用者在同一個位置快速點選了兩下螢幕。
  • Long pressing
    • onLongPress — 使用者在同一個位置長時間接觸螢幕。
  • Vertical dragging
    • onVerticalDragStart — 接觸了螢幕,並且可能會垂直移動。
    • onVerticalDragUpdate — 接觸了螢幕,並繼續在垂直方向移動。
    • onVerticalDragEnd — 之前接觸了螢幕並垂直移動,並在停止接觸螢幕前以某個垂直的速度移動。
  • Horizontal dragging
    • onHorizontalDragStart — 接觸了螢幕,並且可能會水平移動。
    • onHorizontalDragUpdate — 接觸了螢幕,並繼續在水平方向移動。
    • onHorizontalDragEnd — 之前接觸螢幕並水平移動的觸控點與螢幕分離。

主題和文字

怎麼給 App 設定主題?

Flutter 實現了一套漂亮的 MD 元件,並且開箱可用。它接管了一大堆你需要的樣式和主題。

為了充分發揮你的 App 中 MD 元件的優勢,宣告一個頂級 widget,MaterialApp,用作你的 App 入口。MaterialApp 是一個便利元件,包含了許多 App 通常需要的 MD 風格元件。它通過一個 WidgetsApp 新增了 MD 功能來實現。

怎麼給 Text widget 設定自定義字型?

在 iOS 中,你在專案中引入任意的 ttf 檔案,並在 info.plist 中設定引用。在 Flutter 中,在資料夾中放置字型檔案,並在 pubspec.yaml 中引用它,然後在你的 Text widget 中指定字型。

表單輸入

Flutter 中表單怎麼工作?我怎麼拿到使用者的輸入?

在表單處理的實踐中,就像在 Flutter 中任何其他的地方一樣,要通過特定的 widgets。如果你有一個 TextField 或是 TextFormField,你可以通過 TextEditingController 來獲得使用者輸入。

Text field 中的 placeholder 相當於什麼?

在 Flutter 中,你可以輕易地通過向 Text widget 的裝飾構造器引數重傳遞 InputDecoration 來展示“小提示”,或是佔位符文字。

怎麼展示驗證錯誤資訊?

就像展示“小提示”一樣,向 Text widget 的裝飾器構造器引數中傳遞一個 InputDecoration。然而,你並不想在一開始就顯示錯誤資訊。相反,當使用者輸入了驗證資訊,更新狀態,並傳入一個新的 InputDecoration 物件。

和硬體、第三方服務以及平臺互動

怎麼和平臺,以及平臺的原生程式碼互動?

Flutter 提供了 platform channels ,來和管理你的 Flutter view 的 ViewController 通訊和互動資料。平臺管道本質上是一個非同步通訊機制,橋接了 Dart 程式碼和宿主 ViewController,以及它執行於的 iOS 框架。你可以用平臺管道來執行一個原生的函式,或者是從裝置的感測器中獲取資料。

除了直接使用平臺管道之外,你還可以使用一系列預先製作好的 plugins。例如,你可以直接使用外掛來訪問相機膠捲或是裝置的攝像頭,而不必編寫你自己的整合層程式碼。你可以在 Pub 上找到外掛,這是一個 Dart 和 Flutter 的開源包倉庫。其中一些包可能會支援整合 iOS 或 Android,或兩者均可。

如果你在 Pub 上找不到符合你需求的外掛,你可以自己編寫,並且釋出在 Pub 上。

怎麼訪問 GPS 感測器?

通過 location 社群外掛。

怎麼訪問攝像頭?

通過 image_picker 外掛。

怎麼登入 Facebook?

登入 Facebook 可以使用 flutter_facebook_login 社群外掛。

怎麼建立自己的原生整合層?

如果有一些 Flutter 和社群外掛遺漏的平臺相關的特性,可以根據 developing packages and plugins 頁面構建自己的外掛。

資料庫和本地儲存

怎麼在 Flutter 中訪問 UserDefaults?

在 iOS 中,你可以使用屬性列表來儲存鍵值對的集合,即我們熟悉的 UserDefaults。

在 Flutter 中,可以使用 Shared Preferences plugin 來達到相似的功能。它包裹了 UserDefaluts 以及 Android 上等價的 SharedPreferences 的功能。

CoreData 相當於 Flutter 中的什麼?

在 iOS 中,你通過 CoreData 來儲存結構化的資料。這是一個 SQL 資料庫的上層封裝,讓查詢和關聯模型變得更加簡單。

在 Flutter 中,使用 SQFlite 外掛來實現這個功能。

通知

怎麼推送通知?

在 iOS 中,你需要向蘋果開發者平臺中註冊來允許推送通知。

在 Flutter 中,使用 firebase_messaging 外掛來實現這一功能。


更多文章請點選:Articles