Flutter 3.29.2にアップデート

対象者

  • Flutter 3.29にアップデートしたら、Androidがビルドできなくて困ってるFlutterエンジニア
  • 別のアプリをアップデートしようとしたらできなくて、「前、やったのに覚えてねぇorz」になるであろう自分

はじめに

Flutterアプリを新しいバージョンへアップグレードする際、スムーズに進む場合もあれば、思わぬエラーやビルドトラブルに遭遇してしまうこともあります。今回、Flutter SDKを 3.19.0 から 3.29.2 へアップデートするにあたって、Androidのビルドでいくつかの修正が必要でした。

この記事では、実際に行った修正内容を紹介していきます。参考になれば幸いです。

方針と前提

方針としては、以下のつもりです。

  • Flutter 3.29.2を使用
    とりあえず、Flutterの最新版を使いたい!
  • Android SDK 35を使用
    Warning: The plugin flutter_local_notifications requires Android SDK version 35 or higher.とかめっちゃ言われる
  • Java 17を使用
    Java8とJava17があって、バージョンが違うと言われてので、とりあえず統一。
  • Gradle 8を使用
    Android SDK 35をビルドするには8以上じゃないとダメみたい。

前提として、以下に対応済み。

【Flutter】「You are applying Flutter’s main Gradle plugin」の警告を解決

Flutter SDKのバージョン変更 (.fvmrc)

Flutterのバージョン管理をfvmで行っている場合、.fvmrcファイル内で下記のようにFlutterのバージョンを切り替えます。

-  "flutter": "3.19.0",
  "flutter": "3.29.2",

上記のように、古いバージョンを削除し、新たに 3.29.2 を指定。これだけでFlutter SDK自体は切り替わりますが、実際にはAndroid側の設定ファイルも変更が必要で、俺たちの戦いは始まったばかりです。

Android Gradle設定の修正 (android/app/build.gradle)

Flutter 3.29.2では、Gradle 8系やKotlin 2系といった新しいバージョンへの対応が求められました。そこで、android/app/build.gradleファイルで以下の変更を行いました。

android {
    ndkVersion "27.0.12077973"
    namespace 'salon.flutter.test_app'
    defaultConfig {
       targetSdkVersion 35
       compileSdk 35
    }
    // 中略

    compileOptions {
        coreLibraryDesugaringEnabled true
        sourceCompatibility JavaVersion.VERSION_17
        targetCompatibility JavaVersion.VERSION_17
    }
}

dependencies {
    coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.4'
    implementation platform('com.google.firebase:firebase-bom:32.7.0')
-    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.22"
    implementation "org.jetbrains.kotlin:kotlin-stdlib:2.1.20"
    implementation 'com.google.firebase:firebase-analytics-ktx'
    implementation 'com.google.android.recaptcha:recaptcha:18.7.0'
}

ポイント解説

  • NDKのバージョン: Flutterやプラグインの要件に合わせるため、ndkVersionを上げています。
  • compileSdk / targetSdk: AndroidのAPIレベルを 35 に更新することで、最新のAndroid機能や互換性を確保します。
  • Java / Kotlinバージョン: Kotlin 2.x系を使うため、sourceCompatibilitytargetCompatibility を Java17 にし、kotlin-stdlib2.1.0 に変更。
  • namespace: もともとAndroidManifext.xmlに会ったパッケージ名がnamespaceとして、こちらに移動になりました
  • coreLibraryDesugaring: Javaの新しい言語機能やライブラリAPIを、古いバージョンのバイトコードに置き換えて動作させる仕組み。外部パッケージでエラーが出たので、使用しました。(外部パッケージによっては、不要かも)

AndroidManifest.xmlの修正 (android/app/src/main/AndroidManifest.xml)

Android 35 では、namespace が導入され、AndroidManifest.xml 上の package 属性が不要になったので、削除しました。

- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="jp.co.fignny.applimoFlutter">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

namespacebuild.gradle( android { namespace = "xxx" } ) で定義してます。

