flutpak 0.3.0 copy "flutpak: ^0.3.0" to clipboard
flutpak: ^0.3.0 copied to clipboard

PlatformLinux

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 #

Pub Version

A Dart CLI tool that automates Flatpak packaging for Flutter applications. Analogous to flutter pub run build_runner build — describe your config once in pubspec.yaml and let flutpak prepare generate everything needed for a Flathub-compatible offline sandboxed build.

  • One commandflutpak prepare generates sources, resolves patches, and creates/updates the manifest
  • No Python — pure Dart, compiles to a single native binary for CI
  • Config in pubspec.yaml — same pattern as msix_config, flutter_native_splash
  • Manifest generation — writes flatpak/<app_id>.yml with __FLATPAK_TAG__ / __FLATPAK_COMMIT__ placeholders that CI patches
  • Patches registry — known packages (e.g. objectbox_flutter_libs) get their patches resolved automatically
  • Retry on transient errors — pub.dev and Flutter artifact downloads retry on 429/5xx

Table of Contents #

  1. Prerequisites
  2. Installation
  3. Step-by-step setup
  4. Full config reference
  5. Manifest lifecycle
  6. Commands
  7. CI/CD integration
  8. Validating and building locally
  9. Why include flutter_tools/pubspec.lock?
  10. How it works

Prerequisites #

Requirement When needed Notes
Linux Always flutpak targets Linux only
git Always Version detection and commit-hash resolution
Internet access prepare / sources / flutter Downloads from pub.dev and storage.googleapis.com
Flutter SDK Flutter projects Path via $FLUTTER_ROOT or --sdk; pure-Dart projects can omit this
Dart SDK Building from source only Not required when using a pre-built binary
flatpak-builder Local Flatpak builds Only needed when you actually build the Flatpak, not during flutpak prepare

Installation #

dart pub global activate flutpak

Or compile a native binary (for CI without a Dart SDK):

git clone --depth 1 https://github.com/o-murphy/flutpak.git /tmp/flutpak_src
cd /tmp/flutpak_src && dart pub get
dart compile exe bin/flutpak.dart -o /tmp/flutpak

Step-by-step setup #

Step 1 — Add config to pubspec.yaml #

Add a flutpak: section to your app's pubspec.yaml (or create a standalone flutpak.yaml — but not both):

flutpak:
  output: flatpak                              # directory for all generated files
  flutter_version_file: flatpak/flutter.version  # optional — records the Flutter version

  pub:
    locks:
      - pubspec.lock
      - $FLUTTER_ROOT/packages/flutter_tools/pubspec.lock  # include flutter_tools deps

  flutter:
    sdk: $FLUTTER_ROOT

  manifest:
    app_id: io.github.YourOrg.YourApp
    runtime_version: "25.08"
    command: yourapp
    repo_url: https://github.com/YourOrg/YourApp.git
    finish_args:
      - --share=ipc
      - --socket=wayland
      - --device=dri

See Full config reference for all options.


Step 2 — Prepare the project structure #

The generated manifest installs app assets from a fixed app/ directory in your repository root. Create these files before running flutpak prepare:

app/
└── share/
    ├── applications/
    │   └── <app_id>.desktop          # XDG desktop entry
    ├── icons/
    │   └── hicolor/
    │       └── 512x512/
    │           └── apps/
    │               └── <app_id>.png  # 512×512 application icon
    └── metainfo/
        └── <app_id>.metainfo.xml     # AppStream metainfo

Note: The app/ source directory is fixed in the generated manifest. After the initial generation you can manually edit the manifest to point to a different path; only the tag: and commit: lines are updated automatically on subsequent flutpak prepare runs. For questions about what files belong in the share directory and their required format, see the Flathub app-author docs.

These files are read directly from your git source tree during the Flatpak build. flutpak does not generate them — maintain them manually.

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>
  <metadata_license>MIT</metadata_license>
  <project_license>MIT</project_license>
  <name>Your App</name>
  <summary>Short one-line description</summary>
  <releases>
    <release version="0.1.0" date="2025-01-01"/>
  </releases>
</component>

Step 3 — First run: generate everything #

