UnityAR接入完整Flutter项目尝试
前言:
因为Flutter集成Unity时所使用的插件对Flutter和Dart版本有要求,无法适配新版的Flutter,也就无法适配和新版Flutter一起的Dart3.0,所以第一步其实是先对项目组其他人写的Flutter项目进行降级,将Flutter3.27.4降级为Flutter3.3.10,同时其对应的Dart也从3.6.2降级为2.18.6,再将Unity项目在合适位置接入。
1.降级时遇到的问题
主要有两个,一是语法不兼容,Dart3.0和Dart2.0写法可能不一样,所以不能直接复制粘贴,需要检查并手动修改这些不兼容的语法,把3.0退回到2.0的风格。二是项目依赖的包可能不兼容,新Flutter项目可能依赖了一些只支持Dart3.0的包,如果有包不支持Dart2.18,需要修改pubspec.yaml中的依赖版本,降级到Dart 2支持的版本。所以我选择在粘贴代码时查看修改语法,并手动添加依赖。
不过降级过程中只遇到了两处错误:
1.Dart2.0和3.0不兼容
//lib/main.dart
onGenerateRoute: (RouteSettings settings) {
DebugLog.info('Generating route for: ${settings.name} with arguments: ${settings.arguments}');
switch (settings.name) {
case '/':
return MaterialPageRoute(builder: (context) => MainScaffold());
case '/details':
if (settings.arguments is Map<String, dynamic>) {
final args = settings.arguments as Map<String, dynamic>;
final int? itemId = args['id'] as int?; // 安全地获取和转换参数
if (itemId != null) {
return null;
}
}
case '/settings':
default:
}
return null;
},
在 Dart 2 版本里,switch-case 不允许case 穿透(fall-through),也就是说:
- 每个 case 代码块必须用 break、return、throw 或类似语句明确终止。
- 如果没有,编译器会报错。
但是Dart 3 引入了对 switch 语句更灵活的语法支持,特别是支持了模式匹配的增强,并且允许了更加灵活的 case 块语法。具体体现在:
- Dart 3 允许 case 代码块不写 break 或 return,只要不发生隐式穿透。
- 只要没有明确的穿透行为,Dart 3 会自动理解控制流的结束。
- 有点类似其他现代语言中更灵活的 switch-case。
所以修改加上明确的终止语句,如下:
//lib/mian.dart
onGenerateRoute: (RouteSettings settings) {
DebugLog.info('Generating route for: ${settings.name} with arguments: ${settings.arguments}');
switch (settings.name) {
case '/':
return MaterialPageRoute(builder: (context) => MainScaffold());
case '/details':
if (settings.arguments is Map<String, dynamic>) {
final args = settings.arguments as Map<String, dynamic>;
final int? itemId = args['id'] as int?;
if (itemId != null) {
// 这里应该返回一个有效的页面路由:
return MaterialPageRoute(builder: (context) => DetailsPage(itemId: itemId));
} else {
// 如果itemId为空,可以返回错误页面或首页,避免返回null
return MaterialPageRoute(builder: (context) => MainScaffold());
}
} else {
// 参数类型不对,跳转默认页面
return MaterialPageRoute(builder: (context) => MainScaffold());
}
case '/settings':
return MaterialPageRoute(builder: (context) => SettingPage());
default:
// 未匹配到路由时,显示一个默认页面或返回null
return MaterialPageRoute(
builder: (context) => Scaffold(
appBar: AppBar(title: Text('页面未找到')),
body: Center(child: Text('未找到路由: ${settings.name}')),
));
}
},
//lib/details_page.dart
import 'package:flutter/material.dart';
import 'package:lingo_pals/utils/app_colors.dart';
class DetailsPage extends StatelessWidget {
final int itemId;
const DetailsPage({required this.itemId, Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('详情页')),
body: Center(
child: Text(
'Item ID: $itemId',
style: TextStyle(
fontSize: 24,
color: AppColors.textPrimary,
),
),
),
);
}
}
2.Flutter渲染问题
在新版本中,emoji 被自动缩放为适合 Text 所在布局的默认字体大小。 在旧版本中,emoji 被当作普通字符处理,使用系统字体直接渲染,导致大小不一致、对齐失常等问题。正常应该是P2:

先找到渲染emoji的代码
Text('👓', style: TextStyle(fontSize: 80)),
给 emoji 加入固定字体 + 调整大小
Text(
'👓',
style: TextStyle(
fontSize: 60,
fontFamilyFallback: ['EmojiOne', 'NotoColorEmoji', 'AppleColorEmoji'], // 针对不同平台 fallback
),
),
然后把所有有关emoji的地方找到并改成新代码即可
2.接入Unity页面
使用插件的方法前文已经写过,在该项目里重新配置一遍就行。然后将Unity界面接到项目的AR实景界面:
先添加一个打开unity窗口的按钮
//lib/ar_page.dart
import 'package:flutter/material.dart';
import 'package:lingo_pals/view/android/base_widget/base_app_bar.dart';
import 'package:lingo_pals/view/android/base_widget/new_scaffold.dart';
import 'package:lingo_pals/view/android/feature_pages/unity_view_page.dart';
import '../../../utils/app_colors.dart';
class ArPage extends StatelessWidget{
const ArPage({super.key});
@override
Widget build(BuildContext context) {
return BasePage(
appBar: BaseAppBar(
titleWidget: Container(),
actions: [],
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () => Navigator.of(context).pop(),
),
),
pageBody: ArField()
);
}
}
class ArField extends StatelessWidget {
const ArField({super.key});
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'👓',
style: TextStyle(
fontSize: 60,
fontFamilyFallback: ['EmojiOne', 'NotoColorEmoji', 'AppleColorEmoji'], // 针对不同平台 fallback
),
),
SizedBox(height: 20),
Text(
'AR页面',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: AppColors.textPrimary,
),
),
SizedBox(height: 10),
Text(
'这里将启动AR功能',
style: TextStyle(
fontSize: 16,
color: AppColors.textSecondary,
),
),
const SizedBox(height: 30),
ElevatedButton.icon(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (_) => const UnityViewPage()),
);
},
icon: const Icon(Icons.play_arrow),
label: const Text('启动 AR 功能'),
),
],
),
);
}
}
启动unity窗口渲染:
//lib/unity_view_page.dart
import 'package:flutter/material.dart';
import 'package:flutter_unity_widget/flutter_unity_widget.dart';
class UnityViewPage extends StatefulWidget {
const UnityViewPage({Key? key}) : super(key: key);
@override
State<UnityViewPage> createState() => _UnityDemoScreenState();
}
class _UnityDemoScreenState extends State<UnityViewPage> {
static final GlobalKey<ScaffoldState> _scaffoldKey =
GlobalKey<ScaffoldState>();
UnityWidgetController? _unityWidgetController;
@override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
body: SafeArea(
bottom: false,
child: WillPopScope(
onWillPop: () async {
return true;
},
child: Container(
color: Colors.yellow,
child: UnityWidget(
onUnityCreated: onUnityCreated,
),
),
),
),
);
}
void onUnityCreated(controller) {
_unityWidgetController = controller;
}
}
3.效果
接入项目
更多推荐

所有评论(0)