Open Source

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).

v2.0.222.6k / month21 likesMITUnity 2022.3 LTS → Unity 6

API reference

Class signatures, parameters and code examples for the Dart API: UnityView, UnityBridge, UnityMessage, UnityConfig and friends.

source: unity_kit/doc/api.md

Widget

UnityView

UnityView is the widget entry point. It embeds the native Unity player as a platform view and surfaces the bridge streams as callbacks. The bridge parameter is optional: when omitted the widget creates and owns an internal UnityBridgeImpl; when you pass one, you own it and the widget will never dispose an external bridge (it survives the widget).

Embedding Unitydart
UnityView(
  config: const UnityConfig(sceneName: 'GameScene'),
  placeholder: const Center(child: CircularProgressIndicator()),
  onReady: (bridge) {
    bridge.send(UnityMessage.command('StartGame'));
  },
  onMessage: (message) {
    if (message.type == 'score_updated') {
      // handle score
    }
  },
)
ParameterTypeDescription
bridgeUnityBridge?External bridge. When null, one is created internally from UnityKitPlatform.instance; an external bridge is never disposed by the widget.
configUnityConfigConfiguration (defaults to const UnityConfig()). Controls scene, fullscreen, status bar, frame rate, view mode and AR.
placeholderWidget?Overlay shown on top of the platform view until the bridge emits UnityLifecycleState.ready.
onReadyvoid Function(UnityBridge bridge)?Called once when the Unity player is ready to receive messages.
onMessagevoid Function(UnityMessage message)?Called for every message received from Unity.
onEventvoid Function(UnityEvent event)?Called for every lifecycle event emitted by the Unity player.
onSceneLoadedvoid Function(SceneInfo scene)?Called when a Unity scene finishes loading.
gestureRecognizersSet<Factory<OneSequenceGestureRecognizer>>?Passed through to the underlying AndroidView / UiKitView so the host app can intercept touches before they reach Unity.

Supported platforms

Production platforms are Android and iOS only. On desktop the view renders a “Platform not supported” placeholder, and while a web (WebGL) path exists via HtmlElementView it is not production-ready. iOS builds run on a physical device only — never the Simulator.

Bridge & messaging

UnityBridge

UnityBridge is the abstract Flutter ⟷ Unity interface — typed messaging, lifecycle control and event streams. UnityBridgeImpl is the default implementation created for you by UnityView when you do not supply your own.

UnityBridgedart
abstract class UnityBridge {
  UnityLifecycleState get currentState;
  bool get isReady;

  // Streams
  Stream<UnityMessage> get messageStream;
  Stream<UnityPerformanceStats> get performanceStream;  // 2.0.0
  Stream<UnityEvent> get eventStream;
  Stream<SceneInfo> get sceneStream;
  Stream<UnityLifecycleState> get lifecycleStream;

  // Sending (JSON)
  Future<void> send(UnityMessage message);
  Future<void> sendWhenReady(UnityMessage message);

  // Sending (binary, 2.0.0)
  Future<void> sendBinary(UnityMessage message);
  Future<void> sendBinaryWhenReady(UnityMessage message);

  // Lifecycle
  Future<void> initialize();
  Future<void> pause();
  Future<void> resume();
  Future<void> unload();
  Future<void> dispose();
}

Sending

MethodBehaviour
send(message)Sends immediately. Throws EngineNotReadyException if Unity is not ready.
sendWhenReady(message)Queues the message until Unity becomes ready; sends immediately if already ready.
sendBinary(message)Encodes with UnityBinaryCodec and delivers to Unity's ReceiveBinary entry point. Prefer this for high-frequency traffic. Throws EngineNotReadyException if not ready.
sendBinaryWhenReady(message)Queues a binary message until Unity becomes ready.

Lifecycle & state

