[譯] 使用 Flutter 實現跨平臺移動端開發

京東設計中心JDC發表於2018-08-02

作者: Mike Bluestein | 譯:孫印鳳

原文地址:[https://www.smashingmagazine.com/2018/06/google-flutter-mobile-development/]

【譯者注:連結序號對應下面索引列表,另外可以點選閱讀原文檢視詳細的連結文章】

簡介:Flutter 使得建立跨平臺移動端應用變得輕而易舉。本文將介紹 Flutter 並將其與其他移動端開發平臺進行比較,還會闡述如何使用它來構建應用程式。

Flutter 是一款由 Google 開發的開源、跨平臺移動端開發框架。它允許使用同一個程式碼庫構建高效能、漂亮的 iOS 和 Android 應用,同時它也是 Google 即將推出的 Fuchsia 作業系統的開發平臺。此外,通過自定義的 Flutter 引擎可以將其嵌入到其他平臺。

Flutter 為什麼會出現?為什麼要使用它呢?

一直以來,跨平臺工具採用以下兩種方法之一:

  • 在原生應用程式中嵌入 web view ,像構建網站一樣構建應用程式。
  • 封裝原生平臺裡的控制元件併為它們提供一些跨平臺的引數。

為了使移動端開發變得更好,Flutter 嘗試了一種不同的方法。它提供了開發人員工作的框架應用程式和能夠託管應用程式的可移植執行時的引擎。該框架依託 Skia 圖形庫而構建,提供了實際渲染時用到的 widgets,而不僅僅是原生應用控制元件的包裝器。就像 web 包裝器選項提供的那樣,該方法可以靈活的以完全自定義的方式構建跨平臺應用程式,同時還會提供流暢的效能體驗。與此同時,Flutter 自帶的豐富的 widget 庫以及一些開源的 widgets 使其成為一個功能豐富的平臺。簡言之,Flutter 目前是移動端開發者接觸到的最接近跨平臺開發的東西。

Dart

Flutter 應用程式使用 Dart 編寫,Dart 是最初由 Google 開發的一種程式語言。它是一種支援預編譯和實時編譯的面嚮物件語言,所以比較適合開發原生應用程式,配合 Flutter 的熱載入可以提供高效的開發工作流程。Flutter 最近也轉向使用 Dart 2.0 版。Dart 語言提供了許多其他程式語言具有的功能,包括垃圾收集、非同步等待、強型別、泛型以及豐富的標準庫等等。這些功能對於各種程式語言的開發者們來說都比較熟悉,例如 C#、JavaScript、F#、Swift 和 Java。此外,Dart 可以編譯為 Javascript,與 Flutter 結合可以在 web 和移動平臺實現程式碼共享。

事件歷史時間表

  • 2015.04

在 Dart 開發者峰會 [1] 上提出 Flutter(最初命名為 Sky )。

  • 2015.11

Sky 重新命名為 Flutter。

  • 2018.02

在2018年世界行動通訊大會上,Flutter beta 1 [2] 版本釋出。

  • 2018.04

Flutter beta 2 [3] 版本釋出

  • 2018.05

Flutter beta 3 [4] 版本在 Google I/O 上釋出。Google 宣佈 Flutter 可以用於開發應用程式。

與其他開發平臺比較

APPLE/ANDROID NATIVE

原生應用程式在使用新功能時帶來的困擾是最少的。由於應用程式是使用平臺供應商自己(Apple 或 Google)的控制元件構建,為了讓使用者體驗更加符合給定的平臺,因此他們通常遵循這些供應商制定的設計指南。大多數情況下,原生的應用將會比那些跨平臺構建的應用效能要好一些,儘管在很多情況下兩者的差異可以忽略不計,不過具體還要取決於底層跨平臺技術。原生應用的一大優勢是:當需要時,他們可以立即採用 Apple 和 Google 在測試版中開發的新技術而不用等待第三方的整合。構建原生應用的主要缺點是缺乏跨平臺的程式碼複用,如果同時開發 iOS 和 Android 應用,那麼開發成本可能會很高。

REACT NATIVE

React Native 允許原生應用使用 JavaScript 構建。應用中用到的控制元件實際上都是原生平臺裡的控制元件,所以使用者使用起來感覺和原生應用一樣。對於那些 React Native 沒有提供的需要自定義的應用,仍然需要使用原生開發。當需要定製的模組比較多時,某些情況下,在 React Native 中開發不如使用原生開發更合適。

XAMARIN

當談到 Xamarin 時,有兩種不同的方法將會被提及。跨平臺方法:Xamarin.Forms。該方法不同於 React Native,但是從概念上講是相似的,因為它也是抽象原生控制元件。同樣的,在定製方面它也有和 React Native 同樣的缺點。第二種方法:Xamarin-classic。該方法分開使用 Xamarin 的 iOS 和 Android 產品來構建適用於特定平臺的功能,就像直接使用 Apple/Android 原生功能一樣,只不過在 Xamarin 中需要使用 C# 或 F# 。使用 Xamarin 的好處是可以共享非平臺特定的程式碼,例如網路、資料訪問、Web 服務等。

與上面的替代方法不同,Flutter 試圖給開發者一個更加完整的跨平臺解決方案,包括程式碼複用、高效能、流暢的使用者介面和出色的工具。

一個 Flutter 應用概述

建立一個應用程式

安裝 Flutter [5] 之後,使用 Flutter 建立應用程式則非常簡單:開啟命令列,輸入 flutter create [app_name], 在 VS Code 中選擇 Flutter: New Project;在 Android Studio 或 IntelliJ 中選擇 Start a new Flutter Project。無論你是選擇使用 IDE 還是使用首選編輯器裡的命令列,新的 Flutter 應用程式模板都為你提供了一個良好的應用起點。

該應用程式引入了 flutter/material.dart 包,它為應用程式提供了一些基本的元素,例如標題欄、icons 和主題。它還設定了一個帶有狀態的 widget 來演示當應用程式裡的 state 發生變化時使用者介面是如何更新的。下面是 Flutter 應用執行在 iOS 和 Android 上的圖片:

[譯] 使用 Flutter 實現跨平臺移動端開發

工具選項

Flutter 在工具方面提供了令人難以置信的靈活性。就像可以從支援的 IDE( 比如 VS CodeAndroid Studio、或 IntelliJ )中進行開發一樣,應用程式可以簡單的在任何編輯器的命令列中進行開發。使用何種開發工具很大程度上取決於開發者的喜好。Android Studio提供了大部分的功能,比如用於分析正在執行應用的 widgets 以及監控應用程式效能的 Flutter 檢查器。它還提供了一些重構模板,在開發帶有層次結構的 widget 時用起來將會很方便;VS Code 提供了更加輕快的開發體驗,因為它啟動的速度比 Android StudioIntelliJ 都要快,而且每個 IDE 都內建了編輯助手,例如程式碼補全、各種 API 處理以及良好的除錯支援;命令列也很好的支援了 Flutter 命令,這使得建立、更新和釋出應用都變得簡單 ,除了編輯器外不再依賴於其他工具。以下是在各種環境中使用 Flutter 的情景:

[譯] 使用 Flutter 實現跨平臺移動端開發 [譯] 使用 Flutter 實現跨平臺移動端開發 [譯] 使用 Flutter 實現跨平臺移動端開發

熱載入

無論使用哪種工具,Flutter 都可以很好的支援熱載入。這樣在許多情況下就可以修改正在執行的應用程式、維護其狀態,而不必停止執行、重新構建和部署了。通過快速迭代,熱載入可以極大的提升開發效率。這樣也使得這個平臺使用起來更友好。

測試

Flutter 包含 WidgetTester 實用程式,用於和測試中的 widgets 互動。新的應用程式模板包含一個示例測試,用來演示在編寫測試時如何使用它,如下所示:

// Test included with the new Flutter application template

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

import 'package:myapp/main.dart';

void main() {
  testWidgets('Counter increments smoke test', (WidgetTester tester) async {
    // Build our app and trigger a frame.
    await tester.pumpWidget(new MyApp());

    // Verify that our counter starts at 0.
    expect(find.text('0'), findsOneWidget);
    expect(find.text('1'), findsNothing);

    // Tap the '+' icon and trigger a frame.
    await tester.tap(find.byIcon(Icons.add));
    await tester.pump();

    // Verify that our counter has incremented.
    expect(find.text('0'), findsNothing);
    expect(find.text('1'), findsOneWidget);
  });
}

複製程式碼

包和外掛的使用

雖然 Flutter 剛宣佈可以使用,但已經有一個豐富的開發者生態系統可以使用:A plethora of packages and plugins [6]。當新增一個包或者外掛時,只需要在應用程式根目錄下的 pubspec.yaml 檔案中新增依賴即可。然後通過命令列或 IDE 執行 flutter packages get, Flutter 就會引入所需的全部依賴。例如,在 Flutter 中使用比較流行的 image picker 外掛,則只需要在 pubspec.yaml 檔案中新增依賴,如下所示:

dependencies:
  image_picker: "^0.4.1"

複製程式碼

接著執行 flutter packages get 命令就會引入使用時所需的一切東西,然後在 Dart 中匯入和使用:

import 'package:image_picker/image_picker.dart';

複製程式碼

Widgets

在 Flutter 中一切皆 widget 。widget 包括使用者介面元素,例如 ListView, TextBoxImage 以及框架的其他部分,例如佈局、動畫、手勢識別和主題等等。widget 化的設計使得整個應用程式也可以嵌入帶有層次結構的其他 widget 中(整個應用程式也是一個 widget)。widget 化的體系結構可以清楚的追蹤應用程式中一部分的屬性和行為,這與其他大部分應用程式框架不同,大多數的框架是將屬性和行為不一致的關聯起來,有時將它們與層次結構中的其他元件相關聯,有時將其與控制元件本身相關聯。

簡單的 UI WIDGET 示例

一個 Flutter 應用的入口是其主要功能。如下所示,在使用者介面元素中放入一個 widget,在函式 main() 中呼叫 runApp() 。

import 'package:flutter/material.dart';

void main() {
  runApp(
    Container(color: Colors.lightBlue)
  );
}

複製程式碼

結果是一個淡藍色的 Container widget 鋪滿了螢幕。

[譯] 使用 Flutter 實現跨平臺移動端開發

無狀態和有狀態 widgets

widgets 分為兩種:無狀態的 widgets 和有狀態的 widgets 。無狀態的 widgets 在他們建立和初始化之後內容不再改變,而有狀態的 widgets 當應用程式在執行中時允許改變某些狀態,例如與使用者之間的互動。舉個例子,在應用中同時引用了 FlatButton widgetText widgetText widget 的 state 設定了預設的 String。當按下按鈕時導致 state 值改變,這會引起 Text widget 更新從而顯示一個新的 String 值。如果要對 widget 封裝則需要建立一個派生自 StatelessWidgetStatefulWidget的類。例如,淡藍色的 Container 可像下面這樣寫:

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(color: Colors.lightBlue);
  }
}

