flutpak 0.8.1
flutpak: ^0.8.1 copied to clipboard
CLI tool that generates Flatpak manifests and offline source bundles for Flutter applications. One command prepares everything needed for a Flathub-compatible offline build: generated-sources.json, ma [...]
flutpak #
Dart CLI tool that automates Flatpak packaging for Flutter applications.
Describe your config once in flutpak.yaml, run flutpak init once to
scaffold your template manifest, then run flutpak generate on every CI
build to produce a fully-substituted, flatpak-builder-ready output.
Highlights #
init+generatesplit — one-time scaffold vs. every-build generation; the editable template is committed to git, the substituted output is gitignored- No Python, no local Flutter SDK — pure Dart, compiles to a single native binary for CI; engine versions fetched from GitHub API via
flutter.ref - Standalone
flutpak.yaml— recommended; keeps Flatpak config separate from your Dart project metadata (aflutpak:section inpubspec.yamlis also supported) - yaml_edit injection —
generatesetstag:andcommit:directly in the git source block viayaml_edit; no placeholder strings to maintain in the template - Foreign deps registry — known packages (e.g.
objectbox_flutter_libs,sqlite3_flutter_libs) resolved and injected automatically; local overrides viaforeign-deps:in config - Rust/Cargo support — cargokit-based packages (e.g.
rhttp,metadata_god) handled automatically:Cargo.lockextracted from pub archives,cargo-sources.jsongenerated,rustupoffline-installer module injected; configure viarust:influtpak.yaml - Validation on every run —
generateerrors early if the template is missing or itsapp-id,command, orruntime-versiondiverge from config - Retry on transient errors — pub.dev and Flutter artifact downloads retry on 429/5xx
sdk-modcommand — generates a standalone Flutter SDK module JSON for!includein any manifest or SDK Extension
flutpak vs flatpak-flutter #
Both tools solve the same core problem: Flutter apps cannot build offline inside the Flatpak sandbox, so all dependencies must be pre-downloaded and listed as flatpak-builder sources before the build starts. flatpak-flutter (Python) and flutpak (Dart) take different approaches to the same goal.
What they share #
- Same problem domain — both produce an offline sources JSON and a
Flatpak manifest for Flutter apps. (flutpak names its pub sources file
pubspec-sources.json; flatpak-flutter names itgenerated-sources.json.) - Compatible
foreign_deps.jsonformat — the registry schema is identical; entries fromflatpak-flutter's registry can be used in flutpak as-is. flutpak extends the schema with one field (crlf:on patch sources);flatpak-flutterignores unknown fields. - Same flatpak-builder output format — both write standard
flatpak-builder
sourcesJSON arrays. The generated manifests are consumed byflatpak-builderin exactly the same way. - Same Flathub requirements — manifests from either tool satisfy Flathub's "build from source / no network during build" requirements.
How they differ #
| flutpak | flatpak-flutter | |
|---|---|---|
| Language / distribution | Dart, compiled to a single native binary | Python, installed via pip or Docker |
| Usage model | App authors packaging their own app | External maintainers / Flathub infra packaging many apps |
| Project access | Works inside your own repo — no clone needed | Clones the app repo and runs flutter pub get locally |
| Workflow | init once → generate on every CI build |
Single command regenerates everything each time |
| Config | Structured flutpak.yaml (or pubspec.yaml section) |
CLI flags + the manifest itself |
| Template lifecycle | Template committed to git; generated output gitignored | Input manifest modified in place; output replaces it |
Flutter SDK needed for generate? |
No — engine versions fetched from GitHub API via flutter.ref |
Yes — Flutter SDK must be installed and discoverable |
| Foreign dep Cargo.lock discovery | Downloads from pub.dev archives automatically | Reads from locally cloned source after flutter pub get |
| Rust / Cargo support | Yes — cargokit packages via rust: config (see Known limitations) |
Yes |
| Version matching in registry | Highest registry version ≤ installed | Same |
Which one to use #
Use flutpak if you are the app author and want a config-driven, CI-native
workflow where the template manifest lives in your repo alongside your source
code. flutpak's generate step runs without a local Flutter SDK installation
and produces a gitignored generated/ directory that CI rebuilds on every push.
Use flatpak-flutter if you are a Flathub maintainer packaging an app you do not own, or if your project has Rust crate dependencies beyond standard cargokit (e.g. git-sourced crates, which flutpak skips with a warning). Its all-in-one approach (clone → pub get → generate → output) is better suited for batch packaging workflows.
Both tools are compatible at the registry level — if a package is supported in
flatpak-flutter's foreign_deps.json it can be added to flutpak's registry
with minimal or no changes.
Known limitations #
Compared to flatpak-flutter, flutpak's Rust/Cargo support has the following
MVP-scope limitations:
| Limitation | Detail |
|---|---|
Git crate dependencies (git+https://…) |
Skipped with a warning — resolving them requires cloning the crate's repo at generate time, which is out of scope for an offline-first tool. Crates.io dependencies are fully supported. |
stable toolchain shorthand |
rust.version must be an explicit semver string (e.g. 1.85.0). The stable alias cannot be resolved to a concrete version without network access at generate time. |
| Architectures | Only x86_64 and aarch64 — matching flatpak-flutter's scope. |
Git crate dependencies are rare in Flutter plugins, so the MVP covers virtually all real-world use cases.
Demo application #
The repository ships a real Flutter application in examples/demo_app/
that is built on every pull request via the demo.yml workflow — an end-to-end
proof of concept that the tool works, and a proof of work that every PR
is gated on a real Flatpak build.
The CI pipeline runs the full cycle:
flutpak generate --commit <sha>— generates the manifest and sourcesflatpak-builderviaflathub-build— builds and lints the Flatpakflatpak install+ smoke test — installs the app and verifies it starts
The demo is intentionally built from the same git repository it lives in
(repo-url: https://github.com/o-murphy/flutpak.git), with
subdir: examples/demo_app in the Flatpak module. This exercises several
features added in v0.6.0:
- Subdirectory project support — the Flutter project lives inside a larger
git repo; the generated
cpstamp commands use$FLATPAK_BUILDER_BUILDDIRso they resolve correctly regardless ofsubdir:. - LLVM SDK extension auto-injection —
runtime-version: '25.08'is all that is needed;flutpakinjectsorg.freedesktop.Sdk.Extension.llvm20automatically. --configwith a subdirectory path — the workflow passes--config examples/demo_app/flutpak.yaml; all paths are resolved relative to the config file's directory.- Rust/Cargo via cargokit — the demo uses
rhttp ^0.12.0(Rust HTTP client via cargokit);flutpak generateresolves theCargo.lock, generatescargo-sources.json, and injects arustupoffline-installer module. Smoke checks in the template verifylibrhttp.sois bundled andlibsqlite3is visible from the Flatpak runtime. - SQLite — the demo uses
sqlite3_flutter_libs ^0.6.0;0.6.0links against the system SQLite provided byorg.freedesktop.Platform(no extra sources needed). For localflutter run, installlibsqlite3-dev.
The demo app config: examples/demo_app/flutpak.yaml
The template manifest: examples/demo_app/flatpak/io.github.o_murphy.flutpak.demo.yml
Table of Contents #
- flutpak
- Highlights
- flutpak vs flatpak-flutter
- Demo application
- Table of Contents
- Prerequisites
- Installation
- Step-by-step setup
- Foreign deps registry
- Full config reference
- Commands reference
- Manifest lifecycle
- CI/CD integration
- Validating and building locally
- How it works
- Building from source / contributing
- License
Prerequisites #
| Tool | When needed | Notes |
|---|---|---|
| Linux | Always | flutpak targets Linux Flatpak packaging only |
| git | Always | Version detection and commit-hash resolution |
| Internet access | init, generate |
Downloads from pub.dev, storage.googleapis.com, and raw.githubusercontent.com |
| Flutter SDK | CI — running the app | flutter pub get and flutter build still need a real SDK in CI; flutpak generate itself does not — engine versions are fetched via flutter.ref |
| Dart SDK ≥ 3.0 | Building from source only | Not required when using a pre-built binary |
flatpak-builder |
Local Flatpak builds | Only needed when actually building the Flatpak, not during flutpak generate |
org.freedesktop.Sdk |
Local Flatpak builds | Install via flatpak install flathub org.freedesktop.Sdk//25.08 |
Installation #
Pre-built binary (recommended) #
Download the binary for your platform from the
releases page and place it
somewhere on your $PATH:
sudo mv flutpak /usr/local/bin/
Via setup-flutpak action (CI) #
The repository ships a composite action that compiles and installs flutpak
automatically. See CI/CD integration for full usage.
- uses: o-murphy/flutpak/.github/actions/setup-flutpak@v0.6.1
From source #
Using make:
git clone https://github.com/o-murphy/flutpak.git
cd flutpak
make build # regenerates version.dart + compiles binary
sudo mv flutpak /usr/local/bin/
Without make:
dart compile exe bin/flutpak.dart -o flutpak --define=version=$(git describe --tags --always)
Step-by-step setup #
Step 1 — Create flutpak.yaml #
Create a standalone flutpak.yaml in your project root. This is the
recommended approach — it keeps Flatpak config separate from your Dart project
metadata and avoids any key-convention ambiguity with pubspec.yaml.
Tip
Alternative: you can put a flutpak: section directly in pubspec.yaml
instead, but not both at once. Note that pubspec.yaml conventionally uses
snake_case for its own keys; flutpak uses kebab-case, so a standalone
flutpak.yaml is cleaner.
Minimum viable config — only app-id is truly required:
# flutpak.yaml
app-id: io.github.YourOrg.YourApp
Everything else is either optional or auto-detected at runtime. For Flutter projects, add the Flutter git ref (tag, branch, or commit SHA):
# flutpak.yaml
app-id: io.github.YourOrg.YourApp
flutter:
ref: "3.44.1" # tag, "stable", "main", or commit SHA
flutter.ref tells flutpak generate which Flutter version to fetch engine
artifacts for — no local Flutter installation required for source generation.
The Flutter SDK is still needed in CI to run flutter pub get / flutter build.
Tip
Sandbox permissions (finish-args) — flutpak init writes sensible
Flutter desktop defaults into the template. To add extras (e.g.
--filesystem=xdg-documents), list them under finish-args: in your config
— they are merged after the mandatory args with duplicates removed. You can
also edit the template directly.
See Full config reference for all options.
Step 2 — Create required asset files #
flutpak init validates that these files exist before generating anything.
Create and commit them first:
<your-project>/
├── pubspec.yaml
├── pubspec.lock
└── app/
└── share/
├── metainfo/
│ └── <app-id>.metainfo.xml
├── applications/
│ └── <app-id>.desktop
└── icons/
└── hicolor/
└── 256x256/
└── apps/
└── <app-id>.png # minimum 256x256 required by Flathub
Minimal <app-id>.desktop:
[Desktop Entry]
Name=Your App
Exec=yourapp
Icon=io.github.YourOrg.YourApp
Type=Application
Categories=Utility;
Minimal <app-id>.metainfo.xml:
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop-application">
<id>io.github.YourOrg.YourApp</id>
<name>Your App</name>
<summary>Short one-line description</summary>
<metadata_license>MIT</metadata_license>
<project_license>MIT</project_license>
<description>
<p>A longer description of your application.</p>
</description>
<developer id="io.github.YourOrg">
<name>Your Name</name>
</developer>
<categories>
<category>Utility</category>
</categories>
<url type="homepage">https://github.com/YourOrg/YourApp</url>
<url type="bugtracker">https://github.com/YourOrg/YourApp/issues</url>
<launchable type="desktop-id">io.github.YourOrg.YourApp.desktop</launchable>
<supports>
<control>pointing</control>
<control>keyboard</control>
<control>touch</control>
</supports>
<releases>
<release version="0.1.0" date="2025-01-01"/>
</releases>
<content_rating type="oars-1.1"/>
</component>
For full requirements on these files, see the Flathub app-author documentation.
Step 3 — First run #
flutpak init
This is a one-time command. It generates the following files and then
immediately runs generate to populate flatpak/generated/:
flatpak/
├── <app-id>.yml # editable template manifest — commit this
├── <name>-wrapper.sh # Flutter launcher wrapper — commit this
├── .gitignore # contains: generated/
└── generated/ # gitignored, ready for flatpak-builder
├── <app-id>.yml
├── pubspec-sources.json
├── flutter-sdk-<version>.json # when flutter.ref is set
├── rustup-<version>.json # when rust: is configured
├── cargo-sources.json # when rust: is configured
└── patches/
Commit flatpak/<app-id>.yml, flatpak/<name>-wrapper.sh,
and flatpak/.gitignore. Do not commit
flatpak/generated/ — it is gitignored by design.
flutpak init errors if the template already exists. Use --force to
overwrite an existing scaffold.
Step 4 — Review and customize the template #
Open flatpak/<app-id>.yml and adjust build-commands, sdk-extensions,
finish-args, and any other fields for your project. The template is yours —
flutpak never overwrites it on subsequent runs.
The generated template already contains inline guidance comments that explain which sections are safe to edit. Key rules:
| Section | Guidance |
|---|---|
finish-args: |
Safe to edit — add/remove sandbox permissions |
build-commands: |
Safe to edit — add steps, adjust install paths |
build-options: |
Safe to edit — add env vars, path overrides |
sdk-extensions: |
Safe to edit |
modules: |
Safe to edit — add extra modules |
sources: — extra archives |
Safe to add/remove |
tag: / commit: in git source |
Set automatically by generate via yaml_edit — you can leave them absent or set them manually; generate will overwrite them |
Flutter cp stamp commands |
Use $FLATPAK_BUILDER_BUILDDIR/flutter/… — always the module build root, works with or without subdir: |
Patch sources (type: patch) |
Do not add manually — injected automatically by generate from the foreign deps registry and foreign-deps: config; adding them to the template causes duplicates |
Tip
If your Flutter project lives in a subdirectory of the git repo (e.g. apps/myapp/),
set subdir: apps/myapp in flutpak.yaml — flutpak init will emit it on the
app module automatically. The generated cp stamp commands use $FLATPAK_BUILDER_BUILDDIR
(always the module root, not the subdir), so they resolve correctly. Other paths in
build-commands that reference files inside the Flutter project remain relative to subdir:.
Step 5 — Build locally #
Point flatpak-builder at the generated manifest:
flatpak-builder --repo=repo --force-clean build-dir \
flatpak/generated/<app-id>.yml
See Validating and building locally for the full lint and validation workflow.
Step 6 — CI: generate on each release #
On every release build, run generate with the release tag:
flutpak generate --tag ${{ github.ref_name }}
This reads the committed template, resolves the tag and commit SHA, sets
tag: and commit: in the git source block, generates pubspec-sources.json
(and flutter-sdk-<version>.json / cargo-sources.json when applicable),
and writes everything to flatpak/generated/.
See CI/CD integration for a complete GitHub Actions workflow.
Step 7 — Submit to Flathub #
Open a PR to your app's Flathub repository. Copy the contents of
flatpak/generated/ into the repo root:
<flathub-repo>/
├── <app-id>.yml # flatpak/generated/<app-id>.yml
├── pubspec-sources.json # flatpak/generated/pubspec-sources.json
├── flutter-sdk-<version>.json # when flutter.ref is set
├── cargo-sources.json # Rust/cargokit only
├── rustup-<version>.json # Rust/cargokit only
└── patches/ # flatpak/generated/patches/ (if any)
For complete submission requirements see the Flathub app-author documentation.
Foreign deps registry #
flutpak generate automatically resolves known pub packages from a remote
registry at
foreign_deps/foreign_deps.json, using
the same format as flatpak-flutter. For each package found in your lock
files the registry provides the complete set of flatpak sources — prebuilt
archives and patches — needed for an offline Flatpak build.
How it works #
- The registry is fetched from GitHub on every
generaterun (network required; cached in~/.cache/flutpak/as offline fallback). - For every package in your
pubspec.lockthat appears in the registry at the exact locked version, its sources are appended topubspec-sources.json. - Patch files from the registry are downloaded to
generated/patches/<path>and referenced with a relativepath:so flatpak-builder can find them alongside the manifest.
Local overrides and suppression #
Local foreign-deps: entries in flutpak.yaml are deep-merged on top of the
remote registry before resolution. Two formats are supported:
foreign-deps:
# Shorthand — version resolved from pubspec.lock:
sqlite3_flutter_libs:
manifest:
sources:
- type: patch
path: patches/sqlite3.patch
crlf: true # flutpak-specific, stripped from output
# Versioned — explicit version key:
some_package:
"1.2.3":
manifest:
sources: [] # empty list = suppress remote entry entirely
- Local entries override the registry for the same
(package, version). sources: []suppresses a remote registry entry without replacing it.crlf: trueon atype: patchsource normalises the file to CRLF after download; the key is stripped before writing topubspec-sources.json.
Currently supported packages #
| Package | Versions | Notes |
|---|---|---|
audiotags |
1.4.5 | |
flutter_discord_rpc |
1.0.0 | cargokit |
flutter_new_pipe_extractor |
0.1.0 | |
flutter_vodozemac |
0.5.0 | cargokit |
flutter_webrtc |
1.3.0 | |
fvp |
0.35.0 | |
media_kit_libs_linux |
1.2.1 | |
metadata_god |
1.1.0 | cargokit |
objectbox_flutter_libs |
5.3.1, 5.3.2 | |
objectbox_sync_flutter_libs |
5.3.1, 5.3.2 | |
pdfium_dart |
0.1.2, 0.2.0, 0.2.1, 0.2.2, 0.2.3 | |
powersync |
2.1.0 | |
printing |
5.14.2 | |
rhttp |
0.12.0 | cargokit |
simple_secure_storage_linux |
0.2.5 | |
sqlcipher_flutter_libs |
0.6.8 | |
sqlite3 |
2.9.4, 3.0.0, 3.3.0 | |
sqlite3_flutter_libs |
0.5.30, 0.5.32, 0.5.34, 0.5.39, 0.5.41, 0.5.42, 0.6.0 | |
super_native_extensions |
0.8.24 | cargokit |
For the full list see foreign_deps/foreign_deps.json.
Offline / air-gapped use #
flutpak generate --no-foreign-deps
Pinning the registry version #
# flutpak.yaml
foreign-deps-ref: v0.6.1 # tag, branch, or SHA; default: main
Full config reference #
Config lives in one of two places (error if both exist):
| Location | Notes |
|---|---|
flutpak.yaml |
Recommended — standalone file |
pubspec.yaml |
flutpak: section — also works; note that flutpak uses kebab-case keys which look unusual alongside pubspec's own snake_case keys |
Complete YAML with all options #
# flutpak.yaml (recommended) — or flutpak: section in pubspec.yaml
output: flatpak # default
pub:
locks:
- pubspec.lock # default; add more if needed
flutter:
ref: "3.44.1" # tag, branch ("stable", "main"), or commit SHA
# omit for pure-Dart projects
patch: flatpak/patches/flutter/shared.sh.patch # optional custom patch
# Rust/Cargo support (cargokit-based packages). Omit for projects without Rust.
rust:
version: 1.85.0 # Rust toolchain version (default: 1.85.0)
rustup-path: /var/lib/rustup # CARGO_HOME / RUSTUP_HOME path (default: /var/lib/rustup)
locks: # extra Cargo.lock paths, resolved relative to config dir
- rust/Cargo.lock
# Local overrides / additions for the foreign deps registry.
# Deep-merged on top of the remote registry before source resolution.
# Shorthand (no version key) resolves version from pubspec.lock:
foreign-deps:
sqlite3_flutter_libs:
manifest:
sources:
- type: patch
path: patches/sqlite3.patch
crlf: true # normalise to CRLF; key stripped from output
# Suppress a remote entry by providing an empty sources list:
some_package:
"1.2.3":
manifest:
sources: []
# Git ref used to fetch the foreign_deps registry (default: main):
foreign-deps-ref: main
# Git ref of the flutpak repo used to fetch pre-built flutter-sdk modules (default: main):
flutter-sdk-ref: main
# flutpak-specific overrides (not part of the Flatpak manifest schema):
repo-url: https://github.com/... # default: git remote get-url origin
disable-submodules: false # default: false (matches flatpak-builder default)
metainfo-path: app/share/metainfo/<app-id>.metainfo.xml # default
desktop-entry-path: app/share/applications/<app-id>.desktop # default
icons: # default: single 256x256 entry
- size: 256x256 # required when icons: key is present
path: app/share/icons/hicolor/256x256/apps/<app-id>.png
# optional additional sizes:
- size: 512x512
path: app/share/icons/hicolor/512x512/apps/<app-id>.png
- size: scalable
path: app/share/icons/hicolor/scalable/apps/<app-id>.svg
modules:
- flatpak/modules/bclibc.yml
app-id: io.github.YourOrg.YourApp # required
runtime-version: '25.08' # default: '25.08'
# For Flutter projects, org.freedesktop.Sdk.Extension.llvmXX is auto-injected
# based on runtime-version (25.08→llvm20, 24.08→llvm19, 23.08→llvm17).
# Add extra extensions manually if needed:
sdk-extensions:
- org.freedesktop.Sdk.Extension.rust-stable
# When the Flutter project lives in a subdirectory of the git repo:
subdir: apps/myapp # optional; emitted as subdir: on the app module
# Auto-detected if omitted (info printed on init):
command: yourapp # default: last segment of app-id
# Extra finish-args merged after the mandatory Flutter defaults:
finish-args:
- --filesystem=xdg-documents
- --share=network
# Verbatim flatpak source entries appended to the app module:
sources: []
env: {}
build-options:
append-path: /custom/bin
prepend-ld-library-path: /custom/lib
env:
MY_VAR: value
Field reference table #
| Field | Type | Default | Notes |
|---|---|---|---|
output |
string | flatpak |
Output directory for generated files |
pub.locks |
list | [pubspec.lock] |
Lock files to scan; $ENV vars expanded |
flutter.ref |
string | — | Flutter git ref (tag, branch, or SHA). When set, engine versions fetched from GitHub API — no local Flutter install needed for generate. Omit for pure-Dart projects. |
flutter.patch |
string | — | Custom patch for Flutter's shared.sh bootstrap script. Defaults to the built-in patch when omitted. |
rust.version |
string | 1.85.0 |
Rust toolchain version to install via rustup. |
rust.rustup-path |
string | /var/lib/rustup |
Path used for CARGO_HOME and RUSTUP_HOME during the Flatpak build. |
rust.locks |
list | [] |
Extra Cargo.lock paths (relative to the config dir, or absolute) merged with any paths from the foreign deps registry. |
foreign-deps |
map | {} |
Local overrides for the remote foreign deps registry. Deep-merged on top before resolution. See Local overrides and suppression. |
foreign-deps-ref |
string | main |
Git ref used to fetch the foreign deps registry. Pin to a tag or SHA for reproducible builds. |
flutter-sdk-ref |
string | main |
Git ref (branch or tag) of the flutpak repo used to fetch pre-built flutter-sdk-<version>.json modules via FlutterSdkRegistry. |
repo-url |
string | git remote get-url origin |
Source git URL written into template |
disable-submodules |
bool | false |
When true, adds disable-submodules: true to the git source in the generated template, preventing flatpak-builder from cloning git submodules |
metainfo-path |
string | app/share/metainfo/<id>.metainfo.xml |
Validated on init and generate |
desktop-entry-path |
string | app/share/applications/<id>.desktop |
Validated on init and generate |
icons |
list | [{size: 256x256, path: app/share/icons/…}] |
Must include 256x256 if key is present |
modules |
list | [] |
Path strings or inline module maps injected as extra modules before the app module |
app-id |
string | required | Reverse-DNS app ID |
runtime-version |
string | 25.08 |
Freedesktop runtime version |
command |
string | last segment of app-id |
Executable name inside /app/bin/ |
subdir |
string | — | Subdirectory within the source tree where the build runs. Emitted as subdir: on the app module. Useful when the Flutter project lives in a subdirectory of the git repo. The generated stamp cp commands use $FLATPAK_BUILDER_BUILDDIR and are unaffected by subdir:. |
sdk-extensions |
list | [] |
For Flutter projects, llvmXX is auto-injected based on runtime-version (25.08 → llvm20, 24.08 → llvm19, 23.08 → llvm17). Add here only extensions beyond llvm (e.g. rust-stable). |
finish-args |
list | [] |
Extra sandbox permission flags appended after the mandatory Flutter defaults (--share=ipc, --socket=fallback-x11, --socket=wayland, --device=dri). Duplicates are silently removed. |
sources |
list | [] |
Verbatim flatpak source entries appended to the app module |
env |
map | {} |
Build-time env vars (shorthand) |
build-options.append-path |
string | — | Appended to PATH during build |
build-options.prepend-ld-library-path |
string | — | Prepended to LD_LIBRARY_PATH |
build-options.env |
map | {} |
Merged with top-level env: |
Note
Only the fields listed above are read from flutpak.yaml. Fields like
build-commands and the full modules tree are not read from config —
edit the template file (flatpak/<app-id>.yml) directly to change those.
Commands reference #
init #
flutpak init [--config <path>] [--flutter <ref>] [--force]
One-time setup. Generates the editable template manifest, wrapper script,
and flatpak/.gitignore, then immediately runs generate.
- Errors if the template (
flatpak/<app-id>.yml) already exists — use--forceto overwrite. - Validates that all asset files (metainfo, desktop entry, icons) exist.
- Auto-detects
repo-urlfromgit remote get-url origin.
| Flag | Description |
|---|---|
--config / -c |
Path to config file (default: auto-detected pubspec.yaml or flutpak.yaml) |
--flutter / -f |
Flutter git ref; overrides flutter.ref: in config |
--force |
Overwrite existing template and wrapper |
generate #
flutpak generate [--tag v1.2.3] [--commit sha] [--config <path>]
[--flutter <ref>] [--no-foreign-deps] [--dry-run]
Every-build command. Reads the committed template, validates it against
config, sets tag: and commit: in the git source block via yaml_edit,
generates pubspec-sources.json (and flutter-sdk-<version>.json /
cargo-sources.json / rustup-<version>.json when applicable), and writes
everything to flatpak/generated/.
- Errors if the template does not exist (run
flutpak initfirst). - Errors if
app-id,command, orruntime-versionin the template differ from config. - Errors if any asset file (metainfo, desktop entry, icon) does not exist.
- When
flutter.refis set (or--flutterpassed), fetchesflutter_tools/pubspec.lockfrom GitHub automatically and includes flutter_tools deps inpubspec-sources.json.
| Flag | Description |
|---|---|
--tag |
Git tag embedded in the manifest (e.g. v1.2.3) |
--commit |
Full git commit SHA; defaults to git rev-parse HEAD |
--config / -c |
Path to config file |
--flutter / -f |
Flutter git ref; overrides flutter.ref: in config |
--no-foreign-deps |
Skip foreign deps registry fetch (offline / air-gapped use) |
--dry-run / -n |
Print what would be written without writing any files |
sdk-mod #
flutpak sdk-mod [--flutter <ref>] [--output <dir>] [--patch <path>] [--config <path>]
Generates a standalone Flutter SDK module JSON that can be !include-d in
any Flatpak manifest or wrapped as an SDK Extension. The output module installs
Flutter to /var/lib/flutter.
# In your manifest:
modules:
- !include modules/flutter-sdk/flutter-sdk-3.44.1.json
- name: myapp
...
| Flag | Description |
|---|---|
--flutter / -f |
Flutter git ref (tag, branch, or SHA); overrides flutter.ref: in config |
--output / -o |
Output directory for the module JSON (default: modules/flutter-sdk) |
--patch |
Path to shared.sh.patch; defaults to built-in patch |
--config / -c |
Path to config file (default: flutpak.yaml) |
Pre-generated module JSONs for recent Flutter releases are available in
modules/flutter-sdk/.
cache #
flutpak cache clear
Manages the local flutpak cache at ~/.cache/flutpak/.
| Subcommand | Description |
|---|---|
clear |
Deletes the entire ~/.cache/flutpak/ directory — SHA-256 download cache and cached flutter-sdk modules. |
--tag / --commit behavior #
--tag |
--commit |
Tag in manifest | Commit in manifest |
|---|---|---|---|
v1.2.3 |
— | v1.2.3 |
Resolved via git rev-parse v1.2.3^{} |
| — | abc123 |
abc123 |
abc123 |
| — | — | HEAD SHA | HEAD SHA |
v1.2.3 |
abc123 |
v1.2.3 |
abc123 (explicit override) |
When neither flag is passed, the current HEAD SHA is used for both values so
flatpak-builder can fetch and build the revision without a real release tag.
For release builds, always pass --tag. --tag requires the tag to exist
locally — use fetch-depth: 0 in your checkout step so the tag is available.
Manifest lifecycle #
The architecture separates the editable template (committed to git) from the generated output (gitignored):
flatpak/
├── <app-id>.yml <- editable template — committed to git
├── <name>-wrapper.sh <- committed to git
├── patches/ <- patch files — committed to git
├── .gitignore <- contains: generated/
└── generated/ <- gitignored, never commit this
├── <app-id>.yml <- final manifest (tag/commit set, sources injected)
├── pubspec-sources.json <- pub + foreign-deps sources
├── flutter-sdk-<v>.json <- Flutter SDK module (when flutter.ref is set)
├── rustup-<v>.json <- Rust toolchain module (when rust: is configured)
├── cargo-sources.json <- Cargo sources (when rust: is configured)
└── patches/ <- copy
Phase 1 — flutpak init (run once)
Creates flatpak/<app-id>.yml — the editable template with guidance comments
and a git source block (no tag: / commit: yet). Commit this file — it is
the starting point that reviewers inspect and that generate reads on every
subsequent build.
Phase 2 — flutpak generate --tag vX.Y.Z (run on every CI build)
Reads the committed template, resolves the tag and commit SHA, sets tag: and
commit: directly in the git source block (via yaml_edit), generates
pubspec-sources.json (and flutter-sdk-<version>.json / cargo-sources.json
/ rustup-<version>.json when applicable), and copies everything to
flatpak/generated/. The template is never modified. flatpak-builder consumes
flatpak/generated/<app-id>.yml.
CI/CD integration #
setup-flutpak action #
- uses: o-murphy/flutpak/.github/actions/setup-flutpak@v0.6.1
# Builds from the same ref as the 'uses:' directive by default.
# Pin to a specific release:
# with:
# version: v
| Input | Description | Default |
|---|---|---|
version |
Release tag, commit SHA, or branch to compile | ref pinned in uses: directive |
install_dir |
Directory where the flutpak binary is placed |
/usr/local/bin |
The repository also ships a flutter composite action with
runner.arch-aware cache keys — it works correctly on both ubuntu-latest
(x86_64) and ubuntu-24.04-arm (aarch64) runners:
- uses: o-murphy/flutpak/.github/actions/flutter@v0.6.1
with:
flutter-version: stable # or read from flatpak/flutter.version
cache: true
| Input | Description | Default |
|---|---|---|
flutter-version |
Version tag (e.g. 3.41.6), stable, or beta |
stable |
cache |
Cache the SDK between runs | true |
| Output | Description |
|---|---|
flutter-path |
Absolute path to the installed Flutter SDK |
The action also exports FLUTTER_ROOT and FLUTTER_HOME env vars and adds
flutter/bin to $PATH, so flutter pub get and flutter build work
without additional configuration.
generate action #
Runs flutpak generate, validates metainfo, and uploads the generated manifest
and sources as a workflow artifact. Flutter is still needed in CI for
flutter pub get / flutter build; engine version files are fetched from
GitHub via flutter-ref.
- uses: o-murphy/flutpak/.github/actions/generate@v0.7.0
with:
tag: ${{ inputs.tag }} # or pass 'commit:' for non-tag builds
# commit: ${{ github.sha }} # used when 'tag' is empty
flutter-ref: "3.44.1" # optional; overrides flutter.ref from config
metainfo-path: app/share/metainfo/<app-id>.metainfo.xml
artifact-name: flatpak-generated
| Input | Description | Default |
|---|---|---|
tag |
Git tag to embed in the manifest (mutually exclusive with commit) |
— |
commit |
Commit SHA; defaults to github.sha when empty |
— |
flutter-ref |
Flutter git ref; overrides flutter.ref: in config |
— |
metainfo-path |
Path to .metainfo.xml; appstream is installed automatically if needed |
— |
artifact-name |
Upload generated files under this artifact name | flutpak-artifacts |
retention-days |
Artifact retention in days | 2 |
flutpak-version |
flutpak version to install; defaults to the same ref as the uses: directive |
— |
build-flatpak action #
Installs org.flatpak.Builder, lints the manifest, builds via flathub-build,
lints the repo, exports a .flatpak bundle, and optionally uploads it.
- uses: o-murphy/flutpak/.github/actions/build-flatpak@v0.6.1
id: build
with:
manifest: flatpak/generated/<app-id>.yml
app-id: io.github.YourOrg.YourApp
artifact-name: myapp-flatpak
github-token: ${{ secrets.GITHUB_TOKEN }} # for private source downloads
# Downstream steps can reference:
# ${{ steps.build.outputs.bundle }} — path to the .flatpak file
# ${{ steps.build.outputs.arch }} — x86_64 or aarch64
# ${{ steps.build.outputs.artifact-url }} — download URL of the uploaded artifact
| Input | Description | Default |
|---|---|---|
manifest |
Path to the generated Flatpak manifest | required |
app-id |
Flatpak application ID | required |
arch |
Target architecture (x86_64 or aarch64); auto-detected from flatpak --default-arch |
— |
bundle |
Output .flatpak filename |
<app-id>_<arch>.flatpak |
artifact-name |
Upload bundle under this artifact name; skip upload if empty | — |
retention-days |
Artifact retention in days | 2 |
github-token |
GitHub token written to ~/.netrc for private source downloads |
— |
| Output | Description |
|---|---|
bundle |
Path to the exported .flatpak bundle |
arch |
Architecture used for the build |
artifact-url |
Download URL of the uploaded artifact (empty if artifact-name is unset) |
Release workflow example #
The generate and build-flatpak actions let you split generation (runs once)
from building (can fan out across multiple architectures):
name: Flatpak release
on:
push:
tags: ['v*']
jobs:
generate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0 # required for --tag to resolve the commit
- uses: o-murphy/flutpak/.github/actions/flutter@v0.6.1
id: flutter
with:
flutter-version: stable
cache: true
- run: flutter pub get
- uses: o-murphy/flutpak/.github/actions/generate@v0.6.1
with:
tag: ${{ github.ref_name }}
metainfo-path: app/share/metainfo/<app-id>.metainfo.xml
artifact-name: flatpak-generated
build:
needs: generate
strategy:
matrix:
arch: [amd64, arm64]
runs-on: ${{ matrix.arch == 'arm64' && 'ubuntu-24.04-arm' || 'ubuntu-latest' }}
steps:
- uses: actions/download-artifact@v8
with:
name: flatpak-generated
path: flatpak/generated
- uses: o-murphy/flutpak/.github/actions/build-flatpak@v0.6.1
id: build
with:
manifest: flatpak/generated/<app-id>.yml
app-id: io.github.YourOrg.YourApp
artifact-name: myapp-${{ matrix.arch }}
github-token: ${{ secrets.GITHUB_TOKEN }}
For a minimal single-job workflow that uses setup-flutpak + manual steps, see
the legacy example below.
Legacy single-job example (manual steps)
name: Flatpak release (manual)
on:
push:
tags: ['v*']
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: o-murphy/flutpak/.github/actions/setup-flutpak@v0.6.1
- uses: o-murphy/flutpak/.github/actions/flutter@v0.6.1
with:
flutter-version: stable
cache: true
- run: flutter pub get
- run: flutpak generate --tag ${{ github.ref_name }}
Validating and building locally #
flutpak generates sources and manifests. The actual lint, build, and
validation steps use the official Flatpak toolchain.
Install toolchain #
# Install appstreamcli (Debian/Ubuntu)
sudo apt-get install appstream
# Install org.flatpak.Builder (required for build + lint)
flatpak remote-add --user --if-not-exists flathub \
https://dl.flathub.org/repo/flathub.flatpakrepo
flatpak install --user flathub org.flatpak.Builder
# Install the Freedesktop runtime
flatpak install flathub org.freedesktop.Sdk//25.08
1. Validate metainfo #
appstreamcli validate --explain --no-net \
app/share/metainfo/<app-id>.metainfo.xml
2. Lint the manifest #
dbus-run-session flatpak run \
--filesystem=host \
--command=flatpak-builder-lint \
org.flatpak.Builder \
--exceptions \
manifest flatpak/generated/<app-id>.yml
--exceptions applies the built-in Flathub exception list. Run this before
building to catch manifest errors early.
Important
Do not set a top-level branch: in your manifest —
flatpak-builder-lint flags this as toplevel-unnecessary-branch.
3. Build with flathub-build #
flathub-build is the same script Flathub CI uses. It runs flatpak-builder
with --sandbox, --install-deps-from=flathub, --repo=repo, and other
Flathub-specific flags.
dbus-run-session flatpak run --command=flathub-build \
org.flatpak.Builder \
flatpak/generated/<app-id>.yml
4. Lint the built repo #
dbus-run-session flatpak run \
--filesystem=host \
--command=flatpak-builder-lint \
org.flatpak.Builder \
--exceptions \
repo repo
5. Export a single-file bundle (optional) #
flatpak build-bundle repo myapp.flatpak <app-id>
flatpak install --user myapp.flatpak
flatpak run <app-id>
How it works #
Pub sources #
For each hosted package in your lock file(s), flutpak fetches the SHA-256
checksum from the pub.dev API and generates two flatpak-builder source
entries:
[
{
"type": "archive",
"url": "https://pub.dev/packages/yaml/versions/3.1.2.tar.gz",
"sha256": "abc123...",
"dest": ".pub-cache/hosted/pub.dev/yaml-3.1.2",
"strip-components": 0
},
{
"type": "inline",
"contents": "abc123...",
"dest": ".pub-cache/hosted-hashes/pub.dev",
"dest-filename": "yaml-3.1.2.sha256"
}
]
Both entries are required: pub get --offline checks the hash file and fails
if it is missing even when the archive is present.
Flutter SDK sources #
When flutter.ref is set, generate produces a standalone
flutter-sdk-<version>.json module. Before generating from scratch it checks
FlutterSdkRegistry:
~/.cache/flutpak/flutter_sdk/flutter-sdk-<version>.json(local cache)flutter_sdk/flutter-sdk-<version>.jsonon themainbranch of the flutpak GitHub repo (GitHub raw)- Fallback: generate from Flutter's GitHub release artifacts
When generating, flutpak fetches the engine version files directly from GitHub
raw API — no local Flutter installation needed:
raw.githubusercontent.com/flutter/flutter/{ref}/bin/internal/engine.version
raw.githubusercontent.com/flutter/flutter/{ref}/bin/internal/material_fonts.version
raw.githubusercontent.com/flutter/flutter/{ref}/bin/internal/gradle_wrapper.version
raw.githubusercontent.com/flutter/flutter/{ref}/packages/flutter_tools/pubspec.lock
From these it constructs download URLs for each artifact — Dart SDK, engine
binaries, fonts, and the Gradle wrapper — for both x86_64 and aarch64.
SHA-256 checksums are cached in ~/.cache/flutpak/ by URL hash.
The resulting flutter-sdk-<version>.json module uses buildsystem: simple
and installs Flutter to /var/lib/flutter. It is inserted as the first module
in the generated manifest (before the app module and rustup). The
flutter_tools/pubspec.lock is included in pubspec-sources.json so the
Flutter toolchain can bootstrap itself offline inside the Flatpak sandbox.
Two entries are always present in the flutter-sdk module's sources:
sky_engine/pubspec.yaml(inline) —packages/sky_engine/was removed from the Flutter git tree in Flutter 3.x. Written inline sopub get --offlineresolves it.shared.sh.patch— patches Flutter's bootstrap script to usepub upgrade --offlineinstead of the network-requiringpub upgrade.
Foreign deps registry #
Source injection happens at generate time, not init time. The template
manifest contains no type: patch or foreign-dep entries — this is by design.
On every generate run, flutpak:
- Fetches
foreign_deps/foreign_deps.jsonfrom GitHub (cached in~/.cache/flutpak/). - Deep-merges any local
foreign-deps:entries fromflutpak.yamlon top. - Resolves the current package versions from
pubspec.lock. - Downloads patch files to
generated/patches/<path>. - Appends all resolved sources to
pubspec-sources.json.
crlf: true on a type: patch source normalises the patch file to CRLF line
endings when writing to generated/patches/; the key is stripped before
writing to pubspec-sources.json. Patches without crlf: true are
normalised to LF, making output deterministic on any host OS.
Note
Add *.patch -text to your project's .gitattributes to prevent git from
re-normalising line endings in patch files on Windows checkouts.
Wrapper script #
For Flutter apps, init generates flatpak/<name>-wrapper.sh:
#!/bin/sh
# Generated by flutpak — https://github.com/o-murphy/flutpak
APP=/app/<name>
export LD_LIBRARY_PATH="$APP/lib:${LD_LIBRARY_PATH:-}"
exec "$APP/<name>" "$@"
This bridges Flatpak's command: entry (pointing to /app/bin/<name>) to the
Flutter bundle at /app/<name>/<name>, setting LD_LIBRARY_PATH for shared
libraries bundled with the app.
Template vs generated #
The template (flatpak/<app-id>.yml) is a standard Flatpak manifest that you
commit to git. It contains no placeholder strings — generate writes tag: and
commit: directly into the git source block via yaml_edit.
generate copies the template to flatpak/generated/<app-id>.yml, sets tag:
and commit: in the git source block, generates pubspec-sources.json (pub +
foreign deps), and writes standalone modules (flutter-sdk-<v>.json,
rustup-<v>.json, cargo-sources.json) when applicable. Foreign dep sources
are resolved from pubspec.lock at generate time — never baked into the
template. The template is never modified; the generated
file is gitignored and rebuilt on every CI run.
generate also validates consistency between the template and config before
writing anything:
app-idin template must matchapp-idin configcommandin template must matchcommandin config (or its default)runtime-versionin template must matchruntime-versionin config
Building from source / contributing #
git clone https://github.com/o-murphy/flutpak.git
cd flutpak
make test # run test suite
make build # compile binary (version from git describe --tags)
Bumping the version #
- Update
version:inpubspec.yaml - Commit and tag (
git tag v1.2.3) —make buildpicks the version from the tag automatically viagit describe
During a release, release.yml performs step 1 automatically when a v*
tag is pushed, so the compiled binaries always report the correct version.
License #
MIT