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

TNFE發表於2019-05-22

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

讓前端開發者失業的技術,Flutter 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 的 Mobile 架構

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

瞭解了FLutter 之後,我來說一下今天的重頭戲,Flutter for Web。要想知道Flutter為什麼能在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:\Java\jdk1.8.0_181bin。但是這些命令由於不屬於windows自己的命令,所以要想使用,就需要進行路徑配置。單擊“計算機-屬性-高階系統設定”,單擊“環境變數”。在“系統變數”欄下單擊“新建”,建立新的系統環境變數(或使用者變數,等效)。

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

(1)新建->變數名"JAVA_HOME",變數值"C:\Java\jdk1.8.0_181"(即JDK的安裝路徑) (2)編輯->變數名"Path",在原變數值的最後面加上“;%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin” (3)新建->變數名“CLASSPATH”,變數值“.;%JAVA_HOME%\lib;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar”

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

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

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

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

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

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

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

4、安裝Flutter SDK

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

將安裝包zip解壓到你想安裝Flutter SDK的路徑(如:C:\src\flutter;注意,不要將flutter安裝到需要一些高許可權的路徑如C:\Program Files\)。記住,之後往環境變數的path中新增;C:\src\flutter\bin,以便於你能在命令列中使用flutter。

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

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

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

Pub是Dart的包管理工具,類似npm,捆綁安裝。 Dart安裝版地址:www.gekorm.com/dart-window… 預設安裝即可,安裝之後記住Dart的路徑,並且配置到環境變數path中,以便於可以在命令列中使用dart與pub,預設的路徑是:C:\Program Files\Dart\dart-sdk\bin 先安裝stagehand,stagehand是建立專案必須的工具。檢視一下C:\Users\chunpengliu\AppData\Roaming\Pub\Cache\bin目錄下是否包含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,安裝之後重啟。

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

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

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

7、執行 flutter doctor

開啟一個新的命令提示符或PowerShell視窗並執行以下命令以檢視是否需要安裝任何依賴項來完成安裝:

flutter doctor
複製程式碼

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

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

一切就緒!

建立應用

1、啟動 VS Code

呼叫 View>Command Palette…(快捷鍵ctrl+shift+p) 輸入 ‘flutter’, 然後選擇 ‘Flutter: New web Project’

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

輸入 Project 名稱 (如flutterweb), 然後按Enter鍵 指定放置專案的位置,然後按藍色的確定按鈕 等待專案建立繼續,並顯示main.dart檔案。到此,一個Demo建立完成。

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

我們看到了熟悉的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頁面結構:

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

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,我們將實現下圖的頁面。它是一個上下兩欄的佈局,下欄又分為左右兩欄。

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

第一步:更改主應用內容,開啟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之外,相容性還是有些問題。

安利時間

我們在web開發過程中,都見過或者使用過一些奇技淫巧,這種技術我們統稱為黑魔法,這些黑魔法散落在各個角落,為了方便大家查閱和學習,我們做了收集、整理和歸類,並在github上做了一個專案——awesome-blackmargic,希望各位愛鑽研的開發者能夠喜歡,也希望大家可以把自己的獨門絕技分享出來,如果有興趣可以給我們發pr。

如果你對Flutter感興趣,想進一步瞭解Flutter,加入我們的QQ群(784383520)吧!

相關文章