在嵌入式 Linux 设备上使用 Flutter 开发图形界面

Posted toradexsh

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在嵌入式 Linux 设备上使用 Flutter 开发图形界面相关的知识,希望对你有一定的参考价值。

​By Toradex胡珊逢

在嵌入式设备上开发图形用户界面通常会选择 Qt。这是一种经验证的方案,我们可以在多个领域看到用 Qt 开发的 UI。但随着移动端和 web 端界面更为广泛地使用,源自于这两个领域的技术也开始向嵌入式设备渗透。Flutter 就是一个例子。本文将介绍如何在 Torizon 平台上如何使用 Flutter 来开发用户界面。

Google 面向 android, ios 推出的跨平台移动应⽤开发框架 Flutter 可以构建高质量的原⽣⽤户界⾯,并可以扩展支持 Web 和桌面应用。Flutter 尚未官方支持嵌入式系统,但目前 Sony 和 Ubuntu 正在致力于该工作。例如来自 Sony 的 elinux 可以在嵌入式平台上使用 Flutter。我们也将以此为基础,在 Verdin iMX8M Plus 的 Torizon 上运行 Flutter 应用。

我们来看看 Flutter 的构架。如下图所示,其由三个部分构成,User app, Framework 和 Engine。flutter-elinux-linux 属于为嵌入式提供支持的 embbedder。它可以运行在 wayland 显示后台,这也是 Torizon 提供的显示框架。flutter-elinux-linux 将提供 flutter-client ,libflutter_elinux_wayland.so 和 libflutter_engine.so。这些软件的功能参考该网页描述。

https://github.com/sony/flutter-embedded-linux/wiki/Features-of-Embedded-Linux-embedding-for-Flutter

flutter-elinux 是 Flutter SDK 的一个非官方插件,用于为嵌入式设备创建、编译和调试 Flutter 应用,并使用 flutter-elinux-linux 在设备上显示。

为了减少对编译电脑的软件环境影响,我们将使用 docker 容器进行编译。使用下面命令获取 ubuntu:20.04 容器并进入其中。由于后面需要向容器内提供文件,这里将 /home/user/flutter 映射到容器内的 /opt/flutter。

-----------------------------------------------

~$ docker pull ubuntu:20.04

~$ docker run -it -v /home/user/flutter:/opt/flutter --name flutter_build ubuntu:20.04 /bin/bash

-----------------------------------------------

如果后续进入该容器重新编译,可以使用下面命令:

-----------------------------------------------

~$ docker container start flutter_build

~$ docker exec -it flutter_build bash

-----------------------------------------------

在容器中安装所需的软件。

-----------------------------------------------

# apt update

# apt upgrade

# apt install clang cmake build-essential pkg-config libegl1-mesa-dev libxkbcommon-dev libgles2-mesa-dev

# apt install libwayland-dev wayland-protocols git curl wget unzip git

# apt install python2

# apt install virtualenv

-----------------------------------------------

下载编译工具。

-----------------------------------------------

# mkdir -p /opt/flutter

# cd /opt/flutter

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

# export PATH=$PATH:$(pwd)/depot_tools

-----------------------------------------------

默认的 ubuntu:20.04 使用 Python3,在容器里使用 virtualenv 创建 Python2 环境。

-----------------------------------------------

# virtualenv .env -p python2

# source .env/bin/activate

-----------------------------------------------

创建 .gclient 文件并指定版本。

-----------------------------------------------

# cat .gclient

solutions = [

  "managed": False,

  "name": "src/flutter",

  "url": "https://github.com/flutter/engine.git@bd539267b42051b0da3d16ffa8f48949dce8aa8f",

  "custom_deps": ,

  "deps_file": "DEPS",

  "safesync_url": "",

  "custom_vars" :

    "download_android_deps" : False,

    "download_windows_deps" : False,

  ,

,

]

-----------------------------------------------

上面的 bd539267b42051b0da3d16ffa8f48949dce8aa8f 对应 $path_to_flutter_sdk_install/flutter/bin/internal/engine.version,两者需要一致。如果不指定的话,会下载最新的版本。除非确实需要编译最新版本的 Engine,否则并不推荐。

获取代码。

-----------------------------------------------

# gclient sync

-----------------------------------------------

编译 embbedder。这里编译为 arm64 目标 release 模式的 embedder。

-----------------------------------------------

# cd src

# ./flutter/tools/gn --target-os linux --linux-cpu arm64 --runtime-mode release --embedder-for-target --disable-desktop-embeddings --no-build-embedder-examples

# ninja -C out/linux_release_arm64

-----------------------------------------------

编译成功后在 out/linux_release_arm64 目录中可以看到 libflutter_engine.so 文件。

接下来将编译 Embedded Linux embedding for Flutter,这里会生成 flutter-client 和 libflutter_elinux_wayland.so。如果在 X86 电脑上交叉编译需要使用 Yocoto Project 生成包含 clang 在内的 SDK,这会涉及大量的工作内容。在 Torizon 中我们可以直接使用 debian 容器并通过 apt 命令安装相应的软件,在 Verdin iMX8M Plus 本地编译。这通常适用于代码量不是很多的项目。在 Verdin iMX8M Plus 上运行下面命令启动 debian 容器。

