When developing an app you generally go through many cycles of testing your app on a physical device. That’s very easy with flutter. You just say one of these:
flutter build apk --release
flutter build apk --debug
flutter build apk --profile
possibly preceded by:
flutter clean
to clear any cached build artifacts. Then you simply run:
flutter install
There’s usually no need to script this. But sometimes, you just want to make life a bit easier. That’s why I use the following PowerShell script in all my Flutter apps.
It lets you choose between release, debug, or profile build types, and whether to do a clean install or an upgrade. A clean install will first uninstall your app (erasing all data) before installing the new APK — which is sometimes exactly what you want. But often, I prefer to keep my app data intact and just update the app. That’s easily done using adb
(Android Debug Bridge) with the -r
flag.
Testing both flows is important, especially if you’ve made changes to your data structures.
At the end of the script, you can choose which connected device (including emulators) you want to install the app on.

Here it is — just save it as build-and-install-apk.ps1
in the root of your project. This script is tailored for Android development on Windows using PowerShell, but can easily be adapted to Bash.
# build-and-install-apk.ps1
# This script performs the following:
# 1. Asks user what build type to create (release, debug, or profile)
# 2. Asks if user wants to install or upgrade the app
# 3. Cleans the Flutter project using `flutter clean`
# 4. Builds APK for the selected build type
# 5. Installs or upgrades the app on a connected device
# Start the script
Write-Host "Starting the Flutter build and install process..." -ForegroundColor Cyan
# Ask user for build type
Write-Host "Select build type to create:" -ForegroundColor Yellow
Write-Host "1. Release (optimized, smaller size, no debugging info)" -ForegroundColor White
Write-Host "2. Debug (includes debugging symbols, larger size)" -ForegroundColor White
Write-Host "3. Profile (performance profiling build)" -ForegroundColor White
$buildChoice = Read-Host "Enter your choice (1-3)"
# Set build type and file path based on user selection
switch ($buildChoice) {
"1" {
$buildType = "release"
$buildCommand = "flutter build apk"
Write-Host "You selected: Release build" -ForegroundColor Green
}
"2" {
$buildType = "debug"
$buildCommand = "flutter build apk --debug"
Write-Host "You selected: Debug build" -ForegroundColor Green
}
"3" {
$buildType = "profile"
$buildCommand = "flutter build apk --profile"
Write-Host "You selected: Profile build" -ForegroundColor Green
}
default {
Write-Host "Invalid selection. Defaulting to release build." -ForegroundColor Yellow
$buildType = "release"
$buildCommand = "flutter build apk"
}
}
# Ask user if they want to install or upgrade
Write-Host "Select installation method:" -ForegroundColor Yellow
Write-Host "1. Install (uninstalls first, clears all app data)" -ForegroundColor White
Write-Host "2. Upgrade (preserves app data)" -ForegroundColor White
$installChoice = Read-Host "Enter your choice (1-2)"
# Set installation method based on user selection
switch ($installChoice) {
"1" {
$installMethod = "install"
Write-Host "You selected: Install (fresh installation)" -ForegroundColor Green
}
"2" {
$installMethod = "upgrade"
Write-Host "You selected: Upgrade (preserve data)" -ForegroundColor Green
}
default {
Write-Host "Invalid selection. Defaulting to upgrade to preserve data." -ForegroundColor Yellow
$installMethod = "upgrade"
}
}
$apkFile = Join-Path (Get-Location) "build\app\outputs\flutter-apk\app-$buildType.apk"
# Step 1: Run `flutter clean`
Write-Host "Step 1/3: Running 'flutter clean' to clean the project..." -ForegroundColor Yellow
try {
flutter clean
Write-Host "✓ 'flutter clean' completed successfully." -ForegroundColor Green
} catch {
Write-Host "✗ Error: Failed to run 'flutter clean'. Ensure Flutter is installed and in your PATH." -ForegroundColor Red
exit 1
}
# Step 2: Build the APK with selected build type
Write-Host "Step 2/3: Running '$buildCommand'..." -ForegroundColor Yellow
try {
Invoke-Expression $buildCommand
Write-Host "✓ '$buildCommand' completed successfully." -ForegroundColor Green
# Get APK info
if (Test-Path $apkFile) {
$fileInfo = Get-Item $apkFile
Write-Host " APK built: $apkFile" -ForegroundColor Cyan
Write-Host " Size: $([math]::Round($fileInfo.Length / 1MB, 2)) MB" -ForegroundColor Cyan
} else {
Write-Host " Note: Expected APK not found at $apkFile" -ForegroundColor Yellow
Write-Host " Check the build/app/outputs/flutter-apk directory for your APK" -ForegroundColor Yellow
}
} catch {
Write-Host "✗ Error: Failed to build APK. Check for build errors." -ForegroundColor Red
exit 1
}
# Step 3: Check for connected devices
Write-Host "Step 3/3: Deploying the app to connected device..." -ForegroundColor Yellow
try {
# Get list of connected devices
$devicesOutput = adb devices
$deviceLines = $devicesOutput -split "`n" | Select-Object -Skip 1 | Where-Object { $_ -match '\S' }
if ($deviceLines.Count -eq 0) {
Write-Host "✗ Error: No devices connected. Please connect a device and try again." -ForegroundColor Red
exit 1
}
# Parse device IDs
$deviceIds = @()
foreach ($line in $deviceLines) {
if ($line -match '(\S+)\s+device') {
$deviceIds += $matches[1]
}
}
# Handle case with multiple devices
$selectedDeviceId = $null
if ($deviceIds.Count -gt 1) {
Write-Host "Multiple devices detected:" -ForegroundColor Yellow
for ($i = 0; $i -lt $deviceIds.Count; $i++) {
$deviceInfo = adb -s $deviceIds[$i] shell getprop ro.product.model
Write-Host " $($i+1). $($deviceIds[$i]) - $deviceInfo" -ForegroundColor White
}
$deviceChoice = Read-Host "Select a device (1-$($deviceIds.Count))"
$deviceIndex = [int]$deviceChoice - 1
if ($deviceIndex -ge 0 -and $deviceIndex -lt $deviceIds.Count) {
$selectedDeviceId = $deviceIds[$deviceIndex]
Write-Host "Selected device: $selectedDeviceId" -ForegroundColor Green
} else {
Write-Host "Invalid device selection. Using first device: $($deviceIds[0])" -ForegroundColor Yellow
$selectedDeviceId = $deviceIds[0]
}
} else {
$selectedDeviceId = $deviceIds[0]
$deviceInfo = adb -s $selectedDeviceId shell getprop ro.product.model
Write-Host "Using connected device: $selectedDeviceId - $deviceInfo" -ForegroundColor Green
}
# Find the APK path for the current build type
$fullApkPath = Resolve-Path $apkFile -ErrorAction SilentlyContinue
if ($null -eq $fullApkPath) {
Write-Host " Warning: Could not find the APK file. Looking for alternatives..." -ForegroundColor Yellow
$possibleApks = Get-ChildItem "build\app\outputs\flutter-apk\*.apk" -ErrorAction SilentlyContinue
if ($possibleApks.Count -gt 0) {
$fullApkPath = $possibleApks[0].FullName
Write-Host " Found alternative APK: $fullApkPath" -ForegroundColor Cyan
} else {
Write-Host "✗ Error: No APK files found. Build may have failed." -ForegroundColor Red
exit 1
}
} else {
$fullApkPath = $fullApkPath.Path
}
# Install or upgrade the app based on user choice
if ($installMethod -eq "install") {
# Use flutter install with the appropriate build type and device
$installCommand = "flutter -d $selectedDeviceId install"
if ($buildType -eq "debug") {
$installCommand += " --debug"
} elseif ($buildType -eq "profile") {
$installCommand += " --profile"
}
Write-Host " Installing app on device $selectedDeviceId..." -ForegroundColor Cyan
Invoke-Expression $installCommand
Write-Host "✓ App ($buildType build) installed successfully on device." -ForegroundColor Green
} else {
# Use ADB to install with the -r flag (upgrade) targeting specific device
Write-Host " Upgrading app using adb (preserving data)..." -ForegroundColor Cyan
$upgradeCommand = "adb -s $selectedDeviceId install -r `"$fullApkPath`""
try {
$upgradeResult = Invoke-Expression $upgradeCommand
if ($upgradeResult -match "Success") {
Write-Host "✓ App ($buildType build) upgraded successfully on device $selectedDeviceId." -ForegroundColor Green
} else {
Write-Host "✗ Warning: There might be an issue with the upgrade: $upgradeResult" -ForegroundColor Yellow
Write-Host " Attempting fallback to flutter install..." -ForegroundColor Yellow
# Fallback to flutter install with specific device
$installCommand = "flutter -d $selectedDeviceId install"
if ($buildType -eq "debug") {
$installCommand += " --debug"
} elseif ($buildType -eq "profile") {
$installCommand += " --profile"
}
Invoke-Expression $installCommand
Write-Host " Note: Fallback installation successful, but app data may have been lost." -ForegroundColor Yellow
}
} catch {
Write-Host "✗ Error during upgrade. Falling back to flutter install." -ForegroundColor Yellow
Write-Host " Note: This will uninstall the existing app and all data will be lost." -ForegroundColor Yellow
# Fallback to flutter install with specific device
$installCommand = "flutter -d $selectedDeviceId install"
if ($buildType -eq "debug") {
$installCommand += " --debug"
} elseif ($buildType -eq "profile") {
$installCommand += " --profile"
}
Invoke-Expression $installCommand
Write-Host "✓ App installed, but data could not be preserved." -ForegroundColor Yellow
}
}
} catch {
Write-Host "✗ Error: Failed to deploy the app. Details: $_" -ForegroundColor Red
exit 1
}
# End script
Write-Host "✓ Flutter build and deploy process completed successfully!" -ForegroundColor Cyan
if ($installMethod -eq "upgrade") {
Write-Host "The $buildType build of your app has been upgraded on your device, preserving app data." -ForegroundColor Green
} else {
Write-Host "The $buildType build of your app has been freshly installed on your device." -ForegroundColor Green
}
Exit
PowerShell
Leave a Reply