Examples
Messaging Patterns
Learn all the messaging patterns available in Game Framework: string messages, JSON/typed messages, request-response patterns, and event broadcasting.
Overview
The MessagingExample.cs template demonstrates:
- String messaging for simple commands
- JSON/typed messaging with automatic deserialization
- Request-response pattern for async operations
- Event broadcasting for game events
- Error handling and validation
This example covers all the messaging patterns you'll need for production applications.
Setup
Sync Templates
game sync scripts --templatesUnity Setup
- Create an empty GameObject named
Messaging - Attach
MessagingExample.csscript - Configure in Inspector if needed
Message Types
1. String Messaging
Simple text-based communication.
Flutter → Unity
// Send string message
await controller.sendMessage(
'Messaging',
'sendString',
'Hello Unity!'
);Unity Script
[FlutterMethod("sendString")]
public void ReceiveString(string message)
{
Debug.Log($"String received: {message}");
// Echo back
SendToFlutter("onStringReceived", $"Echo: {message}");
}Unity → Flutter
controller.messageStream.listen((msg) {
if (msg.method == 'onStringReceived') {
print('Received: ${msg.data}');
}
});2. JSON/Typed Messaging
Structured data with type safety.
Define Message Classes
[System.Serializable]
public class TypedMessage
{
public string action;
public int value;
public bool enabled;
}
[System.Serializable]
public class TypedResponse
{
public string originalAction;
public int processedValue;
public string timestamp;
}Flutter → Unity
// Send typed message
await controller.sendJsonMessage('Messaging', 'sendTyped', {
'action': 'greet',
'value': 42,
'enabled': true,
});Unity Script
[FlutterMethod("sendTyped")]
public void ReceiveTyped(TypedMessage message)
{
Debug.Log($"Typed: action={message.action}, value={message.value}");
// Process and respond
var response = new TypedResponse
{
originalAction = message.action,
processedValue = message.value * 2,
timestamp = DateTime.UtcNow.ToString("o")
};
SendToFlutter("onTypedReceived", response);
}Unity → Flutter
controller.messageStream.listen((msg) {
if (msg.method == 'onTypedReceived') {
final data = jsonDecode(msg.data);
print('Action: ${data['originalAction']}');
print('Value: ${data['processedValue']}');
print('Time: ${data['timestamp']}');
}
});3. Request-Response Pattern
Async operations with callbacks.
Flutter → Unity
// Send request
final requestId = DateTime.now().millisecondsSinceEpoch.toString();
await controller.sendJsonMessage('Messaging', 'request', {
'requestId': requestId,
'operation': 'calculate',
'params': {
'a': 10,
'b': 20,
},
});
// Listen for response
controller.messageStream.listen((msg) {
if (msg.method == 'onResponse') {
final data = jsonDecode(msg.data);
if (data['requestId'] == requestId) {
print('Result: ${data['result']}');
}
}
});Unity Script
[FlutterMethod("request")]
public void HandleRequest(RequestMessage request)
{
Debug.Log($"Request {request.requestId}: {request.operation}");
// Process request
var result = ProcessOperation(request.operation, request.params);
// Send response
SendToFlutter("onResponse", new ResponseMessage
{
requestId = request.requestId,
success = true,
result = result,
error = null
});
}4. Event Broadcasting
Broadcast game events to Flutter.
Unity Script
// Broadcast event to Flutter
public void BroadcastEvent(string eventType, object eventData)
{
SendToFlutter("onGameEvent", new GameEvent
{
type = eventType,
data = eventData,
timestamp = Time.time
});
}
// Example usage
void OnPlayerScored(int points)
{
BroadcastEvent("score", new { points = points, total = totalScore });
}
void OnGameOver()
{
BroadcastEvent("gameOver", new { finalScore = totalScore });
}Flutter
controller.messageStream.listen((msg) {
if (msg.method == 'onGameEvent') {
final event = jsonDecode(msg.data);
switch (event['type']) {
case 'score':
_handleScore(event['data']);
break;
case 'gameOver':
_handleGameOver(event['data']);
break;
}
}
});Complete Flutter Example
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:gameframework/gameframework.dart';
class MessagingExample extends StatefulWidget {
@override
State<MessagingExample> createState() => _MessagingExampleState();
}
class _MessagingExampleState extends State<MessagingExample> {
GameEngineController? _controller;
final _messageController = TextEditingController();
List<String> _messages = [];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Messaging Patterns')),
body: Column(
children: [
// Unity view
Expanded(
flex: 2,
child: GameWidget(
engineType: GameEngineType.unity,
config: GameEngineConfig(runImmediately: true),
onEngineCreated: (controller) {
setState(() => _controller = controller);
},
onMessage: _onMessage,
),
),
// Message log
Expanded(
child: ListView.builder(
itemCount: _messages.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(_messages[index]),
dense: true,
);
},
),
),
// Controls
Padding(
padding: EdgeInsets.all(8),
child: Row(
children: [
Expanded(
child: TextField(
controller: _messageController,
decoration: InputDecoration(
hintText: 'Enter message',
border: OutlineInputBorder(),
),
),
),
SizedBox(width: 8),
ElevatedButton(
onPressed: _sendString,
child: Text('Send String'),
),
SizedBox(width: 8),
ElevatedButton(
onPressed: _sendTyped,
child: Text('Send Typed'),
),
],
),
),
],
),
);
}
void _onMessage(GameEngineMessage message) {
if (message.target == 'Messaging') {
setState(() {
_messages.add('← ${message.method}: ${message.data}');
});
}
}
void _sendString() {
final text = _messageController.text;
if (text.isNotEmpty) {
_controller?.sendMessage('Messaging', 'sendString', text);
setState(() {
_messages.add('→ sendString: $text');
});
_messageController.clear();
}
}
void _sendTyped() {
_controller?.sendJsonMessage('Messaging', 'sendTyped', {
'action': 'test',
'value': Random().nextInt(100),
'enabled': true,
});
setState(() {
_messages.add('→ sendTyped: {action: test, ...}');
});
}
}Message Flow Diagrams
String Messaging
sequenceDiagram
participant Flutter
participant Unity
Flutter->>Unity: sendMessage("Hello")
Unity->>Unity: Process message
Unity-->>Flutter: onStringReceived("Echo: Hello")Request-Response
sequenceDiagram
participant Flutter
participant Unity
Flutter->>Unity: request(id: 123, operation: "calc")
Unity->>Unity: Process operation
Unity-->>Flutter: onResponse(id: 123, result: 42)Event Broadcasting
sequenceDiagram
participant Unity
participant Flutter
Note over Unity: Game event occurs
Unity->>Flutter: onGameEvent(type: "score")
Unity->>Flutter: onGameEvent(type: "powerup")
Unity->>Flutter: onGameEvent(type: "gameOver")Best Practices
1. Use Appropriate Message Types
// Simple commands → String
controller.sendMessage('Player', 'jump', '');
// Structured data → JSON
controller.sendJsonMessage('Player', 'moveTo', {
'x': 10.5,
'y': 0,
'z': 20.3,
});
// Binary data → Binary
controller.sendBinaryMessage('Texture', 'upload', imageBytes);2. Type Safety
Always define classes for typed messages:
// Good - Type safe
[System.Serializable]
public class MoveCommand
{
public float x;
public float y;
public float z;
}
[FlutterMethod("moveTo")]
public void MoveTo(MoveCommand cmd) { }
// Avoid - Untyped
[FlutterMethod("moveTo")]
public void MoveTo(string json) { }3. Error Handling
[FlutterMethod("riskyOperation")]
public void RiskyOperation(OperationRequest request)
{
try
{
var result = PerformOperation(request);
SendToFlutter("onSuccess", result);
}
catch (Exception e)
{
SendToFlutter("onError", new ErrorMessage
{
error = e.Message,
requestId = request.id
});
}
}4. Async Operations
Use request IDs for tracking:
class RequestManager {
final _pending = <String, Completer>{};
Future<dynamic> request(
GameEngineController controller,
String target,
String method,
Map<String, dynamic> params,
) {
final id = Uuid().v4();
final completer = Completer();
_pending[id] = completer;
controller.sendJsonMessage(target, method, {
'requestId': id,
...params,
});
return completer.future;
}
void handleResponse(String id, dynamic data) {
_pending[id]?.complete(data);
_pending.remove(id);
}
}Performance Tips
Message Batching
For high-frequency updates:
// Enable batching in Unity
protected override void Awake()
{
base.Awake();
EnableBatching = true;
BatchInterval = 0.016f; // 60 FPS
}Throttling
Throttle rapid updates in Flutter:
Timer? _throttle;
void _sendUpdate(Map<String, dynamic> data) {
_throttle?.cancel();
_throttle = Timer(Duration(milliseconds: 16), () {
controller?.sendJsonMessage('Target', 'update', data);
});
}Master these messaging patterns and you'll be able to build any Flutter-Unity integration!
What You'll Learn
- ✅ String, JSON, and typed messaging
- ✅ Request-response patterns
- ✅ Event broadcasting
- ✅ Error handling
- ✅ Type safety
- ✅ Performance optimization