Game Framework
SDK DocumentationAPI Reference

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 Engine
  • GameEngineType.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 overhead
  • virtualDisplay - 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 UiKitView for 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...'),
    ],
  ),
)

See Also