複製程式碼

當建立的 widget 插入到 widget 樹中後,Flutter 將會呼叫 widget 的 build 方法,所以 UI 會被渲染。對於一個有狀態的 widget 應該是派生自 StatefulWidget 類:

class MyStatefulWidget extends StatefulWidget {

  MyStatefulWidget();

  @override
  State createState() {
    return MyWidgetState();
  }
}

複製程式碼

有狀態的 widget 將會返回一個 State 類,該類負責為給定的 state 構建 widget 樹。當 state 改變時,相應的 widget 樹將會重新構建。在下面的程式碼中,當按鈕被點選時,State 類將會更新 String 的值:

class MyWidgetState extends State {
  String text = "some text";

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.lightBlue,
      child: Padding(
        padding: const EdgeInsets.all(50.0),
        child: Directionality(
          textDirection: TextDirection.ltr,
          child: Column(
            children: [
              FlatButton(
                child: Text('Set State'),
                onPressed: () {
                  setState(() {
                    text = "some new text";
                  });
                },
              ),
               Text(
                text,
                style: TextStyle(fontSize: 20.0)),
            ],
          )
        )
      )
    );
  }
}

複製程式碼

通過 setState() 可以更新 state 值。當 setState() 被呼叫時,這個函式可以重置任何內部的 state 值,像上述例子中的 String;然後呼叫 build 方法,更新狀態 widget 樹。