FLUTTER_ROOT=/path/to/flutter flutpak prepare

This creates:

flatpak/
├── <app_id>.yml               # manifest template (with __FLATPAK_TAG__ placeholders)
├── generated-sources.json     # pub packages + Flutter SDK artifacts
├── patches/
│   └── flutter/
│       └── shared.sh.patch    # built-in offline patch for Flutter bootstrap
└── flutter.version            # pinned Flutter version (if flutter_version_file set)

Commit all generated files to git. The manifest is a template artifact (analogous to *.g.dart from build_runner) that Flathub reviewers inspect.


Step 4 — Customize the generated manifest #

After the first run, open flatpak/<app_id>.yml and review it. You can freely edit everything in this file — it will not be overwritten on the next flutpak prepare run.

The only lines that flutpak prepare --tag ... --commit ... updates automatically are the tag: and commit: values in the app git source block (the one marked disable-submodules: true). Everything else is yours to edit:

Section Safe to edit?
finish-args: Yes
build-commands: Yes
build-options: (env, PATH) Yes
sdk-extensions: Yes
modules: (extra modules) Yes
sources: (extra archives, patches) Yes
app/ directory in install commands Yes — change to any path you need
tag: / commit: in the app git source Updated automatically by flutpak

Common customisations:

  • Add a flathub.json exception for lint rules (see Step 6)
  • Add extra build dependencies as modules before the app module
  • Adjust finish-args for Wayland/X11 fallback, filesystem permissions, etc.
  • Change the app/ prefix in install commands if your repo uses a different layout

Step 5 — CI: pin tag/commit on each release #

On every release, regenerate sources and pin the tag:

flutpak prepare \
  --tag "$TAG" \
  --commit "$COMMIT_SHA" \
  --sdk "$FLUTTER_ROOT"

This updates the manifest's tag: and commit: lines and rewrites generated-sources.json with fresh checksums.

Using the setup-flutpak composite action handles Flutter setup and binary download automatically.


Step 6 — Submit to Flathub #

A Flathub submission is a PR to the app's dedicated Flathub repository. The repository must contain these files at its root:

<flathub-repo>/
├── <app_id>.yml               # pinned manifest (tag + commit set)
├── generated-sources.json     # pub + Flutter SDK sources
├── flathub.json               # Flathub build metadata (see below)
└── patches/
    └── flutter/
        └── shared.sh.patch    # offline patch for Flutter bootstrap

The CI example in this README uploads these as a flathub-submission-* artifact on tag-triggered builds — download it and copy the contents into your Flathub repository.

flathub.json lives in the root of your Flathub repository alongside the manifest. Keep a copy in flatpak/flathub.json in your app's repo so it is picked up by the CI artifact upload. Minimal example:

{
  "only-arches": ["x86_64", "aarch64"]
}

Common fields:

Field Description
only-arches Architectures Flathub should build for
end-of-life Mark the app as end-of-life with a message
end-of-life-rebase Suggest a replacement app ID

For all flathub.json options and full submission requirements — including what the app/share/ tree must contain, required metainfo fields, icon specifications, and the review process — see the Flathub app-author documentation.

Before opening the PR, validate locally:

# 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/<app_id>.yml

# 3. Build
dbus-run-session flatpak run --command=flathub-build \
  org.flatpak.Builder flatpak/<app_id>.yml

See Validating and building locally for the full workflow.


Full config reference #

Config lives in one of two places (error if both exist):

