Flutter 初体验

3,151 阅读9分钟

初识 Flutter 还是在上个月的 GMTC 大会上。来自 Google 的工程师现场演示了如何使用 Flutter 构建美观、高性能的移动应用。个人对其中一些特性,比如良好的开发者体验、优秀的跨平台能力很感兴趣。于是决定在会后亲自体验一下。

最近几天尝试使用 Flutter 把京东 APP 中的排行榜频道的首屏布局实现了一下,算是对基于 Flutter 的开发有了一个最简单的了解,特地记录一下,方便其他想了解、尝试 Flutter 框架的小伙伴。

首先看看我实现了一个什么样的界面:

接下来让我们从不同方面说说 Flutter 的开发。

Flutter 开发环境搭建

安装 Flutter

不同平台的安装流程基本一致,这里就以 macOS 为例。首先下载 flutter macOS 版,并解压。

把 Flutter 的 bin 目录添加到系统 PATH 中:

export PATH=~/Software/flutter/bin:$PATH

提示:记得把这行代码放到你的 shell 启动脚本中,避免每次都要手动执行。

路径添加完成后就可以执行 flutter 命令啦。 Flutter 提供了一个 flutter doctor 命令协助我们安装 Flutter 的依赖。它会检查本地是否有 iOS 和 Android 的开发环境。如果检测到依赖缺失,它还会给出对应依赖的安装方法。你只需要不断执行该方法,然后安装缺失的依赖,直到全部依赖安装完成即可。

配置代码编辑器

主要是给 IDE 安装相关插件。

VS Code 上只需要安装 flutter 扩展即可。 Android Studio 上需要安装 flutter 和 dart 两个插件。

这里简单说一下两个 IDE 的差异。从开发过程来说,VS Code 完胜 Android Studio。VS Code 提供了非常好的代码提示功能。以下图为例,鼠标移到某个对象或方法上,VS Code 会给出非常详细的信息。

而在 Android Studio 中,或许是 Android Studio 上的 dart 插件目前功能还不够完善的原因,IDE 能提供给我的只有一行干瘪的函数签名。

在调试阶段,两款 IDE 都提供了完善的断点调试机制。区别在于,Android Studio 还提供了一个名为 Flutter Inspector 的工具。你可以把它理解成为 Flutter 版的审查元素。

综合以上几点,目前我在编写代码以及调试简单的逻辑问题时会使用 VS Code,在遇到布局问题时会启动 Android Studio。具体的选择大家根据自己的个人喜好决定即可。

配置依赖源镜像

Flutter 项目有两个依赖源。

第一个就是 Flutter 框架自身的源。每当 Flutter 框架更新后,我们可以从源中更新最新的 Flutter 框架代码。 此外,由于 Flutter 使用 Dart 作为其开发语言。Dart 语言的 packages 统一在 pub.dartlang.org/ 维护,这就是第二个依赖源。

这两个源目前都是由 Google 在维护,而由于众所周知的原因,从国内访问 Google 的服务非常的不稳定。因此需要配置这两个源的国内镜像:

export PUB_HOSTED_URL=https://pub.flutter-io.cn
export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn

提示:记得把这些代码放到你的 shell 启动脚本中,避免每次都要手动执行。

配置调试设备

调试设备又分为两类:真机 or 模拟器。我们分别介绍。

iOS 模拟器

这一项是唯一一个不需要任何配置的。只要按照之前的步骤正确安装依赖。直接在 IDE 中开始调试就会自动启动一个 iPhone X 的模拟器。

Android 模拟器

Android Studio 默认是不带设备模拟器的,需要手动添加。

  1. 启动 Android Studio
  2. Tools -> AVD Manager
  3. 点击 “Create Virtual Devices…”
  4. 选择一个手机型号(其实就是一个屏幕分辨率和像素密度的组合),点击“Next”
  5. 选择并下载一个 Android 镜像。点击“Next”
  6. 点击“Finish”完成虚拟设备的创建。

iOS 真机

需要分别在电脑端和手机端进行配置。

电脑端

进入项目目录,执行命令:

open ios/Runner.xcworkspace

在弹出的 Xcode 界面依次执行以下操作:

注意:这里说的开发者账号可以是你个人的 Apple ID,不必是花 99 刀购买的正式的开发者账号。

手机端

在完成电脑端的配置后,使用数据线将 iOS 设备接入电脑。如果弹出“要信任此电脑吗?”的弹框,请选择“信任”。

然后进入 iOS 的设置界面,依次进入 “通用” -> “描述文件与设备管理”。在这个界面你会看到一个以你 Apple ID 命名的开发者应用证书,选择信任。

至此 iOS 的真机调试就配置完成了。

Android 真机

电脑端

如果是 Windows 系统,请安装 Google USB Driver。其他操作系统无需特殊配置。

手机端

  1. 开启“开发者模式”和“USB调试”选项。
  2. 将手机通过数据线连接电脑,如果提示“是否允许USB调试”,选择“是”。

至此,Flutter 的开发环境就基本搭建完成了。

Dart

