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

做者: Mike Bluestein | 譯:孫印鳳html

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

【譯者注:連接序號對應下面索引列表,另外能夠點擊閱讀原文查看詳細的連接文章】git

簡介:Flutter 使得建立跨平臺移動端應用變得垂手可得。本文將介紹 Flutter 並將其與其餘移動端開發平臺進行比較,還會闡述如何使用它來構建應用程序。github

Flutter 是一款由 Google 開發的開源、跨平臺移動端開發框架。它容許使用同一個代碼庫構建高性能、漂亮的 iOS 和 Android 應用,同時它也是 Google 即將推出的 Fuchsia 操做系統的開發平臺。此外,經過自定義的 Flutter 引擎能夠將其嵌入到其餘平臺。web

Flutter 爲何會出現?爲何要使用它呢?

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

  • 在原生應用程序中嵌入 web view ,像構建網站同樣構建應用程序。
  • 封裝原平生臺裏的控件併爲它們提供一些跨平臺的參數。

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

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 在工具方面提供了使人難以置信的靈活性。就像能夠從支持的 IDE( 好比 VS CodeAndroid Studio、或 IntelliJ )中進行開發同樣,應用程序能夠簡單的在任何編輯器的命令行中進行開發。使用何種開發工具很大程度上取決於開發者的喜愛。Android Studio提供了大部分的功能,好比用於分析正在運行應用的 widgets 以及監控應用程序性能的 Flutter 檢查器。它還提供了一些重構模板,在開發帶有層次結構的 widget 時用起來將會很方便;VS Code 提供了更加輕快的開發體驗,由於它啓動的速度比 Android StudioIntelliJ 都要快,並且每一個 IDE 都內置了編輯助手,例如代碼補全、各類 API 處理以及良好的調試支持;命令行也很好的支持了 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 鋪滿了屏幕。

無狀態和有狀態 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 樹。

還要注意可使用 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),
      ],
    );
  }
}

複製代碼

響應觸摸

觸摸交互是由手勢處理的,手勢是封裝在 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 包含一個 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毫秒時產生了規模效應。

使用原生的功能

平臺通道

爲了給 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/

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

相關文章
相關標籤/搜索