0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

「既存Androidアプリ + Flutter画面」の追加開発

Posted at

はじめに

既存のAndroidアプリにFlutterで作った画面を追加することができます。
「Add-to-app」という機能を使うことで、ネイティブアプリ(Android, iOS)にFlutterで開発した画面やロジックを追加することができます。
Add-to-appはFlutter 1.12以降で正式に安定版に導入されているので、最新のFlutter Stableバージョンでも利用できます。
今回は実際にAdd-to-appを使用して、Android(kotlin)のネイティブアプリから、Flutterで開発した画面を呼び出してみたいと思います。

サンプルアプリ

native_android_app_with_flutter  
├── android_app // androidネイティブアプリ
└── flutter_module // Flutterで開発した画面があるモジュール

Androidアプリ作成 【Androidアプリ側】

画面遷移するためのボタンがあるアプリを作成します。
※今回はJetpack Compose使用

スクリーンショット 2024-12-22 11.19.04.png

Flutterのモジュール作成 【Flutterモジュール側】

Flutterのバージョンを最新に上げる(必要に応じて)

flutter upgrade

Flutterのモジュール作成

以下のコマンドを実行してflutter_moduleを作成します。
このモジュール内にFlutterで開発した画面やロジックを配置します。

flutter create -t module flutter_module

AndroidアプリでFlutterモジュールを使えるようにする

Flutterモジュールを汎用的なローカル Maven リポジトリとして使用するためにパッケージ化します。
汎用ローカル Maven リポジトリは、ビルド済みの Android ライブラリ(AARファイル)と、ライブラリの依存関係や設定を記述したPOMファイルで構成されています。

/flutter_moduleで以下のコマンドを実行します。

flutter build aar

!エラー発生

以下のエラーが発生、gradleがサポートしていないjavaを使用していた模様。

FAILURE: Build failed with an exception.

* What went wrong:
※省略
> BUG! exception in phase 'semantic analysis' in source unit '_BuildScript_' Unsupported class file major version 65
java --version

openjdk 21.0.5 2024-10-15
OpenJDK Runtime Environment Homebrew (build 21.0.5)
OpenJDK 64-Bit Server VM Homebrew (build 21.0.5, mixed mode, sharing)

flutter_module/.android/gradle/wrapper/gradle-wrapper.propertiesで以下のようにgradleのバージョン変更

distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-all.zip
↓
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-all.zip

ビルド成功

flutter_module % flutter build aar

Running Gradle task 'assembleAarDebug'...                         109.4s
✓ Built build/host/outputs/repo
Running Gradle task 'assembleAarProfile'...                        31.3s
✓ Built build/host/outputs/repo
Running Gradle task 'assembleAarRelease'...                        29.9s
✓ Built build/host/outputs/repo

Consuming the Module
  1. Open <host>/app/build.gradle
  2. Ensure you have the repositories configured, otherwise add them:

      String storageUrl = System.env.FLUTTER_STORAGE_BASE_URL ?: "https://storage.googleapis.com"
      repositories {
        maven {
            url '/../w2406/native_android_app_with_flutter/flutter_module/build/host/outputs/repo'
        }
        maven {
            url "$storageUrl/download.flutter.io"
        }
      }

  3. Make the host app depend on the Flutter module:

    dependencies {
      debugImplementation 'com.example.flutter_module:flutter_debug:1.0'
      profileImplementation 'com.example.flutter_module:flutter_profile:1.0'
      releaseImplementation 'com.example.flutter_module:flutter_release:1.0'
    }


  4. Add the `profile` build type:

    android {
      buildTypes {
        profile {
          initWith debug
        }
      }
    }

To learn more, visit https://flutter.dev/to/integrate-android-archive

Flutterモジュールの依存関係を追加 【Androidアプリ側】

以下の依存関係を追加します。

  • maven("https://storage.googleapis.com/download.flutter.io")
    Dart/Flutterランタイムや関連する依存関係を取得するために必要です。
  • maven(url = "../../flutter_module/build/host/outputs/repo")
    Flutterモジュールを依存関係に追加して、Androidアプリ内から使用できるようにします。
settings.gradle

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
        maven("https://storage.googleapis.com/download.flutter.io") ←追加
        maven(url = "../../flutter_module/build/host/outputs/repo") ←追加
    }
}

Flutter画面のActivityを定義【Androidアプリ側】

AndroidアプリにFlutter画面を表示するために、画面(Activity)をAndroidManifest.xmlに定義します。

        <activity
            android:name="io.flutter.embedding.android.FlutterActivity"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:theme="@style/LaunchTheme"
            android:windowSoftInputMode="adjustResize" />

@style/LaunchThemeのFlutterActivityの起動時のテーマを作成しておきます。

res/values/style.xml

<resources>

    <style name="LaunchTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="android:windowBackground">@android:color/white</item>
    </style>
</resources>

AndroidアプリからFlutter画面を呼び出す【Androidアプリ側】

画面内でcontext.startActivityを実行することで、Flutter画面をActivityとして呼び出すことができます。

@Composable
fun PageOnFlutterButton() {
    val context = LocalContext.current
    Button(onClick = {
        // 遷移処理
        context.startActivity(
            FlutterActivity
                .withNewEngine()
                .initialRoute("/")
                .build(this)
        )
    }) {
        Text("Flutterで作成された画面")
    }
}

画面収録 2024-12-22 12.37.20.gif

※(任意)アプリ内で1つのFlutterEngineを使用する

上記のようにFlutterEngineを呼び出すと、毎回FlutterEngineを作成されます。
これにより、リソース不足が発生する原因になったり、Flutter画面を表示するときに初回表示が遅くなります。
これらを解消するために、1つのFlutterEngineをキャッシュ内に保持しておいて、使い回すようにします。

// FlutterEngineのキャッシュキー
const val flutterEngineId = "flutter_engine_id"

class MainActivity : ComponentActivity() {
    private lateinit var flutterEngine: FlutterEngine

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // 毎回FlutterEngineが作られることで、リソースが無駄・起動が遅くになるのでFlutterEngineをキャッシュに保持
        // Flutterエンジンを事前にインスタンス化
        flutterEngine = FlutterEngine(this)

        // FlutterEngineに初期ルートを設定
        flutterEngine.navigationChannel.setInitialRoute("/");

        // FlutterEngineの初期化
        flutterEngine.dartExecutor.executeDartEntrypoint(
            DartExecutor.DartEntrypoint.createDefault()
        )

        // FlutterEngineを保持して使いまわせるようにする
        FlutterEngineCache
            .getInstance()
            .put(flutterEngineId, flutterEngine)
            
        setContent {...}
省略..
}

@Composable
fun PageOnFlutterButton() {
    val context = LocalContext.current
    Button(onClick = {
        // 遷移処理
        context.startActivity(
            FlutterActivity
                .withCachedEngine(flutterEngineId)
                .build(context)
        )
    }) {
        Text("Flutterで作成された画面")
    }
}

まとめ

Add-to-appを使用して、Android(kotlin)のネイティブアプリから、Flutterで開発した画面を呼び出すことができました。実際に試してみた感想としては、結構簡単にFlutterで開発された画面を呼び出すことができました。
「ネイティブアプリをFlutterで一部分リプレース→すべてリプレース」や「ネイティブアプリの新機能・画面をFlutter開発」に活用できると思いました。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?