在说 Flutter 之前,先来聊聊 Flutter 项目的开发语言 —— Dart。Dart 是由 Google 开发的一种面向对象语言。它也可以编译成 JavaScript,也有类型系统。很多人可能会把它和 TypeScript 比较,两者在这两点上还是很相似的。这里简单说一下。

TypeScript 是 JavaScript 的超集。这意味所有的 JavaScript 代码也都是合法的 TypeScript 代码。TypeScript 在原有 JavaScript 语法的基础之上提供了一套镜头类型检查系统,可以帮助我们有效的减少由类型不一致导致的代码问题。另外,TypeScript 必须编译成 JavaScript 才可以被执行。

Dart 本身除了可以编译成 JavaScript 外就和 JavaScript 没什么关系了。它其实是一门完全独立的语言。除了编译成 JavaScript 在 JavaScript 引擎上执行外,Dart 还可以编译成 ARM 和 x86 代码直接运行在 iOS、Android 设备上。Dart 同时支持 JITAOT

由于是一门完全独立的语言,和 JavaScript 没有任何关系,在学习的过程中请时刻保持清晰的思路。不要把两个语言中类似的概念混淆,否则很容易造成困扰。

学习资源的话推荐官方的这篇 A Tour of the Dart Language。Dart 语言中最基础,最常用的语法都有介绍。建议一开始通读一遍。

在具体使用过程中如果对某个类的某个方法不清楚,可以去 Dart SDK 文档站搜索具体的类查看完整的文档。

最后,如果你疑惑 Google 为何会选择一个非常冷门的 Dart 而不是更为流行的 JavaScript/TypeScript 作为 Flutter 框架的开发语言,那么强烈推荐你阅读这篇由 Flutter 团队成员写的 Why Flutter Uses Dart

Flutter

终于,终于要说到 Flutter 框架自身了。由于这两天的开发过程中其实也只是使用了 Flutter 框架中很少的一部分功能。所以这里只就我认为对 Flutter 入门最需要理解的两点来展开聊聊。

Widget

在 Flutter 中所有展示在页面上的元素其实都是一个 Widget。整个 APP/页面对应一个顶级 Widget。

根据 Widget 内部是否存在可变状态,在 Flutter 中又可分为 StatelessWidget 和 StatefulWidget。

顾名思义,StatelessWidget 就是指无可变状态的 Widget。换句话说,这类 Widget 的状态只由创建 Widget 时传入的参数决定,一旦创建,其状态、在页面上的展示效果也就不再改变。

而 StatefulWidget 内部则存在着可变状态。当这些状态改变时,Flutter 会重新渲染该 Widget。

在实际项目开发过程中,我们要做的就是根据实际需求开发不同的 Widget,并将这些 Widget 组合嵌套以形成一个完整的页面。在开发之前我们首先要做的就是根据页面的功能划分不同的功能模块,将这些模块细分成众多独立的 Widget。然后再逐一实现其逻辑。

布局、样式

首先从宏观上来说,Flutter 中的布局、样式中绝大多数的概念其实还是沿用了 CSS 中的概念。例如在布局方面与 CSS 中 flex 布局对应的有 Row、Column 两个 Widget,分别提供了水平和垂直两个方向的布局方式。再比如 Stack Widget 提供了一种 Widget 之间相互堆叠的机制,这又和 CSS 中的 position: absolute; 很像。

点此查看 Flutter 中所有和布局相关的 Widget。

概念上的相似是不是就可以让我们轻松上手了呢?其实并不是,因为在具体的代码层面,为 Flutter 中的 Widget 添加样式 和为一个 HTML 元素添加样式还是有着很大的差别。这些差别主要表现在以下两个方面:

  • 不是所有 Widget 都可以添加任意的样式属性。举例来说,如果你想给一段文字添加一个 border。你必须创建一个 Container,把这段问题设置为这个 Container 的 child。然后给这个 Container 设置一个 BoxDecoration 属性,并在该属性中设置具体的边框样式。像下面这样:

    Container(
      decoration: BoxDecoration(
        border: Border.all(color: Colors.red)
      ),
      child: new Text("My Awesome Border"),
    )
  • 由于 Dart 面向对象的特点,基本上所有的样式属性都不在支持以字符串的形式书写,而是必须创建特定类的实例或是使用 Flutter 中预先定义好的常量,再看一个例子:

    ListView.builder(
      scrollDirection: Axis.horizontal,
      padding: EdgeInsets.all(10.0),
      itemCount: subCategories.length,
      itemBuilder: (BuildContext context, int index) {
    
      }
    )

    这里为了指定 ListView 的滚动方向,我们使用了 Flutter 中预先定义好的 Axis.horizontal 常量,为了表示 4 个方向上的 padding 值,我们创建了一个 EdgeInsets 类的实例。

以上两个方面在我们刚开始使用 Flutter 开发时会造成一定的困扰,大可不必慌张。只需要一段时间的适应过程,适应之后再配合 IDE 的智能提示功能,开发效率可以得到很大的提升。

以上就是过去几天我在尝试使用 Flutter 框架开发一个完整页面的过程中积累的一些经验和思考。欢迎对 Flutter 感兴趣的小伙伴一起学习,讨论。