Introducing flutpak - automating Flathub submission for Flutter apps
Repo: GitHub - o-murphy/flutpak: Automates Flatpak packaging and offline builds for Flutter apps from pubspec.yaml. · GitHub
Version: 0.4.0-rc.2 (pre-1.0, breaking changes expected until stable)
The problem
Flathub requires every source to be declared upfront with a SHA-256 checksum -
no network access at build time. For a typical Flutter project that means:
- Every pub.dev dependency needs two source entries in
generated-sources.json: an archive download and an inline hash file that
pub get --offlinelooks for separately. Miss the hash file and the build
fails even if the archive is present. - The Flutter SDK brings its own set of artifacts - engine binaries, Dart SDK,
material fonts, Gradle wrapper - each with architecture-specific variants for
x86_64 and aarch64, all requiring correct checksums at a specific engine
revision. - Packages like
objectbox_flutter_libsrequire patch sources whosedest:
path embeds the exact package version
(.pub-cache/hosted/pub.dev/objectbox_flutter_libs-5.3.1). Update the
package and that path silently goes stale, producing a manifest that passes
linting but breaks at build time. - The Flutter SDK bootstrap script (
shared.sh) callspub upgrade, which
requires network. It needs to be patched topub get --offlinefor every
Flatpak build, every Flutter version. - None of the existing generator tooling (flatpak-pip-generator and its
siblings) understands the Dart/Flutter ecosystem.
Doing all of this by hand for a non-trivial app means tracking hundreds of
source entries, re-verifying checksums on every dependency bump, and
re-hunting Flutter artifact URLs on every Flutter release. This is the main
reason Flutter apps are underrepresented on Flathub - not lack of interest, but
the sheer amount of manual work required just to get to the point where you can
open a submission PR.
What flutpak targets
flutpak is not a general Flatpak build tool. It is specifically aimed at
producing the output that a Flathub submission expects:
- A
generated-sources.jsonin the format Flathub reviewers are familiar with - A manifest that passes
flatpak-builder-lint --exceptionswithout manual
tweaks - A
flathub.jsonwith correct submission metadata - Metainfo and desktop entry validation via
appstreamclias part of the
generate step - GitHub Actions that mirror the
flathub-buildpipeline used in Flathub CI,
so what passes locally also passes in review
The goal is to reduce the gap between “my app builds with Flutter” and “my app
is published on Flathub” to a config file and two commands.
Where flutpak started
The first version (0.1.0, May 2026) was a single prepare command that
generated generated-sources.json from a lock file and patched a manifest
using __FLATPAK_TAG__ / __FLATPAK_COMMIT__ placeholder strings. It also
generated metainfo XML and .desktop files from config, and had lint,
validate, and export wrapper commands around the official Flatpak toolchain.
It worked for one app. It was also immediately clear what was wrong with it:
- The metainfo and desktop file generators were fragile and duplicated what
appstreamcliand the Flathub submission process already validate. Removed
in0.2.8. - The
lintandvalidatewrappers around official tools added complexity
without value. Removed in0.2.5-0.2.6. - Patch sources were baked into
generated-sources.jsonatinittime. The
dest:path included the package version, so any version bump silently
produced a broken manifest. Fixed in0.4.0-beta.2by moving patch injection
togeneratetime, where the currentpubspec.lockis always available. - The manifest template carried
__FLATPAK_TAG__and__FLATPAK_COMMIT__
strings thatgeneratereplaced withsed. Any YAML structural change near
those strings risked breaking substitution. Replaced in0.4.0-rc.1with
directyaml_editinjection into the correct YAML node - no strings to
maintain, no fragile text replacement. - Config keys were inconsistently cased (
app_id,runtime_version, etc.)
and mixed flutpak-specific fields with Flatpak manifest fields in the same
section. Cleaned up in0.4.0-rc.1: all keys are now kebab-case, and
flutpak-specific fields (repo-url,metainfo-path,icons) live at the
root level rather than insidemanifest:.
The general direction has been: remove everything that duplicates official
tooling, automate everything that is mechanical and error-prone, and keep
the config minimal.
What it looks like now
Two commands cover the full workflow:
flutpak init - run once. Generates the editable template manifest, a
Flutter wrapper shell script, flathub.json, and .gitignore. Validates that
your metainfo, desktop entry, and icon files exist before writing anything.
flutpak generate --tag vX.Y.Z - run on every CI build. Reads the
committed template, writes tag: and commit: directly into the git source
block via yaml_edit, generates generated-sources.json, and copies
everything to flatpak/generated/ ready for flatpak-builder.
The minimum config to get started:
# flutpak.yaml
flutter:
sdk: $FLUTTER_ROOT
manifest:
app-id: io.github.YourOrg.YourApp
Everything else - command name, repo URL, flutter version file path, sandbox
permissions - is either auto-detected or written into the template on init
with sensible defaults. You edit the template directly; flutpak never
overwrites it.
The known-patches/ directory ships reference patch files and companion
Flatpak module files for packages that commonly need offline build
modifications. For example, objectbox_flutter_libs needs a CMakeLists.txt
patch to use a prebuilt library instead of fetching it at build time. Copying
the relevant files and adding two lines to config is enough - generate handles
the rest, including the version-stamped dest: path and stripping CRLF line
endings if the upstream archive has them.
GitHub Actions composite actions are provided for CI, mirroring the Flathub
build pipeline:
- uses: o-murphy/flutpak/.github/actions/generate@v0.4.0-rc.2
with:
tag: ${{ github.ref_name }}
metainfo-path: app/share/metainfo/<app-id>.metainfo.xml
- uses: o-murphy/flutpak/.github/actions/build-flatpak@v0.4.0-rc.2
with:
manifest: flatpak/generated/<app-id>.yml
app-id: io.github.YourOrg.YourApp
Dogfooding
I am using flutpak to package ebalistyka
for Flathub. The tool exists because I needed it and nothing else filled this
gap. That submission is still in progress - which is exactly why the API is not
stable yet. Working through a real Flathub submission is what surfaces the
remaining rough edges.
Where community help matters most
known-patches/ is where the most useful community contributions can land.
If you use a Flutter package that requires a patch source - pre-built native
libraries, CMake modifications, anything that needs a type: patch or
type: shell source in the manifest - contributing a patch file and companion
module there makes it work automatically for everyone using that package.
Beyond patches:
- Bug reports and edge cases from real apps are the most valuable feedback at
this stage - Feedback on the manifest structure that
flutpak initgenerates - Packages that require source handling not yet covered
Issue tracker: Issues · o-murphy/flutpak · GitHub
*Pre-1.0 means breaking changes are possible between minor versions. The
changelog documents them. If you are packaging a Flutter app for Flathub - or
have been putting it off because the source generation work seemed too painful
- this is worth a look.*