flitz publish
Build a bundle from your project against the pinned SDK and publish it; flags, output streams, --json, and the on-demand SDK download.
publish is the build-and-ship command. With a pinned SDK, the whole happy
path is:
cd /path/to/your/flutter/app
flitz publish✓ Compiling 12.3s
✓ Packaging 1.1s
✓ Uploading 3.8s 4.2 MB
Published acme_app in 17.2s
Landing page https://…
Deeplink flitz://download?url=…
Bundle https://…followed by a QR code of the deeplink. Scan it with a host that has the Flitz loader embedded and the bundle downloads and runs. Share the landing-page link with a teammate and their browser shows the same QR plus an "open" button.
A bundle is a complete build of your app's Dart code — never a patch — and it runs only inside a host built against the same SDK tag. See How it works for the host/bundle split.
What happens
publish runs entirely in-process; there is no separate builder binary to
fetch. It:
- Reads the pin from
flitz.yamlin the current directory and resolves the cached SDK for that tag — fetching it first if you allow (below). - Asks the Flitz service for upload and download links. This is the licensed
call, so an unauthenticated or unlicensed
publishfails here, with exit 3, before any compilation. - Compiling —
flutter build bundlecompiles the app to kernel, thendart2bytecodecompiles the kernel to bytecode, validated against the dynamic interface that ships inside the pinned SDK. The interface always matches the SDK; there is no override flag. - Packaging — writes
manifest.jsonand packs it with the bytecode andflutter_assets/into a ZIP that is encoded incrementally. - Uploading — streams the archive straight into the upload as it is
encoded, then uploads a single-file HTML landing page. The
.flitzis never written to disk and never held whole in memory.
The published bundle lives in private storage. Possession of the unguessable link is what authorizes a download; there is no anonymous listing. The link stays valid for as long as the object exists — there is no expiry to re-publish around.
Share within your team
Anyone who holds the link can download the bundle, and a bundle runs with the full privileges of the host. Flitz is for your own team's work-in-progress builds, reviewed by collaborators during development; it is not a channel for delivering finished apps to outside users.
Flags
publish accepts the global options — --json,
--yes, -v, --no-color — plus these:
| Flag | Default | Purpose |
|---|---|---|
--app <dir> | . | The Flutter app directory; must contain pubspec.yaml. The SDK pin is still read from the current directory, not from --app. |
--target <file> | lib/main.dart | Dart entry point, relative to the app. Recorded in the bundle manifest as the entry library. |
--target-platform <id> | android-arm64 on Linux, ios on macOS | Forwarded to flutter build bundle. Drives which target's hook/build.dart runs when enumerating native assets. Values: android-arm, android-arm64, android-x64, ios, linux-x64, macos-arm64. |
--dart-define <KEY=VALUE>, -D | — | A compile-time constant, readable via String.fromEnvironment and friends. Repeatable; commas inside a value are not split. Values are redacted in every diagnostic, because defines routinely carry API keys. |
--dart-define-from-file <path> | — | A .json or .env file of compile-time constants. Repeatable. Resolved relative to the directory you ran flitz from. |
--schema <scheme> | flitz | The URL scheme of the emitted deeplink and QR. Must match the scheme your host registers; see URL scheme → Custom schemes. Affects only the deeplink, never the bundle. |
--qr-output <path> | — | Also write the deeplink QR as a standalone PNG. The parent directory must exist; an existing file is overwritten. |
--no-qr | — | Suppress the inline terminal QR. The result block is still printed. |
There is no --output flag: publish never writes a .flitz to disk.
--dart-define values become constants in the compiled bytecode; they do not
change the manifest, the deeplink, or the landing page. Reserved
framework-managed defines cannot be set this way.
Output and --json
Progress and result use different streams. The three timed phases and the
Published … in … line go to stderr; the labelled result block and the QR
go to stdout. So
flitz publish > urls.txtcaptures the three result lines and nothing else, while the terminal still
shows the animated progress. The QR is drawn only when stdout is a terminal in
rich mode; in plain output (CI, --no-color) the phases appear as timestamped
lines and there is no QR.
The labelled block carries no line-format promise. Scripts read the result
with --json, which replaces the block with one document on stdout and
suppresses the QR:
flitz publish --json > result.json{ "page_url": "https://…", "deeplink": "flitz://download?url=…", "bundle_url": "https://…" }page=$(jq -r .page_url result.json)
deeplink=$(jq -r .deeplink result.json)
bundle=$(jq -r .bundle_url result.json)On failure publish --json emits no JSON. Check the exit status first and
parse stdout only on 0 — see Exit codes.
On-demand SDK download and --yes
publish works on a fresh checkout that contains only flitz.yaml. When the
pinned SDK is not in the cache, it offers to fetch it:
The pinned SDK <tag> is not installed. Download it now (about 570 MB)? [Y/n]Accepting runs the download in the same invocation — without re-pinning — and
then continues the build. Declining aborts with
the pinned SDK <tag> is not installed and the flitz sdk use <tag> hint
(exit 1).
Non-interactively — a CI job, a redirected terminal — publish does not
start a large unattended download. It fails immediately with that same error
unless you pass --yes:
flitz publish --yes # fresh checkout, no prompt, one command--yes answers every prompt affirmatively; assume_yes: true in
config.yaml does the same permanently.
This offer exists only in publish. No other command downloads an SDK
implicitly.
A one-command CI job, on a checkout that carries only flitz.yaml:
export FLITZ_APIKEY="$FLITZ_APIKEY_SECRET" # from your CI secret store
flitz publish --yes --jsonSee flitz apikey → Using FLITZ_APIKEY in CI.
When it fails
Every failure follows the message convention: one sentence, then the next command. The exit code says which class of thing went wrong.
| You see | Code | Meaning |
|---|---|---|
no SDK is pinned for this project | 1 | No flitz.yaml with an sdk: key in the current directory. Run flitz sdk use latest here — the pin is not read from --app. |
the pinned SDK <tag> is not installed | 1 | Non-interactive without --yes, or you declined the download. |
pubspec.yaml not found at … | 1 | --app does not point at a Flutter app. |
you are not logged in to Flitz / your Flitz session has ended / your organization's Flitz license is not active | 3 | Credential or license. Nothing was compiled. |
kernel compilation failed (exit N) | 5 | flutter build bundle rejected the app; its diagnostics are printed verbatim above. |
bytecode compilation failed (exit N) | 5 | dart2bytecode rejected the kernel. See Troubleshooting → Build failures. |
the bundle destination is already occupied | 4 | A previous, interrupted publish reserved that object. Re-run; a fresh one is allocated. |
the bundle upload failed: … | 4 | Re-run flitz publish. |
Two partial-success cases are worth knowing. If the bundle upload succeeded
but the landing-page upload did not, the failure message includes the bundle
URL and the command exits 4 without a deeplink or page URL. If everything
uploaded and the URLs were printed, a later local failure — a --qr-output
PNG that could not be written — exits 2 without retracting the printed result.
Ctrl-C during a publish stops the build tool it started, deletes its scratch
directory, and exits 2. If the interrupt landed during an upload, the notice
adds that a partial object may remain at the destination and that re-running
publish allocates a fresh one — which is why the "already occupied" error
above never arrives as a surprise.
Re-run with -v to watch the compilers live instead of only seeing their
output quoted on failure.