[譯] 使用 Flutter 實現跨平臺移動端開發

還要注意可以使用 Directionality widget 為其子樹中需要它的 widget 設定文字方向,比如 Text widgets。這裡的示例是從頭開始構建程式碼,所以在 widget 層次結構的一些地方是需要使用 Directionality。然而,使用 MaterialApp widget 會隱式設定文字方向(例如使用預設應用程式模板)。

佈局

預設情況下,runApp 函式會將 widget 放大至鋪滿整個螢幕。為了控制 widget 的佈局,Flutter 提供了很多佈局 widgets。這些 widgets 允許垂直或水平對齊子 widgets、將 widget 放大以鋪滿某個特定區域、將 widget 限制在某個區域中、將其置於螢幕中心以及允許 widgets 之間相互重疊。比較常用的兩個 widgets 是 RowColumn。它們可以水平(Row)或者垂直(Column)顯示其子 widgets。使用這些佈局 widgets 只需將它們包裝在子 widgets 的列表中,mainAxisAlignment 會將 widgets 控制在佈局軸的位置,不論是居中、開始、結束還是使用各種間距選項。下面的程式碼展示了怎樣在 Row 或者 Column 中對齊子 widgets。

class MyStatelessWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Row( //change to Column for vertical layout
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Icon(Icons.android, size: 30.0),
        Icon(Icons.pets, size: 10.0),
        Icon(Icons.stars, size: 75.0),
        Icon(Icons.rowing, size: 25.0),
      ],
    );
  }
}

