FLUTTER ECOSYSTEM

erykkruk/flutter_unity_kit

Plugin Flutter para incorporar cenas 3D do Unity (Unity como biblioteca) — mensageria de ponte, gerenciamento de ciclo de vida, streaming de ativos. Disponível no pub.dev como unity_kit.

Capa do projeto unity_kit
Stars
12
Forks
6
Último push (UTC)
24 de ago. de 2026
Status do projeto
Ativo
erykkruk GitHub avatar
GITHUB User

erykkruk ↗

Codigee
LinguagensDartC#KotlinSwiftC++Objective-C++RubyCMakeHTMLCObjective-C

Tópicos técnicos

Pacotes publicados por este repositório

README original

Texto original em inglês. Visite o GitHub para a versão atual.

Expandir / recolher README

Unity Kit

pub package pub points License: MIT

A Flutter plugin for Unity 3D integration. Embed Unity content in Flutter apps with typed communication, lifecycle management, asset streaming, and support for Unity 2022.3 LTS through Unity 6.


📚 Documentation

Full hosted documentation — a complete zero-to-running walkthrough, native setup guides, API reference and troubleshooting — lives at codigee.com/open-source/unity-kit.

Guide What's in it
Step-by-step walkthrough Zero-to-running: every file, path, and edit
Unity export Configure & export for Android, iOS, WebGL
Android setup Native layer, Gradle, rendering
iOS setup UnityFramework embedding, Metal crash prevention
Content loading Scenes, prefabs, AssetBundles at runtime
API reference Class signatures & parameters
Asset streaming CDN manifest + verified cache
Architecture Bridge, lifecycle, streams
FAQ Troubleshooting common issues

How It Works

Flutter handles UI (screens, buttons, navigation). Unity handles 3D (rendering, game logic). They run in the same app but are separate worlds -- they talk by sending JSON messages through a native bridge.

