Ship Open-Testing Build to Production on Google Play (Flutter)

Posted by

Limited Time Offer!

For Less Than the Cost of a Starbucks Coffee, Access All DevOpsSchool Videos on YouTube Unlimitedly.
Master DevOps, SRE, DevSecOps Skills!

Enroll Now

What youโ€™re doing (in one line)

You already uploaded 1.8.0 (versionCode 39) to Open testing. You want to publish the same build (or a minor update) to Production. You canโ€™t โ€œmoveโ€ it; you reuse the same .aab (or upload a new one with a higher versionCode) to create a Production release.


Pre-flight Checklist (do these first)

Project files

  • android/app/src/main/AndroidManifest.xml is valid (see AD_ID note below).
  • android/app/build.gradle has correct applicationId, minSdkVersion, targetSdkVersion, and unique versionCode.

Play Console

  • App content โ†’ Data safety, Ads/Monetization, Privacy policy, Content rating are filled.
  • Store listing (title, short+full description, screenshots, icon, feature image) done.
  • App integrity: Play App Signing is set up (recommended).
  • Policy: no unresolved policy issues.

Tip: Take 2 minutes to open Policy โ†’ App content and ensure every section has a green check.


Fixing the common โ€œAdvertising ID (AD_ID)โ€ error

What is Advertising ID?

A resettable device identifier that ad/analytics SDKs use for personalization, attribution, and fraud prevention.

Why Play Console complains

  • You declared โ€œApp uses Advertising IDโ€ in App content, but your app manifest lacks the permission.
  • On Android 13+ (API 33), without the permission, the ID gets zeroed out.

Choose ONE path

A) Your app (or SDKs like AdMob, AppsFlyer, Adjust, some analytics) use Ad ID โ†’ Add permission

Copy-paste this outside <application> in AndroidManifest.xml:

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Advertising ID permission (Android 13+) -->
    <uses-permission android:name="com.google.android.gms.permission.AD_ID" />

    <application
        android:label="professional"
        android:name="${applicationName}"
        android:enableOnBackInvokedCallback="true"
        android:icon="@mipmap/ic_launcher">
        <!-- ... your existing activity/meta-data ... -->
    </application>

    <queries>
        <intent>
            <action android:name="android.intent.action.PROCESS_TEXT"/>
            <data android:mimeType="text/plain"/>
        </intent>
    </queries>
</manifest>

Then rebuild and upload (you must bump versionCode, see Section 4).

B) Your app does NOT use Ad ID โ†’ Update declaration

  • Play Console โ†’ Policy โ†’ App content โ†’ Advertising ID
  • Select โ€œNo, my app does not use the Advertising ID.โ€
  • Save. No manifest change needed.

โš ๏ธ If you claim โ€œNoโ€ but an SDK reads Ad ID, review may fail. Be honest.


Other frequent Play Console blockers (and fixes)

  1. Duplicate versionCode / โ€œYou need to use a different version codeโ€
    • Fix: Increment versionCode in android/app/build.gradle:
    defaultConfig { applicationId "com.professnow.professional" minSdkVersion flutter.minSdkVersion targetSdkVersion flutter.targetSdkVersion versionCode 40 versionName "1.8.1" }
  2. Target API requirement (e.g., must target Android 14 / API 34)
    • Fix: In android/app/build.gradle or android/gradle.properties (Flutter 3.22+), set targetSdkVersion 34.
    • If you use a recent Flutter, flutter.targetSdkVersion usually aligns; otherwise set explicitly.
  3. Data safety form incomplete or mismatched
    • Fix: Fill exactly what SDKs collect/share. If you use Firebase/AdMob/Analytics, include them.
  4. Privacy policy missing (especially if you collect personal data or show ads)
    • Fix: Add a public URL (website/GitHub Pages) that clearly explains data usage.
  5. App signing mismatch
    • Fix: Enable Play App Signing and keep your upload key safe. Donโ€™t change keystores mid-life.
  6. โ€œRelease without permissionโ€ warning for AD_ID
    • Fix: Either add AD_ID permission (if used) or declare โ€œNoโ€ in App content (if not used). Donโ€™t ignore.
  7. Policy rejections (Device/Network abuse, Deceptive behavior, etc.)
    • Fix: Read the rejection email, fix the exact APK behavior, and resubmit.

Build & Upload (Flutter)

# from your Flutter project root
flutter clean
flutter pub get

# (optional) verify Gradle/SDK versions if prompted
# Update versionCode/versionName in android/app/build.gradle FIRST

# Build Play App Bundle (.aab)
flutter build appbundle
# output: build/app/outputs/bundle/release/app-release.aab

Upload the .aab in Play Console during Production โ†’ Create new release.


Reusing the Open-Testing AAB for Production

