Interactive Rotating Cube Demo
An interactive 3D rotating cube demo showcasing bidirectional Flutter-Unity communication with a beautiful overlay UI. Perfect as your first Game Framework example!
Overview
This example demonstrates:
- Real-time parameter control from Flutter
- Continuous feedback from Unity to Flutter
- Touch-based interaction in Unity
- UI overlay without blocking the Unity view
- Color customization and state synchronization
This is the perfect starting point for learning Game Framework. It covers all the essential communication patterns in a simple, visual way.
Features
Unity Side
- Procedurally generated 3D cube with metallic material
- Real-time rotation with customizable speed and axis
- Physics-based touch controls with momentum
- Live UI display showing:
- Speed (degrees/second and RPM)
- Last message received
- Communication direction indicator
- Color-coded indicators:
- Green: ← FROM FLUTTER
- Orange: → TO FLUTTER
- Dynamic lighting for visual appeal
Flutter Side
- Beautiful overlay UI that doesn't block Unity view
- Speed slider (-180° to 180°/second) with live preview
- Axis selector buttons (X, Y, Z, All)
- Action buttons:
- Reset: Restore defaults
- Get State: Request current state
- Random Color: Apply random cube color
- Live info card showing real-time stats
- Dark theme with semi-transparent cards
Setup
1. Sync Template Scripts
cd your_flutter_project
game sync scripts --templates2. Unity Setup
In Unity Editor:
- Create a new scene (or use existing)
- Create an empty GameObject
- Name it
GameFrameworkDemo - Attach the
GameFrameworkDemo.csscript - Press Play to test
The script automatically creates:
- The rotating cube with material
- Camera positioned to view the cube
- Directional light for proper lighting
- Canvas with TextMeshPro UI elements
No manual setup required! The script creates everything it needs at runtime.
3. Export Unity
# Export for your target platform
game export unity --platform android
# Sync to Flutter
game sync unity --platform androidFlutter Integration
Basic Implementation
import 'package:flutter/material.dart';
import 'package:gameframework/gameframework.dart';
import 'package:gameframework_unity/gameframework_unity.dart';
class RotatingCubeDemo extends StatefulWidget {
@override
State<RotatingCubeDemo> createState() => _RotatingCubeDemoState();
}
class _RotatingCubeDemoState extends State<RotatingCubeDemo> {
GameEngineController? _controller;
double _rotationSpeed = 45.0;
String _rotationAxis = 'Y';
Map<String, dynamic>? _cubeState;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
// Unity view
GameWidget(
engineType: GameEngineType.unity,
config: GameEngineConfig(runImmediately: true),
onEngineCreated: _onEngineCreated,
onMessage: _onMessage,
),
// Flutter UI overlay
_buildControlPanel(),
],
),
);
}
void _onEngineCreated(GameEngineController controller) {
setState(() => _controller = controller);
}
void _onMessage(GameEngineMessage message) {
if (message.target == 'GameFrameworkDemo') {
switch (message.method) {
case 'onStateUpdate':
setState(() {
_cubeState = jsonDecode(message.data);
});
break;
}
}
}
Widget _buildControlPanel() {
return Positioned(
bottom: 20,
left: 20,
right: 20,
child: Card(
color: Colors.black.withOpacity(0.7),
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
children: [
// Speed slider
Text('Speed: ${_rotationSpeed.toStringAsFixed(0)}°/s'),
Slider(
value: _rotationSpeed,
min: -180,
max: 180,
onChanged: (value) {
setState(() => _rotationSpeed = value);
_setSpeed(value);
},
),
// Axis buttons
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_axisButton('X'),
_axisButton('Y'),
_axisButton('Z'),
_axisButton('All'),
],
),
// Action buttons
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: _reset,
child: Text('Reset'),
),
ElevatedButton(
onPressed: _getState,
child: Text('Get State'),
),
ElevatedButton(
onPressed: _randomColor,
child: Text('Random Color'),
),
],
),
],
),
),
),
);
}
Widget _axisButton(String axis) {
return ElevatedButton(
onPressed: () => _setAxis(axis),
style: ElevatedButton.styleFrom(
backgroundColor: _rotationAxis == axis
? Colors.blue
: Colors.grey,
),
child: Text(axis),
);
}
void _setSpeed(double speed) {
_controller?.sendJsonMessage('GameFrameworkDemo', 'setSpeed', {
'speed': speed,
});
}
void _setAxis(String axis) {
setState(() => _rotationAxis = axis);
_controller?.sendJsonMessage('GameFrameworkDemo', 'setAxis', {
'axis': axis,
});
}
void _reset() {
_controller?.sendMessage('GameFrameworkDemo', 'reset', '');
}
void _getState() {
_controller?.sendMessage('GameFrameworkDemo', 'getState', '');
}
void _randomColor() {
_controller?.sendMessage('GameFrameworkDemo', 'randomColor', '');
}
}Unity Script
The GameFrameworkDemo.cs script is included in the templates. Key methods:
using UnityEngine;
using Xraph.GameFramework.Unity;
public class GameFrameworkDemo : FlutterMonoBehaviour
{
protected override string TargetName => "GameFrameworkDemo";
// Set rotation speed from Flutter
[FlutterMethod("setSpeed")]
public void SetSpeed(SpeedMessage message)
{
rotationSpeed = message.speed;
SendStateUpdate();
}
// Set rotation axis from Flutter
[FlutterMethod("setAxis")]
public void SetAxis(AxisMessage message)
{
// Update rotation axis (X, Y, Z, or All)
UpdateRotationAxis(message.axis);
SendStateUpdate();
}
// Reset to defaults
[FlutterMethod("reset")]
public void Reset()
{
rotationSpeed = 45f;
rotationAxis = Vector3.up;
SendStateUpdate();
}
// Get current state
[FlutterMethod("getState")]
public void GetState()
{
SendStateUpdate();
}
// Apply random color
[FlutterMethod("randomColor")]
public void RandomColor()
{
cubeRenderer.material.color = Random.ColorHSV();
SendStateUpdate();
}
// Send state to Flutter
private void SendStateUpdate()
{
SendToFlutter("onStateUpdate", new StateMessage
{
speed = rotationSpeed,
axis = GetAxisName(),
rpm = rotationSpeed / 6f,
color = ColorToHex(cubeRenderer.material.color)
});
}
}Communication Flow
sequenceDiagram
participant Flutter
participant Unity
Flutter->>Unity: setSpeed(45)
Unity-->>Flutter: onStateUpdate(speed: 45, rpm: 7.5)
Flutter->>Unity: setAxis("Y")
Unity-->>Flutter: onStateUpdate(axis: "Y")
Flutter->>Unity: randomColor()
Unity-->>Flutter: onStateUpdate(color: "#FF5733")
Flutter->>Unity: getState()
Unity-->>Flutter: onStateUpdate(full state)What You'll Learn
Bidirectional Communication
- Sending messages from Flutter to Unity
- Receiving responses from Unity to Flutter
- Real-time parameter updates
Message Types
- JSON Messages: Structured data with type safety
- String Messages: Simple commands
- State Updates: Continuous feedback
UI Patterns
- Overlay UI on Unity view
- Non-blocking interactive controls
- Real-time state display
Unity Patterns
FlutterMonoBehaviourbase class[FlutterMethod]attribute for exposed methodsSendToFlutter()for responses
Tips & Tricks
Performance
The demo uses efficient messaging:
// Throttle slider updates
Timer? _throttle;
void _setSpeed(double speed) {
_throttle?.cancel();
_throttle = Timer(Duration(milliseconds: 16), () {
_controller?.sendJsonMessage('GameFrameworkDemo', 'setSpeed', {
'speed': speed,
});
});
}Touch Controls
Unity side implements touch-based rotation:
void Update()
{
// Touch input for spinning
if (Input.touchCount > 0)
{
Touch touch = Input.GetTouch(0);
if (touch.phase == TouchPhase.Moved)
{
// Apply angular velocity based on swipe
ApplyTouchRotation(touch.deltaPosition);
}
}
}Color Synchronization
Keep colors in sync between Flutter and Unity:
void _onMessage(GameEngineMessage message) {
if (message.method == 'onStateUpdate') {
final state = jsonDecode(message.data);
setState(() {
_currentColor = Color(
int.parse(state['color'].substring(1), radix: 16) + 0xFF000000
);
});
}
}The demo is fully interactive - try adjusting the speed, changing axes, and applying random colors to see real-time communication in action!
Troubleshooting
Cube Not Visible
- Ensure camera is positioned correctly
- Check Unity lighting
- Verify material is applied
No Communication
- Check
onEngineCreatedis called - Verify script is attached in Unity
- Check console for errors
UI Not Responsive
- Ensure
GameWidgetis in Stack - Check z-index of overlay
- Verify touch events aren't blocked