搭建Flutter Web开发调试环境

背景

关于参与 Flutter 项目的共建,从 framework 到 engine,本文提供了环境搭建到调试的主要流程说明,可以快速帮助你搭建起来。

由于侧重讲述 Flutter web 平台的建设,可以通过本文提供的官方文档链接获得其它平台更多指引。

搭建 framework 开发环境

参考官方文档:Setting up the Framework development environment

需要具备这些条件搭建 framework 开发环境:

  • Linux,macOS 或 Windows。

  • git(代码版本管理)。

  • IDE:Android Studiovscode 等主流IDE可安装插件支持 flutter/Dart 代码高亮。

  • Python( 一些工具需要使用)。

  • ssh 客户端(GitHub 身份验证)。


请先 参考 Connecting to GitHub with SSH配置 Github 认证需要的 SSH Key

  1. 检查是否已经有 SSH 密钥:Reviewing your SSH keys
  2. 生成新的 SSH 密钥对(id_rsa,id_rsa.pub):Generating a new SSH key and adding it to the ssh-agent
  3. 将 SSH 公钥(id_rsa.pub)上传到 github:Adding a new SSH key to your GitHub account - Settings - SSH and GPG keys
  4. git clone 使用 SSH 地址替代 HTTPS 链接:Switching remote URLs from HTTPS to SSH

按照以下步骤搭建 Framework 开发环境:

  1. Fork https://github.com/flutter/flutter 到自己的 GitHub 账号。如果已经 frok 过了,可先更新 Sync Fork。

  2. git clone git@github.com:<github_username>/flutter.git (替换 <github_username> 为你的 GitHub 账户名)。如果是参与外部开源项目,可考虑执行 git config 命令配置个人开发者用户名和邮箱:

    • git config user.name <github_username>
    • git config user.email <github_useremail>
  3. cd flutter

  4. git remote add upstream git@github.com:flutter/flutter.git:添加跟踪官方上游源仓(fetch-merge同步)。

  5. 配置仓库的 flutter/bin 目录到环境变量,这样后续将使用本地编译出来的 SDK/Framework 工具链。

$ export PATH=/path/to/flutter/bin:$PATH
  • 执行 vim ~/.zhsrc 编辑 zsh 配置文件,将flutter仓库编译产物目录bin前插到环境变量 PATH(override fvm):
    • 为了后续脚本引用方便,可以将一些常用目录定义成环境变量。
# ~/.zhsrc

# fvm home
export FVM_HOME="$HOME/.fvm"
export PATH=$HOME/.fvm/default/bin:$PATH

# flutter project
export FLUTTER_PROJECT_DIR=$HOME/Projects/github/flutter
# framework(sdk)
export FLUTTER_SDK_PROJECT_DIR=$FLUTTER_PROJECT_DIR/flutter
# override fvm
export PATH=$FLUTTER_SDK_PROJECT_DIR/bin:$PATH
## packages/flutter
export FLUTTER_SDK_PACKAGES=$FLUTTER_SDK_PROJECT_DIR/packages
export FLUTTER_SDK_SRCROOT=$FLUTTER_SDK_PACKAGES/flutter
  1. flutter update-packages (拉取 Flutter 依赖的 Dart 包)。
  2. 执行 which flutterflutter --version 验证,flutter 工具链版本是否符合预期。

修改调试 framework 源码

用 IDE 打开 packages/flutter 目录(FLUTTER_SDK_SRCROOT)可以修改 framework 源码。


用 IDE 打开示例项目,确保已经安装了 Flutter/Dart 相关语言支持插件。
指定 Flutter SDK 目录(即上面第 3 步 clone 到本地的 SDK/Framework 目录),具体步骤如下:

  1. vscode 打开偏好设置(JSON),配置 “dart.flutterSdkPath”,指定 Flutter SDK 目录:

The location of the Flutter SDK to use. If blank, Dart Code will attempt to find it from the project directory, FLUTTER_ROOT environment variable and the PATH environment variable.

    "dart.flutterSdkPath": "/Users/fantasy/Projects/github/flutter/flutter", // FLUTTER_SDK_PROJECT_DIR
    // "dart.flutterSdkPaths": [
    //     "/Users/fantasy/.fvm/versions"
    // ],
  1. Android Studio 打开偏好设置,Languages & Frameworks | Flutter | Flutter SDK path: 指定 Flutter SDK 目录为 /Users/fantasy/Projects/github/flutter/flutter(FLUTTER_SDK_PROJECT_DIR)。