You can reuse the same artifact:

  • Production โ†’ Create new release โ†’ Reuse app bundle from a previous release โ†’ pick 1.8.0 (39)
  • If you changed code/manifest, you must upload a new AAB with a higher versionCode (e.g., 40).

Play Console: Step-by-step to Production

  1. Open app โ†’ left nav Release โ†’ Production.
  2. Click Create new release.
  3. App bundles:
    • Reuse previous (select 39) or click Upload a new .aab (with versionCode 40+).
  4. Release details:
    • Name (e.g., 1.8.1)
    • Release notes (see template below)
  5. Review release:
    • Fix errors/warnings (AD_ID, Data safety, Target API, etc.).
  6. Start rollout to Production:
    • Choose staged rollout (e.g., 10%) or 100% if confident.
  7. Submit and wait for review.

Copy-paste snippets youโ€™ll likely need

AndroidManifest.xml (with AD_ID)

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-permission android:name="com.google.android.gms.permission.AD_ID" />

    <application
        android:label="professional"
        android:name="${applicationName}"
        android:enableOnBackInvokedCallback="true"
        android:icon="@mipmap/ic_launcher">

        <activity
            android:name="com.professnow.professional.MainActivity"
            android:exported="true"
            android:launchMode="singleTop"
            android:taskAffinity=""
            android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize">
            <meta-data
                android:name="io.flutter.embedding.android.NormalTheme"
                android:resource="@style/NormalTheme" />
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

        <meta-data
            android:name="flutterEmbedding"
            android:value="2" />
    </application>

    <queries>
        <intent>
            <action android:name="android.intent.action.PROCESS_TEXT"/>
            <data android:mimeType="text/plain"/>
        </intent>
    </queries>
</manifest>

build.gradle (app) โ€“ bump version

android {
    defaultConfig {
        applicationId "com.professnow.professional"
        minSdkVersion 21          // match your project needs
        targetSdkVersion 34       // meet Play requirement
        versionCode 40            // increment every upload
        versionName "1.8.1"
    }
    // ...
}

Release notes template (paste in Play Console)

Whatโ€™s new in 1.8.1
โ€ข Stability improvements and minor bug fixes
โ€ข Polished onboarding and performance on low-memory devices
โ€ข Updated dependencies and compliance (Android 13/14)

Troubleshooting table (quick fixes)

Symptom / ErrorCauseFix
โ€œApp uses Advertising ID but permission missingโ€Declared AD_ID usage but no manifest permissionAdd <uses-permission android:name="com.google.android.gms.permission.AD_ID" /> or declare โ€œNoโ€ in App content
โ€œUse a different version codeโ€Same versionCode already usedIncrease versionCode (e.g., 39 โ†’ 40)
โ€œTarget API level too lowโ€targetSdkVersion < requiredSet targetSdkVersion 34
โ€œData safety incompleteโ€Forms not filled accuratelyComplete Data safety with actual SDK usage
โ€œPrivacy policy requiredโ€Collects data / shows adsProvide a hosted privacy policy URL
โ€œApp signing errorโ€Keystore mismatchUse Play App Signing; keep upload key consistent
โ€œReview rejected (policy violation)โ€Behavior or metadata issuesRead email reason, fix code/content, resubmit
โ€œApp not visible to everyone after releaseโ€Staged rolloutIncrease rollout % after verifying metrics

Best practices before you click โ€œSubmitโ€

  • Test release build locally: flutter build appbundle then install an APK split or test via Internal testing.
  • Verify deep links, Google Sign-In, in-app updates/billing if used.
  • Keep changelogs short & clear; avoid mentioning internal IDs or sensitive info.
  • Consider staged rollout 10โ€“20% first. Watch ANRs/crashes in Android vitals.

FAQ (fast answers)

Q: Can I move Open testing release to Production directly?
A: No โ€œmoveโ€ button. Create a new Production release and reuse the same AAB or upload a new one.

Q: Do I need to rebuild if I just add the AD_ID permission?
A: Yesโ€”manifest changes require building a new AAB with a higher versionCode.

Q: I donโ€™t show adsโ€”do I still need AD_ID?
A: No. If no SDK reads Ad ID, set App content โ†’ Advertising ID = No and donโ€™t add the permission.

Q: How long does review take?
A: Varies (hours to a few days). First releases or policy-sensitive apps may take longer.


One-page โ€œDo it nowโ€ checklist

  • Decide AD_ID path (add permission or declare โ€œNoโ€).
  • Update AndroidManifest.xml (if needed).
  • Bump versionCode (and versionName).
  • flutter clean && flutter pub get && flutter build appbundle.
  • Play Console โ†’ Production โ†’ Create new release โ†’ Upload or Reuse AAB.
  • Fill release notes โ†’ Review โ†’ Fix errors/warnings.
  • Start staged rollout (10โ€“20%) โ†’ Monitor โ†’ Rollout to 100%.

Leave a Reply

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

0
Would love your thoughts, please comment.x
()
x