【Flutter】362- 讓前端開發者失業的技術,Flutter Web 初體驗

  • 2019 年 10 月 11 日
  • 筆記

前端的技術真是層出不窮?還學得動不…???

Flutter 是一種新型的 「客戶端」 技術。它的最終目標是替代包含幾乎所有平台的開發:iOS,Android,Web,桌面;做到了一次編寫,多處運行。掌握 Flutter web 可能是 Web 前端開發者翻盤的唯一機會。

在前些日子舉辦的 Google IO 2019 年度開發者大會上,Flutter web 作為一個很亮眼的技術受到了開發者的追捧。這是繼 Flutter 支持 Android、IOS 等設備之後,又一個里程碑式的版本,後續還會支持 windows、linux、Macos、chroms 等其他嵌入式設備。Flutter 本身是一個類似於 RN、WEEX、hHybrid 等多端統一跨平台解決方案,真正做到了一次編寫,多處運行,它的發展超出了很多人的想像,值得前端開發者去關注,今天我們來體驗一下 Flutter Web。

概覽

先了解一下 Flutter, 它是一個由谷歌開發的開源移動應用軟件開發工具包,用於為 Android 和 iOS 開發應用,同時也將是 Google Fuchsia 下開發應用的主要工具。自從 FLutter 1.5.4 版本之後,支持了 Web 端的開發。它採用 Dart 語言來進行開發,與 JavaScript 相比,Dart 在 JIT(即時編譯)模式下,速度與 JavaScript 基本持平。但是當 Dart 以 AOT 模式運行時,Dart 性能要高於 JavaScript。

Flutter 內置了 UI 界面,與 Hybrid App、React Native 這些跨平台技術不同,Flutter 既沒有使用 WebView,也沒有使用各個平台的原生控件,而是本身實現一個統一接口的渲染引擎來繪製 UI,Dart 直接編譯成了二進制文件,這樣做可以保證不同平台 UI 的一致性。它也可以復用 Java、Kotlin、Swift 或 OC 代碼,訪問 Android 和 iOS 上的原生系統功能,比如藍牙、相機、WiFi 等等。我們公司的 Now 直播、企鵝輔導等項目、阿里的閑魚等商業化項目已經大量在使用。

架構

Flutter 的頂層是用 dart 編寫的框架,包含 Material(Android 風格 UI)和 Cupertino(iOS 風格)的 UI 界面,下面是通用的 Widgets(組件),之後是一些動畫、繪製、渲染、手勢庫等。 框架下面是引擎,主要用 C / C ++ 編寫,引擎包含三個核心庫,Skia 是 Flutter 的 2D 渲染引擎,它是 Google 的一個 2D 圖形處理函數庫,包含字型、坐標轉換,以及點陣圖,都有高效能且簡潔的表現。Skia 是跨平台的,並提供了非常友好的 API。第二是 Dart 運行時環境以及第三文本渲染布局引擎。 最底層的嵌入層,它所關心的是如何將圖片組合到屏幕上,渲染變成像素。這一層的功能是用來解決跨平台的。

了解了 FLutter 之後,我來說一下今天的重頭戲,Flutter for Web。要想知道 Flutter 為什麼能在 web 上運行,得先來看看它的架構。

通過對比,可以發現,web 框架層和 mobile 的幾乎一模一樣。因此只需要重新實現一下引擎和嵌入層,不用變動 Flutter API 就可以完全可以將 UI 代碼從 Android / IOS Flutter App 移植到 Web。Dart 能夠使用 Dart2Js 編譯器把 Dart 代碼編譯成 Js 代碼。大多數原生 App 元素能夠通過 DOM 實現,DOM 實現不了的元素可以通過 Canvas 來實現。

安裝

Flutter Web 開發環境搭建,以我的 windows 環境為例進行講解,其他環境類似,安裝環境比較繁瑣,需要耐心,有 Android 開發經驗最好。

1、在 Windows 平台開發的話,官方的環境要求是 Windows 7 SP1 或更高版本(64 位)。

2、Java 環境,安裝 Java 1.8 + 版本之上,並配置環境變量,因為 android 開發依賴 Java 環境。

對於 Java 程序開發而言,主要會使用 JDK 的兩個命令:javac.exe、java.exe。路徑:C:Javajdk1.8.0_181bin。但是這些命令由於不屬於 windows 自己的命令,所以要想使用,就需要進行路徑配置。單擊 「計算機 – 屬性 – 高級系統設置」,單擊「環境變量」。在「系統變量」 欄下單擊「新建」,創建新的系統環境變量(或用戶變量,等效)。

