flutter_unity_widget
用于在Flutter中嵌入Unity的Flutter Unity 3D Widget。现在您可以在Unity中制作应用程序的精彩游戏化功能,并使其在全屏和可嵌入模式下都能在Flutter应用程序中渲染。在Android、iPad OS和iOS上运行良好。Unity文件夹中现在有两个Unity应用程序示例,一个带有默认场景,另一个基于Unity AR Foundation示例。
注意:仅支持Unity 2019.4.3或更高版本。UnityFramework不支持模拟器。
安装
首先,通过将此添加到您的`pubspec.yaml`文件中来依赖该库
dependencies:
flutter_unity_widget: ^4.1.0
空安全版本
dependencies:
flutter_unity_widget: ^4.2.1
现在,您可以在Dart代码中导入它。
import 'package:flutter_unity_widget/flutter_unity_widget.dart';
预览
30 fps的gif,展示了Flutter和Unity之间的通信


设置
为此,还有一个视频教程,您可以在此处找到。
在下面的教程中,有特定于每个平台的步骤,用:information_source:图标后跟
平台名称(Android或iOS)表示。您可以点击其图标进行展开。
先决条件
-
一个现有的Flutter项目(如果没有,您可以创建一个新的)
-
一个现有的Unity项目(如果没有,您可以创建一个新的)。
-
一个
FlutterUnityPackage.unitypackage
文件(您也可以在scripts文件夹中访问Unity包)
步骤
- 创建一个名为*unity*的文件夹,并将Unity项目移入其中。
预期的路径是*unity/project-name/...*
- 将*FlutterUnityPackage.unitypackage*文件复制到Unity项目文件夹中。
预期的路径是*unity/project-name/FlutterUnityPackage.unitypackage*
- 使用Unity,打开Unity项目,转到**文件>构建设置>玩家设置**
并在**配置**部分更改以下设置
-
在**脚本后端**中,更改为IL2CPP
-
在**目标架构**中,选择ARMv7和ARM64
:information_source: iOS
在**目标SDK**中选择适当的SDK,取决于您要在何处测试或运行应用程序(模拟器或物理设备)。
确保您的构建中至少有一个场景。
-
转到**Assets > Import Package > Custom Package**并选择
FlutterUnityPackage.unitypackage文件。点击**导入**。 -
导入后,点击**Flutter**并选择**导出Android**选项(将导出到*android/unityLibrary*)或**导出iOS**
选项(将导出到*ios/UnityLibrary*)。
不要使用**Flutter > 导出*平台*插件**,因为它专门用于与
flutter_unity_cli配合使用,适用于大型项目。
:information_source: Android
导出脚本会自动为您设置好一切,因此您无需为Android做任何事情。但如果您想手动设置,请继续。
6.1。打开*android/settings.gradle*文件并进行以下更改
+ include ":unityLibrary"
+ project(":unityLibrary").projectDir = file("./unityLibrary")
6.2。打开*android/app/build.gradle*文件并进行以下更改
dependencies {
+ implementation project(':unityLibrary')
}
6.3。如果您需要在*android/app/build.gradle*文件中构建发布包,请打开*android/app/build.gradle*文件并进行以下更改
buildTypes {
release {
signingConfig signingConfigs.debug
}
+ debug {
+ signingConfig signingConfigs.debug
+ }
+ profile {
+ signingConfig signingConfigs.debug
+ }
+ innerTest {
+ matchingFallbacks = ['debug', 'release']
+ }
+ }
上面的代码对所有buildTypes都使用了
debugsignConfig,如果您需要指定signConfig,可以根据需要更改。
6.4。如果您在*android/app/build.gradle*文件中使用了minifyEnabled true,请打开*android/unityLibrary/proguard-unity.txt*并进行以下更改
+ -keep class com.xraph.plugin.** {*;}
6.5。如果您想让Unity在其自己的活动中作为替代方案,请打开*android/app/src/main/AndroidManifest.xml*并进行以下更改
+ <activity
+ android:name="com.xraph.plugin.flutter_unity_widget.OverrideUnityActivity"
+ android:theme="@style/UnityThemeSelector"
+ android:screenOrientation="fullSensor"
+ android:launchMode="singleTask"
+ android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale|layoutDirection|density"
+ android:hardwareAccelerated="false"
+ android:process=":Unity">
+ <meta-data android:name="com.xraph.plugin.flutter_unity_widget.OverrideUnityActivity" android:value="true" />
+ </activity>
:information_source: iOS
6.1。在Xcode中打开*ios/Runner.xcworkspace*(工作空间,而不是项目)文件,右键单击导航器(而不是在某个项目上),然后转到**Add Files to "Runner"**并添加
ios/UnityLibrary/Unity-Iphone.xcodeproj文件。
6.2。(可选)选择*Unity-iPhone/Data*文件夹,并将Data文件夹的Target Membership更改为UnityFramework。
6.3.1。如果您使用的是Swift,请打开*ios/Runner/AppDelegate.swift*文件并进行以下更改
import UIKit
import Flutter
+ import flutter_unity_widget
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
+ InitUnityIntegrationWithOptions(argc: CommandLine.argc, argv: CommandLine.unsafeArgv, launchOptions)
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
6.3.2。如果您使用的是Objective-C,请打开*ios/Runner/main.m*文件并进行以下更改
+ #import "flutter_unity_widget.swift.h"
int main(int argc, char * argv[]) {
@autoreleasepool {
+ InitUnityIntegration(argc, argv);
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
6.4。打开*ios/Runner/Info.plist*并进行以下更改
<dict>
+ <key>io.flutter.embedded_views_preview</key>
+ <string>YES</string>
</dict>
6.5。将*UnityFramework.framework*文件作为库添加到Runner项目中。
设置AR Foundation

查看演示仓库中的Unity AR Foundation示例。
此仓库不保证与最新的flutter-unity-view-widgetmaster保持同步。请确保遵循
以下步骤来为您的项目设置iOS和Android上的AR Foundation。
:information_source: Android
-
打开*lib/architecture/*文件夹,并检查是否存在*libUnityARCore.so*和*libarpresto_api.so*文件。
似乎存在一个bug,Unity导出不包含所有lib文件。如果缺失,请使用Unity构建独立的.apk
您的AR项目,解压缩生成的apk,并将缺少的.lib文件复制到unityLibrary模块。 -
对Android重复步骤6.1和6.2,将
unityLibrary替换为arcore_client、unityandroidpermissions和UnityARCore。 -
使用Flutter中的
UnityWidget时,将fullscreen: false设置为禁用全屏。
:information_source: iOS
- 打开*ios/Runner/Info.plist*并进行以下更改
<dict>
+ <key>Privacy - Camera Usage Description</key>
+ <string>$(PRODUCT_NAME) uses Cameras</string>
</dict>
设置Vuforia
感谢@PiotrxKolasinski记录了确切的步骤
- 打开*android/unityLibrary/build.gradle*文件并进行以下更改
- implementation(name: 'VuforiaWrapper', ext: 'aar')
+ implementation project(':VuforiaWrapper')
- 使用Android Studio,转到**文件>打开**并选择*android/*文件夹。一个
新项目将打开。
如果出现错误消息“Project with path ':VuforiaWrapper' could not be
found in project ':unityLibrary'”,请不用担心。下一步将修复它。
- 在此新项目窗口中,转到**文件>新建>新建模块>导入.JAR/.AAR包**
并选择*android/unityLibrary/libs/VuforiaWrapper.aar*文件。一个名为
VuforiaWrapper的新文件夹将创建在*android/*内。您现在可以关闭这个
新项目窗口。
通信
Flutter-Unity
-
在
UnityWidget小部件上,获取在onUnityCreated回调中接收到的UnityWidgetController。 -
使用
postMessage方法发送一个字符串,使用GameObject的名称和一个应该被调用的行为方法的名称。
Unity-Flutter
- 选择应该执行通信的GameObject,然后转到**Inspector > Add Component > Unity Message Manager**。
-
创建一个新的
MonoBehaviour子类,并将其作为脚本添加到同一个GameObject上。 -
在此新行为中,调用
GetComponent<UnityMessageManager>()来获取一个UnityMessageManager。 -
使用
SendMessageToFlutter方法发送一个字符串。使用UnityWidget的onUnityMessage回调来接收此消息。
故障排除
位置: Unity
错误
InvalidOperationException: The build target does not support build appending.
解决方案
- 打开*unity/project-name/Assets/FlutterUnityIntegration/Editor/Build.cs*文件。
1.1。在第48行,进行以下更改
- var options = BuildOptions.AcceptExternalModificationsToPlayer;
+ var options = BuildOptions.AllowDebugging;
+ EditorUserBuildSettings.exportAsGoogleAndroidProject = true;
1.2。在第115行,进行以下更改
- var options = BuildOptions.AcceptExternalModificationsToPlayer;
+ var options = BuildOptions.AllowDebugging;
位置: Android Studio
错误
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
解决方案
- 打开*android/app/build.gradle*文件并进行以下更改
- minSdkVersion XX
+ minSdkVersion 19
位置:Android Studio
错误
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
解决方案
- 打开*android/build.gradle*文件并进行以下更改
- ext.kotlin_version = '1.3.50'
+ ext.kotlin_version = '1.4.31'
位置: Android Studio
错误
Unable to find a matching variant of project :unityLibrary:
解决方案
- 打开*android/app/build.gradle*文件并进行以下更改
lintOptions {
disable 'InvalidPackage'
+ checkReleaseBuilds false
}
示例
简单示例
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;
}
}
与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(在Android上启用或禁用全屏模式)
API
pause()(使用此方法暂停Unity播放器)resume()(使用此方法恢复Unity播放器)unload()(使用此方法卸载Unity播放器)*需要Unity 2019.4.3或更高版本*quit()(使用此方法退出Unity播放器)postMessage(String gameObject, methodName, message)(允许您从Flutter调用Unity中的命令)onUnityMessage(data)(Unity到Flutter的绑定和监听器)onUnityUnloaded()(Unity到Flutter的监听器,当Unity被卸载时)onUnitySceneLoaded(String name, int buildIndex, bool isLoaded, bool isValid,)(Unity到Flutter的绑定和监听器,当新场景加载时)
已知问题
- 请记住在Unity播放器设置中禁用全屏以禁用Unity全屏。
- 由于您的Unity项目中的某些原生依赖项,项目构建失败,请在Android或iOS上集成这些依赖项的原生库
- 屏幕退出和重新进入时崩溃,请执行此操作
构建设置 - iOS - 其他设置 - 配置 - 启用自定义后台行为