接下来,看看如何 修改和调试 SDK/Framework 源码

在 flutter web app 项目工程目录执行 flutter pub get,IDE 点击符号 MaterialApp 可跳转打开源码所在文件 flutter/packages/flutter/lib/src/material/app.dart。

也可使用 vscode 或 Android Studio 等 IDE,打开 FLUTTER_SDK_SRCROOT(flutter/packages/flutter)文件夹进行编辑修改。

修改打开的 packages/flutter/lib/src/material/app.dart 文件,添加调试日志:

class _MaterialAppState extends State<MaterialApp> {
  late HeroController _heroController;

  bool get _usesRouter => widget.routerDelegate != null;

  @override
  void initState() {
    super.initState();
    if (kDebugMode) {
      print('_MaterialAppState initState');
    }
    _heroController = MaterialApp.createMaterialHeroController();
  }

flutter web app 调试运行起来,查看 console 日志以验证 framework 代码修改效果,正常会有以下输出:
在这里插入图片描述
修改 SDK/Framework 源码,web app 项目支持热加载,可实时调试查看修改后的效果。

web app 打开 chrome 运行起来后,可使用浏览器调试工具(例如 Chrome DevTools )打开查看和调试 Framework 源码。

在 Chrome DevTools 的 Sources 标签面板下,可查找定位到对应文件 packages/flutter/src/material/app.dart,可以确认修改效果和断点调试。

http://localhost:8080/packages/flutter/src/material/app.dart

在这里插入图片描述

运行 framework 测试用例

flutter test 可以运行 framework 测试用例。

# 在 Chrome 上运行所有测试用例
flutter test -v --platform=chrome
# 在 Chrome 上运行测试用例,并指定渲染方式为移动端的 html 方式
flutter test -v --platform=chrome --web-renderer=html
# 在 Chrome 上运行指定测试用例,并指定渲染方式为移动端的 html 方式
flutter test -v --platform=chrome --web-renderer=html test/material/text_field_focus_test.dart

同步更新 framework 源码

在 Flutter SDK 目录下 master 分支,git pull 即可更新最新仓库代码。

如果需要同步上游官方源头仓库,可以参考 Configuring a remote for a fork & Syncing a fork

  • 也可 cd $FLUTTER_SDK_PROJECT_DIR,执行 git checkout 命令切换源码到指定版本进行开发调试或验证。

搭建 engine 开发环境

参考官方文档:

  1. Setting up the Engine development environment
  2. Compiling the engine - Compiling for the Web
  3. Contributing to the Flutter engine

需要具备这些条件搭建 engine 开发环境:

  • git(代码版本管理)。

  • IDE:Android Studiovscode 等主流IDE可安装插件支持 flutter/Dart 代码高亮。

  • Python( gclient 等工具依赖)。

  • ssh 客户端(GitHub 身份验证),请先 配置 Github 认证需要的 SSH Key

  • chromium depot_tools 是基于 Python(封装 git 等工具)实现的用于代码迁出管理的工具,包含 gclientgnninja 等工具。

    • ninja 是 Google 推出的注重速度的构建工具,将编译任务并行组织,大大提高构建速度。
    • 在 macOS 和 Linux 中,gclient sync 命令依赖 curl 和 unzip (macOS 已自带)。
  • felt:flutter engine 内置的编译 web engine 的工具,支持 Linux、macOS 和 Windows。对于其它平台,下表展示了 Linux,macOS 或 Windows 跨平台产物编译支持情况。

    AndroidiOSFuchsia
    Linuxoxo
    Windowsxxx
    macOSooo
  • Windows 平台需要:

    • Visual Studio 2017 或更高版本。
    • Windows 10 SDK。
      • 确保安装了 Debugging Tools for Windows。
  • macOS 平台需要最新版本的 Xcode。

准备 depot_tools

Linux / Mac

Clone depot_tools 仓库到本地 /path/to/depot_tools(例如 $HOME/Library/Developer/chromium/tools/depot_tools):

$ git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git

将 git clone 下来的 depot_tools 本地仓库目录添加到环境变量 PATH,以使命令行可以找到相关工具链。

# ~/.bashrc 或 ~/.zshrc
$ export PATH=/path/to/depot_tools:$PATH

Windows

参考 depot_tools_tutorial 介绍。


确认 depot_tools 是否安装成功:

