# ar_tourism_flutter_unity
### 1.文字渐变
ShaderMask(
shaderCallback: (Rect bounds) {
return const LinearGradient(
colors: [
Color(0xFF80DAA4),
Color(0xFF79DDED),
],
).createShader(bounds);
},
child: const Text('确定',
style: TextStyle(
color: Colors.white,
fontSize: 18)),
),
# flutter 打包后的文件 build/app/outputs/flutter-apk/app-debug.apk
# **\***不要忘记:online_time_service 文件修改 ws 部署地址**\***
# ndroid/app/src/main/res/xml 目录下创建 network_security_config.xml 文件:创建网络安全配置文件 82.156.153.112
# flutter run --release 运行打包文件
# flutter build apk --target-platform android-arm,android-arm64,android-x64 --split-per-abi 可以减小打包体积
# flutter build apk
# 使用 flutter analyze 命令检查代码中的潜在问题,确保没有冗余的代码和依赖。
# git 大文件上传报错 https://blog.csdn.net/qq_22903677/article/details/138123394
git lfs install
git add .
git commit -m ""
# git push 报错
错误 batch response: LFS only supported repository in paid or trial enterpri
rm .git/hooks/pre-push
git push -u origin "master"
#接口调用
import '../../apis/app.dart';
userApi.getLoginCode(data).then((result) {
if (result['code'] == 200) {
print("发送成功");
Navigator.pushNamed(context, '/tabs');
} else {
print("发送失败");
}
});
# formData 上传文件
// 构建实名认证信息对象
var infoMsg = {
"user_name": \_phoneController.text,
"real_name": \_nameController.text,
"id_card": \_idCardController.text
};
// 将 infoMsg 转换为 JSON 字符串
var infoMsgJson = jsonEncode(infoMsg);
// 获取文件对象
File frontFile = File(\_frontImage!.path);
print("frontFile: $frontFile");
File backFile = File(\_backImage!.path);
print("backFile: $backFile");
// 创建 FormData
FormData formData = FormData.fromMap({
"cardFront": await MultipartFile.fromFile(frontFile.path,
filename: "card_front.png"),
"cardBack": await MultipartFile.fromFile(backFile.path,
filename: "card_back.png"),
"realNameAuthenticationDto": infoMsgJson, // 添加 JSON 字符串
});
print("请求数据: $formData");
// 调用实名认证API
userApi.getRealNameAuth(formData).then((result) {
if (result['code'] == 200) {
print("实名认证成功");
// 跳转到认证成功页面
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const AuthSuccessPage(),
),
);
}
});
# 格式化时间
String \_formatDateTime(String dateTimeString) {
DateTime dateTime = DateTime.parse(dateTimeString).toLocal();
return '${dateTime.year}-${dateTime.month}-${dateTime.day} ${dateTime.hour}:${dateTime.minute}:${dateTime.second}'; // 格式化为 年月日 时分秒
}
# 跳转到底部导航页
Navigator.pushNamedAndRemoveUntil(
context,
'/tabs',
(route) => false,
arguments: {'initialIndex': 1}, // 传递参数,设置初始选中的 tab
);
# 集成 unity
1.baseProjectTemplate.gradle 文件
classpath 'com.android.tools.build:gradle:7.3.0',
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.0"
2.NDK 版本 21.3
#运行清除缓存 adb shell pm clear com.example.ar_tourism_flutter_unity
# unityLibrary 文件夹
1.添加.so 文件 src/main/jniLibs/arm64-v8a/libEasyAR.so libc++\_shared.so
### 每次从 unity 导入 flutter 时要检查:
1.unity--->Player Company Name: com.example Product Name: ar_tourism_flutter_unity
2.unity--> build Settings Export Project 勾选;IL2CPP Code Generation:Faster(smaller)builds
3.unity--> Player -->otherSettings OpenGLES3 下都取消勾选 Auto Graphics API 勾选
4.unity--> Player -->otherSettings Package Name:com.example.ar_tourism_flutter_unity
5.unity--> Player -->Minimum APl Level:Android 5.1 'Lollipop'(APl level 22);Target APl Level:APllevel 34
6.unity--> Player -->Scripting Backend:IL2CPP 勾选 ARM64
7.unity-->Project Settings-->EasyAR/Sence EasyAR Sence License Key:URzW8VUPzu1NaWbUsPbv6VUBDY95e6AyZGz+imEu4NpVPubHYTPxii5/tJAnabCYJWWynSIdtJ4nc+bHeX+pink89txxL87NbRThii5sqYp4NObNei7g2zZn3tM2P/DGcDHg4XAup5JPAKmKYjz3wXUz8ds2Z96KdzLoxWEz7Nxtf9iENi3pyWA76tp5LqeST3/ywXo56t9nf6mKeTzmiklxp8V7OfDEcS6nkk9/9s16LuCGXTDkz3EJ98l3NuzGc3+pimc469txc8bEeyjh+nE+6s96NPHBezOnhDYu4MZnOKv6cT7q2nA06882cafbcTP2zToS58JxPvH8Zjzmw30z4oo4f/bNei7ghkco9851PuD8Zjzmw30z4oo4f/bNei7ghkct5NpnONbYdSnsyXgQ5Ng2cafbcTP2zToQ6tx9Muv8Zjzmw30z4oo4f/bNei7ghlA469txDvXJYDTkxFk89Yo4f/bNei7ghlccwfxmPObDfTPiiklxp81sLezacQnsxXEO8cl5LaeSeijpxDh/7NtYMubJeH+/znUx9s1pcf6KdijrzHg4zMxnf7/zNj7qxTo4/cl5LenNOjz392Ay8Np9Luj3cjHw3GA49/dhM+zcbX/YhDYr5Np9POvcZ3+/8zY+6sV5KOvBYCSn9Th/9cR1KePHZjD2ii4Gp8l6OffHfTmn9Th/6MdwKOnNZ3+/8zYu4MZnOKvheTzizUAv5Mt/NOvPNnGn23Ez9s06HunHYTnXzXcy4sZ9KezHen+pimc469txc9fNdzL3zH0z4oo4f/bNei7ghls/7813KdHadT7uwXo6p4Q2LuDGZzir+2Ev48l3ONHadT7uwXo6p4Q2LuDGZzir+2Q899txDvXJYDTkxFk89Yo4f/bNei7ghlky8cF7M9HadT7uwXo6p4Q2LuDGZzir7HEz9s1HLeTcfTzp5XUtp4Q2LuDGZzir61UZ0dp1Pu7Bejqn9Th/4NBkNPfNQDTozUcp5MVkf7/GYTHphDY09uR7PuTENmfjyXgu4NU4JqfKYTPhxHEU4ds2Z96KNgCpimI898F1M/HbNmfeincy6MVhM+zcbX/YhDYt6clgO+raeS6nkk9/7Mdnf9iENjDqzGEx4Ns2Z96KZzjr23FzzMV1OuD8Zjzmw30z4oo4f/bNei7ghlcx6t1wD+DLezrrwWA06sY2cafbcTP2zToP4Mt7L+HBejqnhDYu4MZnOKvndjfgy2AJ98l3NuzGc3+pimc469txc9bdZjvky3EJ98l3NuzGc3+pimc469txc9bYdS/2zUct5Nx9POnldS2nhDYu4MZnOKvleynsx3oJ98l3NuzGc3+pimc469txc8HNei7g+2Q88cF1McjJZH+pimc469txc8bpUAn3yXc27MZzf9iENjj92H0v4Px9MOD7YDzo2DZn6914MamKfS7Jx3c86YouO+TEZzj49WkSI2M48hCn8PXdlAW8gkVUod0xa+37dOiHJxzEZnb9Yo3mYzP6LHO7eF7u11thk3qJcxk7MRjQFuOc+9AjiOyoEoW+vErOoVFolgphuBc4A2Q05Nd1hEFpx/3xG0ZbuQS5+Uo0hNtfZAiKmM+1XFy9KD3wXluPLXmrZC916jAjwZgp2p0nbsiJEvj57xlXrA7F+IRuCMrgMMav+zdcrMuUAN/8ANbYqXlj6T86dgZqSmkI22nX6P1lYbGJTb0YkvMPDLYaBK5TPfzqnZkb6m647L82gb7nzVCPvLOucFjVAjMG0kwF2xBJ+aEIFbNUBsLN3Dq8YPT4n0I5+SwUXYWo (此 key 为 EasyAR 申请 名字和 key 要对应)
# 因购买 EASY AR 使用 unity 中的即可 无需替换
8.Player-->publishing Settings-->build 下的都勾选
9.MyCamera-->GPS 勾选
# 版本更新
1.修改 yaml 中的版本号 2.修改 Android/app/build.gradle 中的版本号 versionName versionCode 3.打包将 apk 文件拷贝到服务器上
# Flutter 与 Unity 之间的交互说明
## 核心文件说明
FlutterMainActivity 专注处理 Flutter 相关的逻辑和生命周期
UnityMainActivity 专注处理 Unity 相关的渲染和交互
这种分离使得代码更清晰,维护更容易
### 1. FlutterMainActivity.kt
这是 Flutter 应用的主 Activity 文件,主要功能包括:
- 负责 Flutter 和 Unity 之间的桥接通信
- 管理 Unity 的初始化和启动
- 处理权限请求和结果
- 提供方法通道(MethodChannel)供 Flutter 调用原生方法
- 管理位置服务的生命周期
- 处理 Unity 活动的返回结果
主要方法:
- `configureFlutterEngine`: 配置 Flutter 引擎和方法通道
- `startUnityActivity`: 启动 Unity 活动
- `checkAndRequestPermissions`: 检查和请求必要权限
- `cleanupLocationService`: 清理位置服务资源
### 2. UnityMainActivity.kt
这是 Unity 应用的主 Activity 文件,主要功能包括:
- 继承自 UnityPlayerActivity,负责 Unity 内容的显示和控制
- 管理 Unity 播放器的生命周期
- 处理 Unity 和 Flutter 之间的数据传递
- 提供 Unity 返回 Flutter 的机制
- 处理 Unity 资源的清理和释放
主要方法:
- `onCreate`: 初始化 Unity 环境和方法通道
- `setupMethodChannel`: 设置与 Flutter 通信的方法通道
- `handleExitUnity`: 处理 Unity 退出逻辑
- `onBackPressed`: 处理返回按键事件
## 交互流程
1. Flutter 通过 MethodChannel 调用 FlutterMainActivity 中的方法
2. FlutterMainActivity 处理请求并启动 UnityMainActivity
3. UnityMainActivity 加载 Unity 内容并处理 Unity 相关的操作
4. Unity 操作完成后,通过 UnityMainActivity 返回结果给 FlutterMainActivity
5. FlutterMainActivity 将结果通过 MethodChannel 传回 Flutter
## 注意事项
- 两个 Activity 之间的通信需要严格管理生命周期
- Unity 资源的加载和释放需要合理处理,避免内存泄漏
- 权限请求和结果处理需要完整的错误处理机制
- 位置服务的启动和停止需要在合适的时机进行
- Activity 切换时需要确保资源的正确释放和重新初始化
# iOS 与 Unity 集成指南
## iOS 核心文件说明
与 Android 平台的 Activity 分离不同,iOS 使用委托模式处理 Unity 与 Flutter 的集成。
### 1. AppDelegate.swift
这是 iOS 应用的主委托文件,相当于 Android 的 FlutterMainActivity,功能包括:
- 应用程序入口点
- 注册 Flutter 插件和 Unity 插件
- 初始化 Unity 框架
- 设置消息通道
- 管理应用生命周期
主要方法:
- `application(_:didFinishLaunchingWithOptions:)`: 应用启动初始化
- `initUnityFramework`: 初始化 Unity 框架
- `registerUnityViewFactory`: 注册 Unity 视图工厂
- `setupUnityMessageHandlers`: 设置 Unity 消息处理器
### 2. UnityPlugin.swift
作为 Flutter 和 Unity 之间的桥接器,相当于 Android 的 UnityMainActivity 的部分功能:
- 处理来自 Flutter 的方法调用
- 管理 Unity 框架的初始化与关闭
- 处理 Unity 场景的加载
- 发送消息到 Unity
主要方法:
- `handle(_:result:)`: 处理 Flutter 的方法调用
- `initializeUnity`: 初始化 Unity
- `startUnity`: 启动 Unity
- `loadUnityScene`: 加载 Unity 场景
- `closeUnity`: 关闭 Unity
### 3. UnityFrameworkLoader.swift
负责加载 Unity 框架的辅助类:
- 查找和加载 UnityFramework
- 初始化 Unity 运行环境
### 4. UnityViewFactory.swift
用于创建和管理 Unity 视图:
- 创建 Flutter 平台视图
- 管理 Unity 视图控制器
- 处理视图的生命周期
### 5. AppDelegate+Unity.swift
AppDelegate 的扩展,处理 Unity 回调:
- 接收 Unity 消息
- 处理 Unity 返回按钮事件
- 管理 Unity 关闭事件
## iOS 与 Unity 集成配置
### Podfile 配置
```ruby
platform :ios, '12.0'
use_frameworks! # 使用动态框架
target 'Runner' do
# Flutter Pods
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
# Unity集成
pod 'UnityLibrary', :path => 'UnityLibrary'
end
post_install do |installer|
installer.pods_project.targets.each do |target|
# 框架设置
target.build_configurations.each do |config|
config.build_settings['ENABLE_BITCODE'] = 'NO'
# Unity特定设置
if target.name == 'UnityLibrary'
config.build_settings['UNITY_RUNTIME_VERSION'] = '2021.3.33f1fc'
config.build_settings['UNITY_IOS_EXPORT_PATH'] = '${SRCROOT}/UnityLibrary'
end
end
end
end
```
### Unity 导出设置(iOS)
1. 在 Unity 中选择`File > Build Settings > iOS`
2. 配置项:
- 确保`Export Project`被勾选
- 脚本后端选择`IL2CPP`
- 勾选`Development Build`用于调试
- 目标架构选择`ARM64`
- 在`Player Settings > Other Settings`中:
- 设置 Bundle Identifier: `com.example.arTourismFlutterUnity`
- 最低 iOS 版本: 12.0
- 设置必要的权限(Camera, Location 等)
3. 导出项目后,将整个导出的 Unity-iPhone 项目复制到 Flutter 项目的`ios/UnityLibrary`目录
### Info.plist 配置
确保 Info.plist 文件包含所有必要的权限描述:
```xml
NSCameraUsageDescription
此应用需要使用相机进行AR体验
NSPhotoLibraryUsageDescription
需要相册权限用于保存和选择媒体
NSMicrophoneUsageDescription
此应用需要访问您的麦克风以提供录像服务
NSLocationWhenInUseUsageDescription
此应用需要访问您的定位以提供导航服务
NSLocationAlwaysUsageDescription
App需要您的同意,才能始终访问位置
```
## 跨平台数据交互
Unity 与 Flutter 之间通过 MethodChannel 进行数据交互:
```dart
// 在Flutter中
static const platform = MethodChannel('com.example.ar_tourism_flutter_unity.unity');
// 调用Unity方法
await platform.invokeMethod('start_unity', {
'token': userToken,
'sceneName': 'Main',
'returnToHome': true
});
// 接收Unity消息
platform.setMethodCallHandler((call) async {
switch (call.method) {
case 'onUnityMessage':
// 处理来自Unity的消息
break;
case 'onUnityBackPressed':
// 处理Unity返回事件
break;
}
});
```
## iOS 打包流程
1. 更新版本号:
- 在`pubspec.yaml`中更新版本号
- 在 Xcode 中更新版本号和构建号
2. 配置签名和证书:
- 在 Xcode 中配置正确的签名证书和配置文件
3. 构建和归档:
```
flutter build ios --release
```
然后在 Xcode 中进行归档和上传到 App Store
## 注意事项
1. 内存管理:iOS 系统内存限制较严格,需确保 Unity 资源及时释放
2. Framework 路径:确保 UnityFramework.framework 在正确的路径并被正确引用
3. 权限处理:提前在 Info.plist 中配置所有必要权限
4. Bitcode 禁用:Unity 不支持 Bitcode,需在项目配置中禁用
5. 架构兼容:确保所有库使用兼容的架构(推荐仅使用 ARM64)
# iOS
包名:com.example.arTourismFlutterUnity
打开 Xcode 项目:
Apply to README.md
Run
在 Xcode 中,右键点击项目导航器中的项目根目录
选择"Add Files to 'Runner'..."
导航到项目的 ios/UnityLibrary 目录
确保选中"Create folder references"(创建文件夹引用)选项
点击"Add"按钮
Flutter 3.19.3 ; Flutter Unity Widget 2022.2.1 ; Xcode16.3 ;build version 16E140;pod 1.16.2;