MemberBehaviour
currentStateCurrent UnityLifecycleState of the player (getter).
isReadyWhether the player is ready to receive messages (getter).
initialize()Initialize the Unity player and begin listening for events.
pause()Pause the Unity player.
resume()Resume the Unity player from a paused state.
unload()Unload the Unity player while keeping the process alive.
dispose()Dispose all resources. The bridge cannot be reused after this.

UnityBinaryCodec (2.0.0)

A compact, length-prefixed binary frame for UnityMessage, symmetric with the Unity-side UnityKitBinaryCodec. UnityBinaryWriter / UnityBinaryReader hand-pack typed payloads with writeInt32, writeInt64, writeFloat64, writeBool, writeString (and matching read*).

dart
Uint8List bytes = UnityBinaryCodec.encode(UnityMessage.command('Move', {'x': 1}));
UnityMessage msg = UnityBinaryCodec.decode(bytes);
bool ok           = UnityBinaryCodec.isBinaryFrame(bytes);

Messaging

UnityMessage

A typed message exchanged between Flutter and Unity. By default a message targets FlutterBridge.ReceiveMessage; the factory constructors set the type, target and payload for the common cases.

UnityMessagedart
const UnityMessage({
  required this.type,
  this.data,
  this.gameObject = 'FlutterBridge',
  this.method = 'ReceiveMessage',
});

factory UnityMessage.command(String action, [Map<String, dynamic>? data]);
factory UnityMessage.to(String gameObject, String method, [Map<String, dynamic>? data]);
factory UnityMessage.routed(String target, String method, [Map<String, dynamic>? data]);
factory UnityMessage.fromJson(String jsonString);

Fields

FieldTypeDefaultDescription
typeStringrequiredMessage type identifier (e.g. 'LoadScene', 'scene_loaded').
dataMap<String, dynamic>?nullOptional payload data.
gameObjectString'FlutterBridge'Target Unity GameObject name.
methodString'ReceiveMessage'Target method name on the GameObject.
nativeGameObjectString— (getter)Native target for UnitySendMessage; defaults to gameObject. Routed messages override it to 'FlutterBridge'.
nativeMethodString— (getter)Native method for UnitySendMessage; defaults to method. Routed messages override it to 'ReceiveMessage'.

Factory constructors

ConstructorPurpose
UnityMessage.command(action, [data])A command to Unity. type is set to action, delivered to FlutterBridge.ReceiveMessage.
UnityMessage.to(gameObject, method, [data])Targets a specific Unity GameObject and method directly via UnitySendMessage.
UnityMessage.routed(target, method, [data])Routes through FlutterBridge's MessageRouter to a registered handler (e.g. FlutterAddressablesManager) instead of a standalone GameObject.
UnityMessage.fromJson(jsonString)Parses a JSON string received from Unity. Throws FormatException if the JSON is invalid or has no 'type' field.
Usagedart
// Command to Unity
final msg = UnityMessage.command('LoadScene', {'name': 'Level1'});

// Event from Unity
final event = UnityMessage.fromJson('{"type":"scene_loaded","data":"Level1"}');

Configuration

UnityConfig & AR

UnityConfig controls how the Unity player is created. toCreationParams() is the single source of truth for the Dart → native contract — it carries every option plus arMode.wireName to the Android, iOS, web and desktop hosts.

UnityConfigdart
const UnityConfig({
  this.sceneName = 'MainScene',
  this.fullscreen = false,
  this.unloadOnDispose = true,
  this.hideStatusBar = false,
  this.runImmediately = true,
  this.targetFrameRate = 60,
  this.platformViewMode = PlatformViewMode.hybridComposition,
  this.transparentBackground = false,
  this.arMode = UnityArMode.none,
});

factory UnityConfig.fullscreen({String sceneName = 'MainScene', bool transparentBackground = false});
factory UnityConfig.embedded({String sceneName = 'MainScene', bool transparentBackground = false});
factory UnityConfig.ar({String sceneName = 'MainScene', UnityArMode mode = UnityArMode.overlay});

enum UnityArMode { none, passthrough, overlay }

Options