Location Key
pubspec.yaml flutpak: section
flutpak.yaml standalone file
flutpak:
  output: flatpak   # output directory for all generated files (default: flatpak)

  flutter_version_file: flatpak/flutter.version  # optional: write Flutter version here

  pub:
    locks:
      - pubspec.lock
      - $FLUTTER_ROOT/packages/flutter_tools/pubspec.lock  # $ENV vars are expanded

  flutter:
    sdk: $FLUTTER_ROOT
    patch: flatpak/patches/flutter/shared.sh.patch  # optional: custom shared.sh patch

  patches:                    # project-level patches for pub packages
    - package: objectbox_flutter_libs
      path: flatpak/patches/objectbox_flutter_libs/CMakeLists.txt.patch
      dest_subpath: linux     # optional: subdir within the pub package root
      # version: 5.3.1        # optional: auto-resolved from pubspec.lock if omitted

  manifest:
    app_id: io.github.YourOrg.YourApp
    runtime_version: "25.08"
    sdk_extensions:
      - org.freedesktop.Sdk.Extension.llvm20   # auto-adds llvm bin/lib to PATH
    command: yourapp
    repo_url: https://github.com/YourOrg/YourApp.git
    finish_args:
      - --share=ipc
      - --socket=fallback-x11
      - --socket=wayland
      - --device=dri
    extra_modules:
      - flatpak/modules/some-native-dep.yml    # included verbatim in modules list
    env:                                       # build-options env vars (shorthand)
      MY_VAR: value
    build_options:
      append_path: /custom/bin               # appended to PATH
      prepend_ld_library_path: /custom/lib   # prepended to LD_LIBRARY_PATH
      env:                                   # merged with top-level env:
        ANOTHER_VAR: value
    extra_sources:                           # verbatim flatpak sources (arch-specific archives, etc.)
      - type: archive
        only-arches:
          - x86_64
        url: https://github.com/example/lib/releases/download/v1.0/lib-linux-x64.tar.gz
        sha256: abc123...
        dest: lib-prebuilt
        strip-components: 0
      - type: archive
        only-arches:
          - aarch64
        url: https://github.com/example/lib/releases/download/v1.0/lib-linux-aarch64.tar.gz
        sha256: def456...
        dest: lib-prebuilt
        strip-components: 0

Manifest lifecycle #

The generated manifest goes through two phases:

Phase 1 — Template (after first `flutpak prepare`)

  flatpak/<app_id>.yml contains:
    tag: __FLATPAK_TAG__
    commit: __FLATPAK_COMMIT__
    disable-submodules: true

  Commit this file to git — it's the starting point for Flathub review.


Phase 2 — Pinned (after `flutpak prepare --tag vX.Y.Z --commit SHA`)

  flatpak/<app_id>.yml becomes:
    tag: v0.1.15
    commit: abc1234567890abcdef...
    disable-submodules: true

  Commit the pinned manifest + regenerated generated-sources.json.
  These are the files you submit in the Flathub PR.

On each subsequent release, run flutpak prepare --tag ... --commit ... again. Only the tag: and commit: lines change; everything else you edited manually is preserved.


Patches registry #

flutpak ships a built-in registry of patches for packages that need special handling inside the Flatpak sandbox. Patches are applied automatically when the package is found in any of your lock files and a patch file exists in <output>/patches/.

Package What the patch does
objectbox_flutter_libs Replaces prebuilt binary download with a local objectbox-c archive
sqflite_common_ffi Adjusts CMake for sandbox builds

Registry entries may specify a versionConstraint (semver range) to limit which package versions the patch applies to. Project-level patches: entries always take priority over registry entries for the same package.


Commands #

One-shot command — generates sources, resolves patches, and creates or updates the manifest.

# First run: generate everything from scratch
flutpak prepare

# CI: update placeholders + regenerate sources
flutpak prepare --tag v1.2.3 --commit abc1234567890 --sdk "$FLUTTER_ROOT"
Flag Description
--tag Git tag embedded in the manifest (e.g. v0.1.14). Omit to remove the tag: line.
--commit Full git commit SHA. Defaults to git rev-parse HEAD.
-s, --sdk Flutter SDK path. Defaults to $FLUTTER_ROOT.
--no-sources Skip source regeneration (manifest update only).
--pub-only Skip Flutter SDK sources, generate only pub packages.
--flutter-only Skip pub sources, generate only Flutter SDK artifacts.
-n, --dry-run Print what would be done without writing any files.
-c, --config Path to config file (default: flutpak.yaml).

Workflow:

pubspec.yaml  [flutpak: ...]
      ↓
flutpak prepare                    # first run: generates everything
      ↓
flatpak/<app_id>.yml               # manifest with __FLATPAK_TAG__ / __FLATPAK_COMMIT__
flatpak/generated-sources.json     # pub packages + Flutter SDK artifacts
flatpak/patches/                   # patches from registry (if applicable)
      ↓