gradle-wrapper.propertiesの更新 (android/gradle/wrapper/gradle-wrapper.properties)

Flutter 3.29.2のプロジェクトのために、Gradle 8.10.2 へのアップデートしました。下記のように修正しました。

- distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
+ distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip

Gradle 7系から8系へ大きくバージョンが上がるため、プラグインの非互換などに注意が必要です。特にbuild.gradleファイルでnamespace設定が必須化されているなど、細かい部分でエラーになりやすいので慎重にチェックしましょう。

Androidビルド設定の整理 (android/build.gradle)

アプリのモジュール以外にも、ルートレベルの android/build.gradle で以下のようにサブプロジェクトを一括制御し、compileSdkVersionJavaVersion などを上書きしています。

subprojects {
    afterEvaluate { project ->
        if (project.hasProperty('android')) {
            android {
                compileSdkVersion 35

                compileOptions {
                    sourceCompatibility JavaVersion.VERSION_17
                    targetCompatibility JavaVersion.VERSION_17
                }
            }
        }

        tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach {
            kotlinOptions {
                jvmTarget = JavaVersion.VERSION_17.toString()
            }
        }

        if (project.plugins.hasPlugin('com.android.library')) {
            android {
                defaultConfig {
                    def androidManifest = android.sourceSets.main.manifest.srcFile
                    if (androidManifest.exists()) {
                        def parsedManifest = new XmlParser().parse(androidManifest)
                        def packageName = parsedManifest.attribute('package')

                        if (packageName) {
                            namespace = packageName
                        } else {
                            namespace = "${project.name}"
                        }
                    }
                }
            }
        }
    }
    project.evaluationDependsOn(':app')
}

サブモジュールのSDKとJavaのバージョンに統一して、ビルド時のコンフリクトを減らしているつもり。

settings.gradleの修正 (android/settings.gradle)

最後に、 settings.gradle でも使用するプラグインのバージョンを一斉にアップデートしました。

plugins {
    id "dev.flutter.flutter-plugin-loader" version "1.0.0"

-    id "com.android.application" version "7.4.2" apply false
    id "com.android.application" version "8.7.0" apply false
-    id "org.jetbrains.kotlin.android" version "1.9.22" apply false
    id 'org.jetbrains.kotlin.android' version '2.0.21' apply false
-    id "com.google.gms.google-services" version "4.4.0" apply false
    id "com.google.gms.google-services" version "4.4.2" apply false
}
  • com.android.application は 7.4.2 から 8.7.0
  • org.jetbrains.kotlin.android は 1.9.22 から 2.0.21
  • com.google.gms.google-services は 4.4.0 から 4.4.2

これらを正しく揃えないと、ビルド時に「バージョンが合っていない」「コンパイルエラーが発生する」などの問題が起きる可能性が高いです。とりあえず、この組み合わせだといけた。多分もっと適切な組み合わせがあるとは思うが、とりあえず動いたからよしとする。

解決できなかったエラー

Flutter 3.29へのアップデート時、以下のようなエラーが発生し、ビルドに失敗しました。

e: file:///Users/{user_name}/.pub-cache/hosted/pub.dev/fluttertoast-8.2.12/android/src/main/kotlin/io/github/ponnamkarthik/toast/fluttertoast/MethodCallHandlerImpl.kt:49:121 Unresolved reference 'R'.
e: file:///Users/{user_name}/.pub-cache/hosted/pub.dev/fluttertoast-8.2.12/android/src/main/kotlin/io/github/ponnamkarthik/toast/fluttertoast/MethodCallHandlerImpl.kt:50:39 Unresolved reference 'findViewById'.

FAILURE: Build completed with 3 failures.

1: Task failed with an exception.

- What went wrong: Execution failed for task ':image_gallery_saver:compileDebugKotlin'.
A failure occurred while executing org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers$GradleKotlinCompilerWorkAction
Compilation error. See log for more details

