Android程序員的Flutter學習筆記

Android程序員的Flutter學習筆記

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

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



前言

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渲染的差別等.異步

Flutter筆記

如何啓動一個app

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

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).
  • 還有諸如ListViewGridViewCard等等比較熟悉的Widgets.

另外有一個相似於咱們Activity的Widgets:

  • 叫作MaterialApp, 能夠指定themetitle, 以及子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發送回程序進程, 還記得進程間通訊辦法之一的管道嗎...

 

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.

固然也能夠本身去開發須要的Plugin來適配須要的功能, 基於的技術就是上面有提的MethodChannel, NDK的支持也是一樣的道理.