Awesome
flutter_embedded
This repo is focused on building alternative Flutter Shells for Embedded Linux.
Project Status
Note: armv6 is not supported by Google
To generate DEB packages issue make package
from your build folder. If you built with default flags you will see these in your build directory:
libflutter_engine-debug-dev-1.0.0-Linux-armhf.deb
flutter-glfw-1.0.0-Linux-armhf.deb
You can now select the desired channel, the default is stable. Use this cmake .. -DCHANNEL=beta
to swtich channels.
Master, dev, beta, and stable have all been tested building armv7 image for Raspberry Pi..
flutter-pi has been added as an optional shell to build.
No external toolchain is required. I'm using the repo CIPD sync artifact - Clang Toolchain.
The default build configuration (provided a properly configured sysroot), will cross-compile armv7a flutter engine, and selected shells.
If your are using a sysroot different than that included as default, you will need to override a few variables. See examples below.
Yocto Layer to build Engine, Wayland shell, and Gallery App can be found here: meta-flutter
Pre-requisites to build Flutter Engine and flutter_glfw
-
CMake 3.15 or greater
-
Sysroot compatible with the Clang runtime flavors
-
Linux system to build on
Notes: LLVM runtime libraries do not support soft floating point. Google does not support armv6 in Dart. It's fairly straight forward to patch and build for armv6, but you will hit problems in Dart.
Build engine and flutter_glfw (stable channel) example shell for RPI
git clone https://github.com/jwinarske/flutter_embedded
cd flutter_embedded
mkdir build && cd build
cmake ..
make package -j8
To enable build spew:
make -j8 VERBOSE=1
Note: Your build folder can be wherever you want
Build engine (stable channel) and flutter-pi for RPI (mounted SD card)
git clone https://github.com/jwinarske/flutter_embedded
cd flutter_embedded
mkdir build && cd build
cmake .. -DBUILD_PLATFORM_SYSROOT=OFF -DTARGET_SYSROOT=/media/joel/rootfs -DBUILD_FLUTTER_PI=ON -DBUILD_GLFW_FLUTTER=OFF
make package -j8
Note: this requires the following pacakges installed on your target:
sudo apt install libgl1-mesa-dev libgles2-mesa-dev ibegl-mesa0 libdrm-dev libgbm-dev gpiod libgpiod-dev
Switching channels
To switch channels add variable CHANNEL to cmake invocation. Like this
cmake .. -DCHANNEL=beta
make package -j8
To build all channels of Engine/GLFW shell for Raspberry Pi armv7 in your nightly CI build job, you could do this
git clone https://github.com/jwinarske/flutter_embedded
cd flutter_embedded
mkdir build && cd build
cmake ..
make package -j8
cmake .. -DCHANNEL=beta
make package -j8
cmake .. -DCHANNEL=dev
make package -j8
cmake .. -DCHANNEL=master
make package -j8
cmake .. -DCHANNEL=stable -DENGINE_RUNTIME_MODE=release
make package -j8
cmake .. -DCHANNEL=beta
make package -j8
cmake .. -DCHANNEL=dev
make package -j8
cmake .. -DCHANNEL=master
make package -j8
Build gtk3+ dependent engine (stable channel) for HOST
git clone https://github.com/jwinarske/flutter_embedded
cd flutter_embedded
mkdir build && cd build
cmake .. -DENGINE_DISABLE_DESKTOP=OFF -DENGINE_EMBEDDER_FOR_TARGET=OFF -DBUILD_FLUTTER_RPI=OFF -DBUILD_PLATFORM_SYSROOT=OFF -DTARGET_SYSROOT=/usr -DTARGET_ARCH=x64 -DBUILD_PLATFORM_SYSROOT_RPI=OFF
make package -j8
To switch to building flutter_glfw (TARGET) be sure to undefine the variables set prior.
cmake .. -UENGINE_DISABLE_DESKTOP -UENGINE_EMBEDDER_FOR_TARGET -DBUILD_FLUTTER_RPI=ON -DBUILD_PLATFORM_SYSROOT=ON -UTARGET_SYSROOT -UTARGET_ARCH -DBUILD_PLATFORM_SYSROOT_RPI=ON
make package -j8
Build gtk3+ dependent engine (stable channel) for TARGET
git clone https://github.com/jwinarske/flutter_embedded
cd flutter_embedded
mkdir build && cd build
cmake .. -DENGINE_DISABLE_DESKTOP=OFF -DENGINE_EMBEDDER_FOR_TARGET=OFF
make package -j8
Override Variables
To use the override variables, pass them in with the cmake command. One example
cmake -DTOOLCHAIN_DIR=~/Android/Sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64 -DTARGET_SYSROOT=~/Android/Sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64/sysroot -DENGINE_REPO=https://github.com/jwinarske/engine -DCMAKE_BUILD_TYPE=Release
TARGET_SYSROOT
This is the location of the target sysroot. The default value is "${CMAKE_SOURCE_DIR}/sdk/sysroot". One approach would be to download target image such as a Raspberry Pi image, and mount it. Setting TARGET_SYSROOT to the rootfs directory.
TARGET_ARCH
This is the target architecture of your build. It must match your toolchain, and that which the flutter engine build supports.
ENGINE_REPO
This is the repo of the flutter engine. The default value is https://github.com/flutter/engine.git. If you want to use your own fork, set this variable to point to your fork's url.
ENGINE_UNOPTIMIZED
Unoptimized flag, defaults to OFF
ENGINE_RUNTIME_MODE
If ENGINE_RUNTIME_MODE is not set to debug
, profile
, or release
, it defaults to debug
.
ENGINE_SIMULATOR
Enable simulator, defaults to OFF
ENGINE_INTERPRETER
Enable interpreter, defaults to OFF
ENGINE_DART_DEBUG
Enable dart-debug, defaults to OFF
ENGINE_CLANG
Enable clang, defaults to ON
ENGINE_GOMA
Enable goma, defaults to OFF
ENGINE_LTO
Enable link-time optimization, defaults to ON
ENGINE_EMBEDDER_FOR_TARGET
Embedder for Target, defaults to ON
ENGINE_ENABLE_VULKAN
Enable Vulkan, defaults to OFF
Android
Example building Android engine. The flutter engine uses it's own NDK copy. So passing in the toolchain file is for project builds (your own executables and libraries).
cmake .. -GNinja -DCMAKE_TOOLCHAIN_FILE=~/Android/Sdk/ndk-bundle/build/cmake/android.toolchain.cmake -DANDROID_ABI=armeabi-v7a -DANDROID_PLATFORM=17
autoninja
cmake .. -GNinja -DCMAKE_TOOLCHAIN_FILE=~/Android/Sdk/ndk-bundle/build/cmake/android.toolchain.cmake -DENGINE_ENABLE_VULKAN=on -DANDROID_ABI=x86_64
autoninja
cmake .. -GNinja -DCMAKE_TOOLCHAIN_FILE=~/Android/Sdk/ndk-bundle/build/cmake/android.toolchain.cmake -DENGINE_ENABLE_VULKAN=off -DANDROID_ABI=armeabi-v7a
autoninja
Raspberry Pi
To generate target binaries for a Raspberry Pi, you need a valid sysroot, prior to building
sysroot - Default
The target sysroot is created from this rootfs archive:
https://downloads.raspberrypi.org/raspbian/archive/2020-02-14-13:48/root.tar.xz
The resultant build artifacts are compatible with any 2020-02-14 Raspbian image. If you need a different rootfs version, you will need to update the wget command in flutter_embedded/cmake/rpi.sysroot.cmake.
sysroot - Override / create from Raspbian img file
Download Raspbian Lite image from Raspbian Image Download Page
https://downloads.raspberrypi.org/raspbian_lite_latest
Extract the archive, which will give you a .img file. If on Ubuntu Bionic, double click the img file. It will auto-mount the partitions present. You're interested in the rootfs partition. From terminal or nautilus, copy the /lib, /opt, and /usr directories from the rootfs mount. Copy these to the default sysroot folder
cd /media/$USER/rootfs
cp -r lib {flutter_embedded git clone root}/sdk/sysroot
cp -r opt {flutter_embedded git clone root}/sdk/sysroot
cp -r usr {flutter_embedded git clone root}/sdk/sysroot
sdk folder
The sdk folder will look like this after the toolchain has built properly
./sdk
|-- sysroot
| |-- lib
| |-- opt
| `-- usr
`-- toolchain
|-- arm-linux-gnueabihf
|-- bin
|-- include
|-- lib
|-- libexec
`-- share
Flutter Engine Default Font for Linux
Arial.ttf
Check your target using
fc-match Arial
It should return
Arial.ttf: "Arial" "Normal"
If the Arial font is not present, you get this fatal error when attemping to launch Flutter
LOG: /home/joel/git/flutter_embedded/build/rpi_flutter-prefix/src/rpi_flutter/flutter/main.cc:66: Display Size: 800 x 480
flutter: Observatory listening on http://127.0.0.1:34949/
[ERROR:flutter/third_party/txt/src/minikin/FontFamily.cpp(184)] Could not get cmap table size!
Install Arial Font
sudo apt-get install ttf-mscorefonts-installer
sudo fc-cache
Push Native Flutter build artifacts to Target
scp -r {build folder}/target/* pi@raspberrypi.local:/home/pi
Enable Linux as a Platform in your Flutter Repo
cd <flutter git root (not this repo)>
git apply <flutter_embedded repo>/cmake/flutter_platform.patch
Note that that Flutter repo does not have TargetPlatform.linux as part of Material design. You have to add it by hand, or override debugDefaultTargetPlatformOverride and set it to a supported one... There are a couple of cases that need a unique implementation for Linux. Vibrate, etc.
Apply this changelist, if not present. https://github.com/flutter/flutter/pull/24932/files
When adding in Linux support to the Dart code, start by adding "case TargetPlatform.linux:" to all switch cases found via
cd {flutter repo}
grep -r "case TargetPlatform.android:"
Build your Flutter Application
cd {flutter app project folder}
flutter build bundle
Note: You either need to override TargetPlatform prior to running the app
Tested Flutter Examples
Tested apps post Flutter Dart "linux" platfrom add
flutter/examples/catalog * Generates rendered text: "Instead run", "flutter run lib/xxx.dart"
flutter/examples/flutter_gallery * key test case for platform
flutter/examples/flutter_view
flutter/examples/hello_world
flutter/examples/layers * Generates rendered text: "Instead run", "flutter run lib/xxx.dart"
flutter/examples/platform_channel
flutter/examples/platform_view * Android view not impl.. no-op btn
flutter/examples/stocks
flutter-desktop-embedding/example/flutter_app
Push built Flutter Application to Target
scp -r {flutter app root }/build/* pi@raspberrypi.local:/home/pi
Execute Flutter on Target
Presuming you built your Flutter app, and pushed the build folder to your target, you can run it with this
TSLIB_CONFFILE=/etc/ts.conf TSLIB_PLUGINDIR=/home/pi/lib/ts TSLIB_CALIBFILE=/etc/pointercal LD_LIBRARY_PATH=./lib ./bin/flutter ./build/flutter_assets/
If your touch screen is not auto-detected correctly, specify the /dev/input/event[n] using the environmental variable TSLIB_TSDEVICE
This can be run from a SSH session, or directly on the device. You should see output like this, prior to the app being rendered
LOG: /home/joel/git/flutter_embedded/build/rpi_flutter-prefix/src/rpi_flutter/flutter/main.cc:66: Display Size: 800 x 480
flutter: Observatory listening on http://127.0.0.1:34949/
Note: If you get unknown platform exception, you either need to override debugDefaultTargetPlatformOverride, or "Enable Linux as a Platform in your Flutter Repo"
Raspberry Pi 7" Touch Display
Verify your touch display is working correctly on your Raspberry Pi using evtest
sudo apt-get install evtest
Running it, you should see something like
$ evtest
No device specified, trying to scan all of /dev/input/event*
Not running as root, no devices may be available.
Available devices:
/dev/input/event0: HID 0c45:7403
/dev/input/event1: HID 0c45:7403
/dev/input/event2: FT5406 memory based driver
Select the device event number [0-2]:
The touch device in this case, is /dev/input/event2. Selecting 2 will enable touch data to print to the console.
Touch Panel Calibration
sudo LD_LIBRARY_PATH=./lib TSLIB_CONFFILE=/etc/ts.conf TSLIB_PLUGINDIR=/home/pi/lib/ts TSLIB_CALIBFILE=/etc/pointercal ./bin/ts_calibrate
Touch Panel Applications
sudo LD_LIBRARY_PATH=./lib TSLIB_CONFFILE=/etc/ts.conf TSLIB_PLUGINDIR=/home/pi/lib/ts TSLIB_CALIBFILE=/etc/pointercal ./bin/ts_test
sudo LD_LIBRARY_PATH=./lib TSLIB_CONFFILE=/etc/ts.conf TSLIB_PLUGINDIR=/home/pi/lib/ts TSLIB_CALIBFILE=/etc/pointercal ./bin/ts_print
Native Flutter Target Debug
I am successfully able to single step the Flutter Embedder using Host Side gdb-multiarch, and latest Eclipse release. Target side requires gdbserver installed. LLDB will be at a later date.
sudo apt-get install gdbserver
Change build flags in this file
{build folder}/engine-prefix/src/engine/src/build/config/compiler/BUILD.gn
To include
if (is_linux) {
if (current_cpu != "x86") {
cflags_cc += [
"-ggdb",
"-ggdb3",
Rebuild Engine
Copy Artifact
scp {absolute build folder}/engine-prefix/src/engine/src/out/linux_debug_arm/so.unstripped/libflutter_engine.so pi@raspberrypi.local:/home/pi/lib
Inside Eclipse - Import C/C++ Executable, and select the Flutter binary.
Configuration - via Debugger Dialog
Main / C/C++ Application: flutter
Main / Connection: Remote Host
Main / Remote Absolute File Path for C/C++ Application: /home/pi/bin/flutter
Main / Commands to execute before application
export LD_LIBRARY_PATH=/home/pi/lib
Main / Skip download to target path [TRUE]
Arguments: /home/pi/build/flutter_assets/
Debugger / Main / GDB Debugger
gdb-multiarch
Debugger / Shared Libraries
{absolute build folder}/engine-prefix/src/engine/src/out/linux_debug_arm/so.unstripped
{absolute sdk folder}/sysroot/lib
{absolute sdk folder}/toolchain/lib
Debugger / Shared Libraries / Load shared library symbols automatically [TRUE]
Set breakpoint at FlutterEngineRun().
Run the debugger, once breakpoint hits, change to the Debugger Console window, and issue
set step-mode on
Step into FlutterEngineRun()