(1) 新建 -> 變量名 "JAVA_HOME",變量值 "C:Javajdk1.8.0_181"(即 JDK 的安裝路徑)

(2) 編輯 -> 變量名 "Path",在原變量值的最後面加上 「;%JAVA_HOME%bin;%JAVA_HOME%jrebin」

(3) 新建 -> 變量名 「CLASSPATH」, 變量值 「.;%JAVA_HOME%lib;%JAVA_HOME%libdt.jar;%JAVA_HOME%libtools.jar」

3、Android Studio 編輯器,安裝 Android Studio, 3.0 或更高版本。我們需要用它來導入 Android license 和管理 Android SDK 以及 Android 虛擬機。(默認安裝即可)

安裝完成之後設置代理,左上角的 File-》setting-》搜索 proxy,設置公司代理,用來加速下載 Android SDK。

之後點擊右上角方盒按鈕(SDK Manager),用來選擇安裝 SDK 版本,最好選 Android 9 版本,API28,會有一個很長時間的下載過程。SDK 是開發必須的代碼庫。默認情況下,Flutter 使用的 Android SDK 版本是基於你的 adb (Android Debug Bridge,管理連接手機,已打包在 SDK)工具版本。如果您想讓 Flutter 使用不同版本的 Android SDK,則必須將該 ANDROID_HOME 環境變量設置為 SDK 安裝目錄。

右上角有個小手機類型的按鈕(AVD Manager),用來設置 Android 模擬器,創建一個虛擬機。如果你有一台安卓手機,也可以連接 USB 接口,替代虛擬機。這個過程是調試必須的。安裝完成之後,在 AVD (Android Virtual Device Manager) 中,點擊工具欄的 Run。模擬器啟動並顯示所選操作系統版本或設備的啟動畫面。代表了正確安裝。

4、安裝 Flutter SDK

下載 Flutter SDK 有多種方法,看看哪種更適合自己: Flutter 官網下載最新 Beta 版本的進行安裝:https://flutter.dev/docs/deve… 也可 Flutter github 項目中去下載,地址為:https://github.com/flutter/fl… 版本越新越好,不要低於 1.5.4。

將安裝包 zip 解壓到你想安裝 Flutter SDK 的路徑(如:C:srcflutter;注意,不要將 flutter 安裝到需要一些高權限的路徑如 C:Program Files)。記住,之後往環境變量的 path 中添加;C:srcflutterbin,以便於你能在命令行中使用 flutter。

使用鏡像 由於在國內安裝 Flutter 相關的依賴可能會受到限制,Flutter 官方為中國開發者搭建了臨時鏡像,大家可以將如下環境變量加入到用戶環境變量中: PUB_HOSTED_URL:https://pub.flutter-io.cn FLUTTER_STORAGE_BASE_URL:https://storage.flutter-io.cn

5、安裝 Dart 與 Pub。安裝 webdev、stagehand

Pub 是 Dart 的包管理工具,類似 npm,捆綁安裝。 Dart 安裝版地址:http://www.gekorm.com/dart-wi…

默認安裝即可,安裝之後記住 Dart 的路徑,並且配置到環境變量 path 中,以便於可以在命令行中使用 dart 與 pub,默認的路徑是:C:Program FilesDartdart-sdkbin

先安裝 stagehand,stagehand 是創建項目必須的工具。查看一下C:UserschunpengliuAppDataRoamingPubCachein目錄下是否包含 stagehand 和 webdev,如果有,添加到環境變量的 path 裏面,如果沒有,按下面方法安裝:

pub global activate stagehand  

webdev 是一個類似於 Koa 的 web 服務器,執行以下命令安裝

pub global activate webdev  # or  flutter packages pub global activate webdev  

6、配置編輯器安裝 Flutter 和 Dart 插件

Flutter 插件是用來支持 Flutter 開發工作流 (運行、調試、熱重載等)。 Dart 插件 提供代碼分析 (輸入代碼時進行驗證、代碼補全等)。Android Studio 的設置在 File-》setting-》plugins-》搜索 Flutter 和 Dart,安裝之後重啟。

VS code 的設置在 extension-》搜索 Flutter 和 Dart,安裝之後重啟。

7、運行 flutter doctor

打開一個新的命令提示符或 PowerShell 窗口並運行以下命令以查看是否需要安裝任何依賴項來完成安裝:

flutter doctor  

這是一個漫長的過程,flutter 會檢測你的環境,並安裝所有的依賴,直至:No issues found!,如果有缺失,會就會再那一項前面打 x。你需要一一解決。

一切就緒!

創建應用

1、啟動 VS Code

調用 View>Command Palette…(快捷鍵 ctrl+shift+p) 輸入 『flutter』, 然後選擇 『Flutter: New web Project』