git commit + push
      ↓
CI: flutpak prepare --tag $TAG --commit $SHA --sdk $FLUTTER_ROOT
      ↓
flatpak-builder build --repo=...

sources #

Generates generated-sources.json combining pub packages and Flutter SDK.

flutpak sources \
  --lock pubspec.lock \
  --lock "$FLUTTER_ROOT/packages/flutter_tools/pubspec.lock" \
  --sdk "$FLUTTER_ROOT" \
  --output flatpak
Flag Description
-l, --lock pubspec.lock path (repeatable, $ENV expanded)
-s, --sdk Flutter SDK path
-o, --output Output directory (default: flatpak)
--pub-only Skip Flutter SDK sources
--flutter-only Skip pub sources

pub #

Generates sources for pub packages only.

flutpak pub --lock pubspec.lock --output flatpak

flutter #

Generates sources for Flutter SDK artifacts only.

flutpak flutter --sdk "$FLUTTER_ROOT" --output flatpak

sdk-ext #

Generates a Flathub SDK Extension manifest (org.freedesktop.Sdk.Extension.flutter3) to share the Flutter SDK across multiple apps on the same machine.

flutpak sdk-ext \
  --sdk "$FLUTTER_ROOT" \
  --runtime-version 25.08 \
  --output org.freedesktop.Sdk.Extension.flutter3.json

CI/CD integration #

--tag and --commit — test builds vs. releases #

The behaviour of --tag and --commit differs between test/PR builds and official releases:

Scenario --tag --commit Result in manifest
PR / test build omit (or pass the commit SHA) full commit SHA no tag: line; commit: pinned to SHA
Release version tag, e.g. v1.2.3 full commit SHA tag: v1.2.3 + commit: pinned
# PR / test build — no tag, commit only
flutpak prepare \
  --commit "$(git rev-parse HEAD)" \
  --sdk "$FLUTTER_ROOT"

# Release — tag + commit
flutpak prepare \
  --tag "v1.2.3" \
  --commit "$(git rev-parse HEAD)" \
  --sdk "$FLUTTER_ROOT"

Flatpak manifests submitted to Flathub must have both tag: and commit: set to a specific release tag. Test builds without a tag are useful for local validation and PR artifacts.


GitHub Actions — composite actions #

The flutpak repository ships two composite actions for use in your workflows.

setup-flutpak

Clones the flutpak repository, compiles the binary from source, and adds it to PATH. Use it before any flutpak commands:

      - name: Set up flutpak
        uses: o-murphy/flutpak/.github/actions/setup-flutpak@main
        # with:
        #   version: v0.2.8        # pin to a specific release tag (default: latest)
        #   install_dir: /usr/local/bin   # default
Input Description Default
version Release tag, commit SHA, or branch to compile latest release
install_dir Directory where the flutpak binary is placed /usr/local/bin

flutter

Installs the Flutter SDK with runner.arch-aware caching. Unlike subosito/flutter-action, the cache key includes runner.arch, which makes it work correctly on ubuntu-24.04-arm runners — the upstream action has not fixed this.

      - name: Set up Flutter
        uses: o-murphy/flutpak/.github/actions/flutter@main
        with:
          flutter-version: "3.41.9"   # or read from flatpak/flutter.version
          cache: true                 # default
Input Description Default
flutter-version Version tag, stable, or beta stable
cache Cache the SDK between runs true
Output Description
flutter-path Absolute path to the installed Flutter SDK

Full build workflow example #

The example below mirrors a real-world workflow that:

  • Runs on pull_request (both amd64 and arm64) and on workflow_call / workflow_dispatch (single arch)
  • Installs Flutter via the bundled flutter action, which caches correctly on both x86_64 and aarch64 runners
  • Passes the commit SHA as --tag for PR builds and the git tag for releases
  • Caches org.flatpak.Builder and .flatpak-builder/downloads between runs
  • Exports a .flatpak bundle as a workflow artifact
  • Uploads a ready-to-submit Flathub artifact (manifest + sources + patches + flathub.json) on tag-triggered builds only
name: Build Flatpak

