import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import '../controllers/pairing_controller.dart'; class PairingView extends GetView { 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: _buildMainContent(isDarkMode), ), // bottomNavigationBar: Opacity( // opacity: 0.5, // child: const CommonBottomNav(currentIndex: 0), // ), ); } // 主要内容 Widget _buildMainContent(bool isDarkMode) { return Container( width: double.infinity, height: MediaQuery.of(Get.context!).size.height, padding: EdgeInsets.symmetric(horizontal: 24.w), // 添加SingleChildScrollView以防止溢出 child: SingleChildScrollView( physics: BouncingScrollPhysics(), child: ConstrainedBox( constraints: BoxConstraints( minHeight: MediaQuery.of(Get.context!).size.height - 100.h, // 减去底部导航和状态栏高度 ), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ SizedBox(height: 20.h), // 顶部间距 // 标题部分 _buildHeader(isDarkMode), SizedBox(height: 20.h), // 设备动画 - 根据状态显示不同动画 _buildDeviceAnimation(isDarkMode), SizedBox(height: 20.h), // 状态信息 _buildStatusInfo(isDarkMode), SizedBox(height: 40.h), // 底部按钮 _buildActionButtons(isDarkMode), SizedBox(height: 20.h), ], ), ), ), ); } // 头部标题 Widget _buildHeader(bool isDarkMode) { return Column( children: [ Text( 'DeapSound', style: TextStyle( fontSize: 30.sp, fontWeight: FontWeight.bold, color: isDarkMode ? Colors.white : Colors.black, ), ), SizedBox(height: 8.h), Text( 'homeSlogan'.tr, // 连接世界,超越声音 style: TextStyle( fontSize: 16.sp, color: isDarkMode ? Colors.white60 : Colors.black54, ), ), ], ); } // 设备动画 - 根据状态显示不同的动画效果 Widget _buildDeviceAnimation(bool isDarkMode) { return Obx(() { final state = controller.currentState; final isLoading = controller.isLoading.value; // 根据状态决定动画颜色 Color primaryColor = Color(0xFF0066CC); Color secondaryColor = Color(0xFF5E5CE6); // 错误状态使用红色 if (state == PairingState.error) { primaryColor = Colors.red; secondaryColor = Colors.redAccent; } // 成功状态使用绿色 else if (state == PairingState.completed) { primaryColor = Colors.green; secondaryColor = Colors.greenAccent; } // 待机状态的特殊处理 if (state == PairingState.idle || state == PairingState.waitingClassic) { return Container( width: 240.w, height: 240.w, child: Stack( alignment: Alignment.center, children: [ // 静态背景圆圈 Container( width: 200.w, height: 200.w, decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ primaryColor.withOpacity(0.1), secondaryColor.withOpacity(0.1), ], ), shape: BoxShape.circle, ), ), // 设备图标 Container( width: 120.w, height: 120.w, decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [primaryColor, secondaryColor], ), shape: BoxShape.circle, ), child: Icon( Icons.headphones, color: Colors.white, size: 60.w, ), ), ], ), ); } return Container( width: 240.w, height: 240.w, child: Stack( alignment: Alignment.center, children: [ // 脉搏背景 - 只在加载时显示动画 if (isLoading) TweenAnimationBuilder( tween: Tween(begin: 0.8, end: 1.1), duration: const Duration(seconds: 2), curve: Curves.easeInOut, builder: (context, value, child) { return Container( width: 200.w * value, height: 200.w * value, decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ primaryColor.withOpacity(0.3), secondaryColor.withOpacity(0.3), ], ), shape: BoxShape.circle, ), ); }, onEnd: () { // 重新开始动画 }, ), // 旋转边框 - 只在加载时显示 if (isLoading) SizedBox( width: 100.w, height: 100.w, child: CircularProgressIndicator( strokeWidth: 4.w, valueColor: AlwaysStoppedAnimation(primaryColor), backgroundColor: Colors.grey.withOpacity(0.2), ), ), // 设备图标 Container( width: 120.w, height: 120.w, decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [primaryColor, secondaryColor], ), shape: BoxShape.circle, ), child: Icon( _getIconForState(state), color: Colors.white, size: 60.w, ), ), ], ), ); }); } // 根据状态获取图标 IconData _getIconForState(PairingState state) { switch (state) { case PairingState.completed: return Icons.check_circle; case PairingState.associating: return Icons.link; case PairingState.error: final errorType = controller.errorType; switch (errorType) { case ErrorType.bluetoothOff: case ErrorType.noPermission: return Icons.bluetooth_disabled; case ErrorType.noClassicDevice: case ErrorType.deviceNotMatch: return Icons.headphones_outlined; case ErrorType.associationFailed: return Icons.link_off; default: return Icons.error_outline; } default: return Icons.headphones; } } // 状态信息 Widget _buildStatusInfo(bool isDarkMode) { return Obx(() { final status = controller.statusText.value; final state = controller.currentState; final device = controller.currentDevice; return Column( children: [ // 状态文本 - 更大更醒目 Text( status, style: TextStyle( fontSize: 20.sp, fontWeight: FontWeight.w600, color: isDarkMode ? Colors.white : Colors.black, ), textAlign: TextAlign.center, ), SizedBox(height: 10.h), // 子状态提示文本 - 根据状态显示不同提示 Text( _getSubtitleForState(state), style: TextStyle( fontSize: 14.sp, color: isDarkMode ? Colors.white60 : Colors.grey[600], ), textAlign: TextAlign.center, ), // 当前连接设备(有设备时才显示) if (device != null && (state == PairingState.verifying || state == PairingState.binding || state == PairingState.waitingBle || state == PairingState.completed)) ...[ SizedBox(height: 24.h), _buildConnectedDevice(device, isDarkMode), ], ], ); }); } // 根据状态获取副标题 String _getSubtitleForState(PairingState state) { switch (state) { case PairingState.idle: return 'readyToPair'.tr; // 准备开始配对 case PairingState.checking: return 'preparingPairingEnvironment'.tr; // 正在准备配对环境... case PairingState.waitingClassic: return 'connectDeepSoundViaSystemBluetooth'.tr; // 请先通过系统蓝牙连接DeapSound设备 case PairingState.verifying: return 'verifyingDevice'.tr; // 正在验证设备... case PairingState.binding: return 'deviceBindingConfirm'.tr; // 设备绑定确认... case PairingState.associating: return 'establishingDeviceAssociation'.tr; // 正在建立设备关联... case PairingState.waitingBle: return 'waitingCompletePairing'.tr; // 请稍候,正在完成配对... case PairingState.completed: return 'pairingSuccessRedirecting'.tr; // 配对成功!即将跳转到主页... case PairingState.error: final errorType = controller.errorType; switch (errorType) { case ErrorType.bluetoothOff: return 'bluetoothTurnedOff'.tr; // 蓝牙已关闭,请打开蓝牙 case ErrorType.noPermission: return 'lackPermissions'.tr; // 缺少蓝牙权限 case ErrorType.noClassicDevice: return 'connectDeepSoundViaSystemBluetooth' .tr; // 请先通过系统蓝牙连接DeapSound设备 case ErrorType.deviceNotMatch: return 'ensureConnectedDeepSoundDevice'.tr; // 请确保连接的是DeapSound设备 case ErrorType.deviceBound: return 'deviceAlreadyBoundToOtherAccount'.tr; // 该设备已被其他账号绑定 case ErrorType.associationFailed: return 'deviceAssociationFailed'.tr; // 设备关联失败,请重试 case ErrorType.bleTimeout: return 'scanTimeoutEnsureDeviceNearby'.tr; // 扫描超时,请确保设备在附近 case ErrorType.networkError: return 'networkError'.tr; // 网络错误 case ErrorType.authnFailed: return 'authorizationFailedCheckCode'.tr; // 授权失败,请检查授权码是否正确 default: return 'pairingFailedRetry'.tr; // 配对失败,请重试 } default: return ''; } } // 已连接设备信息 Widget _buildConnectedDevice(device, bool isDarkMode) { return Container( width: double.infinity, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'currentlyConnected'.tr, // 当前已连接: style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w600, color: isDarkMode ? Colors.white70 : Colors.black87, ), ), SizedBox(height: 8.h), _buildDeviceItem(device, isDarkMode), ], ), ); } // 设备项 Widget _buildDeviceItem(device, bool isDarkMode) { return Container( margin: EdgeInsets.only(bottom: 8.h), padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 12.h), decoration: BoxDecoration( color: isDarkMode ? Colors.grey[900] : Colors.grey[100], borderRadius: BorderRadius.circular(8.r), ), child: Row( children: [ Icon( Icons.headphones, color: isDarkMode ? Colors.white70 : Colors.black54, size: 24.w, ), SizedBox(width: 12.w), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( device.name ?? 'unknown'.tr, style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w500, color: isDarkMode ? Colors.white : Colors.black, ), ), SizedBox(height: 4.h), Text( device.address ?? '', style: TextStyle( fontSize: 12.sp, color: isDarkMode ? Colors.white54 : Colors.black45, ), ), ], ), ), Icon( Icons.bluetooth_connected, color: Colors.green, size: 20.w, ), ], ), ); } // 获取连接按钮图标 IconData _getConnectButtonIcon() { final buttonText = controller.connectButtonText.value; if (buttonText.contains('bluetooth'.tr)) { return Icons.bluetooth; } else if (buttonText.contains('settings'.tr)) { return Icons.settings; } return Icons.bluetooth_searching; } // 操作按钮 Widget _buildActionButtons(bool isDarkMode) { return Obx(() { final showConnectButton = controller.showConnectButton.value; final showRetryButton = controller.showRetryButton.value; final showBindButton = controller.showBindButton.value; final showRetryChectButton = controller.showRetryChectButton.value; final showSkipButton = controller.showSkipButton.value; final showAutoCodeInput = controller.showAutoCodeInput.value; final showSkipBindingButton = controller.showSkipBindingButton.value; return Container( width: double.infinity, child: Column( children: [ // 显示连接按钮 if (showConnectButton) ElevatedButton( onPressed: () => controller.onConnectTap(), style: ElevatedButton.styleFrom( backgroundColor: Color(0xFF0066CC), foregroundColor: Colors.white, padding: EdgeInsets.symmetric(vertical: 16.h), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16.r), ), elevation: 0, minimumSize: Size(double.infinity, 56.h), ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(_getConnectButtonIcon(), size: 20.w), SizedBox(width: 8.w), Text( controller.connectButtonText.value, style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w600, ), ), ], ), ), if (showBindButton) ElevatedButton( onPressed: () => controller.confirmBindDevice(), style: ElevatedButton.styleFrom( backgroundColor: Colors.blue, foregroundColor: Colors.white, padding: EdgeInsets.symmetric(vertical: 16.h), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16.r), ), elevation: 0, minimumSize: Size(double.infinity, 56.h), ), child: Text( "bind".tr, // 绑定 style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w600, ), ), ), SizedBox(height: 12.h), // 显示重试按钮 if (showRetryButton) ElevatedButton( onPressed: () => controller.onRetryTap(), style: ElevatedButton.styleFrom( backgroundColor: Colors.red, foregroundColor: Colors.white, padding: EdgeInsets.symmetric(vertical: 16.h), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16.r), ), elevation: 0, minimumSize: Size(double.infinity, 56.h), ), child: Text( controller.retryButtonText.value, style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w600, ), ), ), if (showAutoCodeInput) Column( children: [ // 输入框 SizedBox( width: double.infinity, height: 48.h, child: TextField( controller: controller .codeInputController, // 你可以在 Controller 中声明这个 decoration: InputDecoration( hintText: 'enterCode'.tr, // 请输入代码 border: OutlineInputBorder( borderRadius: BorderRadius.circular(12.r), ), contentPadding: EdgeInsets.symmetric(horizontal: 16.w), ), ), ), SizedBox(height: 12.h), SizedBox( width: double.infinity, height: 48.h, child: ElevatedButton( onPressed: controller.onAutoCodeInputTap, style: ElevatedButton.styleFrom( backgroundColor: Theme.of(Get.context!).primaryColor, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12.r), ), elevation: 0, ), child: Text( 'authorizeBind'.tr, // 授权绑定 style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w600, color: Colors.white, ), ), ), ), ], ), // 自动输入代码按钮 // 跳过按钮 if (showSkipButton) ...[ SizedBox(height: 12.h), TextButton( onPressed: () => controller.onSkipTap(), style: TextButton.styleFrom( foregroundColor: Color(0xFF0066CC), padding: EdgeInsets.symmetric(vertical: 12.h), minimumSize: Size(double.infinity, 48.h), ), child: Text( 'talkLater'.tr, // 稍后再说 style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w500, ), ), ), ], // 跳过绑定按钮 if (showSkipBindingButton) ...[ SizedBox(height: 12.h), TextButton( onPressed: () => controller.onSkipBindingTap(), style: TextButton.styleFrom( foregroundColor: Color(0xFF0066CC), padding: EdgeInsets.symmetric(vertical: 12.h), minimumSize: Size(double.infinity, 48.h), ), child: Text( 'skipBinding'.tr, // 跳过绑定 style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w500, ), ), ), ], //重新检测 if (showRetryChectButton) ...[ SizedBox(height: 12.h), TextButton( onPressed: () => controller.onRetryTap(), style: TextButton.styleFrom( foregroundColor: Color(0xFF0066CC), padding: EdgeInsets.symmetric(vertical: 12.h), minimumSize: Size(double.infinity, 48.h), ), child: Text( 'rescan'.tr, // 重新扫描 style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w500, ), ), ), ], ], ), ); }); } }