Android程式員的Flutter學習筆記

来源:https://www.cnblogs.com/mengdd/archive/2018/11/08/self-learning-notes-as-an-android-developer.html
-Advertisement-
Play Games

Google自2017年第一次提出Flutter, 到2018年Beta, 再加之RN的各種風波與問題, 使得Flutter的熱度不斷上升, 國內不少公司都公佈Flutter在其產品中的應用, 如美團, 閑魚等. ...


作為忠實與較資深的Android汪, 最近抽出了一些時間研究了一下Google的親兒子Flutter, 尚屬皮毛, 只能算是個簡單的記錄吧.

Google自2017年第一次提出Flutter, 到2018年Beta, 再加之RN的各種風波與問題, 使得Flutter的熱度不斷上升, 國內不少公司都公佈Flutter在其產品中的應用, 如美團, 閑魚等.

前言

Flutter作為跨平臺框架, 常常被人拿出來與React Native, 以及Xamarin進行對比, 除了大家都是跨平臺框架之外且能達到近乎Native的體驗之外, Flutter與這兩者的原理大不相同.

讓我們來看看這三者的結構圖吧.

可能有一些複雜, 咱大致解釋一下.

React Native跟Xamarin都是基於mapping native代碼來實現所謂的Native體驗的框架, 只是RN基於JS引擎 + Bridge與native打交道, 並且在運行時進行綁定, 而Xamarin是基於微軟的基於Linux的C#虛擬機mono + JNI與native進行通信.

這裡Android與iOS還是有差別的, 如RN在iOS上JS引擎不支持JIT, 會一定程度影響效率, Xamarin在iOS上可以直接編譯成iOS平臺可以執行的程式, 所以在實際運行起來的性能是一樣的, 唯一的差別就是微軟得更快的支持API同步.

對於Flutter來說, 由於他的渲染引擎使用了Skia直繪, 加上基於C++的Dart引擎, 所以在不同平臺上沒有差別, 加之其實現了Android Material Design與iOS Cupertino兩套UI組件, 所以即便是自繪組件, 看起來還是跟原生的一個樣子.

通過對三種跨平臺引擎的大致瞭解, 我們可以看出來, 他們都達到了一定程度的Native體驗, 然則各自都有一定的性能損耗, 比如RN的JS引擎載入JS, 以及Bridge通信的損耗, Xamarin Mono虛擬機與Java通信的損耗, 以及Flutter Skia渲染與Native Android渲染的差異等.

Flutter筆記

如何啟動一個app

Android需要在Manfest裡面指定帶有MAIN action與LAUNCHER category的Activity聲明, 而Flutter只需要一行.

void main() => runApp(MyApp());

其中MyApp就是一個普通的Widgets(View).

View vs Widgets

Flutter沒有View, 與之對應的是Widget, 並且分為StatelessWidgets與StatefulWidgets, 前者是個靜態View, 後者是動態通過Data來更新的View.

  • Stateless
Text(
  'I like Flutter!',
);
  • Stateful
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通過調用StatesetState方法來觸發整個Widgets樹的重繪, 並且在重繪之前會調用傳進去的(){ ... }block.

怎麼寫Layout, XML到哪裡去了.

實際上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: ...,
    )

如何從父View中Remove一個元素

答案是沒有... 因為在Flutter看來, Widgets的樹結構是不可以被更改的, 但是如果想更改, 則是通過StatefulWidgets的方法, 通過setState來更改Data, 觸發Widgets重繪, 從而替換掉之前的Widgets.

喜歡畫Canvas的同學怎麼辦?

Flutter同樣支持, CustomPaint作為一個 Widgets就支持傳入一個實現CustomPainter抽象類的參數, 而CustomPainter的抽象方法也類似於Android View的onDraw.

void paint(Canvas canvas, Size size)

bool shouldRepaint(CustomPainter oldDelegate)

如何自定義View

不用繼承, 而使用類似Android ViewGroup的辦法, 通過組合(composing)與封裝的方法來實現, 通過小Widgets組合成需要的新Widgets.

頁面跳轉怎麼辦, 四大組件之一的Intent跑哪裡去了

貌似在講類似於Activity的MaterialApp的時候劇透了...

就是使用NavigatorRoutes來實現界面跳轉, 實際上是整個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');

如何處理外部的Intent

實際上還是需要在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;
      });
    }
  }

常用的startActivityForResult怎麼辦.

這個Flutter有完全對應的辦法, 而且用起來很方便, 結合之前說的頁面跳轉:

Map xxx = await Navigator.of(context).pushNamed('/xxx');


Navigator.of(context).pop({xxx});

非同步怎麼辦, runOnUiThread()哪裡去了

