🤖

Flutterのビルドに必要なiOSとAndroidの知識

2023/05/21に公開

※Androidのファイル構成に変更がありました。この記事の内容は古くなっています

概要

Flutterを用いると、iOS & Androidスマートフォン向けアプリの同時作成が可能ですが、部分的にはネイティブ開発の知識も必要になります。例えば、各OSではライブラリがどのように管理されているか、ビルド周りの仕組みについてです。
ここでは、シンプルなFlutterモバイルアプリケーションを開発する際に最低限必要な知識についてまとめます。

buildの概念

buildのイメージがわかない場合は次の記事の「ビルドの構成要素」を読むと良いです。
http://mixi-inc.github.io/AndroidTraining/introductions/1.05.how-to-build-for-gradle.html

Android

Androidのビルド、ライブラリの管理について理解するには、Gradleの理解が必須です。

Gradleとは

全てのAndroid Projectは .javaや.xmlファイルからAPKファイルを生成するためにGradleを必要とします。Android アプリの標準プロジェクト構造には、数種類の Gradle ビルド構成ファイルが含まれています。各ファイルのスコープと目的、定義する必要のある基本的な DSL要素(gradleファイル)について理解しておくことが重要です。

image.png

settings.gradle

プロジェクト レベルのリポジトリ設定を定義します。このファイルに基づいて、Gradle は、アプリをビルドする際に含める必要のあるモジュールを識別します。
Flutter Projectでは、settings.gradleでAndroidにFlutter SDKのパスを認識させています。
build.gradle等のファイルは、Groovy 言語で記述されています。

def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"

MyApp / build.gradle

Androidプロジェクト直下のbuild.gradle ファイルは、Androidプロジェクト内のすべてのモジュールに適用される依存関係を定義します。
プロジェクト直下のbuild.gradleは、大きくみるとbuildscript、allprojectsに分かれています。

buildscript {
    ext.kotlin_version = '1.7.20'
    repositories {
        google()
        mavenCentral()
        //jcenter()
    }

    dependencies {
        classpath 'com.android.tools.build:gradle:4.2.1'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath 'com.google.gms:google-services:4.3.10'
        classpath 'com.google.firebase:firebase-crashlytics-gradle:2.7.1'
    }
}

allprojects {
    repositories {
        google()
        mavenCentral()
        //jcenter()
    }
}

rootProject.buildDir = '../build'
subprojects {
    project.buildDir = "${rootProject.buildDir}/${project.name}"
    project.evaluationDependsOn(':app')
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

MyApp / build.gradle /dependencies

dependenciesは依存関係を記述します。外部のライブラリを読み込みたい場合はここに記述します。

例えば、上記サンプルのdependenciesでは、Android gradle Plugin、Kotlin gradle Plugin、Google Services Gradle Pluginのversionを定義しています。
Android gradle Pluginは、Android studioのbuildに必要な複数の機能を追加します。Android studioをupdateした際などに、Android gradle Pluginのupdateを促されることがあります。
Android StudioとAndroid gradle Pluginの対応表はこちらで確認できます。
Kotlin gradle Pluginは、KotlinをgradleでbuildするためのPluginです。KotlinをAndroid Studioで利用可能にするKotlin Pluginとは別物です。
Kotlin Pluginは、同ファイルのbuildscript/set.kotlin_versionで定義されています。Flutterのversionを上げるとKotlinのversionが上がり、Kotlin gradle Pluginのversion upが必要になることがあるようです。
Google Services PluginはGoogle APIやFirebaseを利用する際に追加します。

※単なる記述方法の1つですが、上記サンプルでは

ext.kotlin_version = '1.7.20'

のKotlin Pluginの値が、

classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

Kotlin gradle Pluginの$kotlin_version"部分に適応されています。

MyApp / build.gradle/ allprojects/repositories

allprojects の repositories は、外部からインポートする jar のリポジトリを指定します。デフォルトで jcenter() が指定されています。上記サンプルでは、google firebaseの設定のためgoogle()を追加、Android12からjcenter()が非推奨となっているため、代わりにmavenCentral()を記述しています。

MyApp / app / build.gradle

モジュールレベルのbuild.gradleと呼ばれるものです。

def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
    localPropertiesFile.withReader('UTF-8') { reader ->
        localProperties.load(reader)
    }
}

