Flutter Release
Preview release. macOS, Android and Web pipelines are verified on macOS (Apple Silicon). Windows MSI and Linux AppImage/DEB/RPM packaging are implemented and unit-tested but not yet verified on Windows and Linux hosts — please report issues.
Build, package and verify Flutter release artifacts from a graphical wizard in VS Code.
Select your platforms, architectures and installer formats, click Build Release, watch real progress, and get verified files in your output folder — without remembering flutter build flags, output paths, renaming steps or packaging commands.
| Platform |
Release output |
Installers / packages |
| Windows |
Portable release directory (x64 or ARM64) |
MSI (WiX Toolset), ZIP |
| macOS |
.app bundle (Universal, ARM64 or x64) |
DMG, PKG, ZIP; optional signing & notarization |
| Linux |
Release bundle (x64 or ARM64) |
AppImage, DEB, RPM, ZIP |
| Android |
APK (optionally split per ABI), App Bundle (AAB) |
— |
| Web |
Release web build (optional --base-href) |
— |
Screenshots
| Release wizard |
Platforms & architectures |
 |
 |
| Review before building |
Build progress |
 |
 |

Contents
Installation
From the Marketplace
Search for Flutter Release in the Extensions view, or run:
code --install-extension abdelazizaouiche0725.flutter-release
From a .vsix file
code --install-extension flutter-release-1.0.0.vsix
or in VS Code: Extensions view → … menu → Install from VSIX….
Requirements: VS Code 1.85 or newer, a Flutter SDK (detected automatically — see Flutter and Dart SDK detection), and the native toolchains for the platforms you build (see Prerequisites). No separate application or server is needed.
Usage
Open a folder containing a Flutter app. The Flutter Release icon appears in the activity bar.
Commands
| Command |
What it does |
| Flutter Release: Build & Package |
Opens the five-step release wizard. |
| Flutter Release: Build Release |
Quick build of release folders using a multi-select picker (no installers). |
| Flutter Release: Change App Name |
Previews (with diffs), backs up and applies display-name changes; identifiers are optional and separate. |
| Flutter Release: Change App Icon |
Icon wizard: select PNG → validate → preview → choose platforms → update with backup. |
| Flutter Release: Open Output Folder |
Opens the last release folder. |
| Flutter Release: Check Build Environment |
Report of ready / missing / unsupported / optional dependencies with remediation commands. |
| Flutter Release: Restore Backup |
Rolls back a previous name or icon change. |
| Flutter Release: Select Flutter SDK |
Lists every detected Flutter SDK (version and how it was found), lets you browse for another, or return to automatic detection. |
Flutter and Dart SDK detection
No configuration is needed in most setups. The Flutter SDK is looked up in this order, and the first match wins:
flutterRelease.tools.flutterSdkPath (set by Select Flutter SDK)
- The project's FVM SDK (
.fvm/flutter_sdk, or the version pinned in .fvmrc / .fvm/fvm_config.json under ~/fvm/versions)
- The Dart extension's
dart.flutterSdkPath / dart.flutterSdkPaths
FLUTTER_ROOT
flutter on PATH (symlinks such as Homebrew's are resolved)
flutter on your login shell's PATH — VS Code started from the Dock or a desktop launcher may not see entries added in ~/.zshrc / ~/.bashrc
- Standard install folders:
~/development/flutter, ~/flutter, ~/sdk/flutter, ~/fvm/default, puro, Homebrew Caskroom, /opt/flutter, snap, C:\src\flutter, %LOCALAPPDATA%\flutter, scoop, …
The Dart SDK bundled with that Flutter SDK is used. Without a Flutter SDK, a standalone Dart SDK is taken from flutterRelease.tools.dartSdkPath, dart.sdkPath, DART_SDK, PATH or Homebrew. The detected SDK, its version and where it came from are shown in the status bar (click it to switch), in the Release view and in Check Build Environment. If no SDK is found, the wizard opens a dialog offering Select Flutter SDK… or the Flutter install guide.
The Build & Package wizard, the Change App Icon wizard and the Build Progress view open as popups in their own floating window, which closes when the wizard finishes. Set flutterRelease.ui.openInNewWindow to false to open them as editor tabs instead. If VS Code does not finish loading a page inside its popup window (this occasionally happens with webviews in floating windows), Flutter Release reloads it and, as a last resort, moves it back into the main window so the wizard always stays usable.
The wizard
- Project & name — shows the detected project,
pubspec.yaml, Flutter/Dart versions and current app name. Enter the new app name (e.g. My Awesome App); a package-safe name (my_awesome_app) and folder name (MyAwesomeApp) are derived automatically. Preserve existing application configuration (default) uses the name only for output folders and installers. Untick it to apply the name to the native projects — the files that will change are listed live.
- Platforms — Windows, macOS, Linux, Android, Web. Desktop platforms show an architecture selector that only enables architectures this host and Flutter SDK can really produce, with an explanation for the others. Android: APK, AAB, split per ABI. Web: base href.
- Packaging — installer formats per platform. Formats whose tools are missing, or that cannot be produced on this host, are disabled with the reason and an alternative. Windows: product name, publisher, stable upgrade code, VC++ runtime bundling. macOS: Applications shortcut, signing identities, notarization profile and a Gatekeeper explanation. Linux: maintainer and desktop categories. Optional Dart obfuscation.
- Output — main output directory (browse or type), optional separate directories for MSI / DMG+PKG / AppImage+DEB+RPM, platform/architecture sub-folders, and a live preview of the folder structure.
- Review & build — application name, platforms, architectures, formats, locations, required tools, project changes, warnings and blocking errors. Build Release starts the real pipeline.
The Build Progress view shows one entry per target (e.g. Windows x64 — Building, macOS ARM64 — Waiting, Android AAB — Failed) with current operation, elapsed time, live log, output paths with sizes and verification results, error summary and buttons to cancel, retry failed targets, open the folder, copy error details and view the complete log.
| Target |
Host required |
Architectures |
Notes |
| Windows |
Windows |
Host architecture only (x64 on x64, ARM64 on Windows on ARM) |
Flutter cannot cross-compile Windows apps. |
| macOS |
macOS |
Universal, ARM64, x64 |
Flutter release builds are universal; single-architecture apps are produced by thinning every Mach-O file with lipo and re-signing. |
| Linux |
Linux |
Host architecture; the other one only with a configured sysroot (flutterRelease.linux.sysroots) and Flutter --target-platform support |
Cross-compilation needs a target sysroot and cross toolchain. |
| Android |
Windows, macOS or Linux |
arm, arm64, x64 (Flutter defaults) |
Requires Android SDK and JDK. |
| Web |
Any |
— |
|
The extension never silently builds the host architecture when you selected another one: architectures are validated before the build, and every produced executable is inspected afterwards (PE, ELF and Mach-O headers). A mismatch fails the target with an explanation.
Prerequisites
Run Check Build Environment to see exactly what is missing. Flutter Release never installs system dependencies or edits your shell configuration; it shows commands you can copy.
| Platform |
Needed |
| All |
Flutter SDK (Dart is bundled) |
| Windows |
Visual Studio 2022 with Desktop development with C++ |
| macOS |
Xcode and command line tools (xcodebuild, codesign, lipo, ditto); CocoaPods only for plugins without Swift Package Manager support |
| Linux |
clang, cmake, ninja-build, pkg-config, libgtk-3-dev |
| Android |
Android SDK, JDK 17+ |
| Format |
Tool |
Install |
| MSI |
WiX Toolset v4+ (wix) and WixToolset.UI.wixext |
dotnet tool install --global wix then wix extension add -g WixToolset.UI.wixext/<wix version> |
| DMG |
hdiutil |
Built into macOS |
| PKG |
productbuild, pkgutil |
xcode-select --install |
| AppImage |
appimagetool |
Download from https://github.com/AppImage/appimagetool/releases, chmod +x, put on PATH or set flutterRelease.tools.appimagetoolPath |
| DEB |
dpkg-deb |
sudo apt install dpkg (Debian/Ubuntu) or sudo dnf install dpkg |
| RPM |
rpmbuild (and rpm for verification) |
sudo dnf install rpm-build or sudo apt install rpm |
| ZIP |
— |
Built in (macOS uses ditto) |
Every installer is really generated by the native tool and verified afterwards — a process exiting successfully is not considered proof.
- MSI (Windows) — a WiX v4 source is generated from the release directory: per-machine install into Program Files, installation-directory dialog (
WixUI_InstallDir, license page skipped), Start-menu shortcut, uninstall support through Apps & Features, product icon, and MajorUpgrade so a newer version replaces the older one. The upgrade code is generated once and stored in flutter_release.json at the project root — commit it, so every future MSI upgrades existing installations. The Visual C++ runtime DLLs are copied next to the executable (app-local deployment) when available. Verification: Windows Installer database signature plus ProductName, ProductVersion, Manufacturer and UpgradeCode read back through the Windows Installer API. MSI versions must be x.y.z within 255.255.65535.
- DMG (macOS) — compressed UDZO image with the app and an optional
/Applications shortcut. Verified with hdiutil verify and by mounting it read-only and checking the app executable.
- PKG (macOS) —
productbuild product archive installing into /Applications, optionally signed with a Developer ID Installer identity. Verified by expanding the archive and reading its bill of materials; signatures checked with pkgutil --check-signature.
- AppImage (Linux) — AppDir with
AppRun, desktop entry, icons and the complete Flutter bundle, packed by appimagetool. Verified: ELF runtime, embedded squashfs payload, executable bit. GTK 3 must be present on the target system.
- DEB (Linux) — installs to
/opt/<name>, /usr/bin/<name> symlink, desktop entry and hicolor icon; Depends: libgtk-3-0. Verified with dpkg-deb --info/--contents.
- RPM (Linux) — same layout;
Requires: gtk3; macros in metadata are escaped. Verified with rpm -qlp.
- ZIP — portable archive of the release directory (macOS:
ditto, preserving bundle metadata).
Android APK/AAB files are verified by reading the archive (manifest present, libapp.so per ABI); web builds by checking index.html and the compiled Dart output.
Changing the application name
Change App Name (or step 1 of the wizard):
- Reads the project's actual native configuration — it does not assume a template.
- Asks for the display name and the platforms.
- Shows every file that would change, with a diff for each (uncheck files to skip them).
- Creates a backup in
.dart_tool/flutter_release/backups/ (ignored by git in Flutter projects).
- Applies changes only after confirmation, and lists modified files (with Undo).
| Platform |
Display name (default) |
With "also rename executables" |
| Android |
android:label in AndroidManifest.xml, or the referenced @string/… in values/strings.xml |
— |
| iOS |
CFBundleDisplayName in ios/Runner/Info.plist |
— |
| macOS |
CFBundleName/CFBundleDisplayName in macos/Runner/Info.plist |
PRODUCT_NAME in AppInfo.xcconfig (renames the .app) |
| Windows |
Window title in main.cpp, FileDescription/ProductName in Runner.rc |
BINARY_NAME, InternalName, OriginalFilename |
| Linux |
Header bar / window title in my_application.cc |
BINARY_NAME |
| Web |
<title>, apple-mobile-web-app-title, manifest.json name/short_name |
— |
Only targeted values are edited — there is no repository-wide search-and-replace. Values are escaped per format (XML, plist, C/C++ wide strings, RC, HTML, JSON).
Identifiers are preserved (Android applicationId, iOS/macOS bundle identifiers, Linux APPLICATION_ID, signing, provisioning and entitlements). The command's Advanced option can change them separately, explaining the consequences: a new Android applicationId is a different app on Google Play; a new bundle identifier needs new provisioning and loses keychain/app data; the Kotlin namespace is left unchanged. The Dart package name can also be renamed (with package: import rewriting).
Changing the application icon
Change App Icon opens a wizard:
- Select a PNG (1024 × 1024 recommended; at least 512 × 512).
- The source is validated (valid PNG, square — or padded on request — resolution, transparency, artwork touching the edges, edge-to-edge opacity) and shown next to the current icon.
- Select platforms (e.g. just Android + iOS, or all).
- Preview generated icons (with launcher masks) and the full list of files that will be created, replaced or removed.
- Update App Icon → confirmation → backup → write → every file is re-read and verified; on failure everything is rolled back.
| Platform |
Generated |
| Android |
mipmap-*/ic_launcher.png (48–192 px; uses the name from android:icon), round icons if android:roundIcon is set, adaptive icons (mipmap-anydpi-v26, 108 dp foreground layers with a 66 dp safe zone, background color resource) |
| iOS |
Every entry of AppIcon.appiconset/Contents.json at its exact pixel size, flattened onto the background color (the App Store rejects transparency) |
| macOS |
All AppIcon.appiconset sizes (16–1024 px) and macos/packaging/AppIcon.icns |
| Windows |
app_icon.ico (path read from Runner.rc) with 16–256 px images |
| Linux |
linux/packaging/icons/hicolor/<size>/apps/app_icon.png, used by AppImage/DEB/RPM desktop entries |
| Web |
favicon.png, Icon-192/512, maskable icons (80 % safe zone), manifest.json and index.html references |
Images are resampled with an area-averaging filter in premultiplied alpha, so edges stay clean. The extension's own branding is never applied to your app.
Code signing and notarization
macOS. Without configuration, apps are ad-hoc signed: they run on the build machine but Gatekeeper blocks them on other Macs. For distribution outside the App Store you need:
- A Developer ID Application certificate →
flutterRelease.macos.signingIdentity (the wizard's packaging step). Frameworks and the app are signed with the hardened runtime and macos/Runner/Release.entitlements.
- For PKG: a Developer ID Installer certificate →
flutterRelease.macos.installerSigningIdentity.
- Notarization: store credentials once in the keychain —
xcrun notarytool store-credentials "my-profile" --apple-id you@example.com --team-id TEAMID — and set flutterRelease.macos.notaryKeychainProfile to my-profile. DMGs/PKGs are submitted with notarytool --wait and stapled. Flutter Release never sees your Apple ID password.
The build summary and progress view warn whenever an artifact is unsigned or not notarized.
Android. Flutter's template signs release builds with the debug key. The environment check and the build flag this; configure a release keystore as described in the Flutter documentation and keep key.properties out of version control.
Windows. MSI signing is not performed automatically; sign with signtool after building if required.
Output directory structure
Default root: ~/FlutterRelease (configurable; ~ and ${workspaceFolder} are supported). The project's build/ directory is rejected because flutter clean deletes it.
FlutterRelease/
└── MyAwesomeApp/
├── windows/
│ └── x64/
│ ├── release/ # portable app folder
│ └── installer/ # MyAwesomeApp-1.2.3-windows-x64.msi / .zip
├── macos/
│ └── arm64/
│ ├── release/ # My Awesome App.app
│ └── installer/ # .dmg / .pkg / .zip
├── linux/
│ └── x64/
│ ├── release/
│ └── installer/ # .AppImage / .deb / .rpm / .zip
├── android/
│ ├── app-release.apk
│ └── app-release.aab
├── web/
│ └── build/
├── debug-symbols/ # when obfuscation is enabled
└── logs/ # one redacted log per target
Disable Organize output for a flat layout, or choose separate installer directories per platform. Existing releases are never overwritten silently: you are asked to overwrite the targets being built or to use a versioned directory (MyAwesomeApp-1.2.3+4, then -2, -3, …). With flutterRelease.confirmBeforeOverwrite disabled, a versioned directory is always used.
Settings
| Setting |
Default |
Description |
flutterRelease.outputDirectory |
~/FlutterRelease |
Default output root |
flutterRelease.organizeOutput |
true |
Platform/architecture sub-folders |
flutterRelease.confirmBeforeOverwrite |
true |
Ask before replacing a release (otherwise versioned folder) |
flutterRelease.preferredArchitectures |
host / universal |
Preselected architectures |
flutterRelease.android.defaultFormats |
["appbundle"] |
APK and/or AAB |
flutterRelease.android.splitPerAbi |
false |
Split APKs per ABI |
flutterRelease.defaultInstallerFormats |
MSI / DMG / AppImage |
Preselected installer formats |
flutterRelease.web.baseHref |
— |
Default --base-href |
flutterRelease.buildConcurrency |
1 |
Parallel packaging jobs (Flutter builds of one project are always serialized) |
flutterRelease.preserveBuildLogs |
true |
Write redacted per-target logs |
flutterRelease.appNaming.defaultBehavior |
displayNameOnly |
Default renaming behavior |
flutterRelease.tools.* |
— |
Paths to the Flutter SDK, a standalone Dart SDK, wix, appimagetool, dpkg-deb, rpmbuild (empty = auto-detect) |
flutterRelease.ui.openInNewWindow |
true |
Open wizards and the progress view as popup windows |
flutterRelease.linux.sysroots |
{} |
Sysroots for Linux cross-compilation |
flutterRelease.windows.publisher / bundleVcRuntime |
— / true |
MSI metadata and VC++ runtime |
flutterRelease.macos.signingIdentity / installerSigningIdentity / notaryKeychainProfile |
— |
Signing & notarization |
Tool paths and signing identities are machine-overridable, so they can live in user settings rather than the repository.
Security
- Commands are executed with argument arrays (
child_process.spawn, shell: false). User input is never interpolated into shell command strings. The only exception is flutter.bat on Windows, which must run through cmd.exe: every argument is quoted and arguments containing ", %, !, ^ or line breaks are rejected.
- Logs are redacted (password/token/secret flags,
key=value secrets, secret-looking environment variables).
- No secrets are stored by the extension. Notarization uses a keychain profile; Android signing stays in your Gradle configuration.
- In untrusted workspaces, builds require explicit confirmation (they execute Gradle, CMake and Xcode scripts from the project).
- Project files are only modified after a preview and confirmation, with a backup. Restore uses only the files recorded in the backup manifest and refuses paths outside the project.
- Cancellation terminates the whole process tree; cleanup removes only this session's temporary directory and partial files created by the cancelled step — never existing releases or your files.
Troubleshooting
| Problem |
Fix |
| "No Flutter application was found" |
Open the folder containing pubspec.yaml (with flutter: sdk: flutter) and at least one platform folder. |
| Flutter not found |
Run Flutter Release: Select Flutter SDK and browse to the folder that contains bin/flutter. |
| Wrong Flutter version used |
Click the Flutter version in the status bar and pick another SDK, or pin one with FVM. |
| A platform is disabled in the wizard |
Hover the note: wrong host OS, missing platform folder (flutter create --platforms=<p> .), or missing Android SDK. |
| An architecture is disabled |
See architectures; build on a matching machine or configure a Linux sysroot. |
MSI: WixToolset.UI.wixext not found |
wix extension add -g WixToolset.UI.wixext/<your wix version> |
| MSI: version error |
MSI needs x.y.z with major/minor ≤ 255 and patch ≤ 65535. |
| macOS app "is damaged" / blocked on another Mac |
Expected for ad-hoc builds. Configure Developer ID signing and notarization. |
| Notarization rejected |
xcrun notarytool log <submission-id> --keychain-profile <profile>; usually missing hardened runtime entitlements or unsigned nested code. |
| AppImage requires an icon |
Run Change App Icon with the Linux target. |
| AppImage fails because of FUSE |
APPIMAGE_EXTRACT_AND_RUN=1 is set automatically; install libfuse2 if your appimagetool build still requires it. |
| Android: "Signed with the debug key" warning |
Configure a release keystore. |
| Build failed |
Copy error details or View complete log in the progress view; logs are also in <output>/logs/. Then Retry failed targets. |
| Undo a rename or icon change |
Flutter Release: Restore Backup. |
Known limitations
- Desktop builds require the matching OS. Windows targets only the host architecture. Linux cross-compilation needs a sysroot and toolchain you provide.
- Android flavors and iOS/IPA builds are not supported.
- AppImage/DEB/RPM do not bundle GTK 3; it must be installed on target systems. AppImages for another architecture cannot be created on this host.
- MSI packages are not code-signed automatically; the license page is skipped. WiX v4–v6 command line is supported.
- macOS DMG volumes use the default volume icon and window layout.
- Localized Android
values-xx/strings.xml labels are not changed.
- Only one build session runs at a time per VS Code window.
- VS Code does not let extensions size popup windows or make them modal; the window can be resized and moved freely.
- Verified platforms: the full pipeline (macOS universal + ARM64-thinned app, DMG, PKG, ZIP, Android APK/AAB, Web) was exercised on macOS 14 (Apple Silicon) with Flutter 3.47. Windows MSI and Linux AppImage/DEB/RPM packaging are implemented and unit-tested (generated WiX source, control files, spec files, command lines), but must be validated on Windows and Linux hosts with their toolchains before relying on them.
Development
npm install
npm run compile # TypeScript → out/
npm run bundle:dev # esbuild → dist/extension.js
npm run lint # ESLint
npm run format:check # Prettier
npm run test:unit # Mocha unit tests (external processes mocked)
npm run test:integration # VS Code Extension Development Host tests (downloads VS Code; set VSCODE_EXECUTABLE to reuse one)
Set FLUTTER_RELEASE_E2E=0 to skip the real flutter build web in the integration suite.
Debugging: open this folder in VS Code and run the Run Extension (sample project) launch configuration (F5). It bundles the extension and opens test-fixtures/sample_app in an Extension Development Host.
Packaging a .vsix:
npm run package
This runs the production esbuild bundle (vscode:prepublish) and produces flutter-release-1.0.0.vsix (≈160 KB) with @vscode/vsce. Publish a new version with npm run publish after npx @vscode/vsce login abdelazizaouiche0725.
Architecture
src/
extension.ts activation, command registration
config.ts typed VS Code settings
releaseContext.ts shared state (output channel, session, caches, trust)
commands/ buildRelease, changeAppName, project commands
ui/ ReleaseWizardPanel, IconWizardPanel, ProgressPanel, EnvironmentPanel, tree view
services/ projectDetector, flutterService, architectureService, environmentChecker,
appNameService, appIconService, backupService, outputService,
installerService, configValidator, buildOrchestrator, toolLocator
packaging/ windowsMsi, macosApp, macosDmg, macosPkg, linuxAppImage, linuxDeb, linuxRpm
models/ buildConfiguration, buildResult, projectInfo
utils/ processRunner, pathValidation, logging, platformSupport, image, binaryInspect, fsUtils
test/ unit/ (Mocha), integration/ (@vscode/test-electron)
media/ webview scripts/styles, extension icons
test-fixtures/sample_app Flutter template project used by tests
Services, packaging and utilities do not depend on the VS Code API and are tested directly with mocked processes.
| |