Skip to content
James Allen edited this page Feb 26, 2025 · 12 revisions

Notes for contributors

This plugin was designed as a federated plugin to make it easier to support multiple versions of Unity. Instead of having confusing branching code for different versions of Unity, and cases where fixing a problem with one version of Unity will break another, the idea is to create separate sub-packages which can be maintained and updated separately.

This plugin is organised as follows:

  • flutter_embed_unity: this is the main app-facing package, which you should depend on in your Flutter app's pubspec.yaml
  • flutter_embed_unity_2022_3_android: the Android platform package for this plugin, supporting Unity 2022.3 LTS
  • flutter_embed_unity_2022_3_ios: the iOS platform package for this plugin, supporting Unity 2022.3 LTS
  • flutter_embed_unity_platform_interface: The package that glues the app-facing package to the platform package(s). This package declares an interface that any platform package must implement to support the app-facing package. Having a single package that defines this interface ensures that all platform packages implement the same functionality in a uniform way.
  • example_unity_2022_3_project: a Unity project for building into the example project, for demo and development purposes

For example, if you wanted to support Unity 2019.4 on Android, you would create a separate package called flutter_embed_unity_2019_4_android by copying the flutter_embed_unity_2022_3_android and making changes as required (it would be best to keep the structure as similar as possible to help maintainability by other contributors).

A separate example Unity project for that version of Unity should also be created at the root of the repository to be used in the example app.

The pubspec.yaml for the public facing package defines which packages are currently the default 'endorsed' package, for example:

flutter:
  plugin:
    platforms:
      android:
        default_package: flutter_embed_unity_2022_3_android
      ios:
        default_package: flutter_embed_unity_2022_3_ios

The user can choose to use flutter_embed_unity_2019_4_android instead by depending on it directly it in their app's pubspec.yaml. Alternatively, for newer LTS versions of Unity, the default endorsed package could be updated as a breaking change.

Running the examples

The best way to get started with development is to run the example first.

There are 3 example projects: one for the app-facing package, which will run on both iOS and Android (flutter_embed_unity/flutter_embed_unity/example), one for the Android platform implementation package (flutter_embed_unity/flutter_embed_unity_2022_3_android/example) and one for the iOS platform implementation package (flutter_embed_unity/flutter_embed_unity_2022_3_ios/example).

If you just want to test the example, use the public facing package example.

If you want to do development on the plugin, you should use the platform implementation examples, because this will allow you to more easily edit the source code in Android Studio / xcode etc. with code completion, compiler warnings etc.

Preparing the examples for plugin development

  • Checkout the repository
  • Create a folder called unityLibrary at:
    • flutter_embed_unity/flutter_embed_unity_2022_3_android/example/android/unityLibrary for Android or
    • flutter_embed_unity/flutter_embed_unity_2022_3_ios/example/ios/unityLibrary for iOS
  • Install Unity 2022.3 LTS
  • Open the example Unity project
  • In Unity, go to Flutter Embed -> Export project to Flutter app (iOS or Android), and choose the export folder you created earlier
  • For iOS: In project navigator, expand the Unity-iPhone project, and select the Data folder. In the Inspector, under Target Membership, change the target membership to UnityFramework. Then select the Unity-iPhone project, then in the editor select the Unity-iPhone project under PROJECTS, then select the Build Settings tab. In the Build Settings tab, find the 'Other linker Flags' setting (you can use the search box to help you find it). Add the following : '-Wl,-U,_FlutterEmbedUnityIos_sendToFlutter'

(Android only) Choose a combination of Gradle, AGP, JDK and Android Studio which works for you

Unity 2022.3 is now relatively old, and the Android library project which is produced by the Unity 'export as a library' function relies on versions of Gradle, AGP and JDK which are gradually becoming obsolete. Therefore the example project may not build with your version of Android Studio out of the box due to some of the following issues:

  • Android Studio 2024 / Ladybug now bundles Java 21 as the built-in JDK
  • Flutter by default uses the JDK which is bundled with Android Studio.
  • Java 21 requires gradle 8.4 or later
  • Later versions of Gradle are more picky about the version of NDK which is used, and Unity 2022.3 by default uses NDK 23.1.7779620 which comes bundled in Unity (see flutter_embed_unity/example/android/unityLibrary/build.gradle)
  • Different versions of Flutter have different expectations on what versions of Gradle and AGP are used (you may notice warnings when compiling)