def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
    throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}

def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
    flutterVersionCode = '1'
}

def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
    flutterVersionName = '1.0'
}

apply plugin: 'com.android.application'
apply plugin: 'com.google.gms.google-services'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"


android {
    compileSdkVersion 31
    buildToolsVersion "30.0.2"

    sourceSets {
        main.java.srcDirs += 'src/main/kotlin'
    }

    defaultConfig {
        // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
        applicationId "com.android.application"
        minSdkVersion 23
        targetSdkVersion 31
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
    }

    buildTypes {
        release {
            // TODO: Add your own signing config for the release build.
            // Signing with the debug keys for now, so `flutter run --release` works.
            signingConfig signingConfigs.debug
        }
    }
}

flutter {
    source '../..'
}

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    // Import the BoM for the Firebase platform
    implementation platform('com.google.firebase:firebase-bom:28.3.1')

    // Declare the dependencies for the Crashlytics and Analytics libraries
    // When using the BoM, you don't specify versions in Firebase library dependencies
    implementation'com.google.firebase:firebase-analytics'
    implementation 'com.google.firebase:firebase-crashlytics'

}

apply plugin: 'com.google.firebase.crashlytics'
apply plugin: 'com.google.gms.google-services'

重要なのは、下記の部分でそれが意外はFlutterプロジェクトとAndroidの繋ぎこみ部分のように思います。

apply plugin: 'kotlin-android'
android {
    compileSdkVersion 31
    buildToolsVersion "30.0.2"

    sourceSets {
        main.java.srcDirs += 'src/main/kotlin'
    }

    defaultConfig {
        // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
        applicationId "com.android.application"
        minSdkVersion 23
        targetSdkVersion 31
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
    }

    buildTypes {
        release {
            // TODO: Add your own signing config for the release build.
            // Signing with the debug keys for now, so `flutter run --release` works.
            signingConfig signingConfigs.debug
        }
    }
}

flutter {
    source '../..'
}

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    // Import the BoM for the Firebase platform
    implementation platform('com.google.firebase:firebase-bom:28.3.1')

    // Declare the dependencies for the Crashlytics and Analytics libraries
    // When using the BoM, you don't specify versions in Firebase library dependencies
    implementation'com.google.firebase:firebase-analytics'
    implementation 'com.google.firebase:firebase-crashlytics'

}

apply plugin: 'com.android.application'はAndroidのPluginを使う上での宣言です。

  • android : ビルドの情報を記載します
    • compileSDK
    • buildToolsVersion
    • defaultConfig : デフォルトの設定を記載します
    • buildTypes : ビルド毎の設定を記載します
  • dependencies

Flutterプロジェクトで開いた際に、build.gradleで赤線が表示されることがありますが、これはFlutterアプリとしてAndroid Studioを開いている場合は、参照できないクラスがあるために発生するもので、Androidモジュールで開いたときに消えれば問題ないようです。

appモジュールについてわかりやすそうな記事
https://qiita.com/takahirom/items/e00126586c13aebf1a6b
https://blog.77jp.net/android-about-compilation-sdk-version

iOS

iOSのビルド、ライブラリの管理について理解するには、cocoapodsの理解が必須です。

CocoaPodsとは

CocoaPodsは、Xcode プロジェクトのライブラリ依存関係を管理します。
プロジェクトの依存関係は、Podfile と呼ばれる単一のテキストファイルで記述、指定できます。CocoaPodsにもgradleのようにversionがあります。ターゲットとするiOSのversionをPodfileで指定することができます。
下記sampleの2行目で、iOSでFirebaseライブラリを利用したい場合などに、iOSの下限versionがFirebaseライブラリに対応するよう調整できます。

スクリーンショット 2022-11-20 21.16.55.png
Flutterでは、Projectを新規作成した直後だとPodfileが生成されていません。
Terminalで"pod init"コマンドをうつとPodfileが作成されます。

Podfile.lock

ロック(固定)されたバージョンの情報が格納されているファイル」です。.lockファイルは、pod initやpod install時に自動生成されます。プロジェクトの開発に用いているライブラリを特定バージョンに揃える為にPodfile.lockファイルでのバージョン指定が必要になります。

Discussion