Engine development is the moment Flutter stops feeling like a single SDK download and starts looking like a browser-scale native project. The checkout is large, dependency synchronization can be slow, and a mismatched host build can waste an afternoon. A calm setup—one repository, one pinned dependency graph, and one named build configuration—makes the work manageable.

What Ubuntu can build

  • Linux host/desktop engine artifacts and the engine used by host-side tests.

  • Android engine artifacts for devices and emulators.

  • Selected Fuchsia artifacts when their extra dependencies and virtualization requirements are configured.

  • Not iOS engine artifacts; Flutter’s official matrix requires macOS for iOS cross-compilation.

  • Web engine work uses its own felt workflow and additional .gclient configuration.

Plan disk, memory, and network before syncing

  • Use a native Linux filesystem with ample free space; the checkout, CIPD packages, build output, symbols, and caches are substantial.

  • Prefer a reliable network because gclient sync fetches many pinned repositories and binary packages.

  • Keep the checkout path reasonably short and do not place generated engine/src/out trees in backups.

  • Use a supported 64-bit Ubuntu host and the dependency list from the current Flutter contribution docs.

  • If the objective is only to build Flutter apps, install the normal Flutter SDK instead—an engine checkout is unnecessary.

Install the small set of host prerequisites

Ubuntu shellbash
sudo apt update
sudo apt install git openssh-client python3 curl unzip pkg-config
... packages installed or already at newest version ...

Risk level: caution. Review the command before running it.

What these packages cover

  • Git manages the Flutter checkout and repositories fetched by gclient.

  • SSH supports the recommended authenticated GitHub fork/upstream workflow; HTTPS is also usable for read-only upstream access.

  • Python 3 runs gclient and many Flutter build tools.

  • curl and unzip are required by dependency synchronization on Linux.

  • pkg-config supports native dependency discovery; target-specific setup may require more packages documented for that build.

Install Chromium depot_tools first in PATH

development workspacebash
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
export PATH="$PWD/depot_tools:$PATH"
command -v gclient
gclient --version
/absolute/path/to/depot_tools/gclient
... depot_tools/gclient version information ...

PATH order matters

  • depot_tools supplies gclient and supporting Chromium infrastructure tools.

  • Put it at the front of PATH so a stale system package or unrelated executable is not selected.

  • The export affects only the current shell; add an absolute path to the appropriate shell startup file after verifying it.

  • Do not run depot_tools or gclient with sudo; keep checkout ownership consistent with the developer account.

  • depot_tools updates itself in normal use, so reproduce incidents with both Flutter and tooling revisions recorded.

Clone the Flutter monorepo

development workspacebash
git clone https://github.com/flutter/flutter.git
cd flutter
git remote -v
git rev-parse HEAD
origin  https://github.com/flutter/flutter.git ...
<flutter commit SHA>

Contributors should use a fork

  • For contribution work, point origin at your fork and add the official repository as upstream.

  • Keep engine and framework changes in the same monorepo revision; this avoids incompatible Dart, engine, and tool artifacts.

  • Record the commit SHA in bug reports and build notes.

  • Do not mix a stable Flutter SDK’s tool with a random engine master build without aligning revisions.

  • Use signed commits and the project’s contributor workflow when preparing upstream changes.

Bootstrap gclient from the checked-out repository

flutter repository rootbash
cp engine/scripts/standard.gclient .gclient
gclient sync
... syncing pinned repositories and CIPD packages ...

This is the expensive synchronization step

  • standard.gclient is the current non-Google configuration supplied by the same Flutter revision.

  • Run gclient from the repository root where .gclient lives.

  • The sync downloads a Dart toolchain and Android dependencies; installing Dart separately is not required for engine setup.

  • Never hand-edit downloaded dependency revisions to “fix” a mismatch; update or repair the checkout and sync again.

  • Preserve the first concrete error—later failures are often consequences of one incomplete dependency.

Expose the Engine Tool

flutter repository rootbash
export PATH="$PWD/engine/src/flutter/bin:$PATH"
command -v et
et --help
/absolute/path/flutter/engine/src/flutter/bin/et
... Engine Tool commands and options ...

Why et is now the preferred front door

  • Flutter’s compilation documentation recommends the Engine Tool instead of manually coordinating every build step.

  • et lives inside the synchronized engine source, so its behavior follows that checkout.

  • Configuration names describe platform, runtime mode, optimization state, and sometimes CPU.

  • Use et --help and configuration listing from the checked-out revision rather than guessing flags from an old post.

  • GN and Ninja remain underneath and are valuable for diagnosis and focused builds.

Build a debuggable Linux host engine

flutter repository rootbash
et build -c host_debug_unopt
... generating host_debug_unopt ...
... compiling and linking engine targets ...

Unoptimized is intentional for local work

  • host targets Linux desktop/host artifacts rather than Android device binaries.

  • debug selects the debug runtime mode.

  • unopt enables additional checks/logging and generally improves build iteration and debugging symbols.

  • Use optimized builds for meaningful performance measurements; unoptimized timing is misleading.

  • Generated artifacts live beneath engine/src/out and can consume considerable space.

