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.
392 lines
11 KiB
392 lines
11 KiB
import 'package:flutter/material.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
|
import '../controllers/pairing_controller.dart';
|
|
import '../../../core/widgets/common_bottom_nav.dart';
|
|
import '../../../app/theme/app_theme.dart';
|
|
import 'package:permission_handler/permission_handler.dart';
|
|
|
|
class PairingView extends GetView<PairingController> {
|
|
const PairingView({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 : Colors.white,
|
|
body: SafeArea(
|
|
child: Stack(
|
|
children: [
|
|
// 主要内容
|
|
_buildMainContent(isDarkMode),
|
|
],
|
|
),
|
|
),
|
|
bottomNavigationBar: Opacity(
|
|
opacity: 0.5, // 保持半透明效果
|
|
child: const CommonBottomNav(currentIndex: 0),
|
|
),
|
|
);
|
|
}
|
|
|
|
// 主要内容
|
|
Widget _buildMainContent(bool isDarkMode) {
|
|
return Padding(
|
|
padding: EdgeInsets.fromLTRB(24.w, 40.h, 24.w, 24.h),
|
|
child: SingleChildScrollView(
|
|
child: Column(
|
|
children: [
|
|
// 标题部分
|
|
_buildHeader(isDarkMode),
|
|
|
|
// 设备动画
|
|
SizedBox(height: 40.h),
|
|
_buildDeviceAnimation(),
|
|
|
|
// 搜索状态
|
|
SizedBox(height: 40.h),
|
|
_buildSearchStatus(isDarkMode),
|
|
|
|
// 底部按钮
|
|
SizedBox(height: 60.h),
|
|
_buildBottomButtons(isDarkMode),
|
|
SizedBox(height: 80.h), // 留出底部导航的空间
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
// 标题部分
|
|
Widget _buildHeader(bool isDarkMode) {
|
|
return Column(
|
|
children: [
|
|
Text(
|
|
'DeepVoice',
|
|
style: TextStyle(
|
|
fontSize: 24.sp,
|
|
fontWeight: FontWeight.bold,
|
|
color: isDarkMode ? Colors.white : Colors.black,
|
|
),
|
|
),
|
|
SizedBox(height: 8.h),
|
|
Text(
|
|
'连接世界,超越声音',
|
|
style: TextStyle(
|
|
fontSize: 14.sp,
|
|
color: isDarkMode ? Colors.white70 : Colors.black54,
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
// 设备动画
|
|
Widget _buildDeviceAnimation() {
|
|
return SizedBox(
|
|
width: 240.w,
|
|
height: 240.w,
|
|
child: Stack(
|
|
alignment: Alignment.center,
|
|
children: [
|
|
// 脉动动画背景
|
|
_buildPulseBackground(),
|
|
|
|
// 耳机图片
|
|
_buildHeadphoneImage(),
|
|
|
|
// 旋转加载指示器
|
|
_buildLoadingIndicator(),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
// 脉动背景
|
|
Widget _buildPulseBackground() {
|
|
return TweenAnimationBuilder<double>(
|
|
tween: Tween<double>(begin: 0.8, end: 1.1),
|
|
duration: const Duration(seconds: 2),
|
|
curve: Curves.easeInOut,
|
|
builder: (context, value, child) {
|
|
return Transform.scale(
|
|
scale: value,
|
|
child: Container(
|
|
width: 200.w,
|
|
height: 200.w,
|
|
decoration: BoxDecoration(
|
|
gradient: LinearGradient(
|
|
begin: Alignment.topLeft,
|
|
end: Alignment.bottomRight,
|
|
colors: [
|
|
const Color(0xFF0066CC).withOpacity(0.2),
|
|
const Color(0xFF5E5CE6).withOpacity(0.2),
|
|
],
|
|
),
|
|
shape: BoxShape.circle,
|
|
),
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
// 耳机图片
|
|
Widget _buildHeadphoneImage() {
|
|
return Container(
|
|
width: 140.w,
|
|
height: 140.w,
|
|
decoration: BoxDecoration(
|
|
gradient: const LinearGradient(
|
|
begin: Alignment.topLeft,
|
|
end: Alignment.bottomRight,
|
|
colors: [
|
|
Color(0xFF0066CC),
|
|
Color(0xFF5E5CE6),
|
|
],
|
|
),
|
|
shape: BoxShape.circle,
|
|
),
|
|
child: Icon(
|
|
Icons.headset,
|
|
size: 80.sp,
|
|
color: Colors.white,
|
|
),
|
|
);
|
|
}
|
|
|
|
// 加载指示器
|
|
Widget _buildLoadingIndicator() {
|
|
return SizedBox(
|
|
width: 180.w,
|
|
height: 180.w,
|
|
child: CircularProgressIndicator(
|
|
strokeWidth: 4.w,
|
|
valueColor: const AlwaysStoppedAnimation<Color>(Color(0xFF0066CC)),
|
|
backgroundColor: const Color(0xFF0066CC).withOpacity(0.1),
|
|
),
|
|
);
|
|
}
|
|
|
|
// 搜索状态
|
|
Widget _buildSearchStatus(bool isDarkMode) {
|
|
return Obx(() {
|
|
final String status = controller.statusDescription;
|
|
final bool hasDevice = controller.deviceInfo.isNotEmpty && controller.deviceInfo['device'] != null;
|
|
final bool hasError = controller.errorMessage.isNotEmpty;
|
|
|
|
// 已找到设备
|
|
if (hasDevice) {
|
|
return Column(
|
|
children: [
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Icon(Icons.check_circle, color: Colors.green, size: 20.sp),
|
|
SizedBox(width: 8.w),
|
|
Text(
|
|
'设备信息获取成功',
|
|
style: TextStyle(
|
|
fontSize: 18.sp,
|
|
fontWeight: FontWeight.w600,
|
|
color: Colors.green,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
SizedBox(height: 8.h),
|
|
Text(
|
|
'正在跳转到主页...',
|
|
style: TextStyle(
|
|
fontSize: 14.sp,
|
|
color: isDarkMode ? Colors.white70 : Colors.black54,
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
// 有错误信息
|
|
if (hasError) {
|
|
// 蓝牙关闭错误
|
|
if (status.contains('蓝牙已关闭')) {
|
|
return Column(
|
|
children: [
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Icon(Icons.bluetooth_disabled, color: Colors.orange, size: 20.sp),
|
|
SizedBox(width: 8.w),
|
|
Text(
|
|
'蓝牙已关闭',
|
|
style: TextStyle(
|
|
fontSize: 18.sp,
|
|
fontWeight: FontWeight.w600,
|
|
color: Colors.orange,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
SizedBox(height: 8.h),
|
|
Text(
|
|
controller.errorMessage,
|
|
style: TextStyle(
|
|
fontSize: 14.sp,
|
|
color: isDarkMode ? Colors.white70 : Colors.black54,
|
|
),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
// 权限错误
|
|
if (status.contains('权限')) {
|
|
return Column(
|
|
children: [
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Icon(Icons.no_encryption, color: Colors.orange, size: 20.sp),
|
|
SizedBox(width: 8.w),
|
|
Text(
|
|
'缺少必要权限',
|
|
style: TextStyle(
|
|
fontSize: 18.sp,
|
|
fontWeight: FontWeight.w600,
|
|
color: Colors.orange,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
SizedBox(height: 8.h),
|
|
Text(
|
|
controller.errorMessage,
|
|
style: TextStyle(
|
|
fontSize: 14.sp,
|
|
color: isDarkMode ? Colors.white70 : Colors.black54,
|
|
),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
SizedBox(height: 16.h),
|
|
ElevatedButton(
|
|
onPressed: () => openAppSettings(),
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: Colors.orange,
|
|
foregroundColor: Colors.white,
|
|
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(8.r),
|
|
),
|
|
),
|
|
child: const Text('打开设置'),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
// 其他错误
|
|
return Column(
|
|
children: [
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Icon(Icons.error, color: Colors.red, size: 20.sp),
|
|
SizedBox(width: 8.w),
|
|
Text(
|
|
status.isEmpty ? '未找到设备' : status,
|
|
style: TextStyle(
|
|
fontSize: 18.sp,
|
|
fontWeight: FontWeight.w600,
|
|
color: Colors.red,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
SizedBox(height: 8.h),
|
|
Text(
|
|
controller.errorMessage.isEmpty ?
|
|
'请确保耳机已开机并在附近' : controller.errorMessage,
|
|
style: TextStyle(
|
|
fontSize: 14.sp,
|
|
color: isDarkMode ? Colors.white70 : Colors.black54,
|
|
),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
// 默认状态(正在搜索/初始化)
|
|
return Column(
|
|
children: [
|
|
Text(
|
|
status,
|
|
style: TextStyle(
|
|
fontSize: 18.sp,
|
|
fontWeight: FontWeight.w600,
|
|
color: isDarkMode ? Colors.white : Colors.black,
|
|
),
|
|
),
|
|
SizedBox(height: 8.h),
|
|
Text(
|
|
'请打开耳机充电盒,将耳机放入您的耳朵',
|
|
style: TextStyle(
|
|
fontSize: 14.sp,
|
|
color: isDarkMode ? Colors.white70 : Colors.black54,
|
|
),
|
|
),
|
|
],
|
|
);
|
|
});
|
|
}
|
|
|
|
// 底部按钮
|
|
Widget _buildBottomButtons(bool isDarkMode) {
|
|
return Column(
|
|
children: [
|
|
SizedBox(
|
|
width: double.infinity,
|
|
child: ElevatedButton(
|
|
onPressed: controller.refreshSearch,
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: isDarkMode ? Color(0xFF1A1A1A) : Colors.grey.withOpacity(0.1),
|
|
foregroundColor: isDarkMode ? Colors.white : Colors.black,
|
|
padding: EdgeInsets.symmetric(vertical: 16.h),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(16.r),
|
|
),
|
|
elevation: 0,
|
|
),
|
|
child: Text(
|
|
'刷新搜索',
|
|
style: TextStyle(
|
|
fontSize: 16.sp,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
SizedBox(height: 12.h),
|
|
SizedBox(
|
|
width: double.infinity,
|
|
child: TextButton(
|
|
onPressed: controller.skipConnecting,
|
|
style: TextButton.styleFrom(
|
|
foregroundColor: const Color(0xFF0066CC),
|
|
padding: EdgeInsets.symmetric(vertical: 12.h),
|
|
),
|
|
child: Text(
|
|
'稍后再说',
|
|
style: TextStyle(
|
|
fontSize: 16.sp,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|