複製程式碼
[譯] 使用 Flutter 實現跨平臺移動端開發

響應觸控

觸控互動是由手勢處理的,手勢是封裝在 GestureDetector 類中。由於它也是個 widget,新增手勢識別和在 GestureDetector 中封裝子 widgets 一樣簡單。例如,在一個 Icon 上新增觸控事件,將其封裝在 GestureDetector 中並設定處理程式以捕獲所需的手勢。

class MyStatelessWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () => print('you tapped the star'),
      onDoubleTap: () => print('you double tapped the star'),
      onLongPress: () => print('you long pressed the star'),
      child: Icon(Icons.stars, size: 200.0),
    );
  }
}

複製程式碼

在這種情況下,當輕擊,雙擊或長按圖示時,將列印相關文字:

?  To hot reload your app on the fly, press "r". To restart the app entirely, press "R".
An Observatory debugger and profiler on iPhone X is available at: http://127.0.0.1:8100/
For a more detailed help message, press "h". To quit, press "q".
flutter: you tapped the star
flutter: you double tapped the star
flutter: you long pressed the star

複製程式碼

除了簡單的點選手勢外,還有很多豐富的識別功能,適用於從平移、縮放及拖動的所有內容,這也使得構建帶有互動的應用程式變得簡單。

繪畫

Flutter 還提供了很多繪畫相關的 widgets,包括修改不透明度、設定剪下路徑和應用設定。通過使用 CustomPaint widget、 CustomPainter 和 Canvas 類的結合,它還支援普通的繪畫功能。繪畫 widget 的一個示例是 DecoratedBox,可以在螢幕中畫一個 BoxDecoration。下面的例子說明了如何使用 DecoratedBox 和漸變填充填充螢幕。

class MyStatelessWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new DecoratedBox(
      child: Icon(Icons.stars, size: 200.0),
      decoration: new BoxDecoration(
        gradient: LinearGradient(
          begin: Alignment.topCenter,
          end: Alignment.bottomCenter,
          colors: [Colors.red, Colors.blue, Colors.green],
          tileMode: TileMode.mirror
        ),
      ),
    );
  }
}

複製程式碼
[譯] 使用 Flutter 實現跨平臺移動端開發

