GameWidget API
The GameWidget is the main Flutter widget for embedding game engines into your application. It provides a unified interface for all supported engines (Unity, Unreal, etc.).
Constructor
GameWidget({
Key? key,
required GameEngineType engineType,
required GameEngineCreatedCallback onEngineCreated,
GameEngineMessageCallback? onMessage,
GameEngineSceneLoadedCallback? onSceneLoaded,
GameEngineUnloadCallback? onEngineUnloaded,
GameEngineConfig config = const GameEngineConfig(),
Set<Factory<OneSequenceGestureRecognizer>>? gestureRecognizers,
Widget? placeholder,
bool enablePlaceholder = false,
BorderRadius borderRadius = BorderRadius.zero,
TextDirection? layoutDirection,
})Required Parameters
engineType
Type: GameEngineType
The type of game engine to embed. Available options:
GameEngineType.unity- Unity EngineGameEngineType.unreal- Unreal Engine (future)
GameWidget(
engineType: GameEngineType.unity,
// ...
)onEngineCreated
Type: void Function(GameEngineController controller)
Callback invoked when the engine is fully created and ready to use. Provides the GameEngineController for interacting with the engine.
GameWidget(
engineType: GameEngineType.unity,
onEngineCreated: (controller) {
// Engine is ready - send initial messages
controller.sendMessage('GameManager', 'Initialize', 'data');
},
)This callback is called after the engine view is created and the engine is initialized. It's the right place to send initialization messages.
Optional Parameters
onMessage
Type: void Function(GameEngineMessage message)?
Callback for receiving messages from the game engine.
GameWidget(
engineType: GameEngineType.unity,
onEngineCreated: (controller) {},
onMessage: (message) {
print('From ${message.target}.${message.method}: ${message.data}');
if (message.method == 'GameOver') {
_handleGameOver(jsonDecode(message.data));
}
},
)onSceneLoaded
Type: void Function(GameSceneLoaded scene)?
Callback when a new scene is loaded in the engine.
GameWidget(
engineType: GameEngineType.unity,
onEngineCreated: (controller) {},
onSceneLoaded: (scene) {
print('Scene loaded: ${scene.name} (build index: ${scene.buildIndex})');
},
)onEngineUnloaded
Type: void Function()?
Callback when the engine is unloaded.
GameWidget(
engineType: GameEngineType.unity,
onEngineCreated: (controller) {},
onEngineUnloaded: () {
print('Engine has been unloaded');
},
)config
Type: GameEngineConfig
Configuration object for the engine. See GameEngineConfig below.
GameWidget(
engineType: GameEngineType.unity,
onEngineCreated: (controller) {},
config: GameEngineConfig(
runImmediately: true,
fullscreen: false,
),
)gestureRecognizers
Type: Set<Factory<OneSequenceGestureRecognizer>>?
Custom gesture recognizers for the embedded view. Use this to handle specific touch/mouse gestures.
GameWidget(
engineType: GameEngineType.unity,
onEngineCreated: (controller) {},
gestureRecognizers: {
Factory<PanGestureRecognizer>(() => PanGestureRecognizer()),
Factory<TapGestureRecognizer>(() => TapGestureRecognizer()),
},
)placeholder
Type: Widget?
Widget to show before the engine is ready (only if enablePlaceholder is true).
GameWidget(
engineType: GameEngineType.unity,
onEngineCreated: (controller) {},
enablePlaceholder: true,
placeholder: Center(
child: CircularProgressIndicator(),
),
)enablePlaceholder
Type: bool
Default: false
Whether to show the placeholder widget before engine is ready.
borderRadius
Type: BorderRadius
Default: BorderRadius.zero
Border radius for the game view.
GameWidget(
engineType: GameEngineType.unity,
onEngineCreated: (controller) {},
borderRadius: BorderRadius.circular(12),
)layoutDirection
Type: TextDirection?
Text direction for the embedded view. If null, uses ambient Directionality.
GameEngineConfig
Configuration object for engine behavior.
class GameEngineConfig {
const GameEngineConfig({
this.runImmediately = true,
this.fullscreen = false,
this.androidPlatformViewMode = AndroidPlatformViewMode.hybridComposition,
});
/// Whether to automatically start the engine when created
final bool runImmediately;
/// Whether to run the engine in fullscreen mode
final bool fullscreen;
/// Android platform view rendering mode
final AndroidPlatformViewMode androidPlatformViewMode;
}runImmediately
Type: bool, Default: true
If true, the engine starts automatically after creation. If false, you must manually call controller.create().
// Auto-start (default)
config: GameEngineConfig(runImmediately: true)
// Manual start
config: GameEngineConfig(runImmediately: false)
// Later...
await controller.create();fullscreen
Type: bool, Default: false
Whether the engine should run in fullscreen mode.
androidPlatformViewMode
Type: AndroidPlatformViewMode, Default: hybridComposition
Android-specific rendering mode:
hybridComposition- Better compatibility, slight performance overheadvirtualDisplay- Better performance, some compatibility limitations
config: GameEngineConfig(
androidPlatformViewMode: AndroidPlatformViewMode.virtualDisplay,
)GameEngineMessage
Message object received from the engine.
class GameEngineMessage {
final String target; // Target object name
final String method; // Method name
final String data; // Message data (string or JSON)
}Example Usage
onMessage: (message) {
// String message
if (message.method == 'OnGameOver') {
print(message.data); // "Player lost"
}
// JSON message
if (message.method == 'OnScoreUpdate') {
final data = jsonDecode(message.data);
final score = data['score'];
final combo = data['combo'];
}
}Complete Example
import 'package:flutter/material.dart';
import 'package:gameframework/gameframework.dart';
import 'package:gameframework_unity/gameframework_unity.dart';
class GameScreen extends StatefulWidget {
@override
State<GameScreen> createState() => _GameScreenState();
}
class _GameScreenState extends State<GameScreen> {
GameEngineController? _controller;
@override
Widget build(BuildContext context) {
return Scaffold(
body: GameWidget(
// Required: Specify engine type
engineType: GameEngineType.unity,
// Required: Handle engine creation
onEngineCreated: (controller) {
setState(() => _controller = controller);
// Send initialization message
controller.sendMessage('GameManager', 'Start', 'Level1');
},
// Optional: Handle messages from engine
onMessage: (message) {
if (message.method == 'GameOver') {
_showGameOverDialog();
}
},
// Optional: Handle scene loads
onSceneLoaded: (scene) {
print('Loaded scene: ${scene.name}');
},
// Optional: Configure engine
config: GameEngineConfig(
runImmediately: true,
fullscreen: false,
androidPlatformViewMode: AndroidPlatformViewMode.hybridComposition,
),
// Optional: Show loading indicator
enablePlaceholder: true,
placeholder: Center(
child: CircularProgressIndicator(),
),
// Optional: Rounded corners
borderRadius: BorderRadius.circular(12),
),
);
}
@override
void dispose() {
_controller?.dispose();
super.dispose();
}
}Platform-Specific Considerations
Android
- Hybrid Composition (default): Better compatibility with most devices
- Virtual Display: Better performance but may have issues on some devices
config: GameEngineConfig(
androidPlatformViewMode: AndroidPlatformViewMode.hybridComposition,
)iOS
- Uses
UiKitViewfor native embedding - Generally good performance across all devices
Desktop (macOS, Windows, Linux)
- Uses native window embedding
- Full keyboard and mouse support
Always call controller.dispose() in your widget's dispose() method to properly clean up resources.
Best Practices
1. Store Controller Reference
class _GameScreenState extends State<GameScreen> {
GameEngineController? _controller;
@override
Widget build(BuildContext context) {
return GameWidget(
engineType: GameEngineType.unity,
onEngineCreated: (controller) {
setState(() => _controller = controller);
},
);
}
}2. Handle Initialization
onEngineCreated: (controller) async {
// Wait for engine to be fully ready
if (await controller.isReady()) {
// Send initialization data
controller.sendJsonMessage('GameManager', 'Init', {
'playerId': userId,
'settings': settings,
});
}
}3. Clean Up Properly
@override
void dispose() {
_controller?.dispose();
super.dispose();
}4. Use Placeholder for Loading
GameWidget(
engineType: GameEngineType.unity,
onEngineCreated: (controller) {},
enablePlaceholder: true,
placeholder: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(),
SizedBox(height: 16),
Text('Loading game engine...'),
],
),
)