The equivalent GN and Ninja path

flutter/engine/srcbash
./flutter/tools/gn --unoptimized
ninja -C out/host_debug_unopt
... GN generated Ninja files ...
... build completed ...

Use the lower-level route when diagnosing

  • GN generates the build graph and arguments; Ninja executes that graph.

  • The output directory name must match the flags used during generation.

  • Running Ninja without regenerating after relevant configuration changes can preserve stale assumptions.

  • Add --ccache to the documented GN flow when ccache is installed and appropriate.

  • A focused Ninja target can shorten iteration, but complete validation still requires the relevant engine/test targets.

Build Android host and device artifacts as a pair

flutter/engine/srcbash
./flutter/tools/gn --unoptimized
./flutter/tools/gn --android --android-cpu arm64 --unoptimized
ninja -C out/host_debug_unopt
ninja -C out/android_debug_unopt_arm64
... host and Android ARM64 debug-unoptimized artifacts built ...

The host build is not optional

  • Android and iOS local-engine workflows need corresponding host artifacts used by the Flutter tool.

  • --android-cpu arm64 targets modern 64-bit Android devices; emulator CPU choices differ.

  • Host and device artifacts must share the same synchronized Dart/engine revision.

  • Rebuild the host after a gclient sync that changes the Dart SDK.

  • Choose debug/profile/release consistently with the Flutter command that will consume the engine.

Run a Flutter app with the local engine

Flutter applicationbash
/path/to/flutter/bin/flutter run -d linux   --local-engine-src-path=/path/to/flutter/engine/src   --local-engine=host_debug_unopt   --local-engine-host=host_debug_unopt
... launching application with local engine artifacts ...

All three local-engine choices must agree

  • Use the Flutter tool from the framework revision aligned with the engine checkout.

  • --local-engine-src-path points to the engine src directory, not the output directory.

  • --local-engine selects the target build configuration.

  • --local-engine-host explicitly selects matching host artifacts and is required by current workflows.

  • For Android, replace the target configuration with the built Android directory while retaining its matching host configuration.

Update without creating a mixed checkout

flutter repository rootbash
git fetch upstream master
git rebase upstream/master
gclient sync -D
... Flutter sources updated ...
... dependencies synchronized and deleted entries pruned ...

Risk level: caution. Review the command before running it.

Synchronize immediately after source movement

  • Commit or stash deliberate work before rebasing; understand conflicts rather than discarding changes.

  • gclient sync -D aligns dependencies and removes entries no longer present in DEPS.

  • Reusing outputs across large revisions may fail or hide problems; regenerate/rebuild when toolchains or build files change.

  • Update both host and target configurations after Dart or engine dependency changes.

  • Pin known-good commits for team work instead of letting every workstation drift independently.

Troubleshooting by failure stage

  • gclient: command not found → depot_tools is absent or behind another PATH entry.

  • Early EOF/RPC/curl 18 → stabilize network, retry, or use the documented no-history sync trade-off.

  • CIPD download failure → proxy, TLS interception, clock, disk, permissions, or restricted Google storage access.

  • GN reports a missing dependency → the sync is incomplete, configuration is unsupported, or host prerequisites are missing.

  • Ninja is killed → inspect kernel logs and memory pressure; reduce parallel load or add adequate RAM/swap rather than blindly retrying.

  • Flutter cannot find local engine → wrong source path, configuration name, missing host build, or framework/engine revision mismatch.

  • App starts but changes are absent → rebuild the actual target consumed by the selected device and confirm command-line engine flags.

Keep iteration fast without making results dishonest

  • Use unoptimized builds for debugging and optimized/profile builds for performance work.

  • Install and enable ccache through supported GN flags for repeated C/C++ compilation.

  • Build a focused Ninja target during development, then run the broader relevant test/build set before review.

  • Keep out off synchronized folders and backups; it is generated, large, and revision-sensitive.

  • Do not publish performance numbers from a debug-unoptimized engine.

  • Capture configuration name, GN arguments, commit SHA, host details, and test device with every benchmark or crash report.

A setup is complete only after this proof

  • gclient sync completes without unresolved dependency errors.

  • et --help runs from the synchronized checkout.

  • The selected host configuration builds successfully.

  • A local Flutter application launches with explicit local target and host engine arguments.

  • A small deliberate engine change is rebuilt and observable in that app or a focused engine test.

  • The workflow succeeds again in a fresh shell using documented absolute paths.

Primary references

  • Flutter’s current engine environment setup documents the monorepo, prerequisites, standard gclient bootstrap, no-history fallback, and Engine Tool path.

  • The official engine compilation reference covers host and Android GN/Ninja configurations, unoptimized builds, paired host artifacts, and local development advice.

  • The standalone engine repository is archived; use the current flutter/flutter repository and its in-tree documentation as the source of truth.