Flutter (Dart)                                    Unity (C#)
  UI, screens,          JSON messages               3D rendering,
  business logic   <=========================>      game logic
                    native bridge (iOS/Android)

There are no direct method calls. Everything goes through serialized JSON over the platform bridge. Flutter never calls C# directly, and Unity never calls Dart directly.


Repository Structure

.
├── unity_kit/              # Flutter plugin package (published to pub.dev)
│   ├── lib/                # Dart source code
│   ├── android/            # Android native layer (Kotlin)
│   ├── ios/                # iOS native layer (Swift)
│   ├── unity/              # C# scripts for Unity side
│   ├── example/            # Example Flutter app
│   ├── test/               # Test suite (35 files, 640+ tests)
│   └── doc/                # API reference & asset streaming guide
├── doc/                    # Detailed guides
│   ├── unity-export.md     # Step-by-step Unity export guide
│   ├── unity_integrations.md     # Content loading methods (scenes, Addressables, etc.)
│   ├── android-integration.md    # Android native architecture & known issues
│   ├── ios-integration.md        # iOS native architecture & known issues
│   └── architecture.md           # Overall architecture overview
└── .github/workflows/      # CI/CD (auto-tag + pub.dev publish)

Key Features

Feature What it does
Typed Bridge UnityMessage objects instead of raw strings. Structured, type-safe communication.
Lifecycle Management State machine: uninitialized -> ready -> paused -> disposed. Invalid transitions throw exceptions.
Readiness Guard Messages sent before Unity is ready get queued and auto-flushed when the engine starts. Nothing is silently dropped.
Message Batching Groups rapid-fire messages into single native calls (~16ms windows).
Message Throttling Rate-limits outgoing messages with configurable strategies (drop / keepLatest / keepFirst).
Asset Streaming Download Unity bundles from CDN, cache locally with SHA-256 verification, load via Addressables.
Unity 6 Support Reflection-based player creation handles both legacy UnityPlayer and Unity 6's UnityPlayerForActivityOrService.
Environment Preflight environment() reports which runtime the build actually ships and whether its native libraries pass Google Play's 16 KB page size rule.
Platform Views Android (Virtual Display) and iOS (UiKitView composition).

Quick Start

1. Add dependency
# pubspec.yaml
dependencies:
  unity_kit: ^2.1.0
2. Minimal Flutter code
import 'package:unity_kit/unity_kit.dart';

final bridge = UnityBridgeImpl(platform: UnityKitPlatform.instance);
await bridge.initialize();

UnityView(
  bridge: bridge,
  config: const UnityConfig(sceneName: 'MainScene'),
  onReady: (bridge) => bridge.send(UnityMessage.command('StartGame')),
  onMessage: (msg) => print('Unity says: ${msg.type}'),
);
3. Unity side

Copy unity_kit/unity/Assets/Scripts/UnityKit/ into your Unity project. A FlutterBridge GameObject auto-creates on scene load and handles all communication.

// Receive messages from Flutter
public class MyHandler : FlutterMonoBehaviour
{
    protected override void OnFlutterMessage(string method, string data)
    {
        switch (method)
        {
            case "StartGame":
                // do something
                NativeAPI.SendToFlutter("{\"type\":\"game_started\"}");
                break;
        }
    }
}

Communication

Message Formats

Flutter -> Unity (routed to a handler):

{"target": "MyHandler", "method": "StartGame", "data": "params"}

Unity -> Flutter (typed event):

{"type": "game_started", "data": {"score": 100}}
Naming Convention
Direction Casing Example
Flutter -> Unity (commands) PascalCase LoadToy, StartGame
Unity -> Flutter (responses) snake_case toy_loaded, game_started
How to Add a New Command

Unity side:

  1. Add a case in your handler (or create a new FlutterMonoBehaviour)
  2. Send responses via NativeAPI.SendToFlutter(json)

Flutter side:

  1. Send: bridge.send(UnityMessage.to('HandlerName', 'MethodName', data))
  2. Listen: bridge.messageStream.listen((msg) => ...)

The bridge never changes. You define a JSON contract (command name + payload), implement on both sides, done.


Unity Export Workflow

Unity project and Flutter project are separate. Unity exports a library module, which gets copied to the Flutter project.

Unity Project                              Flutter Project
  Assets/, Scenes/                           android/unityLibrary/  <-- artifact
  Scripts/UnityKit/                          ios/UnityLibrary/      <-- artifact
  Builds/ (export artifacts)  -- copy -->    lib/, pubspec.yaml
Export steps
  1. Open Unity Editor with your project
  2. Flutter > Export Android (Debug) or Flutter > Export iOS (Debug)
  3. Artifacts appear in Builds/
  4. Set Flutter project path in Flutter > Settings for auto-deploy, or copy manually
CI pipeline
Unity -quit -batchmode -projectPath /path/to/project \
  -executeMethod UnityKit.Editor.Build.ExportAndroidRelease

See Unity Export Guide for full details.


Asset Streaming (Addressables)

For downloading 3D content from a server at runtime:

1. Flutter downloads content_manifest.json from CDN
2. User selects a toy/asset in the UI
3. Flutter downloads the .bundle file to local cache (SHA-256 verified)
4. Flutter tells Unity: "cache is at /path" (SetCachePath)
5. Flutter tells Unity: "load asset with key 'alphie'" (LoadToyRemote)
6. Unity Addressables loads from local cache (not from network again)
7. Unity sends "toy_loaded" back to Flutter
Building Addressables
  1. Setup (one-time): Flutter > Setup Addressables
  2. Add asset: place prefab in Addressables group, set address key
  3. Build: Flutter > Build Addressables (generates bundles + content_manifest.json)
  4. Upload: push ServerData/[BuildTarget]/ to CDN

See Asset Streaming Guide for manifest format and configuration.


Development

cd unity_kit && dart pub get       # Install dependencies
cd unity_kit && flutter test       # Run tests
cd unity_kit && dart analyze       # Analyze
cd unity_kit && dart format .      # Format
cd unity_kit && dart pub publish --dry-run  # Publish check

Guides

Hosted documentation: a step-by-step integration guide with troubleshooting is also available at codigee.com/open-source/unity-kit.

Guide What's in it
Unity Export Step-by-step: install scripts, configure build, export for Android/iOS/WebGL
Content Loading All methods: scenes, prefabs, AssetBundles, Addressables, glTF, AR
Android Integration Android native layer: Unity 6 patterns, reflection, touch, rendering activation
iOS Integration iOS native layer: UnityFramework, .mm bridge, Metal crash prevention
Architecture Overall design: bridge, lifecycle, streams, exceptions
Asset Streaming Addressables vs AssetBundles, manifest format, cache management
API Reference Class signatures, parameters, code examples
FAQ Common problems and solutions

FAQ (Common Problems)

See doc/faq.md for the full list. Here are the top issues:

Black screen after Unity loads (Android)

Unity needs an explicit "refocus" after attaching to the view: windowFocusChanged(true) + pause() + resume(). This is handled by unity_kit automatically. If you see a black screen, check that you're using the latest version.

Touch not working (Android)

Unity's New Input System requires InputDevice.SOURCE_TOUCHSCREEN on touch events. The UnityContainerLayout in unity_kit fixes this. If touch doesn't work, make sure you're not overriding the touch handling.

White/blank screen (iOS)

Usually a timing issue: Unity's Metal renderer needs a non-zero view frame before initialization. unity_kit polls with waitForNonZeroFrame(). If it persists, ensure the UnityView widget has a non-zero size in the layout.

Release build works differently than debug (Android)

ProGuard/R8 can strip classes used via reflection or JNI. unity_kit's Build.cs auto-adds ProGuard keep rules during export. If you see silent failures in release, verify proguard-unity.txt contains keep rules for com.unity_kit.** and com.unity3d.player.**.

Messages not reaching Unity / Flutter
  1. Check FlutterBridge GameObject exists in the first scene
  2. Check your handler's target name matches what Flutter sends
  3. Check Unity Console for [UnityKit] No handler registered for target: xxx
  4. On iOS: verify UnityKitNativeBridge.mm is in Assets/Plugins/iOS/
Environment preflight

Before mounting a view, ask what the build actually carries:

final env = await UnityKitPlatform.instance.environment();

if (!env.isReadyForUnity) {
  // No Unity runtime in this build: a UnityView would come up blank.
  debugPrint(env.summary);
}

if (env.failsPageSizeRequirement) {
  // Google Play rejects updates targeting Android 15+ whose native
  // libraries are aligned to less than 16 KB.
  debugPrint('Not 16 KB aligned: '
      '${env.unalignedLibraries.map((l) => l.name).join(', ')}');
}

env.summary prints a single line for logs, for example:

Unity runtime: unity6 (com.unity3d.player.UnityPlayerForActivityOrService),
abi arm64-v8a, device page size 16384 B, 16 KB alignment: aligned

Two Unity upgrade traps this catches:

  • The Unity 6 class rename. env.runtime says which player class was found, so a build that silently shipped without the Unity libraries is obvious instead of showing up as an empty surface.
  • 16 KB page sizes. Since 1 November 2025, Google Play requires every native library in an app targeting Android 15 (API 35) or newer to be aligned to 16 KB. Unity satisfies this from 2022.3.56 and from Unity 6, but an app can still ship an older prebuilt .so. The check reads the p_align of each PT_LOAD segment out of the shipped files, so it reports the binary's truth rather than the Unity version's claim.

iOS reports the runtime and page size but no alignment verdict: the 16 KB rule is an Android packaging requirement.

Unity 6 compatibility

unity_kit uses reflection to detect Unity 6's UnityPlayerForActivityOrService class automatically. No manual configuration needed. If you're using Unity 6 and see issues, check the Android Integration guide for constructor signature details.


Roadmap to 1.0

  • 3D model loading utilities and prefab management helpers
  • Full step-by-step tutorial covering the complete integration flow
  • Additional example scenes and sample Unity project

License

MIT License -- see unity_kit/LICENSE for details.


Created by Eryk Kruk