Powershell script to build and install an Android Flutter app


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

Your email address will not be published. Required fields are marked *