OptionTypeDefaultDescription
sceneNameString'MainScene'Name of the Unity scene to load on initialization.
fullscreenboolfalseWhether Unity should render in fullscreen mode.
unloadOnDisposebooltrueWhether to unload Unity when the widget is disposed.
hideStatusBarboolfalseWhether to hide the system status bar.
runImmediatelybooltrueWhether to start the Unity player immediately on creation.
targetFrameRateint60Target frame rate for Unity rendering.
platformViewModePlatformViewModehybridCompositionPlatform view rendering mode (Android only).
transparentBackgroundboolfalseMarks the native view non-opaque so Flutter content behind Unity shows through. iOS-only for now.
arModeUnityArModenoneAR Foundation rendering mode.

UnityArMode (2.0.0)

ModeMeaning
noneNo AR. Unity renders normally against its own background.
passthroughThe device camera feed is rendered by Unity behind the scene — for fully Unity-driven AR.
overlayAR session active, Unity clears to a transparent background so the camera feed (or Flutter content) shows through. Pair with transparentBackground.

transparentBackground is iOS-only

UnityConfig.ar() defaults to UnityArMode.overlay with a transparent background. On Android the transparentBackground flag is ignored and a warning is logged — it is an iOS-only feature today.

Reactive API

Streams & payload types

The bridge exposes five broadcast streams. Subscribe to the ones you need; each emits a strongly-typed payload.

dart
Stream<UnityMessage> get messageStream;
Stream<UnityPerformanceStats> get performanceStream;  // 2.0.0
Stream<UnityEvent> get eventStream;
Stream<SceneInfo> get sceneStream;
Stream<UnityLifecycleState> get lifecycleStream;
StreamEmitsWhen
messageStreamUnityMessageEvery message received from Unity.
performanceStreamUnityPerformanceStatsWhen the Unity-side UnityKitPerformanceMonitor pushes frame stats (2.0.0).
eventStreamUnityEventLifecycle events emitted by the Unity player.
sceneStreamSceneInfoScene load / unload events.
lifecycleStreamUnityLifecycleStateLifecycle state changes.

SceneInfo

dart
class SceneInfo {
  final String name;
  final int buildIndex;   // -1 if unknown
  final bool isLoaded;
  final bool isValid;
  final Map<String, dynamic>? metadata;
}

UnityPerformanceStats (2.0.0)

dart
class UnityPerformanceStats {
  final double fps;
  final double frameTimeMs;
  final double usedMemoryMb;
  final int drawCalls;
  final int triangles;
}

UnityEvent

dart
class UnityEvent {
  final UnityEventType type;
  final DateTime timestamp;
  final String? message;
  final String? error;
}

UnityEventType values: created, loaded, paused, resumed, unloaded, destroyed, error, message, sceneLoaded.

Lifecycle

Lifecycle state machine

UnityLifecycleState is a strict state machine. Only the transitions below are valid; isActive is true for ready and resumed, and canSend mirrors isActive.

dart
enum UnityLifecycleState {
  uninitialized,
  initializing,
  ready,
  paused,
  resumed,
  disposed,
}
StateValid transitions to
uninitializedinitializing
initializingready, disposed
readypaused, disposed
pausedresumed, disposed
resumedpaused, disposed
disposedTerminal — no further transitions.

Errors

Exceptions

Every typed error extends UnityKitException, which carries a message, an optional cause and a stackTrace.

ExceptionExtendsThrown when
UnityKitExceptionimplements ExceptionBase for all unity_kit errors.
EngineNotReadyExceptionUnityKitExceptionA message is sent before Unity is ready. Message: “Unity engine is not ready. Call initialize() first or use sendWhenReady().”
BridgeExceptionUnityKitExceptionBridge communication fails, e.g. the bridge is used after dispose().
CommunicationExceptionUnityKitExceptionA message cannot be delivered to Unity. Carries target, method and data.
LifecycleExceptionUnityKitExceptionAn invalid lifecycle transition is attempted. Carries currentState and attemptedAction.