昔のAndroidの書き方で、Flutter 3.29から互換性がなくなってしまったのかと。3.29リリース直後に「少ししたら、パッケージがアップデートされるかな」と思ってましたが、ダメでした。
このときにできる手段として、Flutterをダウングレードしたり、フォークして独自実装する事もできます。しかし今回は幸い似たような別パッケージがあったので、そちらに移行しました。参考までに以下のパッケージです。

  • image_gallery_saver → image_gallery_saver_plus
  • fluttertoast → Flutter標準のSnackbar
  • pdf_render → pdfrx

Flutter 3.29に対応していないパッケージを削除することで、Flutterがビルドできるようになりました。

Git関連

実行すると、android/app/.cxxandroid/app/build/intermediates(coreLibraryDesugaring使用時のみ?)と いうディレクトリができていました。ファイル管理はしなくて良さそうだったので、.gitignoreに追加しました

まとめ

Flutterのアップグレードは、単純にSDKバージョンを上げるだけでなく、Androidの設定ファイルやGradle/Kotlinの依存関係もきちんと合わせることが重要です。今回の修正ポイントは以下の通りです。

  • .fvmrcでFlutter SDKを*3.29.2** にアップデート
  • android/app/build.gradleNDK・compileSdk・Kotlin を新バージョンへ
  • AndroidManifest.xml から package属性 を削除し、Gradleのnamespaceへ統一
  • gradle-wrapper.propertiesGradle 8.10.2 に更新
  • ルートの android/build.gradlesettings.gradleサブプロジェクトの統一設定プラグインのバージョン合わせ

これらのステップを踏むことで、皆様のFlutter 3.29以降への アップデートがよりスムーズに進むと幸いです。あと、Android関連のビルド関連の設定も勉強しないと。

発生したエラー

namespace関連

Gradle8 からパッケージ名が名前空間(namespace)になって、記載場所が変わった

  • Android Gradle にnamespaceを記載すると消える
A problem occurred evaluating root project 'android'.
> A problem occurred configuring project ':app'.
   > Could not create an instance of type com.android.build.api.variant.impl.ApplicationVariantImpl.
      > Namespace not specified. Specify a namespace in the module's build file. See https://d.android.com/r/tools/upgrade-assistant/set-namespace for information about setting the namespace.

        If you've specified the package attribute in the source AndroidManifest.xml, you can use the AGP Upgrade Assistant to migrate to the namespace value in the build file. Refer to https://d.android.com/r/tools/upgrade-assistant/agp-upgrade-assistant for general information about using the AGP Upgrade Assistant.
  • AndroidManifest.xml からpackage名を削除すると消える
Execution failed for task ':app:processDebugMainManifest'.
> Incorrect package="com.example.test_app" found in source AndroidManifest.xml:   Setting the namespace via the package attribute in the source AndroidManifest.xml is no longer supported.
  Recommendation: remove package="com.example.test_app" from the source AndroidManifest.xml:

Android SDK関連

  • build.gradleのtargetSdkVersion とcompileSdkを35に設定すると消える
Your project is configured to compile against Android SDK 34, but the following plugin(s) require to be compiled against a higher Android SDK version:
- flutter_local_notifications compiles against Android SDK 35

coreLibraryDesugaring 関連

  • coreLibraryDesugaringEnabled true を追記すると消える
Execution failed for task ':app:checkDebugAarMetadata'.
> A failure occurred while executing com.android.build.gradle.internal.tasks.CheckAarMetadataWorkAction
   > 2 issues were found when checking AAR metadata:

       1.  Dependency ':flutter_local_notifications' requires core library desugaring to be enabled
           for :app.

           See https://developer.android.com/studio/write/java8-support.html for more
           details.
  • coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.4'を追加すると消える
Could not determine the dependencies of task ':app:packageDebug'.
> Could not create task ':app:l8DexDesugarLibDebug'.
   > coreLibraryDesugaring configuration contains no dependencies. If you intend to enable core library desugaring, please add dependencies to coreLibraryDesugaring configuration.

参考