Flutter有點像JS, 是一個單線程模式, 所以只是通過模擬來構建簡單的非同步, 關鍵字就是類似於kotlin coroutines一樣, 通過await+async來處理.

如:

loadData() async {
    response = await http.get(xxx);
    setState(() {xxx});
}

但是由於它的單線程, 所以無法做很長的阻塞操作, 像http請求的延遲正常情況可能都是毫秒級的, 但是數據的處理等, 可能就得秒級了.

這也是RN線上程方面的做android程式的一個痛點, Flutter採用了比較容易想到的曲線救國的辦法, 提供了一個叫Isolate的對象, 它實際是一個基於socket的數據通道, 相當於把數據放在一個獨立的進程進行處理, 然後再通過socket發送回程式進程, 還記得進程間通信辦法之一的管道嗎...

具體API可以參考文檔1...,2....

Flutter 替代OkHttp的網路庫

自帶了http庫, 直接http.get(url), 線上程部分的代碼實例里也有涉及.

通過類似gradle的文件pubspec.yaml引入.

dependencies:
  ...
  http: ^0.12

^表示不升大版本, 並取最新版本, 比gradle的+要範圍更小.

常見的LCE(Loading Content Error)裡面的Loading怎麼show

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)

Gradle變成什麼了

前面說網路庫, 圖片資源的時候提到過, 提供了一個叫pubspec.yaml的文件, 具體支持的規則可以查看這個文檔.

Fragment與Activity呢?

之前做過類比, 如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();
  }

ScrollView vs ListView

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.

Flutter橫豎屏怎麼玩.

因為它實際上還是藉助了Android程式的殼子, 所以如果AndroidManifect定義了android:configChanges="orientation|screenSize", 則Flutter會自己hanlde.

怎麼處理Gesture

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哪裡去了, 錯誤信息怎麼輸出

對於輸入框的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. 這些都可以通過Dartlang來查詢.

當然也可以自己去開發需要的Plugin來適配需要的功能, 基於的技術就是上面有提的MethodChannel, NDK的支持也是同樣的道理.

Reference

https://flutter.io/docs/get-started/flutter-for/android-devs


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 目前,大數據人才短缺。許多人希望通過培訓進入大數據產業。同時,他們會問,大數據培訓難學嗎?零基點能學到大數據嗎?大數據培訓並不難,但仍有賴於個人堅持不懈的學習。大數據的零基礎培訓當然沒有問題。目前,許多大數據培訓機構都開設了零起點的培訓課程。這也是絕大多數學生的福利。在這個人才短缺的時代,把握時間, ...
  • 上邊這種配置方式 會報The reference to entity "useUnicode" must end with the ';' delimiter. 這個錯誤。 這是由xml文件中的編碼規則決定要這麼變換。在xml文件中有以下幾類字元要進行轉義替換: 正確的方式 ...
  • 隱式事務創建註意事項 IMPLICIT_TRANSACTIONS為 ON 時,系統處於“隱式”事務模式。 這意味著如果 @@TRANCOUNT = 0,下列任一 Transact-SQL 語句都會開始新事務。 這等同於先執行一個不可見的 BEGIN TRANSACTION。 IMPLICIT_TRA ...
  • [20181108]with temp as 建立臨時表嗎.txt--//鏈接:http://www.itpub.net/thread-2106304-1-1.html--//作者提到在dg上使用with查詢的sql語句報錯.出現如下錯誤:ORA-00604: error occurred at r ...
  • 剛開始玩 Android ,用Android studio 連接真機做測試,在虛擬機上沒有問題,但是真機就會報錯 檢查了好多地方,最終發現了問題,網上的常規就不介紹了,大家自己去看別的帖子 手機方面 1.打開usb調試 2.打開usb安全調試 3.打開usb安裝(允許通過usb安裝程式),我是這一步 ...
  • 一 報錯顯示 Gradle sync failed: Unknown host 'd29vzk4ow07wi7.cloudfront.net'. You may need to adjust the proxy settings in Gradle。 查看整個項目的build.gradle。buil ...
  • Retrofit2.0 這個網路請求框架使用了很久了,最近一次出現一個小插曲。 有一個介面,返回的數據量因為業務的原因 會返回很大的數據量,此時網路不大好的情況下,會出現請求失敗的情況 也就是回調了 onFaileure()方法,測試一下,大概都在10秒就會回調這個方法。 去後臺驗證,發現數據請求成 ...
  • 參考鏈接 步驟:適配所有的模擬器和真機 ——生成.a文件 <1>建一個靜態庫工程 <2>生成.a文件(註意添加類.h和.m文件) 同理:接入任意款真機,同上述操作,生成真機的.a文件; <4>合併模擬器和真機.a文件,並生成新的.a文件——此處把模擬器和真機的.a文件剪切到了桌面上,也可以不用放到桌 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...