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 |
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:
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:
- in
[project]/android/app/build.gradle
upgradecompileSdkVersion
to 33. - in
[project]/android/app/build.gradle
, upgradetargetSdkVersion
to 31. - in
[project]/android/build.gradle
, upgradekotlin_version
to'1.8.22'
. - in
[project]/android/build.gradle
, upgradecom.android.tools.build:gradle
to 7.4.0. - in
[project]/android/gradle/wrapper/gradle-wrapper.properties
, upgradedistributionUrl
to7.6.1
. - in
android/app/src/main/AndroidManifest.xml
, addandroid:exported="true"
to the main Activity.