Upgrading my Flutter app to Android compileSdkVersion 33

In my project, two dependencies require compileSdkVersion 33. Let's fix my project configuration:

Warning: The plugin sqflite requires Android SDK version 33.
For more information about build configuration, see https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
One or more plugins require a higher Android SDK version.
Fix this issue by adding the following to /Users/anhtuan/git/trading_journal/android/app/build.gradle:
android {
  compileSdkVersion 33
  ...
}

I open the build.grade and change the compileSdkVersion from 31 to 33, then build the project again.

BUILD FAILED in 5s
┌─ Flutter Fix ──────────────────────────────────────────────────────┐
│ [!] Your project requires a newer version of the Kotlin Gradle plugin.                     │
│ Find the latest version on https://kotlinlang.org/docs/releases.html#release-details, then │
│ update /Users/anhtuan/git/trading_journal/android/build.gradle:                            │
│ ext.kotlin_version = '<latest-version>'                                                    │
└────────────────────────────────────────────────────────────────┘
Exception: Gradle task assembleDebug failed with exit code 1

The nice thing is Flutter now tells you how to fix it. So I click the kotlinlang.org link and see that the latest version is 1.8.22. So I fix my build.gradle accordingly:

buildscript {
    ext.kotlin_version = '1.8.22'
    repositories {
        google()
        jcenter()
    }

    dependencies {
        classpath 'com.android.tools.build:gradle:3.5.0'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}
...

However now the build fails with this incomprehensible error:

FAILURE: Build failed with an exception.

* What went wrong:
A problem occurred configuring root project 'android'.
> Could not resolve all artifacts for configuration ':classpath'.
   > Could not resolve org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.22.
     Required by:
         project :
      > Cannot choose between the following variants of org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.22:
          - gradle70JavadocElements
          - gradle70RuntimeElements
          - gradle70SourcesElements
          - gradle71JavadocElements
          - gradle71RuntimeElements
          - gradle71SourcesElements
          - gradle74JavadocElements
          - gradle74RuntimeElements
          - gradle74SourcesElements
          - gradle75JavadocElements
          - gradle75RuntimeElements
          - gradle75SourcesElements
          - gradle76JavadocElements
          - gradle76RuntimeElements
          - gradle76SourcesElements
          - javadocElements
          - runtimeElementsWithFixedAttribute
          - sourcesElements
        All of them match the consumer attributes:
          - Variant 'gradle70JavadocElements' capability org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.22:
              - Unmatched attributes:
                  - Found org.gradle.category 'documentation' but wasn't required.
                  - Found org.gradle.docstype 'javadoc' but wasn't required.
                  - Required org.gradle.jvm.version '11' but no value provided.
                  - Required org.gradle.libraryelements 'jar' but no value provided.
                  - Found org.gradle.plugin.api-version '7.0' but wasn't required.
                  - Found org.gradle.status 'release' but wasn't required.
              - Compatible attributes:
                  - Required org.gradle.dependency.bundling 'external' and found compatible value 'external'.
                  - Required org.gradle.usage 'java-runtime' and found compatible value 'java-runtime'.
          - Variant 'gradle70RuntimeElements' capability org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.22:

[and the logs go on and on...]

And I seem to remember that the Gradle version also depended on the Kotlin version. So I looked it up and found this:

Kotlin version

Gradle min and max versions

Android Gradle plugin min and max versions

1.8.20

6.8.3 – 7.6.0

4.1.3 – 7.4.0

1.8.0

6.8.3 – 7.3.3

4.1.3 – 7.2.1

1.7.20

6.7.1 – 7.1.1

3.6.4 – 7.0.4

Configure a Gradle project | Kotlin

Looking for a similar issue among my previous blog posts, I find this. Turns out the Gradle version is defined in android/gradle/wrapper/gradle-wrapper.properties:

#Fri Jun 23 08:50:38 CEST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip

I am using Grade 6.1.1, which is lower than the minimum supported version of 6.8.3. Let's try it with 7.6.0:

Exception in thread "main" java.io.FileNotFoundException: https://downloads.gradle.org/distributions/gradle-7.6.0-all.zip
	at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1920)

