flutter_unity_widget
Flutter unity 3D widget for embedding unity in flutter. Now you can make awesome gamified features of your app in Unity and get it rendered in a Flutter app both in fullscreen and embeddable mode. Works great on Android, iPad OS and iOS. There are now two unity app examples in the unity folder, one with the default scene and another based on Unity AR foundation samples.
Note: Supports only Unity 2019.4.3 or later. UnityFramework does not support emulator.
Installation
First depend on the library by adding this to your packages pubspec.yaml
:
dependencies: flutter_unity_widget: ^4.1.0
Null-safe version:
dependencies: flutter_unity_widget: ^4.2.1
Now inside your Dart code you can import it.
import 'package:flutter_unity_widget/flutter_unity_widget.dart';
Preview
30 fps gifs, showcasing communication between Flutter and Unity:
Setup
For this, there is also a video tutorial, which you can find a here.
In the tutorial below, there are steps specific to each platform, denoted by a
Prerequisites
- An existing Flutter project (if there is none, you can create a new one)
- An existing Unity project (if there is none, you can create a new one).
- A
FlutterUnityPackage.unitypackage
file (you can access the Unity packages in the scripts folder too)
Steps
- Create a folder named unity and move the Unity project into there.
The expected path is unity/project-name/…
- Copy the FlutterUnityPackage.unitypackage file into the Unity project folder.
The expected path is unity/project-name/FlutterUnityPackage.unitypackage
- Using Unity, open the Unity project, go to File > Build Settings > Player Settings and change the following under the Configuration section:
- In Scripting Backend, change to IL2CPP
- In Target Architectures, select ARMv7 and ARM64
Be sure you have at least one scene added to your build.
- Go to Assets > Import Package > Custom Package and select the FlutterUnityPackage.unitypackage file. Click on Import.
- After importing, click on Flutter and select the Export Android option (will export to android/unityLibrary) or the Export iOS option (will export to ios/UnityLibrary).
Do not use Flutter > Export Platform plugin as it was specially added to work with
flutter_unity_cli
for larger projects.
Setup AR Foundation
Check out the Unity AR Foundation samples in the demo repository. This repository is not guaranteed to be up-to-date with the latest
flutter-unity-view-widget
master. Make sure to follow the steps listed below for setting up AR Foundation on iOS and Android in your project.
Setup Vuforia
Thanks to @PiotrxKolasinski for writing down the exact steps:
- Open the android/unityLibrary/build.gradle file and change the following:
- implementation(name: 'VuforiaWrapper', ext: 'aar') + implementation project(':VuforiaWrapper')
- Using Android Studio, go to File > Open and select the android/ folder. A new project will open.
Don’t worry if the error message “Project with path ‘:VuforiaWrapper’ could not be found in project ‘:unityLibrary'” appears. The next step will fix it.
- In this new project window, go to File > New > New Module > Import .JAR/.AAR package and select the android/unityLibrary/libs/VuforiaWrapper.aar file. A new folder named VuforiaWrapper will be created inside android/. You can now close this new project window.
Communicating
Flutter-Unity
- On a
UnityWidget
widget, get theUnityWidgetController
received by theonUnityCreated
callback. - Use the method
postMessage
to send a string, using the GameObject name and the name of a behaviour method that should be called.
Unity-Flutter
- Select the GameObject that should execute the communication and go to Inspector > Add Component > Unity Message Manager.
- Create a new
MonoBehaviour
subclass and add to the same GameObject as a script. - On this new behaviour, call
GetComponent<UnityMessageManager>()
to get aUnityMessageManager
. - Use the method
SendMessageToFlutter
to send a string. Receive this message using theonUnityMessage
callback of aUnityWidget
.
Troubleshooting
Location: Unity
Error:
InvalidOperationException: The build target does not support build appending.
Solution:
- Open the unity/project-name/Assets/FlutterUnityIntegration/Editor/Build.cs file.
1.1. On line 48, change the following:
- var options = BuildOptions.AcceptExternalModificationsToPlayer; + var options = BuildOptions.AllowDebugging; + EditorUserBuildSettings.exportAsGoogleAndroidProject = true;
1.2. On line 115, change the following:
- var options = BuildOptions.AcceptExternalModificationsToPlayer; + var options = BuildOptions.AllowDebugging;
Location: Android Studio
Error:
minSdkVersion XX cannot be smaller than version 19 declared in library
\ [:flutter_unity_widget] .../AndroidManifest.xml as the library might be using
\ APIs not available in XX
Solution:
- Open the android/app/build.gradle file and change the following:
- minSdkVersion XX + minSdkVersion 19
Location: Android Studio
Error:
e: .../FlutterUnityWidgetBuilder.kt: (15, 42): Expecting a parameter declaration
e: .../FlutterUnityWidgetBuilder.kt: (23, 25): Expecting an argument
e: .../FlutterUnityWidgetController.kt: (22, 44): Expecting a parameter declaration
e: .../FlutterUnityWidgetFactory.kt: (13, 58): Expecting a parameter declaration
Solution:
- Open the android/build.gradle file and change the following:
- ext.kotlin_version = '1.3.50' + ext.kotlin_version = '1.4.31'
Location: Android Studio
Error:
Unable to find a matching variant of project :unityLibrary:
Solution:
- Open the android/app/build.gradle file and change the following:
lintOptions { disable 'InvalidPackage' + checkReleaseBuilds false }
Examples
Simple Example
import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_unity_widget/flutter_unity_widget.dart'; void main() { runApp(MaterialApp( home: UnityDemoScreen() )); } class UnityDemoScreen extends StatefulWidget { UnityDemoScreen({Key key}) : super(key: key); @override _UnityDemoScreenState createState() => _UnityDemoScreenState(); } class _UnityDemoScreenState extends State<UnityDemoScreen>{ static final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>(); UnityWidgetController _unityWidgetController; Widget build(BuildContext context) { return Scaffold( key: _scaffoldKey, body: SafeArea( bottom: false, child: WillPopScope( onWillPop: () { // Pop the category page if Android back button is pressed. }, child: Container( color: colorYellow, child: UnityWidget( onUnityCreated: onUnityCreated, ), ), ), ), ); } // Callback that connects the created controller to the unity controller void onUnityCreated(controller) { this._unityWidgetController = controller; } }
Communicating with and from Unity
import 'package:flutter/material.dart'; import 'package:flutter_unity_widget/flutter_unity_widget.dart'; void main() => runApp(MyApp()); class MyApp extends StatefulWidget { @override _MyAppState createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { static final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>(); UnityWidgetController _unityWidgetController; double _sliderValue = 0.0; @override void initState() { super.initState(); } @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( key: _scaffoldKey, appBar: AppBar( title: const Text('Unity Flutter Demo'), ), body: Card( margin: const EdgeInsets.all(8), clipBehavior: Clip.antiAlias, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20.0), ), child: Stack( children: <Widget>[ UnityWidget( onUnityCreated: onUnityCreated, onUnityMessage: onUnityMessage, onUnitySceneLoaded: onUnitySceneLoaded, fullscreen: false, ), Positioned( bottom: 20, left: 20, right: 20, child: Card( elevation: 10, child: Column( children: <Widget>[ Padding( padding: const EdgeInsets.only(top: 20), child: Text("Rotation speed:"), ), Slider( onChanged: (value) { setState(() { _sliderValue = value; }); setRotationSpeed(value.toString()); }, value: _sliderValue, min: 0, max: 20, ), ], ), ), ), ], ), ), ), ); } // Communcation from Flutter to Unity void setRotationSpeed(String speed) { _unityWidgetController.postMessage( 'Cube', 'SetRotationSpeed', speed, ); } // Communication from Unity to Flutter void onUnityMessage(message) { print('Received message from unity: ${message.toString()}'); } // Callback that connects the created controller to the unity controller void onUnityCreated(controller) { this._unityWidgetController = controller; } // Communication from Unity when new scene is loaded to Flutter void onUnitySceneLoaded(SceneLoaded sceneInfo) { print('Received scene loaded from unity: ${sceneInfo.name}'); print('Received scene loaded from unity buildIndex: ${sceneInfo.buildIndex}'); } }
Props
fullscreen
(Enable or disable fullscreen mode on Android)
API
pause()
(Use this to pause unity player)resume()
(Use this to resume unity player)unload()
(Use this to unload unity player) *Requires Unity 2019.4.3 or laterquit()
(Use this to quit unity player)postMessage(String gameObject, methodName, message)
(Allows you invoke commands in Unity from flutter)onUnityMessage(data)
(Unity to flutter binding and listener)onUnityUnloaded()
(Unity to flutter listener when unity is unloaded)onUnitySceneLoaded(String name, int buildIndex, bool isLoaded, bool isValid,)
(Unity to flutter binding and listener when new scene is loaded)
Known issues
- Remember to disabled fullscreen in unity player settings to disable unity fullscreen.
- Project fails to build due to some native dependencies in your unity project, please integrate the native libraries for those dependencies on Android or iOS
- App crashes on screen exit and re-entry do thisBuild Setting – iOS – Other Settings – Configuration – Enable Custom Background Behaviors
Download flutter unity game widget source code on GitHub
Provides the list of the opensource Flutter apps collection with GitHub repository.