-----------------------------------------------

~$ docker run -it  -v /home/torizon/workspace:/opt/workspace torizon/debian:$CT_TAG_DEBIAN /bin/bash

-----------------------------------------------

在容器中安装所需的软件。

-----------------------------------------------

# apt update

# apt install clang cmake build-essential pkg-config libegl1-mesa-dev libxkbcommon-dev libgles2-mesa-dev

# apt install unzip git

# apt install curl wget

# apt install libwayland-dev wayland-protocols

# apt install libdrm-dev libgbm-dev libinput-dev libudev-dev libsystemd-dev

# cd /opt/workspace

-----------------------------------------------

下载 flutter-embedded-linux 代码。

-----------------------------------------------

# git clone https://github.com/sony/flutter-embedded-linux.git

# cd flutter-embedded-linux/

# mkdir build

-----------------------------------------------

此时将刚才编译的 libflutter_engine.so 复制到 build 目录。然后分别执行下面两个命令。

-----------------------------------------------

# cmake -DUSER_PROJECT_PATH=examples/flutter-wayland-client -DCMAKE_BUILD_TYPE=Release ..

# cmake -DUSER_PROJECT_PATH=examples/flutter-wayland-client -DCMAKE_BUILD_TYPE=Release -DBUILD_ELINUX_SO=ON -DBACKEND_TYPE=WAYLAND -DENABLE_ELINUX_EMBEDDER_LOG=OFF -DFLUTTER_RELEASE=ON ..

-----------------------------------------------

编译完成后在 build 目录下可以看到生成的 flutter-client 和 libflutter_elinux_wayland.so 两个文件。上面使用的编译选项含义请参考该网页说明。

接下来重新回到 X86 编译电脑开始 Flutter 应用的编译。用下面命令重新进入使之前的 ubuntu:20.04 容器。

-----------------------------------------------

~$ docker exec -it flutter_build bash

-----------------------------------------------

下载 flutter-elinux。这个是 Flutter SDK。

-----------------------------------------------

# cd /opt/flutter/

# git clone https://github.com/sony/flutter-elinux

# export PATH=$PATH:/opt/flutter/flutter-elinux/bin

-----------------------------------------------

运行下面命令查看安装情况。

-----------------------------------------------

# flutter-elinux doctor

# flutter-elinux devices

-----------------------------------------------

创建一个示例工程。

-----------------------------------------------

# flutter-elinux create demo1

# cd demo1

-----------------------------------------------

按照这里的说明交叉编译创建的工程。但在这之前需要准备 Verdin iMX8M Plus 的 arm64 格式文件系统。该文件系统可以是来自刚才在 Verdin iMX8M Plus 上编译 Embedded Linux embedding for Flutter 的容器。

在 Verdin iMX8M Plus 运行下面命令,查看容器 ID。

-----------------------------------------------

$ docker ps -a

CONTAINER ID        IMAGE                       COMMAND             CREATED             STATUS                     PORTS               NAMES

3dea07245b24        torizon/debian:2-bullseye   "/bin/bash"         2 days ago          Exited (137) 2 hours ago                       hardcore_nightingale

-----------------------------------------------

将容器的文件系统复制出来并打包。

-----------------------------------------------

$ sudo docker cp 3dea07245b24:/ arm64-sysroot

$ sudo tar cvf arm64-sysroot.tar arm64-sysroot

-----------------------------------------------

然后将 arm64-sysroot.tar 复制到 X86 编译电脑的 flutter_build 容器中,位于 /opt/flutter 目录。回到 flutter_build 容器,解压 arm64-sysroot.tar。

-----------------------------------------------

# cd /opt/flutter/

# tar vxf arm64-sysroot.tar

-----------------------------------------------

进入刚才创建的 demo1 目录,运行下面命令编译。

-----------------------------------------------

# cd demo1

# flutter-elinux build elinux --target-arch=arm64 --target-sysroot=/opt/flutter/arm64-sysroot

-----------------------------------------------

待编译结束后,查看 build/elinux/arm64/release/bundle,这里是 Flutter app 运行所需的所以文件。

-----------------------------------------------

# tree build/elinux/arm64/release/bundle -L 2

.

|-- data

|   |-- flutter_assets

