Unreal Engine Plugin for Game Framework
Integrate Unreal Engine 5.x into your Flutter applications with full bidirectional communication, quality settings control, console commands, and level loading.
Features
⚠️ Development Status: Unreal Engine integration is currently under active development. Features and APIs may change.
✨ Core Features (WIP):
- 🎮 Unreal Engine 5.x integration
- 🔄 Bidirectional communication (Flutter ↔ Unreal)
- 🎯 Blueprint support for non-programmers
- 📱 Multi-platform support focus: Android, iOS
- ⚡ High-performance native bridges (JNI, Objective-C++)
🎨 Unreal-Specific Features (Planned):
- 🎚️ Quality settings with 5 presets (low, medium, high, epic, cinematic)
- 🖥️ Console command execution (
stat fps,r.SetRes, etc.) - 🗺️ Level/map loading and streaming
- 📊 Quality level control (AA, shadows, textures, effects, etc.)
- 🎬 Blueprint events for lifecycle and messaging
🚀 Advanced Features (Planned):
- 📦 Binary messaging with compression and chunked transfers
- ⏱️ Message batching and throttling for performance optimization
- 📊 Delta compression for efficient state synchronization
- 🗃️ Asset management with caching and progress tracking
- 🔀 Message routing with target-based dispatch
Platform Support
| Platform | Status | Requirements |
|---|---|---|
| Android | 🚧 Work in Progress | API 21+, NDK r25+ |
| iOS | 🚧 Work in Progress | iOS 12.0+, Xcode 14+ |
| macOS | ⏳ Planned | macOS 10.14+, Xcode 14+ |
| Windows | ⏳ Planned | Windows 10+, Visual Studio 2022 |
| Linux | ⏳ Planned | Ubuntu 20.04+, GTK 3.0+ |
Note: Unreal Engine integration is currently under active development. Android and iOS support are the primary focus.
Quick Start
Installation
Add to your pubspec.yaml:
dependencies:
gameframework: ^0.0.2
gameframework_unreal: ^0.0.2
Install:
flutter pub get
Basic Usage
import 'package:gameframework_unreal/gameframework_unreal.dart';
class MyGameScreen extends StatefulWidget {
@override
_MyGameScreenState createState() => _MyGameScreenState();
}
class _MyGameScreenState extends State<MyGameScreen> {
UnrealController? _controller;
@override
Widget build(BuildContext context) {
return Scaffold(
body: GameEngineWidget(
engineType: GameEngineType.unreal,
onControllerCreated: (controller) {
_controller = controller as UnrealController;
_initializeUnreal();
},
),
);
}
Future<void> _initializeUnreal() async {
// Wait for engine to be ready
await _controller!.create();
// Apply quality settings
await _controller!.applyQualitySettings(
UnrealQualitySettings.high(),
);
// Load initial level
await _controller!.loadLevel('MainMenu');
// Listen for messages from Unreal
_controller!.messages.listen((message) {
print('Message from Unreal: ${message.method}');
});
}
@override
void dispose() {
_controller?.quit();
super.dispose();
}
}
Core API
UnrealController
The main interface for controlling Unreal Engine:
// Lifecycle
await controller.create(); // Initialize engine
await controller.pause(); // Pause rendering
await controller.resume(); // Resume rendering
await controller.unload(); // Unload but keep ready
await controller.quit(); // Complete shutdown
// Communication
await controller.sendMessage('GameManager', 'onScoreChanged', '{"score": 100}');
await controller.sendJsonMessage('Player', 'takeDamage', {'amount': 25});
// Unreal-specific
await controller.executeConsoleCommand('stat fps');
await controller.loadLevel('Level_01');
await controller.applyQualitySettings(UnrealQualitySettings.epic());
// Get current settings
final settings = await controller.getQualitySettings();
Advanced Features
Binary Messaging
Send binary data efficiently with compression and chunked transfers:
import 'package:gameframework_unreal/gameframework_unreal.dart';
// Send binary data
final imageBytes = await loadImage();
await controller.sendBinaryMessage('TextureManager', 'updateTexture', imageBytes);
// Send compressed binary data (auto-compresses with GZip)
await controller.sendCompressedMessage('AssetLoader', 'loadAsset', largeData);
// Send large data in chunks (for data > 64KB)
await controller.sendChunkedBinaryMessage(
'AssetLoader',
'loadLargeAsset',
veryLargeData,
chunkSize: 32 * 1024, // 32KB chunks
);
// Listen for binary progress
controller.binaryProgressStream.listen((progress) {
print('Transfer: ${progress.progress * 100}%');
});
// Access the binary protocol directly
final protocol = controller.binaryProtocol;
final compressed = protocol.compressGzip(data);
final encoded = protocol.encodeBase64(compressed);
Message Batching
Optimize high-frequency messaging with batching:
import 'package:gameframework_unreal/gameframework_unreal.dart';
// Create a batcher
final batcher = UnrealMessageBatcher(
onFlush: (batch) {
for (final msg in batch) {
controller.sendMessage(msg['target'], msg['method'], msg['data']);
}
},
);
// Configure batching
batcher.configure(
maxBatchSize: 50, // Flush when 50 messages queued
flushIntervalMs: 16, // Or every 16ms (60 FPS)
enableCoalescing: true, // Combine duplicate messages
);
// Queue messages - they'll be batched automatically
batcher.queue('Player', 'updatePosition', '{"x": 10, "y": 20}');
batcher.queue('Player', 'updatePosition', '{"x": 11, "y": 21}'); // Coalesced
batcher.queue('Enemy', 'updateHealth', '{"hp": 50}');
// Manual flush if needed
final batch = batcher.flush();
// Get statistics
final stats = batcher.statistics;
print('Total queued: ${stats.totalMessagesQueued}');
print('Coalesced: ${stats.totalMessagesCoalesced}');
// Cleanup
batcher.dispose();
Message Throttling
Rate-limit high-frequency events:
import 'package:gameframework_unreal/gameframework_unreal.dart';
// Create a throttler
final throttler = UnrealMessageThrottler();
// Set rate limits (messages per second)
throttler.setRateLimit('Player', 'updatePosition', 30); // Max 30/sec
throttler.setRateLimit('UI', 'updateScore', 10); // Max 10/sec
// Throttle strategies
throttler.setRateLimit('Input', 'mouseMove', 60,
strategy: ThrottleStrategy.keepLatest); // Keep most recent
throttler.setRateLimit('Network', 'sync', 5,
strategy: ThrottleStrategy.queue); // Queue for later
// Send throttled messages
await throttler.send(
controller,
'Player',
'updatePosition',
'{"x": 100, "y": 200}',
);
// Flush any pending messages
await throttler.flushPending();
// Statistics
final stats = throttler.statistics;
print('Dropped: ${stats.messagesDropped}');
print('Queued: ${stats.messagesQueued}');
throttler.dispose();
Delta Compression
Efficiently synchronize state with delta encoding:
import 'package:gameframework_unreal/gameframework_unreal.dart';
// Create a compressor
final compressor = UnrealDeltaCompressor();
// Configure
compressor.configure(
maxHistorySize: 10, // Keep last 10 states
enableDeepComparison: true,
);
// Compute delta between states
final oldState = {'x': 0, 'y': 0, 'health': 100};
final newState = {'x': 10, 'y': 0, 'health': 75};
final delta = compressor.computeDelta('player', oldState, newState);
if (delta.hasChanges) {
// Only send changed values: {'x': 10, 'health': 75}
controller.sendJsonMessage('Player', 'syncState', delta.delta);
}
// Record state for future deltas
compressor.recordState('player', newState);
// Compute delta from history
final nextState = {'x': 15, 'y': 5, 'health': 75};
final nextDelta = compressor.computeDeltaFromHistory('player', nextState);
// Only {'x': 15, 'y': 5} will be in delta
// Apply delta on receiving end
final baseState = {'x': 0, 'y': 0, 'health': 100};
final applied = compressor.applyDelta(baseState, delta.delta);
// Statistics
final stats = compressor.statistics;
print('Compression ratio: ${stats.averageCompressionRatio}');
compressor.dispose();
Asset Manager
Manage asset loading with progress tracking and caching:
import 'package:gameframework_unreal/gameframework_unreal.dart';
// Create asset manager
final assetManager = UnrealAssetManager();
// Configure cache
assetManager.setCacheMaxSize(512 * 1024 * 1024); // 512 MB
// Load single asset
await assetManager.loadAsset('/Game/Textures/PlayerTexture');
// Load multiple assets with progress
assetManager.startBatchLoad([
'/Game/Meshes/Character',
'/Game/Textures/CharacterSkin',
'/Game/Animations/Walk',
]);
// Monitor progress
while (!assetManager.isBatchComplete) {
print('Progress: ${(assetManager.batchProgress * 100).toInt()}%');
await Future.delayed(Duration(milliseconds: 100));
}
// Check if loaded
if (assetManager.isLoaded('/Game/Textures/PlayerTexture')) {
final info = assetManager.getAssetInfo('/Game/Textures/PlayerTexture');
print('Size: ${info?.sizeBytes} bytes');
print('Load time: ${info?.loadTimeMs}ms');
}
// Load level
await assetManager.loadLevel('MainMenu');
// Unload assets
assetManager.unloadAsset('/Game/Textures/OldTexture');
// Cache management
print('Cache size: ${assetManager.currentCacheSize} bytes');
assetManager.clearCache();
// Statistics
final stats = assetManager.statistics;
print('Cache hit rate: ${(stats.cacheHitRate * 100).toInt()}%');
print('Total loaded: ${stats.totalAssetsLoaded}');
assetManager.dispose();
Quality Settings
Control Unreal Engine's quality with presets or custom settings:
// Use presets
await controller.applyQualitySettings(UnrealQualitySettings.low()); // Mobile/low-end
await controller.applyQualitySettings(UnrealQualitySettings.medium()); // Balanced
await controller.applyQualitySettings(UnrealQualitySettings.high()); // High-end devices
await controller.applyQualitySettings(UnrealQualitySettings.epic()); // Very high quality
await controller.applyQualitySettings(UnrealQualitySettings.cinematic());// Maximum quality
// Custom settings
await controller.applyQualitySettings(
UnrealQualitySettings(
qualityLevel: 3, // Overall level (0-4)
antiAliasingQuality: 4, // Anti-aliasing (0-4)
shadowQuality: 3, // Shadows (0-4)
postProcessQuality: 4, // Post-processing (0-4)
textureQuality: 4, // Textures (0-4)
effectsQuality: 3, // Effects (0-4)
foliageQuality: 2, // Foliage (0-4)
viewDistanceQuality: 3, // View distance (0-4)
targetFrameRate: 60, // Target FPS
enableVSync: false, // VSync on/off
resolutionScale: 1.0, // Resolution scale (0.5-2.0)
),
);
// Get current settings
final currentSettings = await controller.getQualitySettings();
print('Current quality level: ${currentSettings.qualityLevel}');
Console Commands
Execute Unreal Engine console commands:
// Performance monitoring
await controller.executeConsoleCommand('stat fps');
await controller.executeConsoleCommand('stat unit');
await controller.executeConsoleCommand('stat gpu');
// Quality overrides
await controller.executeConsoleCommand('r.SetRes 1920x1080');
await controller.executeConsoleCommand('r.VSync 0');
await controller.executeConsoleCommand('sg.ViewDistanceQuality 3');
// Debugging
await controller.executeConsoleCommand('showdebug');
await controller.executeConsoleCommand('freezerendering');
See CONSOLE_COMMANDS.md for a complete reference.
Level Loading
Load levels/maps dynamically:
// Load a level
await controller.loadLevel('MainMenu');
await controller.loadLevel('Level_01');
await controller.loadLevel('/Game/Maps/Arena');
// Listen for level loads
controller.sceneLoads.listen((scene) {
print('Level loaded: ${scene.name}');
print('Build index: ${scene.buildIndex}');
print('Is loaded: ${scene.isLoaded}');
});
See LEVEL_LOADING.md for more details.
Event Streams
Listen to engine events:
// Lifecycle events
controller.events.listen((event) {
switch (event.type) {
case GameEngineEventType.created:
print('Engine created');
break;
case GameEngineEventType.loaded:
print('Engine loaded');
break;
case GameEngineEventType.paused:
print('Engine paused');
break;
case GameEngineEventType.resumed:
print('Engine resumed');
break;
case GameEngineEventType.unloaded:
print('Engine unloaded');
break;
case GameEngineEventType.destroyed:
print('Engine destroyed');
break;
case GameEngineEventType.error:
print('Engine error: ${event.message}');
break;
}
});
// Messages from Unreal
controller.messages.listen((message) {
print('From: ${message.target}');
print('Method: ${message.method}');
print('Data: ${message.data}');
// Parse JSON data
if (message.data != null) {
final json = jsonDecode(message.data!);
// Handle data...
}
});
// Scene/level loads
controller.sceneLoads.listen((scene) {
print('Level: ${scene.name}');
print('Index: ${scene.buildIndex}');
});
Unreal Engine Integration
Setting Up Your Unreal Project
- Add Flutter Plugin to Your Unreal Project:
YourUnrealProject/
├── Plugins/
│ └── FlutterPlugin/
│ ├── FlutterPlugin.uplugin
│ ├── Source/
│ │ └── FlutterPlugin/
│ │ ├── Public/
│ │ │ └── FlutterBridge.h
│ │ └── Private/
│ │ ├── FlutterBridge.cpp
│ │ └── Android/
│ │ └── FlutterBridge_Android.cpp
- Add FlutterBridge Actor to Your Level:
In your Unreal Editor:
- Drag
FlutterBridgeactor into your level - Or create it programmatically in your GameMode
- Use in Blueprints:
Send message to Flutter:
FlutterBridge → SendToFlutter
Target: "GameManager"
Method: "onScoreChanged"
Data: "{\"score\": 100}"
Receive messages from Flutter:
FlutterBridge → OnMessageFromFlutter (Event)
→ Print String (Target)
→ Print String (Method)
→ Print String (Data)
Execute console commands:
FlutterBridge → ExecuteConsoleCommand
Command: "stat fps"
Load levels:
FlutterBridge → LoadLevel
LevelName: "Level_01"
Lifecycle events:
FlutterBridge → OnEnginePausedBP (Event)
FlutterBridge → OnEngineResumedBP (Event)
FlutterBridge → OnEngineQuitBP (Event)
C++ Usage
// Get FlutterBridge instance
AFlutterBridge* Bridge = AFlutterBridge::GetInstance(GetWorld());
// Send message to Flutter
Bridge->SendToFlutter(TEXT("GameManager"), TEXT("onScoreChanged"), TEXT("{\"score\": 100}"));
// Execute console command
Bridge->ExecuteConsoleCommand(TEXT("stat fps"));
// Load level
Bridge->LoadLevel(TEXT("Level_01"));
// Apply quality settings
Bridge->ApplyQualitySettings(
3, // Quality level
4, // Anti-aliasing
3, // Shadows
4, // Post-process
4, // Textures
3, // Effects
2, // Foliage
3 // View distance
);
// Get quality settings
TMap<FString, int32> Settings = Bridge->GetQualitySettings();
int32 AAQuality = Settings[TEXT("antiAliasing")];
Platform-Specific Setup
Android
See SETUP_GUIDE.md#Android for complete instructions.
Requirements:
- Unreal Engine 5.3.x or 5.4.x Android build
- Android NDK r25 or later
- Minimum API level 21
Key Steps:
- Build your Unreal project for Android
- Configure Flutter app to embed Unreal APK
- Add required permissions to AndroidManifest.xml
iOS
See SETUP_GUIDE.md#iOS for complete instructions.
Requirements:
- Unreal Engine 5.3.x or 5.4.x iOS framework
- Xcode 14.0 or later
- iOS 12.0+
Key Steps:
- Build your Unreal project for iOS
- Embed UnrealFramework.framework in Flutter app
- Configure build settings in Xcode
macOS
See SETUP_GUIDE.md#macOS for complete instructions.
Requirements:
- Unreal Engine 5.3.x or 5.4.x macOS build
- Xcode 14.0 or later
- macOS 10.14+
Windows & Linux
See SETUP_GUIDE.md#Windows and SETUP_GUIDE.md#Linux.
Examples
Basic Example
import 'package:flutter/material.dart';
import 'package:gameframework_unreal/gameframework_unreal.dart';
void main() {
// Initialize Unreal Engine plugin
UnrealEnginePlugin.initialize();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: GameScreen(),
);
}
}
class GameScreen extends StatefulWidget {
@override
_GameScreenState createState() => _GameScreenState();
}
class _GameScreenState extends State<GameScreen> {
UnrealController? _controller;
String _statusText = 'Initializing...';
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Unreal Engine Game')),
body: Stack(
children: [
// Unreal Engine rendering view
GameEngineWidget(
engineType: GameEngineType.unreal,
onControllerCreated: _onControllerCreated,
),
// UI overlay
Positioned(
top: 16,
left: 16,
child: Container(
padding: EdgeInsets.all(8),
color: Colors.black54,
child: Text(
_statusText,
style: TextStyle(color: Colors.white),
),
),
),
// Controls
Positioned(
bottom: 16,
left: 16,
right: 16,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: _showFPS,
child: Text('Show FPS'),
),
ElevatedButton(
onPressed: _changeQuality,
child: Text('Change Quality'),
),
ElevatedButton(
onPressed: _loadLevel,
child: Text('Load Level'),
),
],
),
),
],
),
);
}
void _onControllerCreated(GameEngineController controller) {
_controller = controller as UnrealController;
_initializeGame();
}
Future<void> _initializeGame() async {
setState(() => _statusText = 'Loading...');
// Create engine
await _controller!.create();
// Apply quality settings
await _controller!.applyQualitySettings(UnrealQualitySettings.high());
// Load first level
await _controller!.loadLevel('MainMenu');
// Listen for events
_controller!.events.listen((event) {
setState(() => _statusText = 'Event: ${event.type}');
});
// Listen for messages
_controller!.messages.listen((message) {
print('Message from Unreal: ${message.method}');
});
setState(() => _statusText = 'Ready');
}
Future<void> _showFPS() async {
await _controller?.executeConsoleCommand('stat fps');
}
Future<void> _changeQuality() async {
await _controller?.applyQualitySettings(UnrealQualitySettings.epic());
setState(() => _statusText = 'Quality: Epic');
}
Future<void> _loadLevel() async {
await _controller?.loadLevel('Level_01');
setState(() => _statusText = 'Loading Level_01...');
}
@override
void dispose() {
_controller?.quit();
super.dispose();
}
}
Advanced Example with Custom Quality
class AdvancedGameScreen extends StatefulWidget {
@override
_AdvancedGameScreenState createState() => _AdvancedGameScreenState();
}
class _AdvancedGameScreenState extends State<AdvancedGameScreen> {
UnrealController? _controller;
UnrealQualitySettings _currentSettings = UnrealQualitySettings.high();
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
// Game view
Expanded(
child: GameEngineWidget(
engineType: GameEngineType.unreal,
onControllerCreated: (controller) {
_controller = controller as UnrealController;
_controller!.create();
},
),
),
// Quality controls
Container(
padding: EdgeInsets.all(16),
child: Column(
children: [
_buildQualitySlider('Overall Quality', 0, 4,
_currentSettings.qualityLevel?.toDouble() ?? 3.0,
(value) => _updateQuality(qualityLevel: value.toInt()),
),
_buildQualitySlider('Anti-Aliasing', 0, 4,
_currentSettings.antiAliasingQuality?.toDouble() ?? 3.0,
(value) => _updateQuality(antiAliasingQuality: value.toInt()),
),
_buildQualitySlider('Shadows', 0, 4,
_currentSettings.shadowQuality?.toDouble() ?? 3.0,
(value) => _updateQuality(shadowQuality: value.toInt()),
),
],
),
),
],
),
);
}
Widget _buildQualitySlider(
String label,
double min,
double max,
double value,
ValueChanged<double> onChanged,
) {
return Row(
children: [
SizedBox(
width: 120,
child: Text(label),
),
Expanded(
child: Slider(
min: min,
max: max,
divisions: max.toInt(),
value: value,
onChanged: onChanged,
),
),
Text(value.toInt().toString()),
],
);
}
Future<void> _updateQuality({
int? qualityLevel,
int? antiAliasingQuality,
int? shadowQuality,
}) async {
_currentSettings = UnrealQualitySettings(
qualityLevel: qualityLevel ?? _currentSettings.qualityLevel,
antiAliasingQuality: antiAliasingQuality ?? _currentSettings.antiAliasingQuality,
shadowQuality: shadowQuality ?? _currentSettings.shadowQuality,
postProcessQuality: _currentSettings.postProcessQuality,
textureQuality: _currentSettings.textureQuality,
effectsQuality: _currentSettings.effectsQuality,
foliageQuality: _currentSettings.foliageQuality,
viewDistanceQuality: _currentSettings.viewDistanceQuality,
);
await _controller?.applyQualitySettings(_currentSettings);
setState(() {});
}
}
Performance Tips
- Use Quality Presets: Start with presets and adjust based on device capability
- Monitor FPS: Use
stat fpsconsole command during development - Adjust Resolution Scale: Lower resolution scale for better performance
- Disable VSync on Mobile: Can improve responsiveness
- Profile with Unreal Insights: Use Unreal's profiling tools
See QUALITY_SETTINGS_GUIDE.md for detailed optimization tips.
Troubleshooting
Common Issues
Engine not starting:
- Check Unreal framework/APK is properly embedded
- Verify minimum platform versions
- Check logs for errors
Black screen:
- Ensure Unreal view is properly attached
- Check quality settings aren't too high for device
- Verify level is loaded correctly
Messages not received:
- Check FlutterBridge actor is in the level
- Verify message target and method names match
- Check JSON formatting
See TROUBLESHOOTING.md for more solutions.
Documentation
- Setup Guide - Complete setup instructions for all platforms
- Quality Settings Guide - Detailed quality settings reference
- Console Commands - Console command reference
- Level Loading - Level loading and streaming
- Troubleshooting - Common issues and solutions
API Reference
See API documentation on pub.dev.
Requirements
Flutter
- Flutter 3.10.0 or later
- Dart 3.0.0 or later
Unreal Engine
- Unreal Engine 5.3.x or 5.4.x
- Visual Studio 2022 (Windows)
- Xcode 14+ (macOS/iOS)
- Android NDK r25+ (Android)
License
MIT License - see LICENSE file for details.
Contributing
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
Support
Related Packages
- gameframework - Core framework
- gameframework_unity - Unity Engine plugin
Credits
Created and maintained by xraph.
Built with Flutter and Unreal Engine.
Libraries
- gameframework_unreal
- Unreal Engine plugin for Flutter Game Framework