  • 执行 gclient --versiongclient help 可以查看 gclient 版本及帮助。
  • 执行 ninja --version 查看 ninja 版本;执行 ninja -h 查看 ninja 帮助。
$ gclient --version
Updating depot_tools...
gclient.py 0.7

$ ninja --version
1.8.2

部署 engine 源码

不需要提前安装 Dart,也不需要手动 clone engine 到本地,gclient 会将 engine 相关工具链和依赖库拉取到本地。

按照下面的步骤部署:

  1. Fork https://github.com/flutter/engine 到自己的 GitHub 账号。如果已经 frok 过了,可先更新 Sync Fork。
  2. 本地创建一个约定俗成的空目录 engine ,用来保存 engine 项目相关工具链和依赖库。
  3. cd engine,创建 .gclient 配置文件,填入以下内容,替换 <your_name_here> 为你的 GitHub 账户名。
solutions = [
  {
    "managed": False,
    "name": "src/flutter",
    "url": "git@github.com:<your_name_here>/engine.git",
    "custom_deps": {},
    "deps_file": "DEPS",
    "safesync_url": "",
  },
]
  1. 在 engine 目录下执行 gclient sync,将自动创建 engine/src 目录。

    • 其中 src/flutter 才是真正的 flutter engine 代码所在地。
    • 此外,src 目录下包括第三方依赖库 third_party、工具链(build、tools)以及 fuchsia sdk 等。
  2. 添加跟踪官方 flutter engine 上游源仓:

$ cd src/flutter
$ git remote add upstream git@github.com:flutter/engine.git
$ cd -
  1. 如果是参与外部开源项目,可考虑执行 git config 命令配置个人开发者用户名和邮箱:

    • git config user.name <github_username>
    • git config user.email <github_useremail>

编译 engine 源码

src/flutter 才是真正的 flutter engine 代码所在地,其中包括 sky、runtime、flutter_frontend_server、common(/graphics)、vulkan、shell(platform,vmservice) 等。lib 目录下包括 SDK/Framework 端 ui 和 web_ui 的实现代码:

src/flutter/lib/web_ui 为 Flutter Web Engine 代码目录。

$ tree -L 1 lib
lib
├── io
├── snapshot
├── spirv
├── ui
└── web_ui

编译 web engine 源码需要使用 felt(Flutter Engine Local Tester) 这个命令行工具,目标是让开发 Flutter web engine 更高效。

felt 内置于 flutter engine 仓库中的 lib/web_ui/dev 中,将 FLUTTER_ENGINE_SRCWEBUI/dev 配置到环境变量,以使命令行可以找到相关工具链:

可执行 felt help(或 felt help build)验证 felt 命令是否安装成功。

# flutter project
export FLUTTER_PROJECT_DIR=$HOME/Projects/github/flutter
# engine
export FLUTTER_ENGINE_PROJECT_DIR=$FLUTTER_PROJECT_DIR/engine
export FLUTTER_ENGINE_PROJECT_SRCROOT=$FLUTTER_ENGINE_PROJECT_DIR/src
export FLUTTER_ENGINE_SRCROOT=$FLUTTER_ENGINE_PROJECT_SRCROOT/flutter
## lib/web_ui
export FLUTTER_ENGINE_SRCWEBUI=$FLUTTER_ENGINE_SRCROOT/lib/web_ui
export PATH=$FLUTTER_ENGINE_SRCWEBUI/dev:$PATH

在存放 .gclient 配置的 engine 目录(FLUTTER_ENGINE_PROJECT_DIR),执行 felt build 命令编译 flutter web 引擎,把 dart 转换成 js。

如果在其他目录执行 felt build,可能会报错 Error: client not configured; see 'gclient config'
默认的产物输出目录为 src/out/host_debug_unopt。

# 编译引擎
$ felt build
……
Done. Made 905 targets from 280 files in 1152ms
Running autoninja...
ninja: Entering directory `/Users/fantasy/Projects/github/flutter/engine/src/out/host_debug_unopt'
ninja: no work to do.

每次修改 web engine 后,在 FLUTTER_ENGINE_PROJECT_DIR 目录执行 felt build 即可重编引擎。


felt build 可能会遇到以下报错:

$ felt build
Running on MacOS. Will check the file and user limits.
File limits too low increasing the file limits
Can't load Kernel binary: Invalid kernel binary format version.

一般是因为flutter内核修改后无法识别,参考 Unable to build web flutter engine locally #70372,执行以下命令清除 felt.snapshot 可解决。

# rm $FLUTTER_SDK_ROOT/bin/cache/flutter_tools.stamp
rm $FLUTTER_ENGINE_SRCWEBUI/.dart_tool/felt.snapshot*

修改调试 engine 源码

请确保指定了 Engine 对应的 Flutter SDK/Framework 目录,具体参见 指定 Flutter SDK 目录

用 IDE 打开 engine/src/flutter/lib/web_ui 目录(FLUTTER_ENGINE_SRCWEBUI),修改 web engine 源码后,执行 flet build 编译 engine。
那么,本地 flutter web app 运行时,如何指定使用本地修改编译的 flutter web engine 进行联调呢

指定 --local-engine

Compiling the engine - Compiling for the Web:

To test Flutter with a local build of the Web engine, add --local-engine=host_debug_unopt to your flutter command.

Contributing to the Flutter engine:

Most developers will use the flutter tool in the main Flutter repository for interacting with their built flutter/engine. To do so, the flutter tool accepts two global parameters local-engine-src-path and local-engine, a typical invocation would be: --local-engine-src-path /path/to/engine/src --local-engine=android_debug_unopt.

根据以上文档,flutter 全局命令选项 --local-engine=host_debug_unopt,支持指定本地引擎(如果已经指定本地引擎目录到环境变量)。

针对 web 指定为 host_debug_unopt,具体参考 felt build 日志中的 ninja: Entering directory
--local-engine 针对终端开发时,其值可能为 ios_debug_unopt、android_debug_unopt。

通过以下方法运行 flutter web app 项目,就可以看到 flutter web engine 源码修改的效果。

$ cd /path/to/web/app
# 指定本地引擎,编译(链接)运行调试 Flutter Web 应用
$ flutter --local-engine=host_debug_unopt run -d chrome

如果未指定本地引擎目录到环境变量,则需要通过 --local-engine-src-path 选项指定(推荐)。

# 指定本地引擎,编译(链接)运行调试 Flutter Web 应用
$ flutter --local-engine=host_debug_unopt --local-engine-src-path=$FLUTTER_ENGINE_PROJECT_SRCROOT run -d chrome

可以在 flutter web app 的 vscode 启动配置 .vscode/launch.json 中的 toolArgs 添加 --local-engine 和 --local-engine-src-path 这两个选项参数:
在这里插入图片描述
也可在 Android Studio Run/Debug Configurations 的 Additional run args 开头指定 --local-engine 和 --local-engine-src-path 这两个选项参数:
在这里插入图片描述

修改源码调试示例

接下来以 PR 31718 修改的 engine/src/flutter/lib/web_ui/lib/src/engine/text_editing/text_editing.dart 为例,我们在 IOSTextEditingStrategy.addEventHandlers 监听的 onBlur 事件中加一句日志。

// text_editing.dart
class IOSTextEditingStrategy extends GloballyPositionedTextEditingStrategy {
  IOSTextEditingStrategy(HybridTextEditing owner) : super(owner);

  @override
  void addEventHandlers() {

    subscriptions.add(activeDomElement.onBlur.listen((_) {
      if (windowHasFocus) {
        print('IOSTextEditingStrategy addEventHandlers onBlur event');
        activeDomElement.focus();
      } else {
        owner.sendTextConnectionClosedToFrameworkIfAny();
      }
    }));
  }

}
  1. flutter web app(coding-app)项目下执行 ./scripts/proxy/launch_shelf.sh -P 启动 shelf_proxy 代理,日志输出 ✅ proxy listening on http://10.20.89.64:8010。
  2. 执行以下命令,通过指定全局选项 --local-engine 和 --local-engine-src-path 指定本地 web engine 产物和源码目录,run -d web-server 启动 web 服务监听 8080 端口。
# 客户端模式,直接拉起 chrome 运行调试
# flutter --local-engine=host_debug_unopt --local-engine-src-path=$FLUTTER_ENGINE_PROJECT_SRCROOT run -d chrome --web-renderer=html --web-port=8080 --dart-define=API_BASE_URL=10.20.89.64:8010 --dart-define=AUTH_TOKEN=$DEBUG_AUTH_TOKEN

# server模式,可供局域网通过 LAN IP 访问
$ flutter --local-engine=host_debug_unopt --local-engine-src-path=$FLUTTER_ENGINE_PROJECT_SRCROOT run -d web-server --web-renderer=html --web-port=8080 --web-hostname=0.0.0.0 --dart-define=API_BASE_URL=10.20.89.64:8010 --dart-define=AUTH_TOKEN=$DEBUG_AUTH_TOKEN

lib/main.dart is being served at http://0.0.0.0:8080
  1. 执行 open -a Simulator 命令打开 iOS 模拟器,在safari浏览器地址栏输入 http://0.0.0.0:8080 访问 coding-app 页面。
  2. 打开 macOS Safari,Develop 菜单点击 Simulator - 0.0.0.0,将打开 Web Inspector。

在这里插入图片描述

  1. 在 Web Inspector 的 Sources 标签面板下,在 dart_sdk.js/lib/_engine/engine 下定位到 text_editing 目录,点击打开 text_editing.dart,查找到修改的地方,点击行号Gutter添加断点。点击搜索栏,调起键盘输入,聚焦或点击键盘 Done 按钮,应该会命中断点:

在这里插入图片描述

  1. 同时切换到 Web Inspector 的 Console 标签面板,应该可以看到 onBlur 日志输出。

在这里插入图片描述


macOS 桌面端 Chrome 访问 http://10.20.89.64:8080,可打开调试工具 DevTools 查看和调试 Engine 源码。在 DevTools 的 Sources 标签面板下,在 lib/_engine/engine 下定位到 text_editing 目录,点击打开 text_editing.dart,可以确认修改是否生效和进行断点调试。
由于修改的示例代码 IOSTextEditingStrategy 仅针对 iOS 设备,可以给 DefaultTextEditingStrategy.setEditingState 加个断点调试验证。点击搜索栏,聚焦输入框,就会命中断点:

在这里插入图片描述

运行 engine 测试用例

felt test 可以运行 web engine 测试用例,默认情况下使用 Chromium 作为平台。

# 运行所有 Chromium 测试用例
felt test
# 运行特定的测试用例
felt test test/engine/util_test.dart
# 在 iOS Safari 上运行测试用例
felt test --browser=ios-safari

更多命令运行 felt help [SUBCOMMAND] 查询。

同步更新 engine 源码

更新远程的官方 engine 仓库,只需要在本地 engine 目录(FLUTTER_ENGINE_PROJECT_DIR)执行 gclient sync 即可。

协同联调 Framework 和 Engine

如果要同时调试 Engine 和 SDK/Framework(FLUTTER_SDK_ROOT),需要先导出工具链到环境变量,并在 IDE 指定好配套的 Flutter SDK 目录(flutterSdkPath)。

最新 fvm global stable 为最新的 2.10.5,执行 flutter --version 可以查看 SDK/Framework 和 Engine 的搭配版本:

$ flutter --version
Flutter 2.10.5 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 5464c5bac7 (4 days ago) • 2022-04-18 09:55:37 -0700
Engine • revision 57d3bac3dd
Tools • Dart 2.16.2 • DevTools 2.9.2

如果在 SDK/Framework 仓库执行 git checkout 切换到了 2.10.5,也可将 Flutter Engine 切换到对应版本 57d3bac3dd。
这样搭配进行开发调试或验证,避免出现一些由于 SDK 和 Engine 版本不协同,造成的接口和工具链兼容性问题。

# engine/src/flutter
$ cd $FLUTTER_ENGINE_SRCROOT
$ git checkout 57d3bac3dd

注意

切换 Flutter Engine 仓库版本后,需要在 FLUTTER_ENGINE_PROJECT_DIR 目录重新执行 gclient sync 命令同步工具链。
否则,可能会报以下错误:

Running gn...
GOMA usage was specified but can't be found, falling back to local builds. Set the GOMA_DIR environment variable to fix GOMA.
Using prebuilt Dart SDK binary. If you are editing Dart sources and wish to compile the Dart SDK, set `--no-prebuilt-dart-sdk`.
Generating GN files in: out/host_debug_unopt
ERROR at //BUILD.gn:28:7: Can't load input file.
      "//flutter/build/archives:artifacts"
      ^-----------------------------------
Unable to load:
  /Users/fantasy/Projects/github/flutter/engine/src/flutter/build/archives/BUILD.gn
I also checked in the secondary tree for:
  /Users/fantasy/Projects/github/flutter/engine/src/build/secondary/flutter/build/archives/BUILD.gn
description: Sub-process failed.executable: /Users/fantasy/Projects/github/flutter/engine/src/flutter/tools/gn arguments: [--unopt, --xcode-symlinks, --full-dart-sdk] exit code: 1
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值