Asset streaming

Streaming module

StreamingController orchestrates manifest fetching, downloading, caching and Unity communication. This is the summary — see the Asset streaming guide for the full CDN / cache / Addressables walkthrough.

StreamingControllerdart
StreamingController({
  required UnityBridge bridge,
  required String manifestUrl,
  UnityAssetLoader? assetLoader,  // defaults to UnityAddressablesLoader
  http.Client? httpClient,
  CacheManager? cacheManager,
})
Property / MethodDescription
assetLoaderThe loader strategy in use.
stateCurrent StreamingState.
downloadProgressStream of DownloadProgress.
errorsStream of StreamingError.
stateChangesStream of StreamingState.
initialize()Fetch manifest, init cache, notify Unity.
preloadContent({bundles, strategy})Download base bundles.
loadBundle(name)Download + tell Unity to load.
loadScene(name, {loadMode})Download + tell Unity to load the scene.
getCachedBundles()List cached bundle names.
isBundleCached(name)Check whether a bundle is cached.
getCacheSize()Total cache size in bytes.
clearCache()Delete all cached content.
dispose()Release all resources.

Two loaders swap behind the same controller: UnityAddressablesLoader (default) targets FlutterAddressablesManager, and UnityBundleLoader targets FlutterAssetBundleManager for raw AssetBundles.

Unity side

C# attribute dispatch

On the Unity side you expose C# to Flutter by name. Annotate methods with [UnityKitMethod] and register the instance with MessageRouter.RegisterMethods — no manual switch. From Flutter, route to it with UnityMessage.routed('player', 'Jump').

[UnityKitMethod] dispatchcsharp
public class PlayerController : MonoBehaviour
{
    [UnityKitMethod]               // callable as "Jump"
    public void Jump() { /* ... */ }

    [UnityKitMethod("move")]       // callable as "move"
    public void Move(MovePayload p) { /* p deserialized from JSON */ }

    void OnEnable()  => MessageRouter.RegisterMethods("player", this);
    void OnDisable() => MessageRouter.Unregister("player");
}

For a message-per-object handler, extend FlutterMonoBehaviour. It auto-registers with MessageRouter on enable, overrides OnFlutterMessage for inbound messages, and sends outbound with SendToFlutter / SendToFlutterBatched.

FlutterMonoBehaviourcsharp
using UnityKit;

public class EnemyManager : FlutterMonoBehaviour
{
    protected override void OnFlutterMessage(string method, string data)
    {
        switch (method)
        {
            case "SpawnWave":
                // parse data, spawn enemies
                break;
            case "Reset":
                // reset game state
                break;
        }
    }

    private void OnWaveCleared()
    {
        // Direct send
        SendToFlutter("wave_cleared", "{\"wave\": 3}");

        // Batched send (via MessageBatcher component on FlutterBridge)
        SendToFlutterBatched("score_updated", "{\"score\": 1500}");
    }
}
C# typeRole
FlutterMonoBehaviourBase MonoBehaviour that auto-registers with MessageRouter. Override OnFlutterMessage(method, data); send with SendToFlutter(type, data) / SendToFlutterBatched(type, data).
MessageRouterStatic registry routing FlutterBridge.ReceiveMessage() to handlers by target name. Register / Unregister / RegisterMethods(target, instance).
NativeAPILow-level native bridge, NativeAPI.SendToFlutter(json). Prefer FlutterMonoBehaviour.SendToFlutter().
MessageBatcherBatches outgoing Unity → Flutter messages per frame; sent as a JSON array in LateUpdate().
SceneTrackerAttach alongside FlutterBridge to notify Flutter on scene load / unload. No configuration.
UnityKitGameManagerHandles LoadScene / UnloadScene / SetTargetFrameRate / PauseGame / ResumeGame and consumes the native __unitykit_init message.
UnityKitPerformanceMonitorSamples FPS / frame time / memory and streams them to bridge.performanceStream.

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.

Codigee
We are using cookies. Learn more