You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
701 lines
24 KiB
701 lines
24 KiB
import 'package:flutter/material.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
|
import 'dart:ui';
|
|
import '../controllers/home_controller.dart';
|
|
import '../../../routes/app_pages.dart';
|
|
import '../../../routes/app_routes.dart';
|
|
import '../../../core/widgets/common_bottom_nav.dart';
|
|
|
|
// 导入枚举
|
|
import '../controllers/home_controller.dart' show ConnectionStatus;
|
|
|
|
// 呼吸光动画组件
|
|
class AnimatedPulsing extends StatefulWidget {
|
|
@override
|
|
_AnimatedPulsingState createState() => _AnimatedPulsingState();
|
|
}
|
|
|
|
class _AnimatedPulsingState extends State<AnimatedPulsing> with SingleTickerProviderStateMixin {
|
|
late AnimationController _controller;
|
|
late Animation<double> _animation;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_controller = AnimationController(
|
|
duration: Duration(seconds: 2),
|
|
vsync: this,
|
|
)..repeat(reverse: true);
|
|
_animation = Tween<double>(begin: 0.5, end: 0.8).animate(
|
|
CurvedAnimation(
|
|
parent: _controller,
|
|
curve: Curves.easeInOut,
|
|
),
|
|
);
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_controller.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return AnimatedBuilder(
|
|
animation: _animation,
|
|
builder: (context, child) {
|
|
return Opacity(
|
|
opacity: _animation.value,
|
|
child: Container(
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular(16.r),
|
|
gradient: RadialGradient(
|
|
colors: [
|
|
Colors.white.withOpacity(0.3),
|
|
Colors.transparent,
|
|
],
|
|
center: Alignment.topRight,
|
|
radius: 0.8,
|
|
stops: [0.0, 1.0],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
class HomeView extends GetView<HomeController> {
|
|
const HomeView({Key? key}) : super(key: key);
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final bool isDarkMode = Theme.of(context).brightness == Brightness.dark;
|
|
|
|
return Scaffold(
|
|
backgroundColor: isDarkMode ? Colors.black : Color(0xFFF9FAFC),
|
|
body: SafeArea(
|
|
child: SingleChildScrollView(
|
|
padding: EdgeInsets.all(16.w),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
// 顶部标题栏
|
|
_buildAppBar(isDarkMode),
|
|
|
|
// 顶部图片
|
|
Center(
|
|
child: _buildEarbudsImage(),
|
|
),
|
|
SizedBox(height: 20.h),
|
|
|
|
// 设备状态卡片
|
|
_buildDeviceStatusCard(isDarkMode),
|
|
SizedBox(height: 20.h),
|
|
|
|
// 热门AI体验
|
|
_buildSectionTitle(isDarkMode),
|
|
SizedBox(height: 16.h),
|
|
_buildPremiumAILayout(),
|
|
|
|
// 在合适的位置添加测试按钮
|
|
ElevatedButton(
|
|
onPressed: () => Get.toNamed(Routes.ASR_TEST),
|
|
child: const Text('Azure语音识别测试'),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
bottomNavigationBar: const CommonBottomNav(currentIndex: 0),
|
|
);
|
|
}
|
|
|
|
// 顶部标题栏
|
|
Widget _buildAppBar(bool isDarkMode) {
|
|
return Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Text(
|
|
'DeepVoice',
|
|
style: TextStyle(
|
|
fontSize: 20.sp,
|
|
fontWeight: FontWeight.bold,
|
|
color: isDarkMode ? Colors.white : Colors.grey[800],
|
|
),
|
|
),
|
|
Container(
|
|
width: 32.w,
|
|
height: 32.w,
|
|
decoration: BoxDecoration(
|
|
color: isDarkMode ? Colors.grey[800] : Colors.grey[200],
|
|
shape: BoxShape.circle,
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.grey.withOpacity(0.2),
|
|
blurRadius: 4,
|
|
offset: const Offset(0, 2),
|
|
),
|
|
],
|
|
),
|
|
child: IconButton(
|
|
icon: Icon(
|
|
Icons.settings_outlined,
|
|
size: 16.sp,
|
|
color: isDarkMode ? Colors.white70 : Colors.grey[600]
|
|
),
|
|
padding: EdgeInsets.zero,
|
|
onPressed: () {
|
|
Get.toNamed(Routes.settings);
|
|
},
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
// 耳机图片
|
|
Widget _buildEarbudsImage() {
|
|
final bool isDarkMode = Get.isDarkMode;
|
|
return Container(
|
|
height: 220.h,
|
|
alignment: Alignment.center,
|
|
child: Image.asset(
|
|
isDarkMode ? 'assets/images/headphone_dark.png' : 'assets/images/headphone_light.png',
|
|
height: 180.h,
|
|
fit: BoxFit.contain,
|
|
),
|
|
);
|
|
}
|
|
|
|
// 设备状态卡片
|
|
Widget _buildDeviceStatusCard(bool isDarkMode) {
|
|
return Container(
|
|
decoration: BoxDecoration(
|
|
color: isDarkMode ? Color(0xFF1A1A1A) : Colors.white,
|
|
borderRadius: BorderRadius.circular(16.r),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.grey.withOpacity(isDarkMode ? 0.05 : 0.1),
|
|
blurRadius: 10,
|
|
offset: const Offset(0, 4),
|
|
),
|
|
],
|
|
),
|
|
padding: EdgeInsets.all(16.w),
|
|
child: Column(
|
|
children: [
|
|
// 状态标题行
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Text(
|
|
'设备状态',
|
|
style: TextStyle(
|
|
fontSize: 14.sp,
|
|
fontWeight: FontWeight.w500,
|
|
color: isDarkMode ? Colors.grey[300] : Colors.grey[700],
|
|
),
|
|
),
|
|
Obx(() => Row(
|
|
children: [
|
|
Container(
|
|
width: 8.w,
|
|
height: 8.w,
|
|
decoration: BoxDecoration(
|
|
color: controller.getConnectionStatusColor(),
|
|
shape: BoxShape.circle,
|
|
),
|
|
),
|
|
SizedBox(width: 4.w),
|
|
Text(
|
|
controller.getConnectionStatusText(),
|
|
style: TextStyle(
|
|
fontSize: 12.sp,
|
|
color: controller.getConnectionStatusColor(),
|
|
),
|
|
),
|
|
// 添加刷新按钮
|
|
SizedBox(width: 8.w),
|
|
InkWell(
|
|
onTap: controller.refreshDeviceInfo,
|
|
child: Icon(
|
|
Icons.refresh,
|
|
size: 16.sp,
|
|
color: isDarkMode ? Colors.grey[400] : Colors.grey[600],
|
|
),
|
|
),
|
|
],
|
|
)),
|
|
],
|
|
),
|
|
|
|
// 设备信息
|
|
SizedBox(height: 12.h),
|
|
Obx(() {
|
|
// 如果连接失败且有错误信息,显示错误提示
|
|
if (controller.connectionStatus == ConnectionStatus.failed &&
|
|
controller.connectionErrorMsg.isNotEmpty) {
|
|
return Column(
|
|
children: [
|
|
Container(
|
|
padding: EdgeInsets.all(8.w),
|
|
decoration: BoxDecoration(
|
|
color: isDarkMode ? Colors.red[900]!.withOpacity(0.2) : Colors.red[50],
|
|
borderRadius: BorderRadius.circular(8.r),
|
|
border: Border.all(color: isDarkMode ? Colors.red[800]! : Colors.red[200]!),
|
|
),
|
|
child: Row(
|
|
children: [
|
|
Icon(
|
|
Icons.error_outline,
|
|
color: Colors.red,
|
|
size: 16.sp,
|
|
),
|
|
SizedBox(width: 8.w),
|
|
Expanded(
|
|
child: Text(
|
|
controller.connectionErrorMsg,
|
|
style: TextStyle(
|
|
fontSize: 12.sp,
|
|
color: isDarkMode ? Colors.red[300] : Colors.red[800],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
SizedBox(height: 12.h),
|
|
// 连接按钮
|
|
ElevatedButton(
|
|
onPressed: controller.rescanDevices,
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: Colors.blue,
|
|
foregroundColor: Colors.white,
|
|
minimumSize: Size(double.infinity, 36.h),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(8.r),
|
|
),
|
|
),
|
|
child: Text('重新连接'),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
// 如果正在连接中,显示连接中提示
|
|
if (controller.connectionStatus == ConnectionStatus.connecting) {
|
|
return Container(
|
|
padding: EdgeInsets.symmetric(vertical: 16.h),
|
|
child: Center(
|
|
child: Column(
|
|
children: [
|
|
SizedBox(
|
|
width: 24.w,
|
|
height: 24.w,
|
|
child: CircularProgressIndicator(
|
|
strokeWidth: 2.w,
|
|
valueColor: AlwaysStoppedAnimation<Color>(Colors.blue),
|
|
),
|
|
),
|
|
SizedBox(height: 8.h),
|
|
Text(
|
|
'正在连接设备...',
|
|
style: TextStyle(
|
|
fontSize: 12.sp,
|
|
color: Colors.blue,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
return controller.isConnected
|
|
? Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Expanded(
|
|
child: Text(
|
|
'已连接到:${controller.getDeviceName()}',
|
|
style: TextStyle(
|
|
fontSize: 12.sp,
|
|
fontWeight: FontWeight.w500,
|
|
color: isDarkMode ? Colors.grey[300] : Colors.grey[700],
|
|
),
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
),
|
|
Container(
|
|
padding: EdgeInsets.symmetric(horizontal: 6.w, vertical: 2.h),
|
|
decoration: BoxDecoration(
|
|
color: Colors.blue.withOpacity(0.1),
|
|
borderRadius: BorderRadius.circular(4.r),
|
|
),
|
|
child: Text(
|
|
'信号: ${controller.getSignalStrength()}',
|
|
style: TextStyle(
|
|
fontSize: 10.sp,
|
|
color: Colors.blue[700],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
SizedBox(height: 8.h),
|
|
Container(
|
|
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 6.h),
|
|
decoration: BoxDecoration(
|
|
color: isDarkMode ? Colors.grey[900] : Colors.grey[50],
|
|
borderRadius: BorderRadius.circular(8.r),
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
'设备ID:${controller.getDeviceId()}',
|
|
style: TextStyle(
|
|
fontSize: 10.sp,
|
|
color: isDarkMode ? Colors.grey[400] : Colors.grey[500],
|
|
),
|
|
),
|
|
SizedBox(height: 4.h),
|
|
Text(
|
|
'固件版本:${controller.firmwareVersion}',
|
|
style: TextStyle(
|
|
fontSize: 10.sp,
|
|
color: isDarkMode ? Colors.grey[400] : Colors.grey[500],
|
|
),
|
|
),
|
|
SizedBox(height: 4.h),
|
|
// 显示设备状态(已连接/已配对)
|
|
Row(
|
|
children: [
|
|
Container(
|
|
padding: EdgeInsets.symmetric(horizontal: 6.w, vertical: 2.h),
|
|
decoration: BoxDecoration(
|
|
color: controller.getConnectionStatusColor().withOpacity(0.1),
|
|
borderRadius: BorderRadius.circular(4.r),
|
|
),
|
|
child: Text(
|
|
controller.getConnectionStatusText(),
|
|
style: TextStyle(
|
|
fontSize: 10.sp,
|
|
color: controller.getConnectionStatusColor(),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
)
|
|
: Container(
|
|
padding: EdgeInsets.symmetric(vertical: 8.h),
|
|
alignment: Alignment.center,
|
|
decoration: BoxDecoration(
|
|
color: isDarkMode ? Colors.grey[900] : Colors.grey[100],
|
|
borderRadius: BorderRadius.circular(8.r),
|
|
),
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Icon(
|
|
Icons.bluetooth_disabled,
|
|
size: 14.sp,
|
|
color: isDarkMode ? Colors.grey[400] : Colors.grey[700],
|
|
),
|
|
SizedBox(width: 4.w),
|
|
Text(
|
|
'未连接设备,点击下方按钮连接',
|
|
style: TextStyle(
|
|
fontSize: 12.sp,
|
|
color: isDarkMode ? Colors.grey[400] : Colors.grey[700],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}),
|
|
|
|
// 连接按钮(仅在未连接时显示)
|
|
SizedBox(height: 12.h),
|
|
Obx(() {
|
|
if (controller.connectionStatus == ConnectionStatus.disconnected) {
|
|
return ElevatedButton(
|
|
onPressed: controller.rescanDevices,
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: Colors.blue,
|
|
foregroundColor: Colors.white,
|
|
minimumSize: Size(double.infinity, 36.h),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(8.r),
|
|
),
|
|
),
|
|
child: Text('连接设备'),
|
|
);
|
|
} else {
|
|
return SizedBox();
|
|
}
|
|
}),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
// 热门AI体验标题
|
|
Widget _buildSectionTitle(bool isDarkMode) {
|
|
return Row(
|
|
children: [
|
|
Text(
|
|
'热门AI体验',
|
|
style: TextStyle(
|
|
fontSize: 18.sp,
|
|
fontWeight: FontWeight.bold,
|
|
color: isDarkMode ? Colors.white : Colors.grey[800],
|
|
),
|
|
),
|
|
SizedBox(width: 8.w),
|
|
Icon(
|
|
Icons.star,
|
|
color: Colors.amber,
|
|
size: 20.w,
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
// 优质AI体验布局(左1右2)
|
|
Widget _buildPremiumAILayout() {
|
|
final bool isDarkMode = Get.isDarkMode;
|
|
return Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
// 左侧大卡片 - AI助理
|
|
Expanded(
|
|
flex: 1,
|
|
child: _buildFeatureCard(
|
|
title: 'AI全能助手',
|
|
description: '24小时专属顾问,解答问题、提供建议',
|
|
icon: Icons.assistant,
|
|
gradient: LinearGradient(
|
|
begin: Alignment.topLeft,
|
|
end: Alignment.bottomRight,
|
|
colors: [
|
|
Color(0xFF0066CC).withOpacity(0.3),
|
|
Color(0xFF5E5CE6).withOpacity(0.3),
|
|
],
|
|
),
|
|
onTap: controller.exploreAIFeatures,
|
|
iconOnTop: true,
|
|
height: 175.h, // 增加高度
|
|
isDarkMode: isDarkMode,
|
|
iconColor: const Color(0xFF0066CC),
|
|
),
|
|
),
|
|
|
|
SizedBox(width: 16.w),
|
|
|
|
// 右侧两个卡片
|
|
Expanded(
|
|
flex: 1,
|
|
child: Column(
|
|
children: [
|
|
// 右上卡片 - 同声翻译
|
|
_buildFeatureCard(
|
|
title: '同声传译',
|
|
description: '80+语言实时翻译,出国无忧',
|
|
icon: Icons.translate,
|
|
gradient: LinearGradient(
|
|
begin: Alignment.topLeft,
|
|
end: Alignment.bottomRight,
|
|
colors: [
|
|
Color(0xFF4CAF50).withOpacity(0.3),
|
|
Color(0xFF009688).withOpacity(0.3),
|
|
],
|
|
),
|
|
onTap: controller.openTranslation,
|
|
height: 83.h, // 增加高度
|
|
isDarkMode: isDarkMode,
|
|
iconColor: const Color(0xFF4CAF50),
|
|
),
|
|
|
|
SizedBox(height: 5.h), // 减少间距
|
|
|
|
// 右下卡片 - AI女友
|
|
_buildFeatureCard(
|
|
title: 'AI伴侣',
|
|
description: '懂你心意,温暖陪伴',
|
|
icon: Icons.favorite,
|
|
gradient: LinearGradient(
|
|
begin: Alignment.topLeft,
|
|
end: Alignment.bottomRight,
|
|
colors: [
|
|
Color(0xFFE91E63).withOpacity(0.3),
|
|
Color(0xFFFF5252).withOpacity(0.3),
|
|
],
|
|
),
|
|
onTap: controller.openAIGirlfriend,
|
|
height: 85.h, // 增加高度
|
|
isDarkMode: isDarkMode,
|
|
iconColor: const Color(0xFFE91E63),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
// 特性卡片构建方法
|
|
Widget _buildFeatureCard({
|
|
required String title,
|
|
required String description,
|
|
required IconData icon,
|
|
required LinearGradient gradient,
|
|
required VoidCallback onTap,
|
|
double? height,
|
|
bool iconOnTop = false,
|
|
bool isDarkMode = false,
|
|
Color iconColor = Colors.white,
|
|
}) {
|
|
return GestureDetector(
|
|
onTap: onTap,
|
|
child: Container(
|
|
height: height ?? 175.h,
|
|
padding: EdgeInsets.all(14.w),
|
|
decoration: BoxDecoration(
|
|
gradient: gradient,
|
|
borderRadius: BorderRadius.circular(16.r),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: isDarkMode
|
|
? Colors.black.withOpacity(0.2)
|
|
: Colors.grey.withOpacity(0.1),
|
|
blurRadius: 8,
|
|
offset: const Offset(0, 4),
|
|
),
|
|
],
|
|
),
|
|
child: Stack(
|
|
children: [
|
|
// 图标
|
|
if (iconOnTop)
|
|
// 大卡片布局
|
|
Align(
|
|
alignment: Alignment.topLeft,
|
|
child: Container(
|
|
width: 45.w,
|
|
height: 45.w,
|
|
decoration: BoxDecoration(
|
|
color: iconColor.withOpacity(0.2),
|
|
shape: BoxShape.circle,
|
|
),
|
|
child: Icon(
|
|
icon,
|
|
color: iconColor,
|
|
size: 24.w,
|
|
),
|
|
),
|
|
)
|
|
else
|
|
// 小卡片布局 - 图标
|
|
Align(
|
|
alignment: Alignment.topLeft,
|
|
child: Container(
|
|
width: 34.w,
|
|
height: 34.w,
|
|
decoration: BoxDecoration(
|
|
color: iconColor.withOpacity(0.2),
|
|
shape: BoxShape.circle,
|
|
),
|
|
child: Icon(
|
|
icon,
|
|
color: iconColor,
|
|
size: 18.w,
|
|
),
|
|
),
|
|
),
|
|
|
|
// 文本内容
|
|
if (iconOnTop)
|
|
// 大卡片布局 - 文本
|
|
Padding(
|
|
padding: EdgeInsets.only(top: 55.h),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Text(
|
|
title,
|
|
style: TextStyle(
|
|
color: isDarkMode ? Colors.white : Colors.grey[800],
|
|
fontSize: 16.sp,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
SizedBox(height: 4.h),
|
|
Flexible(
|
|
child: Text(
|
|
description,
|
|
style: TextStyle(
|
|
color: isDarkMode ? Colors.grey[400] : Colors.grey[600],
|
|
fontSize: 12.sp,
|
|
),
|
|
maxLines: 2,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
)
|
|
else
|
|
// 小卡片布局 - 文本
|
|
Padding(
|
|
padding: EdgeInsets.only(left: 44.w),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Text(
|
|
title,
|
|
style: TextStyle(
|
|
color: isDarkMode ? Colors.white : Colors.grey[800],
|
|
fontSize: 14.sp,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
SizedBox(height: 2.h),
|
|
Flexible(
|
|
child: Text(
|
|
description,
|
|
style: TextStyle(
|
|
color: isDarkMode ? Colors.grey[400] : Colors.grey[600],
|
|
fontSize: 10.sp,
|
|
),
|
|
maxLines: 2,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|