|   `-- icudtl.dat

|-- demo1

`-- lib

  |-- libapp.so

  |-- libflutter_elinux_wayland.so

  `-- libflutter_engine.so

-----------------------------------------------

libflutter_elinux_wayland.so 和 libflutter_engine.so 是 Fltter SDK 预编译的库文件,需要将其替换为之前编译的库文件。

将 flutter-client 和 bundle 文件夹复制到 Verdin iMX8M Plus 的 /home/torizon/flutter_demo 目录。然后先启动 weston 容器。

-----------------------------------------------

$ docker run -e ACCEPT_FSL_EULA=1 -d --rm --name=weston --net=host --cap-add CAP_SYS_TTY_CONFIG \\

           -v /dev:/dev -v /tmp:/tmp -v /run/udev/:/run/udev/ \\

           --device-cgroup-rule='c 4:* rmw' --device-cgroup-rule='c 13:* rmw' \\

           --device-cgroup-rule='c 199:* rmw' --device-cgroup-rule='c 226:* rmw' \\

           torizon/weston-vivante:$CT_TAG_WESTON_VIVANTE --developer weston-launch \\

           --tty=/dev/tty7 --user=torizon

-----------------------------------------------

再启动另外一个 torizon/weston-vivante 容器,在里面我们将用命令行的方式启动编译好的 demo1。

-----------------------------------------------

$ docker run -e ACCEPT_FSL_EULA=1 -it --rm --name=wayland-app --user=torizon \\

           -v /dev/dri:/dev/dri -v /dev/galcore:/dev/galcore -v /tmp:/tmp -v /home/torizon/flutter_demo:/opt/flutter \\

           --device-cgroup-rule='c 199:* rmw' --device-cgroup-rule='c 226:* rmw' \\

           torizon/weston-vivante:$CT_TAG_WESTON_VIVANTE bash

-----------------------------------------------

在启动的容器内运行下面命令。

-----------------------------------------------

# cd /opt/flutter

# LD_LIBRARY_PATH=/opt/flutter/bundle/lib/ ./flutter-client -b /opt/flutter/bundle

-----------------------------------------------

最后,我们将介绍如何导入一个现成的 Flutter 项目并打包为一个容器,用 docker-compose 文件启动。这里以 covid19_mobile_app 为例进行说明。

在 flutter_build 容器中,下载 covid19_mobile_app 代码,将之前 demo1 目录中的 elinux 文件夹复制到 covid19_mobile_app 后再编译。同样,libflutter_elinux_wayland.so 和 libflutter_engine.so 也需要替换为之前编译的库文件。

-----------------------------------------------

# cp -r ../demo1/elinux covid19_mobile_app

# flutter-elinux pub get

# flutter-elinux build elinux --target-arch=arm64 --target-sysroot=/opt/flutter/arm64-sysroot

-----------------------------------------------

编译结束后,将 covid19_mobile_app 的 bundle 目录连同 flutter-client,以及下面的 startup.sh,Dockerfile 放到任一目录中。

startup.sh

-----------------------------------------------

#!/bin/bash

LD_LIBRARY_PATH=/home/torizon/bundle/lib/ /usr/sbin/flutter-client -b /home/torizon/bundle

-----------------------------------------------

Dockerfile

-----------------------------------------------

FROM --platform=linux/arm64 torizon/weston-vivante:2

ADD bundle /home/torizon/bundle

COPY flutter-client /usr/sbin

COPY startup.sh /home/torizon

CMD [ "/home/torizon/startup.sh" ]

-----------------------------------------------

运行下面命令生成一个 flutter app 容器。

-----------------------------------------------

$ docker build -t flutter_demo:1 .

-----------------------------------------------

将 flutter_demo:1 容器和 docker-compose.yml 文件复制到 Verdin iMX8M Plus 上。

docker-compose.yml

-----------------------------------------------

services:

flutter_demo_covid19:

  depends_on:

  - weston

  devices: []

  image: flutter_demo:1

  ports: []

  device_cgroup_rules:

  - c 199:* rmw

  - c 226:* rmw

  volumes:

  - /tmp:/tmp:rw

  - /dev/dri:/dev/dri:rw

  - /dev/galcore:/dev/galcore:rw

weston:

  cap_add:

  - CAP_SYS_TTY_CONFIG

  device_cgroup_rules:

  - c 4:0 rmw

  - c 4:7 rmw

  - c 13:* rmw

  - c 199:* rmw

  - c 226:* rmw

  environment:

  - ACCEPT_FSL_EULA=1

  image: torizon/weston-vivante:2

  network_mode: host

  volumes:

  - source: /tmp

    target: /tmp

    type: bind

  - source: /dev

    target: /dev

    type: bind

  - source: /run/udev

    target: /run/udev

    type: bind

version: '2.4'

-----------------------------------------------

运行 docker-compose up -d 即可启动 weston 和 flutter app 容器。

总结

Flutter 框架为图形界面开发提供一个非常灵活的方案,使得嵌入式开发也可以从中受益。于此同时,嵌入式 Linux 对 Flutter 的支持也处于早期阶段,项目开发需要充分验证。

以上是关于在嵌入式 Linux 设备上使用 Flutter 开发图形界面的主要内容,如果未能解决你的问题,请参考以下文章

在嵌入式 Linux 设备上使用 Flutter 开发图形界面

Flutter开发——Linux嵌入式应用开发

Flutter 成为多平台框架,支持移动Web桌面和嵌入式设备

无法在嵌入式 linux 中使用 bluetoothctl 连接设备

为啥从 https 服务器下载文件在 montavista linux 的嵌入式设备上不起作用?

如何在 linux 和嵌入式 linux 上使用本地链接 (RNDIS) 设置 autoip