讓前端開(kāi)發(fā)者失業(yè)的技術(shù),F(xiàn)lutter Web初體驗(yàn)
Flutter是一種新型的“客戶端”技術(shù)。它的最終目標(biāo)是替代包含幾乎所有平臺(tái)的開(kāi)發(fā):iOS,Android,Web,桌面;做到了一次編寫(xiě),多處運(yùn)行。掌握Flutter web可能是Web前端開(kāi)發(fā)者翻盤(pán)的唯一機(jī)會(huì)。
在前些日子舉辦的Google IO 2019 年度開(kāi)發(fā)者大會(huì)上,F(xiàn)lutter web作為一個(gè)很亮眼的技術(shù)受到了開(kāi)發(fā)者的追捧。這是繼Flutter支持Android、IOS等設(shè)備之后,又一個(gè)里程碑式的版本,后續(xù)還會(huì)支持windows、linux、Macos、chroms等其他嵌入式設(shè)備。Flutter本身是一個(gè)類(lèi)似于RN、WEEX、hHybrid等多端統(tǒng)一跨平臺(tái)解決方案,真正做到了一次編寫(xiě),多處運(yùn)行,它的發(fā)展超出了很多人的想象,值得前端開(kāi)發(fā)者去關(guān)注,今天我們來(lái)體驗(yàn)一下Flutter Web。
概覽
先了解一下Flutter, 它是一個(gè)由谷歌開(kāi)發(fā)的開(kāi)源移動(dòng)應(yīng)用軟件開(kāi)發(fā)工具包,用于為Android和iOS開(kāi)發(fā)應(yīng)用,同時(shí)也將是Google Fuchsia下開(kāi)發(fā)應(yīng)用的主要工具。自從FLutter 1.5.4版本之后,支持了Web端的開(kāi)發(fā)。它采用Dart語(yǔ)言來(lái)進(jìn)行開(kāi)發(fā),與JavaScript相比,Dart在 JIT(即時(shí)編譯)模式下,速度與 JavaScript基本持平。但是當(dāng)Dart以 AOT模式運(yùn)行時(shí),Dart性能要高于JavaScript。
Flutter內(nèi)置了UI界面,與Hybrid App、React Native這些跨平臺(tái)技術(shù)不同,F(xiàn)lutter既沒(méi)有使用WebView,也沒(méi)有使用各個(gè)平臺(tái)的原生控件,而是本身實(shí)現(xiàn)一個(gè)統(tǒng)一接口的渲染引擎來(lái)繪制UI,Dart直接編譯成了二進(jìn)制文件,這樣做可以保證不同平臺(tái)UI的一致性。它也可以復(fù)用Java、Kotlin、Swift或OC代碼,訪問(wèn)Android和iOS上的原生系統(tǒng)功能,比如藍(lán)牙、相機(jī)、WiFi等等。我們公司的Now直播、企鵝輔導(dǎo)等項(xiàng)目、阿里的閑魚(yú)等商業(yè)化項(xiàng)目已經(jīng)大量在使用。
架構(gòu)
Flutter的頂層是用drat編寫(xiě)的框架,包含Material(Android風(fēng)格UI)和Cupertino(iOS風(fēng)格)的UI界面,下面是通用的Widgets(組件),之后是一些動(dòng)畫(huà)、繪制、渲染、手勢(shì)庫(kù)等。
框架下面是引擎,主要用C / C ++編寫(xiě),引擎包含三個(gè)核心庫(kù),Skia是Flutter的2D渲染引擎,它是Google的一個(gè)2D圖形處理函數(shù)庫(kù),包含字型、坐標(biāo)轉(zhuǎn)換,以及點(diǎn)陣圖,都有高效能且簡(jiǎn)潔的表現(xiàn)。Skia是跨平臺(tái)的,并提供了非常友好的API。第二是Dart 運(yùn)行時(shí)環(huán)境以及第三文本渲染布局引擎。
最底層的嵌入層,它所關(guān)心的是如何將圖片組合到屏幕上,渲染變成像素。這一層的功能是用來(lái)解決跨平臺(tái)的。
了解了FLutter 之后,我來(lái)說(shuō)一下今天的重頭戲,F(xiàn)lutter for Web。要想知道Flutter為什么能在web上運(yùn)行,得先來(lái)看看它的架構(gòu)。
通過(guò)對(duì)比,可以發(fā)現(xiàn),web框架層和mobile的幾乎一模一樣。因此只需要重新實(shí)現(xiàn)一下引擎和嵌入層,不用變動(dòng)Flutter API就可以完全可以將UI代碼從Android / IOS Flutter App移植到Web。Dart能夠使用Dart2Js編譯器把Dart代碼編譯成Js代碼。大多數(shù)原生App元素能夠通過(guò)DOM實(shí)現(xiàn),DOM實(shí)現(xiàn)不了的元素可以通過(guò)Canvas來(lái)實(shí)現(xiàn)。
安裝
Flutter Web開(kāi)發(fā)環(huán)境搭建,以我的windows環(huán)境為例進(jìn)行講解,其他環(huán)境類(lèi)似,安裝環(huán)境比較繁瑣,需要耐心,有Android開(kāi)發(fā)經(jīng)驗(yàn)最好。
1、在Windows平臺(tái)開(kāi)發(fā)的話,官方的環(huán)境要求是Windows 7 SP1或更高版本(64位)。
2、Java環(huán)境,安裝Java 1.8 + 版本之上,并配置環(huán)境變量,因?yàn)閍ndroid開(kāi)發(fā)依賴(lài)Java環(huán)境。
對(duì)于Java程序開(kāi)發(fā)而言,主要會(huì)使用JDK的兩個(gè)命令:javac.exe、java.exe。路徑:C:Javajdk1.8.0_181bin。但是這些命令由于不屬于windows自己的命令,所以要想使用,就需要進(jìn)行路徑配置。單擊“計(jì)算機(jī)-屬性-高級(jí)系統(tǒng)設(shè)置”,單擊“環(huán)境變量”。在“系統(tǒng)變量”欄下單擊“新建”,創(chuàng)建新的系統(tǒng)環(huán)境變量(或用戶變量,等效)。
(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或更高版本。我們需要用它來(lái)導(dǎo)入Android license和管理Android SDK以及Android虛擬機(jī)。(默認(rèn)安裝即可)
安裝完成之后設(shè)置代理,左上角的File-》setting-》搜索proxy,設(shè)置公司代理,用來(lái)加速下載Android SDK。
之后點(diǎn)擊右上角方盒按鈕(SDK Manager),用來(lái)選擇安裝SDK版本,最好選Android 9版本,API28,會(huì)有一個(gè)很長(zhǎng)時(shí)間的下載過(guò)程。SDK是開(kāi)發(fā)必須的代碼庫(kù)。默認(rèn)情況下,F(xiàn)lutter使用的Android SDK版本是基于你的 adb (Android Debug Bridge,管理連接手機(jī),已打包在SDK)工具版本。 如果您想讓Flutter使用不同版本的Android SDK,則必須將該 ANDROID_HOME 環(huán)境變量設(shè)置為SDK安裝目錄。
右上角有個(gè)小手機(jī)類(lèi)型的按鈕(AVD Manager),用來(lái)設(shè)置Android模擬器,創(chuàng)建一個(gè)虛擬機(jī)。如果你有一臺(tái)安卓手機(jī),也可以連接USB接口,替代虛擬機(jī)。這個(gè)過(guò)程是調(diào)試必須的。安裝完成之后,在 AVD (Android Virtual Device Manager) 中,點(diǎn)擊工具欄的 Run。模擬器啟動(dòng)并顯示所選操作系統(tǒng)版本或設(shè)備的啟動(dòng)畫(huà)面。代表了正確安裝。
4、安裝Flutter SDK
下載Flutter SDK有多種方法,看看哪種更適合自己:
Flutter官網(wǎng)下載最新Beta版本的進(jìn)行安裝: https://flutter.dev/docs/deve...
也可Flutter github項(xiàng)目中去下載,地址為: https://github.com/flutter/fl...
版本越新越好,不要低于1.5.4。
將安裝包zip解壓到你想安裝Flutter SDK的路徑(如:C:srcflutter;注意,不要將flutter安裝到需要一些高權(quán)限的路徑如C:Program Files)。記住,之后往環(huán)境變量的path中添加;C:srcflutterbin,以便于你能在命令行中使用flutter。
使用鏡像
由于在國(guó)內(nèi)安裝Flutter相關(guān)的依賴(lài)可能會(huì)受到限制,F(xiàn)lutter官方為中國(guó)開(kāi)發(fā)者搭建了臨時(shí)鏡像,大家可以將如下環(huán)境變量加入到用戶環(huán)境變量中:
PUB_HOSTED_URL:https://pub.flutter-io.cn
FLUTTER_STORAGE_BASE_URL: https://storage.flutter-io.cn
5、安裝Dart與Pub。安裝webdev、stagehand
Pub是Dart的包管理工具,類(lèi)似npm,捆綁安裝。
Dart安裝版地址: http://www.gekorm.com/dart-wi...
默認(rèn)安裝即可,安裝之后記住Dart的路徑,并且配置到環(huán)境變量path中,以便于可以在命令行中使用dart與pub,默認(rèn)的路徑是:C:Program FilesDartdart-sdkbin
先安裝stagehand,stagehand是創(chuàng)建項(xiàng)目必須的工具。查看一下 C:\Users\chunpengliu\AppData\Roaming\Pub\Cache\bin
目錄下是否包含stagehand和webdev,如果有,添加到環(huán)境變量的path里面,如果沒(méi)有,按下面方法安裝:
- pub global activate stagehand
webdev是一個(gè)類(lèi)似于Koa的web服務(wù)器,執(zhí)行以下命令安裝
- pub global activate webdev
- # or
- flutter packages pub global activate webdev
6、配置編輯器安裝Flutter和Dart插件
Flutter插件是用來(lái)支持Flutter開(kāi)發(fā)工作流 (運(yùn)行、調(diào)試、熱重載等)。
Dart插件 提供代碼分析 (輸入代碼時(shí)進(jìn)行驗(yàn)證、代碼補(bǔ)全等)。Android Studio的設(shè)置在File-》setting-》plugins-》搜索Flutter和Dart,安裝之后重啟。
VS code的設(shè)置在extension-》搜索Flutter和Dart,安裝之后重啟。
7、運(yùn)行 flutter doctor
打開(kāi)一個(gè)新的命令提示符或PowerShell窗口并運(yùn)行以下命令以查看是否需要安裝任何依賴(lài)項(xiàng)來(lái)完成安裝:
- flutter doctor
這是一個(gè)漫長(zhǎng)的過(guò)程,flutter會(huì)檢測(cè)你的環(huán)境,并安裝所有的依賴(lài),直至:No issues found!,如果有缺失,會(huì)就會(huì)再那一項(xiàng)前面打x。你需要一一解決。
一切就緒!
創(chuàng)建應(yīng)用
1、啟動(dòng) VS Code
調(diào)用 View>Command Palette…(快捷鍵ctrl+shift+p)
輸入 ‘flutter’, 然后選擇 ‘Flutter: New web Project’
輸入 Project 名稱(chēng) (如flutterweb), 然后按回車(chē)鍵
指定放置項(xiàng)目的位置,然后按藍(lán)色的確定按鈕
等待項(xiàng)目創(chuàng)建繼續(xù),并顯示main.dart文件。到此,一個(gè)Demo創(chuàng)建完成。
我們看到了熟悉的HTML文件以及項(xiàng)目入口文件main.dart。
web目錄下的index.html是項(xiàng)目的入口文件。main.dart初始化文件,圖片相關(guān)資源放在此目錄。
lib目錄下的main.dart,是主程序代碼所在的地方。
每個(gè)pub包或者Flutter項(xiàng)目都包含一個(gè)pubspec.yaml。它包含與此項(xiàng)目相關(guān)的依賴(lài)項(xiàng)和元數(shù)據(jù)。
analysis_options.yaml是配置項(xiàng)目的lint規(guī)則。
/dart_tool 是項(xiàng)目打包運(yùn)行編譯生成的文件,頁(yè)面主程序main.dart.js就在其中。
2、調(diào)試Demo,打開(kāi)命令行,進(jìn)入到項(xiàng)目根目錄,執(zhí)行:
- webdev flutterweb
編譯、打包完成之后,自動(dòng)啟動(dòng)(或者按F5)默認(rèn)瀏覽器,看一下轉(zhuǎn)換后的HTML頁(yè)面結(jié)構(gòu):
lib/main.dart是主程序,源碼非常簡(jiǎn)單,整個(gè)頁(yè)面用widgets堆疊而成,區(qū)別于傳統(tǒng)的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!',
- ),
- ],
- ),
- ),
- );
- }
- }
區(qū)別與flutter App應(yīng)用,我們導(dǎo)入的是flutter_web/material.dart庫(kù)而非flutter/material.dart,這是因?yàn)槟壳癆pp的接口并非和Web的完全通用,不過(guò)隨著谷歌開(kāi)發(fā)的繼續(xù),它們最終會(huì)被合并到一塊。
打開(kāi)pubspec.yaml(類(lèi)似于package.json),可以看到只有兩個(gè)依賴(lài)包flutter_web和flutter_web_ui,這兩個(gè)都已在github上開(kāi)源。dev的依賴(lài)頁(yè)非常少,兩個(gè)編譯相關(guān)的包,和一個(gè)靜態(tài)文件分析包。
- 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
實(shí)戰(zhàn)
接下來(lái),我們創(chuàng)建一個(gè)具有圖文功能的下載,根據(jù)實(shí)例來(lái)學(xué)習(xí)flutter,我們將實(shí)現(xiàn)下圖的頁(yè)面。它是一個(gè)上下兩欄的布局,下欄又分為左右兩欄。
第一步:更改主應(yīng)用內(nèi)容,打開(kāi)lib/main.dart文件,替換class MyApp,首先是根組件MyApp,它是一個(gè)類(lèi)組件繼承自無(wú)狀態(tài)組件,是項(xiàng)目的主題配置,在home屬性中調(diào)用了Home組件:
- class MyApp extends StatelessWidget {
- // 應(yīng)用的根組件
- @override
- Widget build(BuildContext context) {
- return MaterialApp(
- title: '騰訊新聞客戶端下載頁(yè)', //meta 里的titile
- debugShowCheckedModeBanner: false, // 關(guān)閉調(diào)試bar
- theme: ThemeData(
- primarySwatch: Colors.blue, // 頁(yè)面主題 Material風(fēng)格
- ),
- home: Home(), // 啟動(dòng)首頁(yè)
- );
- }
- }
第二步,在Home類(lèi)中,是我們要渲染的頁(yè)面頂導(dǎo),運(yùn)用了AppBar組件,它包括了一個(gè)居中的頁(yè)面標(biāo)題和居右的搜索按鈕。文本可以像css一樣設(shè)置外觀樣式。
- 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( // 中心文本
- "下載頁(yè)",
- style:
- TextStyle(color: Colors.black, fontSize: 16.0, fontWeight: FontWeight.w500),
- ),
- // 搜索圖標(biāo)及特性
- actions: <Widget>[
- Padding(
- padding: const EdgeInsets.symmetric(horizontal: 20.0),
- child: Icon(
- Icons.search,
- color: Colors.black,
- ),
- )
- ],
- ),
- //調(diào)用body渲染類(lèi),此處可以添加多個(gè)方法調(diào)用
- body: Stack(
- children: [
- Body()
- ],
- ),
- );
- }
- }
第三步,創(chuàng)建頁(yè)面主體內(nèi)容,一張圖加多個(gè)文本,使用了文本組件和圖片組件,頁(yè)面結(jié)構(gòu)采用了flex布局,由于兩個(gè)Expanded的Flex值均為1,因此將在兩個(gè)組件之間平均分配空間。SizedBox組件相當(dāng)于一個(gè)空盒子,用來(lái)設(shè)置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( // 左側(cè)
- flex: 1,
- child: Image.asset(// 圖片組件
- "background-image.jpg", // 這是一張?jiān)趙eb/asserts/下的背景圖
- fit: BoxFit.contain,
- ),
- ),
- const SizedBox(width: 90.0),
- Expanded( // 右側(cè)
- 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用來(lái)增加間距
- Text(
- "騰訊新聞是騰訊公司為用戶打造的一款全天候、全方位、及時(shí)報(bào)道的新聞產(chǎn)品,為用戶提供高效優(yōu)質(zhì)的資訊、視頻和直播服務(wù)。資訊超新超全,內(nèi)容獨(dú)家優(yōu)質(zhì),話題評(píng)論互動(dòng)。",
- style: TextStyle(
- color: Colors.black, fontWeight: FontWeight.w400, fontSize: 24.0, fontFamily: "Microsoft Yahei"),
- textAlign: TextAlign.justify,
- ),
- const SizedBox(height: 20.0),
- FlatButton(
- onPressed: () {}, // 下載按鈕的響應(yīng)事件
- color: Color(0xFFCFE8E4),
- shape: RoundedRectangleBorder(
- borderRadius: BorderRadius.circular(16.0),
- ),
- child: Padding(
- padding: const EdgeInsets.all(12.0),
- child: Text("點(diǎn)擊下載", style: TextStyle(fontFamily: "Open Sans")),
- ),
- ),
- ],
- ),
- ),
- const SizedBox(width: 100.0),
- ],
- );
- }
- }
到此,頁(yè)面創(chuàng)建結(jié)束,保存,運(yùn)行webdev serve,就可以看到效果了。
總結(jié)
FLutter web是Flutter 的一個(gè)分支,在開(kāi)發(fā)完App之后,UI層面的FLutter代碼在不修改的情況下可以直接編譯為Web版,基本可以做到代碼100%復(fù)用,體驗(yàn)還不錯(cuò)。目前Flutter web作為預(yù)覽版無(wú)論從性能上、易用上還是布局上都超出了預(yù)期,觸摸體驗(yàn)挺好,雖然體驗(yàn)比APP差一些,但是比傳統(tǒng)的web要好很多。試想一下 Flutter 開(kāi)發(fā)iOS 和Android的App 還免費(fèi)贈(zèng)送一份Web版,并且比傳統(tǒng)的web開(kāi)發(fā)出來(lái)的體驗(yàn)還好。Write once ,Run anywhere。何樂(lè)而不為?
我覺(jué)得隨著谷歌的持續(xù)優(yōu)化,等到正式版發(fā)布之后,開(kāi)發(fā)體驗(yàn)越來(lái)越好,F(xiàn)lutter開(kāi)發(fā)者會(huì)吃掉H5很大一部分份額。Flutter 可能會(huì)給目前客戶端的開(kāi)發(fā)模式帶來(lái)一些變革以及分工的變化, Flutter目前的開(kāi)發(fā)體驗(yàn)不是很好, 但是潛力很大,值得前端人員去學(xué)習(xí)。
但是目前還是有一部分問(wèn)題,F(xiàn)lutter web是為客戶端開(kāi)發(fā)(尤其是安卓)人員開(kāi)發(fā)準(zhǔn)備的,對(duì)于前端理解來(lái)說(shuō)學(xué)習(xí)成本有點(diǎn)高。目前FLutter web和 flutter 還是兩個(gè)項(xiàng)目,編譯環(huán)境也是分開(kāi)的,需要在代碼里面修改Flutter相關(guān)庫(kù)的引用為Flutter_web,組件還不能達(dá)到完全通用,這個(gè)谷歌承諾正在解決中,谷歌的最終目標(biāo)是Web、移動(dòng)App、桌面端win mac linux、以及嵌入式版的Flutter代碼庫(kù)之間保持100%的代碼可移植性。
個(gè)人感覺(jué),開(kāi)發(fā)體驗(yàn)還不太好,還有很多坑要去踩,版本變更很快。還有社區(qū)資源稀少的問(wèn)題,需要一定長(zhǎng)期的積累。兼容性問(wèn)題,代碼轉(zhuǎn)換后大量使用了web components,除了chrome之外,兼容性還是有些問(wèn)題。