The solution to what combination of versions you use will depend on what constraints and dependencies you have in your own development environment. The following are some pieces of advice to help you resolve these problems.

Changing the version of Gradle and AGP to fit your version of Flutter

The version of com.android.application in flutter_embed_unity/example/android/settings.gradle is the version of AGP (Android Gradle Plugin) used by the example project. The version of the distributionUrl in flutter_embed_unity/example/android/gradle/wrapper/gradle-wrapper.properties is the version of Gradle used. You can amend these as required. Use the compatibility matrix to figure out which versions of Gradle and AGP are compatible with each other.

The following are the versions of Gradle and AGP which the flutter create command uses in new projects, for different versions of Flutter. These should be considered the recommended versions to use:

Flutter 3.29.0

distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip
plugins {
    id("com.android.application") version "8.7.0" apply false
    id("org.jetbrains.kotlin.android") version "1.8.22" apply false
}

Flutter 3.24.5 & 3.27.4

distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip
plugins {
    id "com.android.application" version "8.1.0" apply false
    id "org.jetbrains.kotlin.android" version "1.8.22" apply false
}

Flutter 3.22.3 & 3.19.6

distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip
plugins {
    id "com.android.application" version "7.3.0" apply false
    id "org.jetbrains.kotlin.android" version "1.7.10" apply false
}

Flutter 3.16.9

distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
ext.kotlin_version = '1.7.10'
plugins {
    id "com.android.application" version "7.3.0" apply false
}

Downgrading the version of JDK used by Android Studio

You can downgrade later versions of Android Studio to use earlier versions of JDK, which support earlier versions of Gradle and may resolve build errors. For example, Android Studio Ladybug by default uses JDK 21, which will not build the example projects because it requires Gradle 8.4. You can change this to use JDK 17 instead, which still supports the versions of Gradle used in the example project.

  • download the latest version of OpenJDK 17.x (on a Mac, it's recommended to download to /Library/Java/JavaVirtualMachines/, which is where Android Studio will look for external JDKs)
  • tell Flutter to use the JDK using flutter config --jdk-dir - for example if you have downloaded to /Library/Java/JavaVirtualMachines/, use flutter config --jdk-dir /Library/Java/JavaVirtualMachines/jdk-17.0.2.jdk/Contents/Home
  • set JAVA_HOME to the same location
  • open the Android sub-project, go to All Settings -> Build, Execution & Deployment -> Build Tools -> Gradle, change the android Gradle project to use the new Gradle JDK)

Upgrading the Unity project to use JDK 21 / Gradle 8.4

Alternatively if you prefer to use JDK 21 with the latest Android Studio, you need to take some steps to get it to work:

  1. Use versions of flutter which support later versions of Gradle, for example Flutter 3.29, and update example/android/settings.gradle and example/android/gradle/wrapper/gradle-wrapper.properties to use suitable versions of Gradle and AGP:
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip
plugins {
    id("com.android.application") version "8.7.0" apply false
    id("org.jetbrains.kotlin.android") version "1.8.22" apply false
}
  1. Set ndkVersion = "27.0.12077973" in example/android/app/build.gradle
  2. Remove ndkPath from example/android/app/unityLibrary/build.gradle which points to the older NDK embedded with Unity and instead set ndkVersion = "27.0.12077973"
  3. Install NDK 27.0.12077973 using Android Studio SDK manager
  4. Update source compatibility targets in example/app/android/build.gradle:
compileOptions {
      sourceCompatibility JavaVersion.VERSION_17
      targetCompatibility JavaVersion.VERSION_17
}
 
kotlinOptions {
    jvmTarget = JavaVersion.VERSION_17
}
  1. Add namespace 'com.UnityTechnologies.XR.Manifest' to example/app/android/unityLibrary/xrmanifest.androidlib/build.gradle