I had blindly just changed the version number. Let's see from the Gradle documentation what the actual URL is. I found the page on Google:

Gradle Distributions

So there is no distribution for version 7.6.0. I should use 7.6.1 instead. So I run the build again, and after spinning for a little while, gives me this error:

FAILURE: Build failed with an exception.

* Where:
Build file '/Users/anhtuan/git/trading_journal/android/app/build.gradle' line: 25

* What went wrong:
A problem occurred evaluating project ':app'.
> Failed to apply plugin 'kotlin-android'.
   > Can't infer current AndroidGradlePluginVersion: Is the Android plugin applied?

I think it comes back to the build.grade's buildscript: classpath 'com.android.tools.build:gradle:3.5.0'. Based on the table above, it should be between 4.1.3 and 7.4.0. I run it again, and finally it runs!

The last thing to fix was the target API, in order to publish to the Play Store:

[!] Google Api Error: Invalid request - Your app currently targets API level 30 and must target at least API level 31.

So I went back to [project]/android/app/build.gradle and upgraded from targetSdkVersion 30 to targetSdkVersion 31. This time compilation failed with this error:

/Users/anhtuan/git/trading_journal/android/app/src/main/AndroidManifest.xml:15:9-43:20 Error:
        android:exported needs to be explicitly specified for element <activity#com.example.trading_journal.MainActivity>. Apps targeting Android 12 and higher are required to specify an explicit value for `android:exported` when the corresponding component has an intent filter defined. See https://developer.android.com/guide/topics/manifest/activity-element#exported for details.

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':app:processReleaseMainManifest'.
> Manifest merger failed : android:exported needs to be explicitly specified for element <activity#com.example.trading_journal.MainActivity>. Apps targeting Android 12 and higher are required to specify an explicit value for `android:exported` when the corresponding component has an intent filter defined. See https://developer.android.com/guide/topics/manifest/activity-element#exported for details.

Apparently this setting is to specify whether the activity can be launched by another app. I don't need this and the default used to be false anyway. So I'll set it to false:

    <application
        android:name="${applicationName}"
        android:label="Trading Journal"
        android:icon="@mipmap/launcher_icon">
        <activity
            android:exported="false"
            android:name=".MainActivity"
            android:launchMode="singleTop"
            android:theme="@style/LaunchTheme"
...

After installing it on my phone, I tried to run the app and it sait "The app is not installed". When trying on the Emulator, it fails with this error:

Launching lib/main.dart on sdk gphone64 arm64 in debug mode...
main.dart:1
✓  Built build/app/outputs/flutter-apk/app-debug.apk.
ProcessException: Process exited abnormally:
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x20000000 cmp=com.wafrat.elysium/.MainActivity (has extras) }


Exception occurred while executing 'start':
java.lang.SecurityException: Permission Denial: starting Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x30000000 cmp=com.wafrat.elysium/.MainActivity (has extras) } from null (pid=32763, uid=2000) not exported from uid 10165
	at com.android.server.wm.ActivityTaskSupervisor.checkStartAnyActivityPermission(ActivityTaskSupervisor.java:1108)

It turns out, it should have been set to true.

To summarize, I had to do these 6 tasks:

  1. in [project]/android/app/build.gradle upgrade compileSdkVersion to 33.
  2. in [project]/android/app/build.gradle, upgrade targetSdkVersion to 31.
  3. in [project]/android/build.gradle, upgrade kotlin_version to '1.8.22'.
  4. in [project]/android/build.gradle, upgrade  com.android.tools.build:gradle to 7.4.0.
  5. in [project]/android/gradle/wrapper/gradle-wrapper.properties, upgrade distributionUrl to 7.6.1.
  6. in android/app/src/main/AndroidManifest.xml, add android:exported="true" to the main Activity.