【Flutter】Gradleの基本と役割について

## 0. はじめに
FlutterでAndroidアプリを開発する際、Gradleというツールを使ってアプリをビルドしています。しかし、「Gradleが何をしているのか」や「エラーメッセージが出た時にどう対処すればいいのか」については、初心者の方にとって悩みの種になりがちです。

この記事では、その**第一歩**としてGradleの基本的な役割や、Flutterでの使い方について、できるだけわかりやすく説明していきます（つもりです...）。あくまで基本の概要を理解するための内容となります。

なお、もし間違いや改善点がございましたら、ご指摘いただけますと幸いです。

## 前提
Flutter 3.16以降では、build.gradleの記述方法が変更されています。もし3.16以前のバージョンをお使いの場合は、[こちらの記事](https://flutter.salon/error_warning/flutter-3-19-with-android/)や[公式ドキュメント](https://docs.flutter.dev/release/breaking-changes/flutter-gradle-plugin-apply)をご覧いただくとスムーズかもしれません。本記事ではこの変更点の詳細については割愛いたします。

## 1. Gradleとは？
Gradle（グレイドル）は、Androidアプリの開発において、プログラムや画像などを自動的にまとめて、アプリの実行ファイル（APKやAAB）を作るためのツールです。
また、ライブラリの依存関係を自動的に管理し、複雑なビルドプロセスを効率的に実行してくれます。

https://docs.gradle.org/current/userguide/userguide.html

## 2. build.gradleとは？
build.gradleは、Gradleの設定ファイルです。

build.gradleファイルに、プロジェクトのビルドに必要な設定や依存関係（使用するライブラリなど）を記述します。

この設定に基づいて、Gradleが自動的にソースコード（.javaや.xmlファイルなど）をコンパイルし、APKやAABファイルを生成します。

#### build.gradleに書かれていること
- どんなライブラリやツールを使うか
- どのバージョンのAndroidをサポートするか（アプリが対応するAndroidのバージョン）
- アプリのバージョン情報（アップデートのための情報）


要するに、GradleはAndroidアプリを自動でビルドしてくれるツールで、build.gradleファイルはその設定を記述する場所です。

https://www.geeksforgeeks.org/android-build-gradle/

:::note info 
Gradleでは、アプリのビルド設定を行うためにDSL（ドメイン固有言語）という特別な書き方が使われています。GradleのDSLには、Groovy（Javaに似た言語）を使ったものと、Kotlinを使ったもの（Gradle Kotlin DSL）の2種類があります。

build.gradleファイルは通常、GroovyベースのDSLで書かれています。本記事では、GroovyやDSLの詳細には深入りしませんが、興味がある方は[公式ドキュメント](https://docs.gradle.org/current/dsl/)をご参照ください。
:::

## 3. FlutterでGradleが必要な理由
Flutterはクロスプラットフォームのフレームワークで、iOSとAndroidの両方に対応しています。しかし、Android側のビルドには、ネイティブのビルドシステムであるGradleが必要です。

そのため、Flutterプロジェクトのandroidディレクトリ内にbuild.gradleファイルが存在し、プロジェクト全体やアプリモジュールごとにビルド設定が行われます。


## 4. Flutterプロジェクト内のGradleファイルの構成
Flutterプロジェクトのandroidディレクトリには、Gradle関連のファイルや設定が以下のような構造で配置されています。

この構造は、Androidアプリのビルドや設定を管理するために重要です。なお、プロジェクトには2つのbuild.gradleファイルが存在し、それぞれ異なる役割を持っています。([階層構造は公式から引用](https://developer.android.com/build?hl=ja#build-files))

プロジェクト全体は、図のように大きく3つの部分に分類できます。
本記事では①と②について説明します。


**① プロジェクトレベル（Androidアプリ全体の設定:/project_root/android/~）**
**② アプリモジュールレベル（個別のアプリ設定:/project_root/android/app/~）**
**③ ソースセット（アプリのソースコードとリソース:/project_root/android/app/src/~）**

<img width="750" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2658262/640f82fc-1830-1fcf-dcf0-4248d8b3ef99.png">

次節から詳しく見ていきます。

## 5. Gradleファイルの詳細

### ① プロジェクトレベル（Androidアプリ全体の設定:/project_root/android/~）
プロジェクト全体の設定を管理する部分です。

ここには、プロジェクト全体に影響する設定を記述するファイルが含まれています。
例えば、Flutterプロジェクトのandroidディレクトリに配置されているbuild.gradleやsettings.gradleなどが該当します。

#### (1) android/gradle/wrapper/gradle-wrapper.properties（Gradleのバージョン管理）

このファイルは、プロジェクトで使うGradleを準備してくれる設定ファイルです。
これによって、プロジェクトを開くときに、指定されたバージョンのGradleを自動的にダウンロードして使えるようにします。

```gradle-wrapper.properties
# Gradleの配布元ディレクトリの基準。
# GRADLE_USER_HOMEは、ユーザーのホームディレクトリ内に設定されたGradleのユーザーディレクトリ
distributionBase=GRADLE_USER_HOME

# Gradleの配布物（ZIPファイル）が保存されるパス
distributionPath=wrapper/dists

# Gradleのバージョンとダウンロード先URL
# ここではGradle 8.6のフルバージョン（すべてのライブラリが含まれたもの）を指定
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-all.zip

# GradleのZIPファイルが保存されるベースディレクトリ
zipStoreBase=GRADLE_USER_HOME

# GradleのZIPファイルが保存される具体的なパス
zipStorePath=wrapper/dists
```

https://developer.android.com/build?hl=ja#wrapper-file


https://docs.gradle.org/current/dsl/org.gradle.api.tasks.wrapper.Wrapper.html

#### (2) android/settings.gradle（Gradleの設定ファイル）
このファイルは、**プロジェクト内にどんなアプリやライブラリが含まれているかをGradleに伝える役割**があります。Flutterプロジェクトでは、このファイルを使ってビルドプロセス全体を設定します。

Flutterプロジェクトでは、主に以下の内容が設定されています。
- **pluginManagement { ... }(プラグイン管理)**
  - ここでは、Flutter SDKのパスと、プラグインをダウンロードするリポジトリ（プラグインが保存されている場所）を設定します。
  - 例えば、google()やmavenCentral()など、プラグインを取得する場所を指定します。これにより、必要なプラグインをプロジェクト全体で使えるようになります。
  - 詳しくは[公式ドキュメント](https://docs.gradle.org/current/userguide/plugins.html#sec:plugin_management)をご覧ください。

- **plugins { ... }(プラグイン定義)**
  - 必要なGradleプラグイン（Flutterプラグイン、Androidプラグイン、Kotlinプラグインなど）を宣言的に定義。プラグインのバージョン管理もここで行い、`apply false` を指定することで、必要なタイミングでのみプラグインを適用できます。

  - `settings.gradle`では、プロジェクト全体で使うプラグインがどのリポジトリから取得され、どのバージョンを使うかを指定しますが、実際にそのプラグインをどのモジュールに適用するかは、後述する`app/build.gradle`で指定します。

:::note info 
GradleとAGP (Android Gradle Plugin) の違い

GradleとAGPは混乱しやすいですが、役割が異なります。

- **Gradle**: ビルド自体を実行するビルドツールです。Gradleのバージョンは、`gradle-wrapper.properties`ファイルで設定され、プロジェクトのビルドプロセス全体を制御します。

- **AGP (Android Gradle Plugin)**: Androidアプリをビルドするために必要なプラグインです。これを使うことで、AndroidアプリのコンパイルやAPKの生成、リソースの処理などが可能になります。AGPのバージョンは、`settings.gradle`や`build.gradle`で指定されます。

詳細は[公式ドキュメント](https://developer.android.com/build/releases/gradle-plugin?hl=ja)を確認ください。
:::

- **include ":app" (アプリの指定)**
  - プロジェクト内には、メインのアプリだけでなく、補助的なライブラリや他の機能も含まれていることがあります。この設定は、「このプロジェクトをビルドする時には、`:app`という名前のメインアプリを対象にしてください」とGradleに指示するためのものです。
  - つまり、`:app`は通常、実際にユーザーが使うアプリ本体の部分を指していて、これをビルドの対象にすることで、最終的なアプリの実行ファイル（APKやAAB）を作ることができます。

```settings.gradle
// プラグイン管理
pluginManagement {
    // Flutter SDKのパスを取得するためのクロージャ（無名関数）を定義
    def flutterSdkPath = {
        def properties = new Properties()
        file("local.properties").withInputStream { properties.load(it) }
        def flutterSdkPath = properties.getProperty("flutter.sdk")
        assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
        return flutterSdkPath
    }
    // プロジェクト設定にFlutter SDKのパスを格納
    settings.ext.flutterSdkPath = flutterSdkPath()

    // Flutterのツールが入っているビルド設定をプロジェクトに含めるための設定
    includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle")
    
    // プラグインを取得するためのリポジトリを設定
    repositories {
        google() // Googleが提供するリポジトリ
        mavenCentral() // JavaやKotlinのライブラリを保管してるリポジトリ
        gradlePluginPortal() // Gradleプラグインが保管されているリポジトリ
    }
}

// プラグイン定義
plugins {
    id "dev.flutter.flutter-plugin-loader" version "1.0.0"
    id "com.android.application" version "8.4.0" apply false
    id "org.jetbrains.kotlin.android" version "2.0.20" apply false
    id "com.google.gms.google-services" version "4.4.0" apply false
    id "com.google.firebase.crashlytics" version "2.9.9" apply false
}

// プロジェクトに含まれるモジュール（appモジュール）をビルド対象として追加 
include ":app"
```

https://developer.android.com/build?hl=ja#settings-file

#### (3) /project_root/android/build.gradle（プロジェクトレベルのbuild.gradle）
このファイルでは、プロジェクト全体に適用される設定を記述します。例えば、プロジェクト全体で使用するプラグインやリポジトリ（ライブラリのダウンロード元）を定義します。

- **allprojects { ... }(リポジトリ定義)**
全プロジェクトで共通して使用するリポジトリを定義します。これは、アプリをビルドする際に必要なライブラリやプラグインを取得するために使います。
- **tasks.register("clean", Delete) { ... }(クリーンタスク)**
`clean`タスクを定義し、プロジェクトのビルドディレクトリを削除します。これにより、新しいビルドを実行する前に過去の成果物をクリアすることができます。

```build.gradle
// 全プロジェクト共通のリポジトリを定義
allprojects {
    repositories {
        google() // Googleのリポジトリを追加（Android関連のプラグインやライブラリを取得）
        mavenCentral() // JavaやKotlinのオープンソースライブラリを取得
    }
}

// ビルド成果物（コンパイル後のAABやAPKファイルなど）が保存されるディレクトリを指定
rootProject.buildDir = '../build'

subprojects {
    // 各サブプロジェクト（モジュール）のビルドディレクトリを設定
    project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
     // appモジュールがプロジェクト内で最初に評価されるように設定
    project.evaluationDependsOn(':app')
}

// プロジェクト全体のcleanタスクを登録
tasks.register("clean", Delete) {
    // ルートプロジェクトのビルドディレクトリを削除する（クリーンビルド用）
    // これにより、新しいビルドを行う前に過去のビルド成果物がクリアし、再ビルドする
    delete rootProject.buildDir
}
```

##### リポジトリについての補足
ここまでで`settings.gradle`と`android/build.gradle`の両方にrepositoriesで同じgoogle()やmavenCentral()が記述されていますが、これらは異なる目的で使用されます。

両方のファイルで同じリポジトリを指定しているものの、取得するものと使われるタイミングが異なるため、どちらにも定義する必要があります。

| ファイル名	 | 目的とタイミング | 取得するもの |
|:-----------|:-----------|:-----------|
| settings.gradle      | Gradleプラグインの取得（プロジェクトの初期設定時に使われる） | ビルドルールやツール（例: com.android.application）|
| android/build.gradle	     | アプリの依存ライブラリの取得（アプリのビルド時に使われる）| 実際のアプリが動作するためのライブラリ（例: Firebase） |

簡単に言うと、settings.gradleはプロジェクトの初期設定時に使う「ビルドのためのルールやプラグイン」を取得するためのリポジトリを指定し、android/build.gradleは実際にアプリを動作させるために必要なライブラリを取得するために使います。

https://developer.android.com/build?hl=ja#top-level


### ② アプリモジュールレベル（個別のアプリ設定:/project_root/android/app/~）
ここでは、特定のアプリケーションモジュールに対する設定を行います。  
この設定は、主にFlutterアプリ自体やアプリが使用するAndroidのバージョン、署名情報など、個別のアプリに特化した設定を管理します。

`app/build.gradle` は、アプリモジュール（つまり、FlutterアプリやAndroidアプリそのもの）に関する設定を行います。

主な設定
- **(1) plugins { ... }(プラグインの適用)**
**アプリのビルドに必要なプラグイン**を定義しています。
例えば、AndroidアプリやFlutterアプリをビルドするためのプラグイン、Firebaseなどのサービスを使うためのプラグインを設定します。
- **(2) android { ... }(アプリ固有の設定)**
アプリが使用する**SDKのバージョン、サポートする最小/ターゲットAndroidバージョン**、およびリリースビルドの**署名情報**などを設定します。

**Androidバージョンに関する設定**

| 設定名| 説明 | 例 |
|--------------------|--------------------|--------------------|
| **compileSdkVersion** | アプリをコンパイルする際に使用するAndroid SDKのバージョン。このバージョンに対応したAPIを使用してアプリがビルドされる。 | `compileSdkVersion 34`（Android 14, APIレベル34でビルド）|
| **minSdkVersion**     | アプリがサポートする最小のAndroidバージョン。ここで設定されたバージョンより古い端末ではアプリがインストールできない。 | `minSdkVersion 26`（Android 8.0, APIレベル26以上でのみ動作）|
| **targetSdkVersion**  | 開発中のアプリがメインターゲットにしているSDKのバージョン。Androidの新しい機能や動作を正しくサポートしているかを示す。compileSdkのAPI レベルが上がったとしても、targetSdkを更新しなければ動作に対する新しい変更を適用しない | `targetSdkVersion 34`（Google Play StoreでAPIレベルを34にする場合） |


:::note info
compileSdkVersion と targetSdkVersion の値は必ずしも同じである必要はありませんが、理想はtargetSdk は compileSdkと同じ値が望ましいです。
:::

https://proandroiddev.com/compilesdkversion-and-targetsdkversion-what-is-the-difference-b4227c663ba8

- **(3) dependencies { ... }(依存関係の管理)**
アプリが**依存する外部ライブラリ**を指定します。ビルド時にこれらのライブラリが自動的にダウンロードされ、アプリ内で使用可能になります。

```build.gradle
// ビルドに必要なツールやサービスを設定
plugins {
    id 'com.android.application' // Androidアプリとしてのビルド設定を適用
    id 'com.google.gms.google-services' // Googleサービス（Firebaseなど）の設定を追加
    id 'kotlin-android' // Kotlin言語を使ったAndroid開発を有効にするプラグイン
    id 'dev.flutter.flutter-gradle-plugin' // Flutterアプリをビルドするためのプラグイン
    id 'com.google.firebase.crashlytics' // Firebase Crashlytics（クラッシュレポート）の機能を追加
}

// アプリのSDKバージョンや署名、最小・ターゲットバージョンなどを設定
android {
    compileSdkVersion 34 // コンパイルに使用するSDKバージョン
    ndkVersion flutter.ndkVersion // Flutterアプリでネイティブコード(C / C++)を使うためのNDKバージョンを指定

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8 // Javaの互換性をバージョン1.8に設定
        targetCompatibility JavaVersion.VERSION_1_8 // Javaのターゲット互換性を1.8に設定
    }

    kotlinOptions {
        jvmTarget = '1.8' // KotlinのJVMターゲットをJava 8に設定
    }

    sourceSets {
        main.java.srcDirs += 'src/main/kotlin' // Kotlinのソースファイルのディレクトリを指定
    }

    defaultConfig {
        // アプリケーションID（アプリを一意に識別するためのID）。Google PlayでこのIDを基に管理される
        applicationId "com.example"
        minSdkVersion 26 // アプリがサポートする最小のAndroidバージョン（Android 8.0）
        targetSdkVersion flutter.targetSdkVersion // アプリがターゲットとする最適化されたAndroidバージョン
        versionCode flutterVersionCode.toInteger() // アプリのバージョンコード（整数値で管理）
        versionName flutterVersionName // アプリのバージョン名（ユーザー向けのバージョン表記）
        resValue "string", "app_name", "${dartDefines.appName}" // リソース値の定義（アプリ名をリソースとして設定）
        archivesBaseName "${dartDefines.flavor}-${flutterVersionName}" // アプリのビルド成果物のファイル名を定義
    }

    signingConfigs {
        release {
            keyAlias keystoreProperties['keyAlias']
            keyPassword keystoreProperties['keyPassword']
            storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
            storePassword keystoreProperties['storePassword']
        }
    }

    buildTypes {
        release {
            signingConfig signingConfigs.release // リリースビルドに署名を設定
        }
    }
}

// Flutterのソースコードのルートディレクトリを指定
flutter {
    source '../..'
}

// アプリで使用する外部ライブラリを管理
dependencies {
    implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1"
}
```
https://developer.android.com/build?hl=ja#module-level

## 6. 参考
https://developer.android.com/build/gradle-build-overview?hl=ja

https://qiita.com/hatimiti/items/a127311d739c9d3e0045

https://qiita.com/_mi/items/4aea84f14e5b35ee6cda#task

https://zenn.dev/bubu/articles/211b12740f9d35

https://kazulog.fun/dev/flutter-build-gradle-setting/

https://qiita.com/Rubydog/items/14d0df45f4114e10a467

https://qiita.com/takagimeow/items/10a010c641ff0241acdb