動畫

Flutter 包含一個 AnimationController 類,它可以控制一段時間內的動畫播放,包括啟動和停止動畫以及將值變為動畫。此外,有一個 AnimatedBuilder 的 widget 允許和 AnimationController 一起使用構建動畫。任何的 widget 都包含它的動畫屬性,像前面展示的裝飾星星。例如,將程式碼重構為一個 StatefulWidget,因為動畫也是 state 變化並且將 AnimationController 傳遞給 State 類允許在構建 widget 時使用動畫值。

class StarWidget extends StatefulWidget {
  @override
  State createState() {
    return StarState();
  }
}

class StarState extends State with SingleTickerProviderStateMixin {
  AnimationController _ac;
  final double _starSize = 300.0;

   @override
  void initState() {
    super.initState();

    _ac = new AnimationController(
      duration: Duration(milliseconds: 750),
      vsync: this,
    );
    _ac.forward();
  }

  @override
  Widget build(BuildContext context) {

    return new AnimatedBuilder(
      animation: _ac,
      builder: (BuildContext context, Widget child) {
        return DecoratedBox(
          child: Icon(Icons.stars, size: _ac.value * _starSize),
          decoration: BoxDecoration(
            gradient: LinearGradient(
              begin: Alignment.topCenter,
              end: Alignment.bottomCenter,
              colors: [Colors.red, Colors.blue, Colors.green],
              tileMode: TileMode.mirror
            ),
          ),
        );
      }
   );
  }
}

複製程式碼

在這種情況下,該值可以用於改變此 widget 的大小。只要動畫值發生變化就會呼叫構建器函式,從而導致當裝飾星星的大小變化超過750毫秒時產生了規模效應。

[譯] 使用 Flutter 實現跨平臺移動端開發

使用原生的功能

平臺通道

為了給 Android 和 iOS 上的原生平臺 APIs 提供支援,Flutter 應用可以使用平臺通道。這將允許 Flutter Dart 程式碼向託管的 iOS 或 Android 應用程式傳送訊息。許多可用的開源外掛都是使用平臺通道上的訊息傳遞構建的。學習如何使用平臺通道,Flutter 文件 [7] 包含了一個訪問原生電池 APIs 的好文件。

總結

即使是 beta 版本,Flutter 也提供了一個很好的構建跨平臺應用程式的解決方案。憑藉其出色的工具和熱載入,給使用者帶來了非常好的開發體驗;豐富的開源軟體包和詳細的文件讓你可以輕鬆入門。接下來,除了 iOS 和 Android,Flutter 的開發者將目標指向了 Fuchsia。考慮到 Flutter 引擎架構的可擴充套件性,看到它應用在其他各種平臺上也不會讓我感到驚訝。隨著社群的發展,現在正是加入 Flutter 的好時機。

擴充套件閱讀

  • 安裝 Flutter: https://flutter.io/get-started/install/

  • Dart 語言之旅: https://www.dartlang.org/guides/language/language-tour

  • Flutter Codelabs: https://flutter.io/codelabs/

  • Flutter Udacity 課程: https://www.udacity.com/course/build-native-mobile-apps-with-flutter--ud905

  • 文章原始碼: https://gist.github.com/mikebluestein/3350443df4689ddac115b68d1598d18e

索引列表

[1] https://www.youtube.com/watch?v=PnIWl33YMwA&feature=youtu.be

[2] https://medium.com/flutter-io/announcing-flutter-beta-1-build-beautiful-native-apps-dc142aea74c0

[3] https://medium.com/flutter-io/https-medium-com-flutter-io-announcing-flutters-beta-2-c85ba1557d5e

[4] https://developers.googleblog.com/2018/05/ready-for-production-apps-flutter-beta-3.html

[5] https://flutter.io/get-started/install/

[6] https://pub.dartlang.org/flutter

[7] https://flutter.io/platform-channels/

文章轉自微信公眾號“全棧探索”,歡迎掃描下面二維碼關注!

[譯] 使用 Flutter 實現跨平臺移動端開發

相關文章