輸入 Project 名稱 (如 flutterweb), 然後按回車鍵 指定放置項目的位置,然後按藍色的確定按鈕 等待項目創建繼續,並顯示 main.dart 文件。到此,一個 Demo 創建完成。

我們看到了熟悉的 HTML 文件以及項目入口文件 main.dart。

web 目錄下的 index.html 是項目的入口文件。main.dart 初始化文件,圖片相關資源放在此目錄。

lib 目錄下的 main.dart,是主程序代碼所在的地方。

每個 pub 包或者 Flutter 項目都包含一個 pubspec.yaml。它包含與此項目相關的依賴項和元數據。

analysis_options.yaml 是配置項目的 lint 規則。 /dart_tool 是項目打包運行編譯生成的文件,頁面主程序 main.dart.js 就在其中。

2、調試 Demo,打開命令行,進入到項目根目錄,執行:

webdev flutterweb  

編譯、打包完成之後,自動啟動(或者按 F5)默認瀏覽器,看一下轉換後的 HTML 頁面結構:

lib/main.dart 是主程序,源碼非常簡單,整個頁面用 widgets 堆疊而成,區別於傳統的 html 和 css。

import 'package:flutter_web/material.dart';    void main() => runApp(MyApp());    class MyApp extends StatelessWidget {    @override    Widget build(BuildContext context) {      return MaterialApp(        title: 'Flutter Demo',        theme: ThemeData(          primarySwatch: Colors.blue,        ),        home: MyHomePage(title: 'Flutter Demo Home Page'),      );    }  }    class MyHomePage extends StatelessWidget {    MyHomePage({Key key, this.title}) : super(key: key);      final String title;      @override    Widget build(BuildContext context) {      return Scaffold(        appBar: AppBar(          title: Text(title),        ),        body: Center(          child: Column(            mainAxisAlignment: MainAxisAlignment.center,            children: <Widget>[              Text(                'Hello, World!',              ),            ],          ),        ),      );    }  }  

區別與 flutter App 應用,我們導入的是 flutter_web/material.dart 庫而非 flutter/material.dart,這是因為目前 App 的接口並非和 Web 的完全通用,不過隨着谷歌開發的繼續,它們最終會被合併到一塊。

打開 pubspec.yaml(類似於 package.json), 可以看到只有兩個依賴包 flutter_web 和 flutter_web_ui,這兩個都已在 github 上開源。dev 的依賴頁非常少,兩個編譯相關的包,和一個靜態文件分析包。

name: flutterweb  description: An app built using Flutter for web  environment:    # You must be using Flutter >=1.5.0 or Dart >=2.3.0    sdk: '>=2.3.0-dev.0.1 <3.0.0'  dependencies:    flutter_web: any    flutter_web_ui: any  dev_dependencies:    build_runner: ^1.4.0    build_web_compilers: ^2.0.0    pedantic: ^1.0.0  dependency_overrides:    flutter_web:      git:        url: https://github.com/flutter/flutter_web        path: packages/flutter_web    flutter_web_ui:      git:        url: https://github.com/flutter/flutter_web        path: packages/flutter_web_ui  

實戰

接下來,我們創建一個具有圖文功能的下載,根據實例來學習 flutter,我們將實現下圖的頁面。它是一個上下兩欄的布局,下欄又分為左右兩欄。

第一步:更改主應用內容,打開 lib/main.dart 文件,替換 class MyApp,首先是根組件 MyApp,它是一個類組件繼承自無狀態組件,是項目的主題配置,在 home 屬性中調用了 Home 組件:

