feat(docker): support native multi-arch (amd64/arm64) builds (#5096)

- Update Dockerfile.base to exclude prepackaged cache on arm64/aarch64
and trigger native SDK bootstrapping.
- Exclude .pub-preload-cache by default on all architectures to optimize
size.
- Update Dockerfile to download yq dynamically based on target
architecture.
- Refactor cloudbuild.yaml to use docker buildx with QEMU emulation and
direct manifest-list pushing.
- Remove hardcoded amd64 platform constraints from docker-compose.yml.
- Update README.md to document arm64 capabilities and simplify local
commands.

fixes: flutter/flutter#188873
diff --git a/cloud_build/flutter_agy_docker/Dockerfile b/cloud_build/flutter_agy_docker/Dockerfile
index 140f144..09d62e1 100644
--- a/cloud_build/flutter_agy_docker/Dockerfile
+++ b/cloud_build/flutter_agy_docker/Dockerfile
@@ -31,7 +31,8 @@
     echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | tee /etc/apt/sources.list.d/github-cli.list > /dev/null; \
     apt-get update && apt-get install -y gh; \
     # install yq, a YAML/XML/TOML processor like jq
-    wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64; \
+    ARCH=$(dpkg --print-architecture); \
+    wget -qO /usr/local/bin/yq "https://github.com/mikefarah/yq/releases/latest/download/yq_linux_${ARCH}"; \
     chmod a+x /usr/local/bin/yq; \
     locale-gen en_US.UTF-8; \
     ln -s $(which fdfind) /usr/local/bin/fd; \
diff --git a/cloud_build/flutter_agy_docker/Dockerfile.base b/cloud_build/flutter_agy_docker/Dockerfile.base
index 74a895a..a67085c 100644
--- a/cloud_build/flutter_agy_docker/Dockerfile.base
+++ b/cloud_build/flutter_agy_docker/Dockerfile.base
@@ -9,6 +9,7 @@
 
 # 1. Define the ARG with a fallback default version
 ARG FLUTTER_VERSION="3.44.3"
+ARG TARGETARCH
 
 ENV TZ="America/Los_Angeles" \
     DEBIAN_FRONTEND=noninteractive \
@@ -39,7 +40,12 @@
 RUN set -ex; \
     export PATH="/opt/flutter/bin:${PATH}"; \
     # 1. Download and uncompress the flutter tool
-    curl -fsSL https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${FLUTTER_VERSION}-stable.tar.xz | tar -xJ --no-same-owner -C /opt; \
+    ARCH="${TARGETARCH:-$(dpkg --print-architecture)}"; \
+    EXCLUDE_CACHE=""; \
+    if [ "$ARCH" = "arm64" ] || [ "$ARCH" = "aarch64" ]; then \
+      EXCLUDE_CACHE="--exclude=flutter/bin/cache"; \
+    fi; \
+    curl -fsSL "https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${FLUTTER_VERSION}-stable.tar.xz" | tar -xJ --exclude=.pub-preload-cache $EXCLUDE_CACHE --no-same-owner -C /opt; \
     # 2. Run config to ONLY ENABLE web (and disable everything else)
     flutter config \
         --enable-web \
diff --git a/cloud_build/flutter_agy_docker/README.md b/cloud_build/flutter_agy_docker/README.md
index 35ceaa1..47b9940 100644
--- a/cloud_build/flutter_agy_docker/README.md
+++ b/cloud_build/flutter_agy_docker/README.md
@@ -6,41 +6,25 @@
 
 ## 1. (Optional) Building the Docker Image Locally
 
-> **Important Platform Note:** Flutter does not fully support `linux/arm64` yet. If you are on an Apple Silicon (M1/M2/M3) machine, you *must* set the platform to `linux/amd64` when building and running.
+> **Platform Note:** This environment natively supports both `linux/amd64` (x86_64) and `linux/arm64` (Apple Silicon/ARM) architectures (for Dart/Flutter Web development only).
 
-### For x86 / Intel:
+### Building the Docker image:
 ```shell
 podman build -t flutter_docker .
 ```
 
-### For Apple Silicon (using podman):
-```shell
-podman build --platform linux/amd64 -t flutter_docker .
-```
-
 ### Building a specific Flutter version:
 You can override the default Flutter version using the `FLUTTER_VERSION` build argument.
 ```shell
-podman build --platform linux/amd64 --build-arg FLUTTER_VERSION=3.45.0 -t flutter_docker:3.45.0 .
+podman build --build-arg FLUTTER_VERSION=3.45.0 -t flutter_docker:3.45.0 .
 ```
 
 ## 2. Running the Docker Image Locally
 
 You can run the container in any project folder. The current working directory (`$PWD`) will be mapped to `/app` inside the container.
 
-### For x86 / Intel:
 ```shell
 podman run --userns=keep-id:uid=1000,gid=1000 -it \
-  -v ".:/app:z" \
-  -v "dart_tool_cache:/app/.dart_tool" \
-  -v "dart_build_cache:/app/build" \
-  -v "$USER/.gemini:/home/coder/.gemini:z" \
-  flutter_docker:latest
-```
-
-### For Apple Silicon (using podman):
-```shell
-podman run --platform linux/amd64 --userns=keep-id:uid=1000,gid=1000 -it \
   -v "${PWD}:/app:z" \
   -v "dart_tool_cache:/app/.dart_tool" \
   -v "dart_build_cache:/app/build" \
diff --git a/cloud_build/flutter_agy_docker/cloudbuild.yaml b/cloud_build/flutter_agy_docker/cloudbuild.yaml
index ba79450..6785012 100644
--- a/cloud_build/flutter_agy_docker/cloudbuild.yaml
+++ b/cloud_build/flutter_agy_docker/cloudbuild.yaml
@@ -4,52 +4,7 @@
 
 steps:
   # ==========================================
-  # STEP 1: Build the Static Base Image
-  # ==========================================
-  - name: 'gcr.io/cloud-builders/docker'
-    entrypoint: 'bash'
-    args: ['-c', 'docker pull us-docker.pkg.dev/flutter-infra/flutter-infra/flutter_base:latest || exit 0']
-
-  - name: 'gcr.io/cloud-builders/docker'
-    dir: 'cloud_build/flutter_agy_docker/'
-    args:
-      - 'build'
-      - '--cache-from'
-      - 'us-docker.pkg.dev/flutter-infra/flutter-infra/flutter_base:latest'
-      - '--build-arg'
-      - 'FLUTTER_VERSION=$_FLUTTER_VERSION'
-      - '-t'
-      - 'us-docker.pkg.dev/flutter-infra/flutter-infra/flutter_base:$_FLUTTER_VERSION'
-      - '-f'
-      - 'Dockerfile.base'
-      - '.'
-
-  # ==========================================
-  # STEP 2: Build the Auto-Updating Dev Image
-  # ==========================================
-  - name: 'gcr.io/cloud-builders/docker'
-    entrypoint: 'bash'
-    args: ['-c', 'docker pull us-docker.pkg.dev/flutter-infra/flutter-infra/flutter_docker:latest || exit 0']
-
-  - name: 'gcr.io/cloud-builders/docker'
-    dir: 'cloud_build/flutter_agy_docker/'
-    args:
-      - 'build'
-      - '--cache-from'
-      - 'us-docker.pkg.dev/flutter-infra/flutter-infra/flutter_docker:latest'
-      - '--build-arg'
-      - 'FLUTTER_VERSION=$_FLUTTER_VERSION'
-      # Force apt-get to run by injecting the unique build ID
-      - '--build-arg'
-      - 'CACHE_BUSTER=$BUILD_ID'
-      - '-t'
-      - 'us-docker.pkg.dev/flutter-infra/flutter-infra/flutter_docker:$_FLUTTER_VERSION'
-      - '-f'
-      - 'Dockerfile'
-      - '.'
-
-  # ==========================================
-  # STEP 3: Conditionally Tag and Push 'latest', 'stable', or 'beta'
+  # STEP 1: Determine tags based on version
   # ==========================================
   - name: 'gcr.io/cloud-builders/docker'
     entrypoint: 'bash'
@@ -58,8 +13,7 @@
       - |
         set -euo pipefail
 
-        # Install curl and jq (handles both Debian and Alpine builder variants)
-        # We temporarily disable exit-on-error to allow the fallback to work cleanly
+        # Install curl and jq
         set +e
         apt-get update && apt-get install -y curl jq 2>/dev/null || apk add curl jq 2>/dev/null
         set -e
@@ -79,16 +33,13 @@
         BETA_VERSION=$$(echo "$$JSON_DATA" | \
           jq -r '.current_release.beta as $$beta_hash | .releases[] | select(.hash == $$beta_hash) | .version' | head -n 1)
 
-        # Validate that the versions were successfully parsed.
-        # -z "$$STABLE_VERSION" checks if the variable is an empty string (e.g., if jq failed entirely).
-        # "$$STABLE_VERSION" == "null" checks if jq succeeded but couldn't find the requested data in the JSON.
         if [ -z "$$STABLE_VERSION" ] || [ "$$STABLE_VERSION" == "null" ]; then
-          echo "Error: STABLE_VERSION could not be parsed from JSON. The JSON structure may have changed."
+          echo "Error: STABLE_VERSION could not be parsed."
           exit 1
         fi
 
         if [ -z "$$BETA_VERSION" ] || [ "$$BETA_VERSION" == "null" ]; then
-          echo "Error: BETA_VERSION could not be parsed from JSON. The JSON structure may have changed."
+          echo "Error: BETA_VERSION could not be parsed."
           exit 1
         fi
 
@@ -96,45 +47,103 @@
         echo "Current beta version is: $$BETA_VERSION"
         echo "Building for version: $_FLUTTER_VERSION"
 
+        BASE_TAGS="us-docker.pkg.dev/flutter-infra/flutter-infra/flutter_base:$_FLUTTER_VERSION"
+        DEV_TAGS="us-docker.pkg.dev/flutter-infra/flutter-infra/flutter_docker:$_FLUTTER_VERSION"
+
         if [ "$_FLUTTER_VERSION" == "$$STABLE_VERSION" ]; then
-          echo "Version $_FLUTTER_VERSION is stable. Tagging and pushing as latest and stable."
-
-          # Tag the previously built versioned images
-          docker tag us-docker.pkg.dev/flutter-infra/flutter-infra/flutter_base:$_FLUTTER_VERSION us-docker.pkg.dev/flutter-infra/flutter-infra/flutter_base:stable
-          docker tag us-docker.pkg.dev/flutter-infra/flutter-infra/flutter_base:$_FLUTTER_VERSION us-docker.pkg.dev/flutter-infra/flutter-infra/flutter_base:latest
-          docker tag us-docker.pkg.dev/flutter-infra/flutter-infra/flutter_docker:$_FLUTTER_VERSION us-docker.pkg.dev/flutter-infra/flutter-infra/flutter_docker:stable
-          docker tag us-docker.pkg.dev/flutter-infra/flutter-infra/flutter_docker:$_FLUTTER_VERSION us-docker.pkg.dev/flutter-infra/flutter-infra/flutter_docker:latest
-
-          # Push the new tags
-          docker push us-docker.pkg.dev/flutter-infra/flutter-infra/flutter_base:stable
-          docker push us-docker.pkg.dev/flutter-infra/flutter-infra/flutter_base:latest
-          docker push us-docker.pkg.dev/flutter-infra/flutter-infra/flutter_docker:stable
-          docker push us-docker.pkg.dev/flutter-infra/flutter-infra/flutter_docker:latest
-
+          echo "Version $_FLUTTER_VERSION is stable. Tagging as stable and latest."
+          BASE_TAGS="$$BASE_TAGS,us-docker.pkg.dev/flutter-infra/flutter-infra/flutter_base:stable,us-docker.pkg.dev/flutter-infra/flutter-infra/flutter_base:latest"
+          DEV_TAGS="$$DEV_TAGS,us-docker.pkg.dev/flutter-infra/flutter-infra/flutter_docker:stable,us-docker.pkg.dev/flutter-infra/flutter-infra/flutter_docker:latest"
         elif [ "$_FLUTTER_VERSION" == "$$BETA_VERSION" ]; then
-          echo "Version $_FLUTTER_VERSION is beta. Tagging and pushing as beta."
-
-          # Tag the previously built versioned images
-          docker tag us-docker.pkg.dev/flutter-infra/flutter-infra/flutter_base:$_FLUTTER_VERSION us-docker.pkg.dev/flutter-infra/flutter-infra/flutter_base:beta
-          docker tag us-docker.pkg.dev/flutter-infra/flutter-infra/flutter_docker:$_FLUTTER_VERSION us-docker.pkg.dev/flutter-infra/flutter-infra/flutter_docker:beta
-
-          # Push the new tags
-          docker push us-docker.pkg.dev/flutter-infra/flutter-infra/flutter_base:beta
-          docker push us-docker.pkg.dev/flutter-infra/flutter-infra/flutter_docker:beta
-
+          echo "Version $_FLUTTER_VERSION is beta. Tagging as beta."
+          BASE_TAGS="$$BASE_TAGS,us-docker.pkg.dev/flutter-infra/flutter-infra/flutter_base:beta"
+          DEV_TAGS="$$DEV_TAGS,us-docker.pkg.dev/flutter-infra/flutter-infra/flutter_docker:beta"
         else
-          echo "Version $_FLUTTER_VERSION is neither the stable nor beta release. Skipping extra tags."
+          echo "Version $_FLUTTER_VERSION is neither stable nor beta. Using version tag only."
         fi
 
+        echo "BASE_TAGS=$$BASE_TAGS" > /workspace/tags.env
+        echo "DEV_TAGS=$$DEV_TAGS" >> /workspace/tags.env
+
+  # ==========================================
+  # STEP 2: Build the Multi-Arch Base Image
+  # ==========================================
+  - name: 'gcr.io/cloud-builders/docker'
+    dir: 'cloud_build/flutter_agy_docker/'
+    entrypoint: 'bash'
+    args:
+      - '-c'
+      - |
+        set -euo pipefail
+
+        # Register QEMU emulators for multi-arch build
+        docker run --privileged --rm tonistiigi/binfmt --install all
+
+        # Create a new buildx builder
+        docker buildx create --use
+
+        # Read tags
+        source /workspace/tags.env
+
+        # Convert comma-separated tags to -t arguments
+        IFS=',' read -ra ADDR <<< "$$BASE_TAGS"
+        TAG_ARGS=()
+        for tag in "$${ADDR[@]}"; do
+          TAG_ARGS+=("-t" "$$tag")
+        done
+
+        # Build and push the base image
+        docker buildx build \
+          --platform linux/amd64,linux/arm64 \
+          --cache-from us-docker.pkg.dev/flutter-infra/flutter-infra/flutter_base:latest \
+          --cache-to type=inline \
+          --build-arg FLUTTER_VERSION=$_FLUTTER_VERSION \
+          "$${TAG_ARGS[@]}" \
+          -f Dockerfile.base \
+          --push \
+          .
+
+  # ==========================================
+  # STEP 3: Build the Multi-Arch Dev Image
+  # ==========================================
+  - name: 'gcr.io/cloud-builders/docker'
+    dir: 'cloud_build/flutter_agy_docker/'
+    entrypoint: 'bash'
+    args:
+      - '-c'
+      - |
+        set -euo pipefail
+
+        # Register QEMU emulators
+        docker run --privileged --rm tonistiigi/binfmt --install all
+
+        # Create a new buildx builder
+        docker buildx create --use
+
+        # Read tags
+        source /workspace/tags.env
+
+        # Convert comma-separated tags to -t arguments
+        IFS=',' read -ra ADDR <<< "$$DEV_TAGS"
+        TAG_ARGS=()
+        for tag in "$${ADDR[@]}"; do
+          TAG_ARGS+=("-t" "$$tag")
+        done
+
+        # Build and push the developer image
+        docker buildx build \
+          --platform linux/amd64,linux/arm64 \
+          --cache-from us-docker.pkg.dev/flutter-infra/flutter-infra/flutter_docker:latest \
+          --cache-to type=inline \
+          --build-arg FLUTTER_VERSION=$_FLUTTER_VERSION \
+          --build-arg CACHE_BUSTER=$BUILD_ID \
+          "$${TAG_ARGS[@]}" \
+          -f Dockerfile \
+          --push \
+          .
+
 substitutions:
   _FLUTTER_VERSION: "3.44.3"
 
-# Push both the base and the final developer image to the registry
-images:
-  - 'us-docker.pkg.dev/flutter-infra/flutter-infra/flutter_base:$_FLUTTER_VERSION'
-  - 'us-docker.pkg.dev/flutter-infra/flutter-infra/flutter_docker:$_FLUTTER_VERSION'
-
-# Force Cloud Build to send logs directly to Cloud Logging
-# instead of an autogenerated GCS bucket
 options:
   logging: CLOUD_LOGGING_ONLY
diff --git a/cloud_build/flutter_agy_docker/docker-compose.yml b/cloud_build/flutter_agy_docker/docker-compose.yml
index 7db5d4e..a37e1f1 100644
--- a/cloud_build/flutter_agy_docker/docker-compose.yml
+++ b/cloud_build/flutter_agy_docker/docker-compose.yml
@@ -9,7 +9,6 @@
 services:
   dev:
     image: us-docker.pkg.dev/flutter-infra/flutter-infra/flutter_docker:stable
-    platform: linux/amd64
     # Dynamically reads the environment variable
     container_name: ${ENV_NAME:-coder-env}
     stdin_open: true