on:
  pull_request:
    paths:
      - "app/**"
      - "lib/**"
      - "linux/**"
      - "flatpak/**"
      - "pubspec.yaml"
  workflow_call:
    inputs:
      arch:
        description: "Target architecture"
        required: true
        type: string   # amd64 | arm64
      build_name:
        description: "Version name override"
        required: false
        type: string
        default: ""
      retention_days:
        required: false
        type: number
        default: 2
    outputs:
      artifact_name:
        value: ${{ jobs.build.outputs.artifact_name }}
      artifact_url:
        value: ${{ jobs.build.outputs.artifact_url }}
  workflow_dispatch:
    inputs:
      arch:
        description: "Target architecture"
        required: true
        default: "amd64"
        type: choice
        options: [amd64, arm64]
      build_name:
        required: false
        default: ""
        type: string

concurrency:
  group: build-flatpak-${{ github.ref }}-${{ inputs.arch || 'all' }}
  cancel-in-progress: true

jobs:
  build:
    name: flatpak-${{ matrix.arch }}
    strategy:
      fail-fast: false
      matrix:
        arch: >-
          ${{
            github.event_name == 'pull_request'
              && fromJSON('["amd64","arm64"]')
              || fromJSON(format('["{0}"]', inputs.arch || 'amd64'))
          }}
    runs-on: ${{ matrix.arch == 'arm64' && 'ubuntu-24.04-arm' || 'ubuntu-latest' }}
    permissions:
      pull-requests: write
      issues: write

    outputs:
      artifact_name: ${{ steps.meta.outputs.artifact_name }}
      artifact_url:  ${{ steps.upload.outputs.artifact-url }}

    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Set build metadata
        id: meta
        run: |
          BUILD_NAME="${{ inputs.build_name }}"
          if [ -z "$BUILD_NAME" ]; then
            BUILD_NAME=$(grep '^version:' pubspec.yaml \
              | sed 's/version:[[:space:]]*//' | sed 's/+.*//')
          fi
          BUILD_NAME="${BUILD_NAME#v}"
          if [ "${{ github.event_name }}" = "pull_request" ]; then
            BUILD_NAME="${BUILD_NAME%%-*}-pr.${{ github.event.number }}"
          fi
          BUILD_NUMBER=$(git rev-list --count --first-parent HEAD)
          ARCH="${{ matrix.arch }}"
          [ "$ARCH" = "amd64" ] && ARCH_SUFFIX="x86_64" || ARCH_SUFFIX="aarch64"
          echo "build_name=$BUILD_NAME"     >> $GITHUB_OUTPUT
          echo "build_number=$BUILD_NUMBER" >> $GITHUB_OUTPUT
          echo "arch_suffix=$ARCH_SUFFIX"   >> $GITHUB_OUTPUT
          echo "bundle=myapp_linux_${ARCH_SUFFIX}.flatpak" >> $GITHUB_OUTPUT
          echo "artifact_name=myapp-flatpak-${ARCH_SUFFIX}-${BUILD_NAME}-${BUILD_NUMBER}" \
            >> $GITHUB_OUTPUT

      - name: Read Flutter version
        id: flutter-ver
        run: echo "version=$(cat flatpak/flutter.version)" >> $GITHUB_OUTPUT

      # Uses the bundled flutter action — cache key includes runner.arch so
      # it works correctly on both ubuntu-latest (x86_64) and ubuntu-24.04-arm.
      - name: Set up Flutter
        uses: o-murphy/flutpak/.github/actions/flutter@main
        with:
          flutter-version: ${{ steps.flutter-ver.outputs.version }}
          cache: true

      - run: flutter pub get

      # For PR/test builds pass the commit SHA as --tag so flatpak-builder
      # can still build a reproducible image without a real release tag.
      # For tag-triggered builds pass the version tag so Flathub can match
      # the source to a GitHub release.
      - name: Resolve ref for flutpak
        id: resolve-ref
        run: |
          if [[ "${{ github.ref }}" == refs/tags/* ]]; then
            echo "tag=${{ github.ref_name }}" >> $GITHUB_OUTPUT
          else
            echo "tag=$(git rev-parse HEAD)"  >> $GITHUB_OUTPUT
          fi

      - name: Prepare Flatpak sources and manifest
        run: |
          dart run flutpak prepare \
            --tag  "${{ steps.resolve-ref.outputs.tag }}" \
            --commit "$(git rev-parse HEAD)" \
            --sdk "$FLUTTER_ROOT"

      - name: Install flatpak and appstream
        run: sudo apt-get update -qq && sudo apt-get install -y flatpak appstream

      - name: Validate metainfo
        run: |
          appstreamcli validate --explain --no-net \
            app/share/metainfo/<app_id>.metainfo.xml

      - name: Cache org.flatpak.Builder + runtimes
        uses: actions/cache@v5
        with:
          path: ~/.local/share/flatpak
          key: flatpak-builder-25.08-${{ runner.arch }}-v1

      - name: Cache flatpak-builder downloads
        uses: actions/cache@v5
        with:
          path: .flatpak-builder/downloads
          key: >-
            flatpak-dl-${{ steps.meta.outputs.arch_suffix }}-${{
              hashFiles('flatpak/<app_id>.yml','pubspec.lock','flatpak/flutter.version')
            }}
          restore-keys: flatpak-dl-${{ steps.meta.outputs.arch_suffix }}-

      - name: Install org.flatpak.Builder
        run: |
          flatpak remote-add --user --if-not-exists flathub \
            https://dl.flathub.org/repo/flathub.flatpakrepo
          dbus-run-session flatpak install --user -y --noninteractive flathub \
            org.flatpak.Builder

      - name: Lint manifest
        run: |
          dbus-run-session flatpak run \
            --filesystem=host \
            --command=flatpak-builder-lint \
            org.flatpak.Builder --exceptions \
            manifest flatpak/<app_id>.yml

      - name: Configure GitHub token for flatpak-builder
        run: |
          echo "machine github.com login x-access-token password ${{ secrets.GITHUB_TOKEN }}" \
            >> ~/.netrc
          chmod 600 ~/.netrc

      - name: Build Flatpak
        run: |
          dbus-run-session flatpak run --command=flathub-build \
            org.flatpak.Builder flatpak/<app_id>.yml

      - name: Lint repo
        run: |
          dbus-run-session flatpak run \
            --filesystem=host \
            --command=flatpak-builder-lint \
            org.flatpak.Builder --exceptions \
            repo repo

      - name: Export .flatpak bundle
        run: |
          flatpak build-bundle \
            --arch="${{ steps.meta.outputs.arch_suffix }}" \
            repo "${{ steps.meta.outputs.bundle }}" <app_id>

      - name: Upload artifact
        id: upload
        uses: actions/upload-artifact@v4
        with:
          name: ${{ steps.meta.outputs.artifact_name }}
          path: ${{ steps.meta.outputs.bundle }}
          retention-days: ${{ inputs.retention_days || 2 }}
          if-no-files-found: error

      # Upload the Flathub submission package on tag-triggered builds only.
      # Download this artifact and copy its contents into your Flathub
      # repository to open a submission PR.
      - name: Upload Flathub submission artifacts
        if: startsWith(github.ref, 'refs/tags/')
        uses: actions/upload-artifact@v4
        with:
          name: flathub-submission-${{ steps.meta.outputs.arch_suffix }}-${{ github.ref_name }}
          path: |
            flatpak/<app_id>.yml
            flatpak/generated-sources.json
            flatpak/flathub.json
            flatpak/patches/
          retention-days: 90
          if-no-files-found: warn

Replace <app_id> with your actual app ID (e.g. io.github.YourOrg.YourApp).


Validating and building locally #

flutpak generates sources and manifests. The actual build, lint, and validation steps use the official Flatpak toolchain.

Prerequisites #

# 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

1. Validate metainfo (AppStream XML) #

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/<app_id>.yml
  • --exceptions — apply the built-in Flathub exception list
  • --filesystem=host — lets the sandboxed linter read files from the host
  • Run this before building to catch manifest errors early

Note: 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/<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, for local testing) #

flatpak build-bundle repo myapp.flatpak <app_id>
flatpak install --user myapp.flatpak
flatpak run <app_id>

Full GitHub Actions CI example #

      - name: Install flatpak and appstream
        run: sudo apt-get install -y flatpak appstream

      - name: Validate metainfo
        run: |
          appstreamcli validate --explain --no-net \
            app/share/metainfo/<app_id>.metainfo.xml

      - name: Cache org.flatpak.Builder
        uses: actions/cache@v5
        with:
          path: ~/.local/share/flatpak
          key: flatpak-builder-25.08-${{ runner.arch }}-v1

      - name: Install org.flatpak.Builder
        run: |
          flatpak remote-add --user --if-not-exists flathub \
            https://dl.flathub.org/repo/flathub.flatpakrepo
          dbus-run-session flatpak install --user -y --noninteractive flathub \
            org.flatpak.Builder

      - name: Lint manifest
        run: |
          dbus-run-session flatpak run \
            --filesystem=host \
            --command=flatpak-builder-lint \
            org.flatpak.Builder \
            --exceptions \
            manifest flatpak/<app_id>.yml

      - name: Build Flatpak
        run: |
          dbus-run-session flatpak run --command=flathub-build \
            org.flatpak.Builder \
            flatpak/<app_id>.yml

      - name: Lint repo
        run: |
          dbus-run-session flatpak run \
            --filesystem=host \
            --command=flatpak-builder-lint \
            org.flatpak.Builder \
            --exceptions \
            repo repo

      - name: Export bundle
        run: |
          flatpak build-bundle repo myapp.flatpak <app_id>

Why include flutter_tools/pubspec.lock? #

Before any flutter command runs, the Flutter tooling bootstraps itself by running pub get inside packages/flutter_tools/. This requires flutter_tools dependencies to be present in the offline pub cache.

Pass both lock files so the generated sources cover the app and the tool:

pub:
  locks:
    - pubspec.lock
    - $FLUTTER_ROOT/packages/flutter_tools/pubspec.lock

When the same package appears at different versions (e.g. yaml 3.1.2 in the app and yaml 3.1.3 in flutter_tools), both versions are included — deduplication is by (name, version) pair.


How it works #

Pub packages #

For each hosted package, flutpak fetches the SHA-256 from the pub.dev API and generates two flatpak 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's missing even when the archive is present.

Flutter SDK artifacts #

Reads version files from a local Flutter install (bin/internal/engine.version, etc.) and constructs download URLs for each artifact (Dart SDK, engine binaries, fonts, Gradle wrapper). SHA-256 checksums are cached in ~/.cache/flutpak/ by URL hash to avoid redundant downloads across runs.

Two extra entries are always added:

  • sky_engine/pubspec.yaml (inline)packages/sky_engine/ was removed from the Flutter git tree in Flutter 3.x. Written inline so pub get --offline resolves it.

  • shared.sh.patch — Flutter's bootstrap script runs pub upgrade (requires network). The built-in patch replaces it with pub get --offline. Written to flatpak/patches/flutter/shared.sh.patch next to the output file.

Flutter version resolution #

flutpak determines the Flutter version from the SDK path using the following fallback chain:

  1. <sdk>/version — flat version file present in Flutter < 3.32
  2. git describe --tags --abbrev=0 — works for any tagged git clone
  3. <sdk>/packages/flutter/pubspec.yaml — inner package version field

Contributing / building from source #

git clone https://github.com/o-murphy/flutpak.git
cd flutpak
dart pub get
make build          # regenerates lib/src/version.dart, then compiles the binary
make test           # runs the test suite

Bumping the version #

  1. Update version: in pubspec.yaml
  2. Run make version (or dart run tool/update_version.dart) — rewrites lib/src/version.dart
  3. Commit both files

During a release, release.yml does steps 1–3 automatically when you push a v* tag, so the compiled binaries always report the correct version.


License #

MIT

0
likes
140
points
901
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

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, manifest with placeholders, patches, and Flutter SDK sources.

Repository (GitHub)
View/report issues

Topics

#flatpak #flathub #flutter #build-tool #linux

License

MIT (license)

Dependencies

args, crypto, glob, http, path, pub_semver, yaml

More

Packages that depend on flutpak