class MyApp extends StatelessWidget {    // 應用的根組件    @override    Widget build(BuildContext context) {      return MaterialApp(        title: '騰訊新聞客戶端下載頁', //meta 里的titile        debugShowCheckedModeBanner: false, // 關閉調試bar        theme: ThemeData(          primarySwatch: Colors.blue, // 頁面主題 Material風格        ),        home: Home(), // 啟動首頁      );    }  }  

第二步,在 Home 類中,是我們要渲染的頁面頂導,運用了 AppBar 組件,它包括了一個居中的頁面標題和居右的搜索按鈕。文本可以像 css 一樣設置外觀樣式。

class Home extends StatelessWidget {    @override    Widget build(BuildContext context) {      return Scaffold(        backgroundColor: Colors.white,        appBar: AppBar(          backgroundColor: Colors.white,          elevation: 0.0,          centerTitle: true,          title: Text( // 中心文本            "下載頁",            style:                TextStyle(color: Colors.black, fontSize: 16.0, fontWeight: FontWeight.w500),          ),          // 搜索圖標及特性          actions: <Widget>[            Padding(              padding: const EdgeInsets.symmetric(horizontal: 20.0),              child: Icon(                Icons.search,                color: Colors.black,              ),            )          ],        ),        //調用body渲染類,此處可以添加多個方法調用        body: Stack(          children: [              Body()          ],        ),      );    }  }  

第三步,創建頁面主體內容,一張圖加多個文本,使用了文本組件和圖片組件,頁面結構採用了 flex 布局,由於兩個 Expanded 的 Flex 值均為 1,因此將在兩個組件之間平均分配空間。SizedBox 組件相當於一個空盒子,用來設置 margin 的距離

class Body extends StatelessWidget {    const Body({Key key}) : super(key: key);      @override    Widget build(BuildContext context) {      return Row(        crossAxisAlignment: CrossAxisAlignment.stretch,        mainAxisAlignment: MainAxisAlignment.spaceBetween,        children: <Widget>[          Expanded( // 左側            flex: 1,            child: Image.asset(// 圖片組件              "background-image.jpg", // 這是一張在web/asserts/下的背景圖              fit: BoxFit.contain,            ),          ),          const SizedBox(width: 90.0),          Expanded( // 右側            flex:1,            child: Column(              mainAxisAlignment: MainAxisAlignment.center,              crossAxisAlignment: CrossAxisAlignment.start,              children: <Widget>[                Text( // 文本組件                  "騰訊新聞",                  style: TextStyle(                      color: Colors.black, fontWeight: FontWeight.w600, fontSize: 50.0, fontFamily: 'Merriweather'),                ),                const SizedBox(height: 14.0),// SizedBox用來增加間距                Text(                  "騰訊新聞是騰訊公司為用戶打造的一款全天候、全方位、及時報道的新聞產品,為用戶提供高效優質的資訊、視頻和直播服務。資訊超新超全,內容獨家優質,話題評論互動。",                  style: TextStyle(                      color: Colors.black, fontWeight: FontWeight.w400, fontSize: 24.0, fontFamily: "Microsoft Yahei"),                  textAlign: TextAlign.justify,                ),                const SizedBox(height: 20.0),                FlatButton(                  onPressed: () {}, // 下載按鈕的響應事件                  color: Color(0xFFCFE8E4),                  shape: RoundedRectangleBorder(                    borderRadius: BorderRadius.circular(16.0),                  ),                  child: Padding(                    padding: const EdgeInsets.all(12.0),                    child: Text("點擊下載", style: TextStyle(fontFamily: "Open Sans")),                  ),                ),              ],            ),          ),          const SizedBox(width: 100.0),        ],      );    }  }  

到此,頁面創建結束,保存,運行 webdev serve,就可以看到效果了。

總結

FLutter web 是 Flutter 的一個分支,在開發完 App 之後,UI 層面的 FLutter 代碼在不修改的情況下可以直接編譯為 Web 版,基本可以做到代碼 100% 復用,體驗還不錯。目前 Flutter web 作為預覽版無論從性能上、易用上還是布局上都超出了預期,觸摸體驗挺好,雖然體驗比 APP 差一些,但是比傳統的 web 要好很多。試想一下 Flutter 開發 iOS 和 Android 的 App 還免費贈送一份 Web 版,並且比傳統的 web 開發出來的體驗還好。Write once ,Run anywhere。何樂而不為?

我覺得隨着谷歌的持續優化,等到正式版發佈之後,開發體驗越來越好,Flutter 開發者會吃掉 H5 很大一部分份額。Flutter 可能會給目前客戶端的開發模式帶來一些變革以及分工的變化, Flutter 目前的開發體驗不是很好, 但是潛力很大,值得前端人員去學習。

但是目前還是有一部分問題,Flutter web 是為客戶端開發(尤其是安卓)人員開發準備的,對於前端理解來說學習成本有點高。目前 FLutter web 和 flutter 還是兩個項目,編譯環境也是分開的,需要在代碼裏面修改 Flutter 相關庫的引用為 Flutter_web,組件還不能達到完全通用,這個谷歌承諾正在解決中,谷歌的最終目標是 Web、移動 App、桌面端 win mac linux、以及嵌入式版的 Flutter 代碼庫之間保持 100% 的代碼可移植性。

個人感覺,開發體驗還不太好,還有很多坑要去踩,版本變更很快。還有社區資源稀少的問題,需要一定長期的積累。兼容性問題,代碼轉換後大量使用了 web components,除了 chrome 之外,兼容性還是有些問題。

原文來着:騰訊新聞前端團隊 地址:https://segmentfault.com/a/1190000019262939