Flutter package
unity_kit
Embed Unity 3D in Flutter with a typed, testable bridge — JSON or binary protocol, an enforced lifecycle, AR Foundation, and CDN asset streaming. Runs on Android and iOS (web/WebGL experimental).
FAQ & troubleshooting
Common problems with concrete fixes — black screens, missing messages, ProGuard stripping, simulator limits and platform quirks.
source: doc/faq.mdRendering
Rendering problems
Black screens, blank views and z-ordering. unity_kit auto-handles most of these; here is what to check when it doesn't. One of them traces back to a real GitHub issue — linked inline.
Black screen after Unity loads (Android)
Unity needs an explicit refocus after being embedded in a Flutter platform view — windowFocusChanged(true) → pause() → resume(). unity_kit does this automatically in UnityKitViewController.
- 1Make sure you're using the latest unity_kit version.
- 2Check that UnityPlayerManager successfully created the player (check logs).
- 3Verify the platform view mode is AndroidView (Virtual Display), not PlatformViewLink (Hybrid Composition — causes app freeze).
White / blank screen (iOS)
Most common cause: Unity's Metal renderer creates textures matching the view size. Initialized before the view has layout, it gets 0×0 and crashes (MTLTextureDescriptor has width of zero). unity_kit prevents this with waitForNonZeroFrame(), which polls until the container view has non-zero bounds.
- 1Make sure UnityView has a non-zero size in the layout (not hidden, not zero-height).
- 2Check that UnityFramework.framework loaded successfully (iOS logs).
- 3Check for onError events on the Dart side.
After a scene change, Unity's window level might reset — unity_kit sets it below Flutter's (UIWindow.Level.normal − 1).
Unity renders on top of Flutter widgets (Android)
GitHub issue #1A known platform issue, especially on Android < 10, where SurfaceView z-ordering is unreliable. Reported from a BottomNavigationBar tab / IndexedStack where Unity covered everything — a real rendering bug fixed in unity_kit 0.9.2.
- 1Upgrade the package to unity_kit ≥ 0.9.2.
- 2Use Android 10+ if possible.
- 3Use AndroidView (Virtual Display) mode — Unity renders into a virtual display that Flutter composites.
Frozen rendering after orientation change (Android)
The UnityContainerLayout overrides onWindowVisibilityChanged(VISIBLE) to call pause() + resume(), which restarts the rendering pipeline.
If you're using a custom container, add this pattern.
Touch doesn't work (Android)
Two common causes, both patched by unity_kit's UnityContainerLayout.
- 1Missing source type: Unity's New Input System requires InputDevice.SOURCE_TOUCHSCREEN — unity_kit sets this automatically.
- 2DeviceId = 0: Flutter's Virtual Display sends events with deviceId = 0, which Unity ignores — unity_kit patches this.
If you use a custom touch handler, make sure both fixes are applied.
Multi-touch doesn't work (iOS)
unity_kit forwards touch events via UIKit. Check that gesture recognizers on the Unity view are configured.
With the old Input Manager multi-touch should work; with the New Input System, verify the EnhancedTouch module is enabled.
Messaging
Messaging problems
The Flutter ⟷ Unity bridge. Most breakages come from the target not being wired, the native path being stripped, or messages sent before the engine is ready.
Messages not reaching Unity from Flutter
The bridge target isn't wired up on the Unity side.
- 1FlutterBridge GameObject must exist in the first loaded scene (auto-creates via [RuntimeInitializeOnLoadMethod], but manual placement is recommended).
- 2sendReadyOnStart must be enabled on the FlutterBridge component.
- 3Check the target name matches — FlutterMonoBehaviour uses GameObject.name by default (or the custom Target Name field).
- 4Check the Unity Console for: [UnityKit] No handler registered for target: xxx.
Messages not reaching Flutter from Unity
The native send path is broken or stripped from the build.
- 1Use NativeAPI.SendToFlutter() (not Debug.Log).
- 2Android: verify com.unity_kit.FlutterBridgeRegistry is not stripped by ProGuard (see Build & release).
- 3iOS: verify UnityKitNativeBridge.mm is in Assets/Plugins/iOS/ and compiled into UnityFramework.
- 4Editor: messages go to Console only — there is no Flutter connection in Play Mode.
Messages sent before Unity is ready are lost
Plain bridge.send() throws EngineNotReadyException if Unity isn't ready. Use bridge.sendWhenReady() — the readiness guard queues messages and auto-flushes when Unity reports ready.
// This throws EngineNotReadyException if Unity isn't ready:
bridge.send(message);
// This queues and sends automatically when ready:
bridge.sendWhenReady(message);Communication breaks after re-navigation
GitHub issue #4unity_kit uses a bridge independent of the widget, so — unlike flutter_unity_widget, where the controller dies with the widget — the bridge survives navigation. Create it once in a service layer and pass it in.
// Create the bridge once (e.g. in a service/provider)
final bridge = UnityBridgeImpl(platform: UnityKitPlatform.instance);
// Pass it to the widget - the widget does NOT dispose it
UnityView(bridge: bridge, ...);If you let the widget create its own bridge (bridge: null), it's disposed when the widget is removed. A related crash — MissingPluginException on re-opening a Unity screen, where the destroyed platform view left Dart targeting the dead com.unity_kit/unity_view_N channel — was fixed in unity_kit 2.0.1: native now emits onViewDisposed, the bridge resets to initializing, sendWhenReady() queues until the next UnityView attaches, and send() throws a typed EngineNotReadyException instead of crashing. Upgrade to ≥ 2.0.1.
Build & release
Build & release problems
Export, ProGuard/R8, IL2CPP stripping and Unity-version differences. Build.cs automates most of the wiring during export — when a build fails, start by re-exporting with the latest Build.cs.
No "Flutter" menu in Unity Editor
Assets/Scripts/UnityKit/Editor/Build.cs is missing or has compilation errors. Editor scripts must live inside an Editor/ folder.
Check the Unity Console for errors.
Android: ClassNotFoundException for com.unity_kit.* in release builds
ProGuard/R8 is stripping classes used via reflection and JNI. Build.cs auto-adds ProGuard rules during export — verify proguard-unity.txt exists in unityLibrary/ and contains the keep rules.
-keep class com.unity_kit.** { *; }
-keep class com.unity3d.player.** { *; }This is critical — without it, release builds silently fail (no crash, just non-functional).
Android: Gradle / Kotlin / JVM version mismatch
Every new Flutter version can change the Gradle/Kotlin/JVM requirements. Build.cs patches build.gradle automatically during export.
- 1Re-export from Unity (Flutter > Export Android).
- 2Make sure Build.cs is up-to-date (latest from unity_kit/unity/).
- 3Flutter 3.29+ uses Kotlin DSL (build.gradle.kts) — Build.cs handles both.
iOS: "No such module 'UnityFramework'"
UnityFramework.framework is not in the expected location.
- 1The iOS export completed successfully (Flutter > Export iOS).
- 2Builds/ios/UnityLibrary/ contains the Xcode project.
- 3For local dev: create a symlink from unity_kit/ios/ to the built framework.
iOS: "Undefined symbol: _SendMessageToFlutter"
The UnityKitNativeBridge.mm file is not compiled into UnityFramework. It must be in Assets/Plugins/iOS/ before the Unity export — Unity's build system automatically includes files from that folder.
IL2CPP stripping removes my serializable classes
Add your custom [Serializable] classes to link.xml. unity_kit's own link.xml already preserves UnityKit DTOs.
<linker>
<assembly fullname="Assembly-CSharp">
<type fullname="YourNamespace.YourClass" preserve="all"/>
</assembly>
</linker>NamedBuildTarget / Il2CppCodeGeneration compilation errors
These APIs exist only in Unity 6000+. Build.cs uses BuildTargetGroup instead (compatible with Unity 2022.3+). Make sure you have the latest Build.cs from unity_kit/unity/Assets/Scripts/UnityKit/Editor/.
Unity 6 constructor & view differences
Unity 6 renamed the player class and changed view extraction. unity_kit handles both automatically via reflection — no manual configuration needed.
- 1Constructor: 2022.3 uses UnityPlayer(Activity); 6000+ uses UnityPlayerForActivityOrService(Context, IUnityPlayerLifecycleEvents?). unity_kit tries both.
- 2View extraction: Unity 6's player is not a View — unity_kit calls getFrameLayout(), falling back through getView(), getPlayerView(), getSurfaceView(), getRootView(), then legacy.
Asset streaming
Asset streaming problems
Addressables loaded from a CDN cache. Almost every failure here is a cache-path, addressable-key or rebuild issue.
Addressable asset not found / toy_error
Unity can't resolve the addressable at runtime.
- 1SetCachePath must be called first — Flutter must tell Unity where the downloaded bundles are before any load.
- 2Verify the addressable key matches between Flutter and Unity (what Flutter sends must match the 'Address' field in Unity's Addressables Groups window).
- 3Check that the bundle was actually downloaded to the cache path (verify the file exists).
- 4Rebuild Addressables after any asset changes: Flutter > Build Addressables.
content_manifest.json missing addressableKeys
AddressablesManifestBuilder generates metadata.addressableKeys by matching Addressable group entries to built bundle filenames.
- 1Rebuild: Flutter > Build Addressables.
- 2Check that the asset has an address set in the Addressables Groups window.
- 3Check that the asset's group has Remote build/load paths.
Bundles download but Unity can't load them
The TransformInternalId callback in FlutterAddressablesManager intercepts Unity's remote URLs and redirects to the local cache.
- 1SetCachePath was called with the correct path.
- 2The bundle filename in cache matches what Unity expects.
- 3The catalog was loaded (LoadContentCatalog or UpdateCatalog).
Platform limits
Platform limits & lifecycle
These are Unity-as-a-Library platform limitations, not unity_kit bugs. Design around them rather than fighting them.
One Unity instance per process
Unity as a Library allows a single shared Unity player per process. UnityPlayerManager.shared survives navigation by design — keep one instance alive and switch scenes rather than mounting two Unity views.
Unity can't be restarted after quit()
This is a Unity limitation, not a unity_kit bug — quit() kills the process and the player cannot restart. Use pause() / resume() instead, or load an empty scene to 'reset'.
Memory keeps growing after navigation (80–180 MB retained)
Unity retains 80–180 MB even after 'unload'. This is a platform limitation.
- 1Keep Unity alive, swap scenes (don't try to destroy/recreate).
- 2Load an empty scene before disposing the widget to free scene-specific resources.
- 3Use Addressables.Release() to unload assets you no longer need.
flutter_unity_widget crashes after 2–3 navigations (issue #281, open 5+ years) because resources are never freed. unity_kit addresses this with proper dispose() chains and WeakListener patterns, but the fundamental Unity memory behavior remains.
iOS Simulator is not supported
UnityFramework.framework is built for arm64 device architecture only. Always test on physical devices — this applies even on Apple Silicon Macs (the simulator runs arm64 but may still have compatibility issues).
Android: Hybrid Composition causes app freeze
Do NOT use PlatformViewLink + initExpensiveAndroidView for the Unity view. Use AndroidView (Virtual Display) mode — Hybrid Composition conflicts with Unity's rendering pipeline.
NSLog not visible in flutter run output
flutter run only captures Debug.Log (Unity) and print() (Dart). Swift NSLog is not forwarded — for debugging, send info through the bridge.
sendEvent(name: "onUnityMessage", data: "debug:someValue=\(value)")General tips
- Always re-export after Unity changes — any change to scripts, assets or settings requires a new export, then redeploy the artifact into the Flutter project.
- FlutterBridge is DontDestroyOnLoad — it persists across scene changes, so there's no need to add it to every scene.
- Bridge ≠ Widget — create the bridge in a service layer and pass it to widgets. Don't let each widget create its own.
- Check logs — Unity logs with the [UnityKit] prefix, and Dart exceptions include the lifecycle state and context.
- Test on device, not simulator — especially on iOS, where the simulator doesn't support UnityFramework.
Help
Still stuck?
If your problem isn't listed here, open a GitHub issue (issues #1 and #4 above are examples of fixes that shipped from user reports) or check the package page on pub.dev.
Let's make something together.
If you have any questions about a new project or other inquiries, feel free to contact us. We will get back to you as soon as possible.