Changing dependencies to local paths

The examples can now be run. However, any local changes you make to plugin source code will not be picked up by the examples, because they depend on published versions. Therefore if you want to work on the plugin and use the examples to test changes, you need to change the dependencies to local paths.

For Android, change flutter_embed_unity/flutter_embed_unity_2022_3_android/example/pubspec.yaml:

dependencies:
  ...
  flutter_embed_unity_2022_3_android: 
    path: ../
  flutter_embed_unity:
    path: ../../flutter_embed_unity

For iOS, change flutter_embed_unity/flutter_embed_unity_2022_3_ios/example/pubspec.yaml:

dependencies:
  ...
  flutter_embed_unity_2022_3_ios: 
    path: ../
  flutter_embed_unity:
    path: ../../flutter_embed_unity

Now to resolve a dependency conflict with local paths and published versions you also need to change flutter_embed_unity/flutter_embed_unity/pubspec.yaml:

dependencies:
  ...
  flutter_embed_unity_2022_3_android: 
    path: ../flutter_embed_unity_2022_3_android
  flutter_embed_unity_2022_3_ios:
    path: ../flutter_embed_unity_2022_3_ios

Develop the plugin

Now you should be able to develop the plugin platform source code, and use the platform example projects to test your changes.

On Android Studio, open flutter_embed_unity/flutter_embed_unity_2022_3_android Flutter project, then open one of the Kotlin files under the android subfolder, then click on "Open for editing in Android Studio" at the top right (or go to Tools -> Flutter -> Open for editing in Android Studio). This should open the Android subproject, with the example project also in the source navigator so it can compile correctly.

In Xcode, open flutter_embed_unity/flutter_embed_unity_2022_3_ios/example/ios/Runner.xcworkspace, then in the project navigator go to Pods/Development Pods/hello/../../example/ios/.symlinks/plugins/flutter_embed_unity to find the plugin Swift files.

Developer notes

Unity classes .jar

The Android package contains a JAR file in flutter_embed_unity\flutter_embed_unity_2022_3_android\android\libs containing bits of Unity code for Android such as the UnityPlayer which basically runs Unity. This was copied from the Unity editor install folder, for example at Unity Editors\2022.3.7f1\Editor\Data\PlaybackEngines\AndroidPlayer\Variations\il2cpp\Release\Classes, and renamed to make it clear which version of Unity it is from.

It is included in the Android plugin project via a dependency in flutter_embed_unity\flutter_embed_unity_2022_3_android\android\build.gradle:

compileOnly fileTree(dir: 'libs', include: ['*.jar'])

To develop for a different version of Unity, make sure to replace this file!

UnityFramework.framework

The iOS package contains a framework file at flutter_embed_unity\flutter_embed_unity_2022_3_ios\ios\UnityFramework.framework, which is required for the plugin to build and for import UnityFramework to work. Without this you will get No such module UnityFramework. It was extracted from the
DerivedData folder after building the launcher Xcode project which Unity exports.

It is included into the iOS plugin build by adding this to flutter_embed_unity_2022_3_ios/ios/flutter_embed_unity_ios.podspec:

# Add UnityFramework
s.vendored_frameworks = 'UnityFramework.framework'

Then run pod install from flutter_embed_unity_2022_3_ios/example/iOS

To develop for a different version of Unity, make sure to rebuild UnityFramework!

-Wl,-U,_FlutterEmbedUnityIos_sendToFlutter

This is a slight hack borrowed from https://pub.dev/packages/flutter_unity which allows us to easily send messages from Unity to Flutter without having to mess around with manually editing Unity files.

-Wl allows us to pass additional options to the linker (ld) when it is invoked -U tells the linker to force the symbol _FlutterEmbedUnityIos_sendToFlutter to be entered in the output file as an undefined symbol. It will be linked instead to a C function defined in flutter_embed_unity_2022_3_ios/ios/Classes/SendToFlutter.swift

See the linker documentation for more

Resources

The following may be useful for plugin developers