乐趣区

让前端开发者失业的技术Flutter-Web初体验

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 的顶层是用 drat 编写的框架,包含 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:\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,安装之后重启。

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 之外,兼容性还是有些问题。

安利时间

我们在 web 开发过程中,都见过或者使用过一些奇技淫巧,这种技术我们统称为黑魔法,这些黑魔法散落在各个角落,为了方便大家查阅和学习,我们做了收集、整理和归类,并在 github 上做了一个项目——awesome-blackmargic,希望各位爱钻研的开发者能够喜欢,也希望大家可以把自己的独门绝技分享出来,如果有兴趣可以给我们发 pr。

如果你对 Flutter 感兴趣,想进一步了解 Flutter,加入我们的 QQ 群(784383520)吧!

退出移动版