Browse Source

Merge remote-tracking branch 'onewaygame/tanls' into lxm

newdev_shunjiawei
lxm 2 years ago
parent
commit
140d53e351
  1. 102
      lib/core/translations/app_translations.dart
  2. 155
      lib/core/translations/language/en_us.dart
  3. 150
      lib/core/translations/language/zh_cn.dart
  4. 19
      lib/core/widgets/common_bottom_nav.dart
  5. 26
      lib/main.dart
  6. 207
      lib/modules/chat/views/chat_view.dart
  7. 80
      lib/modules/explore/controllers/explore_controller.dart
  8. 60
      lib/modules/explore/views/explore_view.dart
  9. 115
      lib/modules/home/controllers/home_controller.dart
  10. 414
      lib/modules/home/views/home_view.dart
  11. 299
      lib/modules/login/views/login_view.dart
  12. 118
      lib/modules/pairing/controllers/pairing_controller.dart
  13. 61
      lib/modules/pairing/views/pairing_view.dart
  14. 98
      lib/modules/settings/controllers/settings_controller.dart
  15. 11
      lib/modules/settings/models/language_model.dart
  16. 398
      lib/modules/settings/views/settings_view.dart
  17. 30
      lib/modules/splash/controllers/splash_controller.dart
  18. 20
      lib/modules/splash/views/splash_view.dart
  19. 164
      lib/modules/translation/views/translation_view.dart
  20. 2
      pubspec.yaml

102
lib/core/translations/app_translations.dart

@ -1,81 +1,29 @@
import 'dart:ui';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
import 'language/en_us.dart';
import 'language/zh_cn.dart';
class AppTranslations extends Translations { class AppTranslations extends Translations {
static const fallbackLocale = Locale('zh', 'CN');
static final supportedLocales = [
const Locale('zh', 'CN'),
const Locale('en', 'US'),
];
static Locale? deviceLocale() {
final GetStorage storage = GetStorage();
Locale? locale = Get.deviceLocale;
String? language = storage.read('language');
if (language != null && language != 'system') {
List languageList = language.split('_');
locale = Locale(languageList[0], languageList[1]);
}
return locale;
}
@override @override
Map<String, Map<String, String>> get keys => { Map<String, Map<String, String>> get keys => {'en_US': enUS, 'zh_CN': zhCN};
'zh_CN': { }
// 通用
'app_name': 'i深音',
'confirm': '确认',
'cancel': '取消',
'retry': '重试',
'error': '错误',
'success': '成功',
// 主页
'add_device': '添加设备',
'searching': '搜索中',
'searching_hint': '请将设备放在手机附近',
'connected': '已连接',
'device_ready': '设备已就绪',
'connection_failed': '连接失败',
'connection_failed_hint': '未找到设备。请确保蓝牙已开启且设备在5米范围内。尝试重启设备。',
'disconnect': '断开连接',
// 聊天页面
'clear_chat': '清空对话',
'thinking': '正在思考...',
'type_message': '发消息...',
'voice_enabled': '语音已开启',
'voice_disabled': '语音已关闭',
// 设置页面
'settings': '设置',
'language_settings': '语言设置',
'about': '关于',
'check_update': '检查更新',
'clear_cache': '清除缓存',
'home': '首页',
'explore': '发现',
'profile': '我的',
'device': '设备',
},
'en_US': {
// General
'app_name': 'iDeep Voice',
'confirm': 'Confirm',
'cancel': 'Cancel',
'retry': 'Retry',
'error': 'Error',
'success': 'Success',
// Home
'add_device': '+ Add Device',
'searching': 'Searching',
'searching_hint': 'Please place the device near your phone',
'connected': 'Connected!',
'device_ready': 'Your device is ready',
'connection_failed': 'Connection Failed',
'connection_failed_hint': 'Device not found. Please ensure Bluetooth is enabled and the device is within 5 meters. Try turning the device off and on again.',
'disconnect': 'Disconnect',
// Chat
'clear_chat': 'Clear Chat',
'thinking': 'Thinking...',
'type_message': 'Type a message...',
'voice_enabled': 'Voice Enabled',
'voice_disabled': 'Voice Disabled',
// Settings
'settings': 'Settings',
'language_settings': 'Language Settings',
'about': 'About',
'check_update': 'Check for Updates',
'clear_cache': 'Clear Cache',
'home': 'Home',
'explore': 'Explore',
'profile': 'Profile',
'device': 'Device',
},
};
}

155
lib/core/translations/language/en_us.dart

@ -0,0 +1,155 @@
const Map<String, String> enUS = {
// 通用
'continue': 'Continue',
'confirm': 'Confirm',
'cancel': 'Cancel',
'retry': 'Retry',
'error': 'Error',
'success': 'Success',
'connected': 'Connected',
'connecting': 'Connecting...',
'connectionFailure': 'Connection Failed',
'unconnected': 'Disconnected',
'search': 'Search',
// 启动页
"slogan": "Connect the World, Beyond Voice",
"sloganInfo": "AI Voice Expert · Anytime, Anywhere",
// 底部导航
"home": "Home",
"explore": "Explore",
"settings": "Settings",
// 语言
"followingSystem": "Following system",
"chinese": "Chinese",
"english": "English",
// 登录页
"welcome": "Welcome",
"loginTips": "Please log in or register with your phone number or email",
"accountNumber": "Phone number or email",
"verificationCode": "Verification Code",
"getCode": "Get Code",
"reacquireAfter": "s Get it again after",
"otherWays": "Or login with",
"expressConsent": "By logging in/registering, you agree to the",
"and": " and ",
"userAgreement": "User Agreement",
"privacyPolicy": "Privacy Policy",
// 首页
"deviceStatus": "Device Status",
"reconnect": "Reconnect",
"connectingDevice": "Connecting to device...",
"connectedTo": "Connected to",
"signal": "Signal",
"deviceId": "Device ID",
"firmwareVersion": "Firmware Version",
"disconnectedDevice": "No device connected, click the button below",
"connectDevice": "Connect Device",
"topAiExperiences": "Top AI Experiences",
"aiAssistant": "AI Assistant",
"aiAssistantInfo": "24/7 personal consultant for questions and advice",
"simultaneousInterpretation": "Real-time Interpretation",
"simultaneousInterpretationInfo": "Real-time translation for 80+ languages",
"aiCompanion": "AI Companion",
"aiCompanionInfo": "Understands you and provides warm companionship",
"homeSlogan": "Connect the World, Beyond Sound",
"turnEarphones":
"Please open the earphone case and put the earbuds in your ears",
"currentlyConnected": "Currently connected:",
"unsystemSetup": "Go to System Settings",
"talkLater": "Remind Later",
"initializing": "Initializing...",
"bluetoothInitializationFailure": "Bluetooth service initialization failed",
"checkingPermissions": "Checking permissions...",
"lackPermissions": "Bluetooth permissions required",
"searchingDevice": "Searching for devices...",
"findConnectedDevice": "Found connected device",
"notFound": "No connected Bluetooth devices found",
"initializationFailure": "Initialization failed",
"connectedDevice": "Connected to device:",
"failedRefreshDevice": "Failed to refresh device",
"noDeviceFound": "No connected devices found",
"failedDeviceInformation": "Failed to get device information",
"unknownEquipment": "Unknown Device",
"zeroSignal": "No Signal",
"good": "Good",
"fine": "Great",
"excellent": "Excellent",
"unknown": "Unknown",
// 发现页
"searchAi": "Search AI features...",
"exploreSlogan": "Smart Earbuds, Infinite Possibilities",
"exploreAi": "Explore AI features provided by DeepVoice",
"aiFunction": "AI Features",
"allRoundAssistant": "All-round Assistant",
"allRoundAssistantInfo": "Helps solve various problems",
"simultaneousTranslation": "Real-time Translation",
"simultaneousTranslationInfo": "Instant translation for 80+ languages",
"aiGirlfriend": "AI Girlfriend",
"aiGirlfriendInfo": "Real-time translation for 80+ languages",
"healthConsultant": "Health Consultant",
"healthConsultantInfo": "Provides scientific health advice",
"conferenceAssistant": "Business Meeting Assistant",
"conferenceAssistantInfo":
"Real-time minutes recording and multilingual translation",
"studyAssistant": "Language Learning Assistant",
"studyAssistantInfo": "Pronunciation correction and speaking practice",
"travelTranslator": "Travel Translator",
"travelTranslatorInfo": "Real-time voice translation and attraction guides",
"beSearching": "Searching for:",
"noAssistantFound": "No corresponding AI assistant found",
"scene": "Scenarios",
"youHaveSelected": "You've selected:",
"newFunction": "New Features",
"youChoseTry": "You've chosen to try",
"aiSinger": "AI Singer Mode",
"aiSingerInfo":
"Sing any song in your favorite artist's voice, even your own creations",
"popularFunction": "Popular Features",
"tryNow": "Try Now",
// 设置页
"premiumMember": "Premium Membership",
"joinDevice": "Connected device:",
"accountId": "Account ID:",
"systemSetting": "System Settings",
"languageSetting": "Language Settings",
"darkMode": "Dark Mode",
"darkModeInfo": "Switch between light/dark themes",
"backups": "Backup & Restore",
"backupsInfo": "Backup settings and AI history",
"lastBackup": "Last backup:",
"privacySettings": "Privacy Settings",
"privacySettingsInfo": "Manage data collection and permissions",
"logout": "Log Out",
"logoutTips": "Are you sure you want to log out of the current account?",
"aboutApplication": "About",
"versions": "Version",
"checkUpdates": "Check Updates",
"selectLanguage": "Select Language",
"voiceService": "Voice Service",
"voiceServiceInfo": "Select speech recognition and synthesis service",
"selectVoiceService": "Select Voice Service",
"azureVoiceService": "Azure Speech Service",
"azureVoiceServiceInfo":
"Microsoft cloud-based service supporting high-quality speech synthesis and recognition",
"flutterVoiceService": "Local voice service",
"flutterVoiceServiceInfo":
"Device-local voice service with no internet required",
// 聊天页
"clearChatHistory": "Clear Chat History",
"online": "Online",
"inputMessage": "Type a message...",
"imListening": "Listening...",
"canStartTalking": "You may start speaking",
// 翻译页
"selectSourceLanguage": "Select Source Language",
"selectTargetLanguage": "Select Target Language",
};

150
lib/core/translations/language/zh_cn.dart

@ -0,0 +1,150 @@
const Map<String, String> zhCN = {
// 通用
'continue': '继续',
'confirm': '确认',
'cancel': '取消',
'retry': '重试',
'error': '错误',
'success': '成功',
'connected': '已连接',
'connecting': '连接中...',
'connectionFailure': '连接失败',
'unconnected': '未连接',
'search': '搜索',
// 启动页
"slogan": "连接世界,岂止声音",
"sloganInfo": "AI语音专家 · 随时随地",
// 底部导航
"home": "首页",
"explore": "发现",
"settings": "设置",
// 语言
"followingSystem": "跟随系统",
"chinese": "中文",
"english": "英语",
// 登录页
"welcome": "欢迎使用",
"loginTips": "请使用手机号或邮箱登录/注册",
"accountNumber": "手机号码或邮箱",
"verificationCode": "验证码",
"getCode": "获取验证码",
"reacquireAfter": "s后重新获取",
"otherWays": "或使用以下方式登录",
"expressConsent": "登录/注册即表示你同意",
"and": " 和 ",
"userAgreement": "《用户协议》",
"privacyPolicy": "《隐私政策》",
// 首页
"deviceStatus": "设备状态",
"reconnect": "重新连接",
"connectingDevice": "正在连接设备...",
"connectedTo": "已连接到",
"signal": "信号",
"deviceId": "设备ID",
"firmwareVersion": "固件版本",
"disconnectedDevice": "未连接设备,点击下方按钮连接",
"connectDevice": "连接设备",
"topAiExperiences": "热门AI体验",
"aiAssistant": "AI全能助手",
"aiAssistantInfo": "24小时专属顾问,解答问题、提供建议",
"simultaneousInterpretation": "同声传译",
"simultaneousInterpretationInfo": "80+语言实时翻译,出国无忧",
"aiCompanion": "AI伴侣",
"aiCompanionInfo": "懂你心意,温暖陪伴",
"homeSlogan": "连接世界,超越声音",
"turnEarphones": "请打开耳机充电盒,将耳机放入您的耳朵",
"currentlyConnected": "当前已连接:",
"unsystemSetup": "去系统设置",
"talkLater": "稍后再说",
"initializing": "正在初始化...",
"bluetoothInitializationFailure": "蓝牙服务初始化失败",
"checkingPermissions": "正在检查权限...",
"lackPermissions": "缺少蓝牙权限",
"searchingDevice": "正在搜索设备...",
"findConnectedDevice": "找到已连接的设备",
"notFound": "未找到已连接的蓝牙设备",
"initializationFailure": "初始化失败",
"connectedDevice": "已连接到设备:",
"failedRefreshDevice": "刷新设备失败",
"noDeviceFound": "未找到已连接的设备",
"failedDeviceInformation": "无法获取设备信息",
"unknownEquipment": "未知设备",
"zeroSignal": "无信号",
"good": "良好",
"fine": "很好",
"excellent": "极佳",
"unknown": "未知",
// 发现页
"searchAi": "搜索AI功能...",
"exploreSlogan": "智能耳机,无限可能",
"exploreAi": "探索DeepVoice提供的AI功能",
"aiFunction": "AI功能",
"allRoundAssistant": "全能助理",
"allRoundAssistantInfo": "帮你解决各类问题",
"simultaneousTranslation": "同声翻译",
"simultaneousTranslationInfo": "实时翻译80+种语言",
"aiGirlfriend": "AI女友",
"aiGirlfriendInfo": "实时翻译80+种语言",
"healthConsultant": "健康顾问",
"healthConsultantInfo": "提供科学健康建议",
"conferenceAssistant": "商务会议助手",
"conferenceAssistantInfo": "实时记录会议纪要,多语言翻译",
"studyAssistant": "语言学习助手",
"studyAssistantInfo": "发音纠正,实时练习口语",
"travelTranslator": "旅行翻译助手",
"travelTranslatorInfo": "实时语音翻译,旅游景点介绍",
"beSearching": "正在搜索:",
"noAssistantFound": "未找到对应的AI助手",
"scene": "场景",
"youHaveSelected": "您选择了:",
"newFunction": "新功能",
"youChoseTry": "您选择了尝试",
"aiSinger": "AI歌手模式",
"aiSingerInfo": "用你喜欢的歌手声音演唱任何歌曲,甚至是你自己创作的歌曲",
"popularFunction": "热门功能场景",
"tryNow": "立即尝试",
// 设置页
"premiumMember": "高级会员",
"joinDevice": "已连接设备:",
"accountId": "账号ID:",
"systemSetting": "系统设置",
"languageSetting": "语言设置",
"darkMode": "暗黑模式",
"darkModeInfo": "切换应用的明暗主题",
"backups": "备份与恢复",
"backupsInfo": "备份设置和AI互动历史",
"lastBackup": "上次备份:",
"privacySettings": "隐私设置",
"privacySettingsInfo": "管理数据收集和权限",
"logout": "退出登录",
"logoutTips": "确定要退出当前账号吗?",
"aboutApplication": "关于应用",
"versions": "版本",
"checkUpdates": "检查更新",
"selectLanguage": "选择语言",
"voiceService": "语音服务",
"voiceServiceInfo": "选择语音识别和合成服务",
"selectVoiceService": "选择语音服务",
"azureVoiceService": "Azure 语音服务",
"azureVoiceServiceInfo": "微软云端语音服务,支持高质量语音合成和识别",
"flutterVoiceService": "Flutter 本地语音服务",
"flutterVoiceServiceInfo": "设备本地语音服务,无需联网即可使用",
// 聊天页
"clearChatHistory": "清空聊天记录",
"online": "在线",
"inputMessage": "输入消息...",
"imListening": "正在聆听...",
"canStartTalking": "你可以开始说话",
// 翻译页
"selectSourceLanguage": "选择源语言",
"selectTargetLanguage": "选择目标语言",
};

19
lib/core/widgets/common_bottom_nav.dart

@ -4,7 +4,7 @@ import '../../routes/app_routes.dart';
class CommonBottomNav extends StatelessWidget { class CommonBottomNav extends StatelessWidget {
final int currentIndex; final int currentIndex;
const CommonBottomNav({ const CommonBottomNav({
Key? key, Key? key,
required this.currentIndex, required this.currentIndex,
@ -13,7 +13,7 @@ class CommonBottomNav extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isDarkMode = Theme.of(context).brightness == Brightness.dark; final isDarkMode = Theme.of(context).brightness == Brightness.dark;
return BottomNavigationBar( return BottomNavigationBar(
currentIndex: currentIndex, currentIndex: currentIndex,
onTap: (index) { onTap: (index) {
@ -30,25 +30,28 @@ class CommonBottomNav extends StatelessWidget {
break; break;
} }
}, },
backgroundColor: isDarkMode ? Colors.black : const Color(0xFFF9FAFB), // 暗黑模式黑色背景,浅色模式浅灰色背景 backgroundColor: isDarkMode
? Colors.black
: const Color(0xFFF9FAFB), // 暗黑模式黑色背景,浅色模式浅灰色背景
selectedItemColor: const Color(0xFF3B82F6), // 蓝色选中项保持不变 selectedItemColor: const Color(0xFF3B82F6), // 蓝色选中项保持不变
unselectedItemColor: isDarkMode ? Colors.grey[500] : const Color(0xFF6B7280), // 暗黑模式灰色调整 unselectedItemColor:
isDarkMode ? Colors.grey[500] : const Color(0xFF6B7280), // 暗黑模式灰色调整
elevation: isDarkMode ? 0 : 8, // 暗黑模式无阴影,浅色模式保持阴影 elevation: isDarkMode ? 0 : 8, // 暗黑模式无阴影,浅色模式保持阴影
type: BottomNavigationBarType.fixed, type: BottomNavigationBarType.fixed,
items: [ items: [
BottomNavigationBarItem( BottomNavigationBarItem(
icon: const Icon(Icons.home), icon: const Icon(Icons.home),
label: 'home'.tr, label: 'home'.tr, // 首页
), ),
BottomNavigationBarItem( BottomNavigationBarItem(
icon: const Icon(Icons.explore), icon: const Icon(Icons.explore),
label: 'explore'.tr, label: 'explore'.tr, // 发现
), ),
BottomNavigationBarItem( BottomNavigationBarItem(
icon: const Icon(Icons.settings), icon: const Icon(Icons.settings),
label: 'settings'.tr, label: 'settings'.tr, // 设置
), ),
], ],
); );
} }
} }

26
lib/main.dart

@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:get_storage/get_storage.dart'; import 'package:get_storage/get_storage.dart';
@ -15,22 +16,22 @@ import 'core/themes/theme_service.dart';
void main() async { void main() async {
// 确保Flutter绑定初始化 // 确保Flutter绑定初始化
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
// 初始化存储 - 在创建应用之前初始化 // 初始化存储 - 在创建应用之前初始化
await GetStorage.init(); await GetStorage.init();
// 初始化环境变量 // 初始化环境变量
await dotenv.load(); await dotenv.load();
// 设置窗口大小(仅在 macOS 下生效) // 设置窗口大小(仅在 macOS 下生效)
if (Platform.isMacOS) { if (Platform.isMacOS) {
const double width = 375.0; const double width = 375.0;
const double height = 812.0; const double height = 812.0;
setWindowTitle('Deep Voice'); setWindowTitle('Deep Voice');
setWindowMinSize(const Size(width, height)); setWindowMinSize(const Size(width, height));
setWindowMaxSize(const Size(width, height)); setWindowMaxSize(const Size(width, height));
// 居中显示窗口 // 居中显示窗口
final screen = await getCurrentScreen(); final screen = await getCurrentScreen();
if (screen != null) { if (screen != null) {
@ -43,7 +44,7 @@ void main() async {
setWindowFrame(windowFrame); setWindowFrame(windowFrame);
} }
} }
// 运行应用 // 运行应用
runApp(const MainApp()); runApp(const MainApp());
} }
@ -55,7 +56,7 @@ class MainApp extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
// 获取保存的主题设置 // 获取保存的主题设置
final themeMode = ThemeService().getThemeMode(); final themeMode = ThemeService().getThemeMode();
return ScreenUtilInit( return ScreenUtilInit(
designSize: const Size(375, 812), designSize: const Size(375, 812),
minTextAdapt: true, minTextAdapt: true,
@ -71,9 +72,14 @@ class MainApp extends StatelessWidget {
getPages: AppPages.pages, getPages: AppPages.pages,
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
translations: AppTranslations(), translations: AppTranslations(),
locale: const Locale('zh', 'CN'), localizationsDelegates: const [
fallbackLocale: const Locale('en', 'US'), GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
locale: AppTranslations.deviceLocale(),
fallbackLocale: AppTranslations.fallbackLocale,
supportedLocales: AppTranslations.supportedLocales,
); );
}, },
); );

207
lib/modules/chat/views/chat_view.dart

@ -14,25 +14,22 @@ class ChatView extends GetView<ChatController> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isDarkMode = Theme.of(context).brightness == Brightness.dark; final isDarkMode = Theme.of(context).brightness == Brightness.dark;
return Scaffold( return Scaffold(
backgroundColor: isDarkMode ? AppColors.darkBackground : Colors.grey[50], backgroundColor: isDarkMode ? AppColors.darkBackground : Colors.grey[50],
appBar: _buildAppBar(isDarkMode), appBar: _buildAppBar(isDarkMode),
body: _buildBody(isDarkMode), body: _buildBody(isDarkMode),
); );
} }
// 构建AppBar // 构建AppBar
PreferredSizeWidget _buildAppBar(bool isDarkMode) { PreferredSizeWidget _buildAppBar(bool isDarkMode) {
return AppBar( return AppBar(
elevation: 1, elevation: 1,
backgroundColor: isDarkMode ? AppColors.cardBackground : Colors.white, backgroundColor: isDarkMode ? AppColors.cardBackground : Colors.white,
leading: IconButton( leading: IconButton(
icon: Icon( icon: Icon(Icons.arrow_back_ios,
Icons.arrow_back_ios, color: isDarkMode ? Colors.white : Colors.black87, size: 20.r),
color: isDarkMode ? Colors.white : Colors.black87,
size: 20.r
),
onPressed: () => Get.back(), onPressed: () => Get.back(),
), ),
titleSpacing: 0, titleSpacing: 0,
@ -101,7 +98,7 @@ class ChatView extends GetView<ChatController> {
), ),
SizedBox(width: 4.w), SizedBox(width: 4.w),
Text( Text(
'在线', 'online'.tr, // 在线
style: TextStyle( style: TextStyle(
fontSize: 12.sp, fontSize: 12.sp,
color: Colors.green, color: Colors.green,
@ -123,7 +120,9 @@ class ChatView extends GetView<ChatController> {
: Icons.volume_off, : Icons.volume_off,
color: controller.isTtsEnabled.value color: controller.isTtsEnabled.value
? AppColors.primary ? AppColors.primary
: isDarkMode ? Colors.grey[400] : Colors.grey, : isDarkMode
? Colors.grey[400]
: Colors.grey,
), ),
onPressed: controller.toggleTts, onPressed: controller.toggleTts,
)), )),
@ -133,7 +132,8 @@ class ChatView extends GetView<ChatController> {
height: 34.r, height: 34.r,
margin: EdgeInsets.only(right: 8.w), margin: EdgeInsets.only(right: 8.w),
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDarkMode ? Colors.white.withOpacity(0.1) : Colors.grey[100], color:
isDarkMode ? Colors.white.withOpacity(0.1) : Colors.grey[100],
shape: BoxShape.circle, shape: BoxShape.circle,
boxShadow: [ boxShadow: [
if (!isDarkMode) if (!isDarkMode)
@ -145,11 +145,8 @@ class ChatView extends GetView<ChatController> {
], ],
), ),
child: PopupMenuButton( child: PopupMenuButton(
icon: Icon( icon: Icon(Icons.more_vert,
Icons.more_vert, color: isDarkMode ? Colors.white : Colors.black87, size: 20.r),
color: isDarkMode ? Colors.white : Colors.black87,
size: 20.r
),
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.r), borderRadius: BorderRadius.circular(12.r),
), ),
@ -162,7 +159,7 @@ class ChatView extends GetView<ChatController> {
Icon(Icons.delete_outline, color: Colors.red), Icon(Icons.delete_outline, color: Colors.red),
SizedBox(width: 8.w), SizedBox(width: 8.w),
Text( Text(
'清空聊天记录', 'clearChatHistory'.tr, // 清空聊天记录
style: TextStyle( style: TextStyle(
color: isDarkMode ? Colors.white : Colors.black87, color: isDarkMode ? Colors.white : Colors.black87,
), ),
@ -183,7 +180,7 @@ class ChatView extends GetView<ChatController> {
], ],
); );
} }
// 构建主体内容 // 构建主体内容
Widget _buildBody(bool isDarkMode) { Widget _buildBody(bool isDarkMode) {
return Column( return Column(
@ -198,13 +195,13 @@ class ChatView extends GetView<ChatController> {
} }
}), }),
), ),
// 底部输入区域 // 底部输入区域
_buildInputArea(isDarkMode), _buildInputArea(isDarkMode),
], ],
); );
} }
// 构建空状态 // 构建空状态
Widget _buildEmptyState(bool isDarkMode) { Widget _buildEmptyState(bool isDarkMode) {
return Center( return Center(
@ -215,13 +212,15 @@ class ChatView extends GetView<ChatController> {
width: 80.w, width: 80.w,
height: 80.w, height: 80.w,
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDarkMode ? Colors.white.withOpacity(0.1) : Colors.grey[100], color:
isDarkMode ? Colors.white.withOpacity(0.1) : Colors.grey[100],
shape: BoxShape.circle, shape: BoxShape.circle,
), ),
child: Icon( child: Icon(
Icons.chat_bubble_outline, Icons.chat_bubble_outline,
size: 40.w, size: 40.w,
color: isDarkMode ? Colors.white.withOpacity(0.5) : Colors.grey[400], color:
isDarkMode ? Colors.white.withOpacity(0.5) : Colors.grey[400],
), ),
), ),
SizedBox(height: 24.h), SizedBox(height: 24.h),
@ -250,7 +249,7 @@ class ChatView extends GetView<ChatController> {
), ),
); );
} }
// 构建聊天列表 // 构建聊天列表
Widget _buildChatList(bool isDarkMode) { Widget _buildChatList(bool isDarkMode) {
return ListView.builder( return ListView.builder(
@ -266,32 +265,34 @@ class ChatView extends GetView<ChatController> {
}, },
); );
} }
// 构建消息项 // 构建消息项
Widget _buildMessageItem(Message message, bool isDarkMode) { Widget _buildMessageItem(Message message, bool isDarkMode) {
final isUser = message.role == 'user'; final isUser = message.role == 'user';
return Padding( return Padding(
padding: EdgeInsets.only(bottom: 16.h), padding: EdgeInsets.only(bottom: 16.h),
child: Row( child: Row(
mainAxisAlignment: isUser ? MainAxisAlignment.end : MainAxisAlignment.start, mainAxisAlignment:
isUser ? MainAxisAlignment.end : MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
if (!isUser) _buildAvatar(false, isDarkMode), if (!isUser) _buildAvatar(false, isDarkMode),
SizedBox(width: 8.w), SizedBox(width: 8.w),
Flexible( Flexible(
child: Column( child: Column(
crossAxisAlignment: isUser ? CrossAxisAlignment.end : CrossAxisAlignment.start, crossAxisAlignment:
isUser ? CrossAxisAlignment.end : CrossAxisAlignment.start,
children: [ children: [
// 消息气泡 // 消息气泡
Container( Container(
padding: EdgeInsets.all(12.r), padding: EdgeInsets.all(12.r),
decoration: BoxDecoration( decoration: BoxDecoration(
color: isUser color: isUser
? AppColors.primary ? AppColors.primary
: isDarkMode ? AppColors.cardBackground : Colors.grey[200], : isDarkMode
? AppColors.cardBackground
: Colors.grey[200],
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(16.r),
), ),
child: Column( child: Column(
@ -302,14 +303,16 @@ class ChatView extends GetView<ChatController> {
SelectableText( SelectableText(
message.content, message.content,
style: TextStyle( style: TextStyle(
color: isUser color: isUser
? Colors.white ? Colors.white
: isDarkMode ? Colors.white : Colors.black, : isDarkMode
? Colors.white
: Colors.black,
fontSize: 16.sp, fontSize: 16.sp,
height: 1.5, height: 1.5,
), ),
), ),
// 加载中动画 // 加载中动画
if (message.isLoading) if (message.isLoading)
Padding( Padding(
@ -325,7 +328,7 @@ class ChatView extends GetView<ChatController> {
], ],
), ),
), ),
// 时间戳 // 时间戳
Padding( Padding(
padding: EdgeInsets.only(top: 4.h, left: 4.w, right: 4.w), padding: EdgeInsets.only(top: 4.h, left: 4.w, right: 4.w),
@ -340,9 +343,7 @@ class ChatView extends GetView<ChatController> {
], ],
), ),
), ),
SizedBox(width: 8.w), SizedBox(width: 8.w),
if (isUser) _buildAvatar(true, isDarkMode), if (isUser) _buildAvatar(true, isDarkMode),
], ],
), ),
@ -351,8 +352,9 @@ class ChatView extends GetView<ChatController> {
// 构建输入区域 // 构建输入区域
Widget _buildInputArea(bool isDarkMode) { Widget _buildInputArea(bool isDarkMode) {
final textController = TextEditingController(text: controller.inputText.value); final textController =
TextEditingController(text: controller.inputText.value);
// 检查是否处于语音识别模式 // 检查是否处于语音识别模式
return Obx(() { return Obx(() {
if (controller.isVoiceRecognitionActive.value) { if (controller.isVoiceRecognitionActive.value) {
@ -362,9 +364,10 @@ class ChatView extends GetView<ChatController> {
} }
}); });
} }
// 构建文本输入面板 // 构建文本输入面板
Widget _buildTextInputPanel(bool isDarkMode, TextEditingController textController) { Widget _buildTextInputPanel(
bool isDarkMode, TextEditingController textController) {
return Container( return Container(
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h), padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h),
decoration: BoxDecoration( decoration: BoxDecoration(
@ -385,7 +388,9 @@ class ChatView extends GetView<ChatController> {
width: 40.w, width: 40.w,
height: 40.w, height: 40.w,
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDarkMode ? Colors.white.withOpacity(0.1) : Colors.grey[100], color: isDarkMode
? Colors.white.withOpacity(0.1)
: Colors.grey[100],
shape: BoxShape.circle, shape: BoxShape.circle,
), ),
child: IconButton( child: IconButton(
@ -398,20 +403,21 @@ class ChatView extends GetView<ChatController> {
onPressed: controller.startVoiceRecognition, onPressed: controller.startVoiceRecognition,
), ),
), ),
SizedBox(width: 8.w), SizedBox(width: 8.w),
// 输入框 // 输入框
Expanded( Expanded(
child: TextField( child: TextField(
controller: textController, controller: textController,
decoration: InputDecoration( decoration: InputDecoration(
hintText: '输入消息...', hintText: 'inputMessage'.tr, // 输入消息...
hintStyle: TextStyle( hintStyle: TextStyle(
color: isDarkMode ? Colors.grey[500] : Colors.grey[500] color: isDarkMode ? Colors.grey[500] : Colors.grey[500]),
),
filled: true, filled: true,
fillColor: isDarkMode ? Colors.white.withOpacity(0.1) : Colors.grey[100], fillColor: isDarkMode
? Colors.white.withOpacity(0.1)
: Colors.grey[100],
border: OutlineInputBorder( border: OutlineInputBorder(
borderRadius: BorderRadius.circular(20.r), borderRadius: BorderRadius.circular(20.r),
borderSide: BorderSide.none, borderSide: BorderSide.none,
@ -435,9 +441,9 @@ class ChatView extends GetView<ChatController> {
}, },
), ),
), ),
SizedBox(width: 8.w), SizedBox(width: 8.w),
// 发送按钮 // 发送按钮
Obx(() { Obx(() {
final hasText = controller.inputText.value.isNotEmpty; final hasText = controller.inputText.value.isNotEmpty;
@ -445,17 +451,21 @@ class ChatView extends GetView<ChatController> {
width: 40.w, width: 40.w,
height: 40.w, height: 40.w,
decoration: BoxDecoration( decoration: BoxDecoration(
color: hasText color: hasText
? AppColors.primary ? AppColors.primary
: isDarkMode ? Colors.white.withOpacity(0.1) : Colors.grey[200], : isDarkMode
? Colors.white.withOpacity(0.1)
: Colors.grey[200],
shape: BoxShape.circle, shape: BoxShape.circle,
), ),
child: IconButton( child: IconButton(
icon: Icon( icon: Icon(
Icons.send, Icons.send,
color: hasText color: hasText
? Colors.white ? Colors.white
: isDarkMode ? Colors.white.withOpacity(0.5) : Colors.grey[400], : isDarkMode
? Colors.white.withOpacity(0.5)
: Colors.grey[400],
size: 18.r, size: 18.r,
), ),
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
@ -473,7 +483,7 @@ class ChatView extends GetView<ChatController> {
), ),
); );
} }
// 构建语音输入面板 // 构建语音输入面板
Widget _buildVoiceInputPanel(bool isDarkMode) { Widget _buildVoiceInputPanel(bool isDarkMode) {
return Container( return Container(
@ -501,7 +511,9 @@ class ChatView extends GetView<ChatController> {
width: 60.r, width: 60.r,
height: 60.r, height: 60.r,
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDarkMode ? Colors.white.withOpacity(0.1) : Colors.white, color: isDarkMode
? Colors.white.withOpacity(0.1)
: Colors.white,
shape: BoxShape.circle, shape: BoxShape.circle,
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
@ -513,13 +525,17 @@ class ChatView extends GetView<ChatController> {
), ),
child: Center( child: Center(
child: Obx(() => Icon( child: Obx(() => Icon(
controller.isListening.value ? Icons.mic : Icons.mic_none, controller.isListening.value
color: controller.isListening.value ? AppColors.primary : Colors.grey.shade400, ? Icons.mic
size: 28.r, : Icons.mic_none,
)), color: controller.isListening.value
? AppColors.primary
: Colors.grey.shade400,
size: 28.r,
)),
), ),
), ),
// 中间音频波形和状态文本 // 中间音频波形和状态文本
Expanded( Expanded(
child: Column( child: Column(
@ -542,10 +558,10 @@ class ChatView extends GetView<ChatController> {
23, 23,
(index) { (index) {
// 随机高度模拟波形 // 随机高度模拟波形
double height = isSpeaking double height = isSpeaking
? (Random().nextDouble() * 20.0 + 5.0).h ? (Random().nextDouble() * 20.0 + 5.0).h
: 2.h; : 2.h;
return SizedBox( return SizedBox(
width: 6.w, width: 6.w,
child: Center( child: Center(
@ -553,12 +569,16 @@ class ChatView extends GetView<ChatController> {
duration: Duration(milliseconds: 500), duration: Duration(milliseconds: 500),
width: 2.5.w, width: 2.5.w,
height: height, height: height,
margin: EdgeInsets.symmetric(horizontal: 1.5.w), margin: EdgeInsets.symmetric(
horizontal: 1.5.w),
decoration: BoxDecoration( decoration: BoxDecoration(
color: isSpeaking color: isSpeaking
? AppColors.primary ? AppColors.primary
: isDarkMode ? Colors.grey.shade600 : Colors.grey.shade400, : isDarkMode
borderRadius: BorderRadius.circular(2.r), ? Colors.grey.shade600
: Colors.grey.shade400,
borderRadius:
BorderRadius.circular(2.r),
), ),
), ),
), ),
@ -571,26 +591,32 @@ class ChatView extends GetView<ChatController> {
}), }),
), ),
SizedBox(height: 8.h), SizedBox(height: 8.h),
// 语音状态文本 // 语音状态文本
Obx(() => Text( Obx(() => Text(
controller.isSpeechDetected.value ? '正在聆听...' : '你可以开始说话', controller.isSpeechDetected.value
style: TextStyle( ? 'imListening'.tr // 正在聆听...
fontSize: 14.sp, : 'canStartTalking'.tr, // 你可以开始说话
color: isDarkMode ? Colors.grey[400] : Colors.grey[700], style: TextStyle(
fontWeight: FontWeight.w500, fontSize: 14.sp,
), color: isDarkMode
)), ? Colors.grey[400]
: Colors.grey[700],
fontWeight: FontWeight.w500,
),
)),
], ],
), ),
), ),
// 关闭按钮 // 关闭按钮
Container( Container(
width: 60.r, width: 60.r,
height: 60.r, height: 60.r,
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDarkMode ? Colors.white.withOpacity(0.1) : Colors.white, color: isDarkMode
? Colors.white.withOpacity(0.1)
: Colors.white,
shape: BoxShape.circle, shape: BoxShape.circle,
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
@ -619,15 +645,15 @@ class ChatView extends GetView<ChatController> {
), ),
); );
} }
// 构建头像 // 构建头像
Widget _buildAvatar(bool isUser, bool isDarkMode) { Widget _buildAvatar(bool isUser, bool isDarkMode) {
return Container( return Container(
width: 36.r, width: 36.r,
height: 36.r, height: 36.r,
decoration: BoxDecoration( decoration: BoxDecoration(
color: isUser color: isUser
? (isDarkMode ? Colors.white.withOpacity(0.1) : Colors.grey[200]) ? (isDarkMode ? Colors.white.withOpacity(0.1) : Colors.grey[200])
: AppColors.primary, : AppColors.primary,
shape: BoxShape.circle, shape: BoxShape.circle,
boxShadow: [ boxShadow: [
@ -668,7 +694,7 @@ class ChatView extends GetView<ChatController> {
), ),
); );
} }
// 构建加载动画点 // 构建加载动画点
Widget _buildLoadingDot(int index, bool isDarkMode) { Widget _buildLoadingDot(int index, bool isDarkMode) {
return AnimatedBuilder( return AnimatedBuilder(
@ -679,21 +705,22 @@ class ChatView extends GetView<ChatController> {
width: 8.r, width: 8.r,
height: 8.r, height: 8.r,
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDarkMode ? Colors.white.withOpacity(0.5) : Colors.grey[400], color:
isDarkMode ? Colors.white.withOpacity(0.5) : Colors.grey[400],
shape: BoxShape.circle, shape: BoxShape.circle,
), ),
); );
}, },
); );
} }
// 格式化时间 // 格式化时间
String _formatTime(DateTime time) { String _formatTime(DateTime time) {
final now = DateTime.now(); final now = DateTime.now();
final today = DateTime(now.year, now.month, now.day); final today = DateTime(now.year, now.month, now.day);
final yesterday = today.subtract(const Duration(days: 1)); final yesterday = today.subtract(const Duration(days: 1));
final messageDate = DateTime(time.year, time.month, time.day); final messageDate = DateTime(time.year, time.month, time.day);
if (messageDate == today) { if (messageDate == today) {
return '今天 ${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}'; return '今天 ${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}';
} else if (messageDate == yesterday) { } else if (messageDate == yesterday) {
@ -702,4 +729,4 @@ class ChatView extends GetView<ChatController> {
return '${time.month}月${time.day}日 ${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}'; return '${time.month}月${time.day}日 ${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}';
} }
} }
} }

80
lib/modules/explore/controllers/explore_controller.dart

@ -13,7 +13,7 @@ class AIFeature {
final Color iconColor; final Color iconColor;
final String agentId; final String agentId;
final String targetRoute; final String targetRoute;
AIFeature({ AIFeature({
required this.title, required this.title,
required this.description, required this.description,
@ -34,7 +34,7 @@ class FunctionScene {
final Color startColor; final Color startColor;
final Color endColor; final Color endColor;
final Color iconColor; final Color iconColor;
FunctionScene({ FunctionScene({
required this.title, required this.title,
required this.description, required this.description,
@ -48,12 +48,12 @@ class FunctionScene {
class ExploreController extends GetxController { class ExploreController extends GetxController {
// 搜索关键词 // 搜索关键词
final searchController = TextEditingController(); final searchController = TextEditingController();
// AI功能列表 // AI功能列表
final List<AIFeature> aiFeatures = [ final List<AIFeature> aiFeatures = [
AIFeature( AIFeature(
title: '全能助理', title: 'allRoundAssistant'.tr, // 全能助理
description: '帮你解决各类问题', description: 'allRoundAssistantInfo'.tr, // 帮你解决各类问题
icon: Icons.assistant, icon: Icons.assistant,
startColor: const Color(0xFF0066CC), startColor: const Color(0xFF0066CC),
endColor: const Color(0xFF5E5CE6), endColor: const Color(0xFF5E5CE6),
@ -63,8 +63,8 @@ class ExploreController extends GetxController {
targetRoute: Routes.chat, targetRoute: Routes.chat,
), ),
AIFeature( AIFeature(
title: '同声翻译', title: 'simultaneousTranslation'.tr, // 同声翻译
description: '实时翻译80+种语言', description: 'simultaneousTranslationInfo.tr', // 实时翻译80+种语言
icon: Icons.translate, icon: Icons.translate,
startColor: const Color(0xFF4CAF50), startColor: const Color(0xFF4CAF50),
endColor: const Color(0xFF009688), endColor: const Color(0xFF009688),
@ -74,8 +74,8 @@ class ExploreController extends GetxController {
targetRoute: Routes.translation, targetRoute: Routes.translation,
), ),
AIFeature( AIFeature(
title: 'AI女友', title: 'aiGirlfriend'.tr, // AI女友
description: '智能陪伴,随时聊天', description: 'aiGirlfriendInfo'.tr, // 智能陪伴,随时聊天
icon: Icons.favorite, icon: Icons.favorite,
startColor: const Color(0xFFE91E63), startColor: const Color(0xFFE91E63),
endColor: const Color(0xFFFF5252), endColor: const Color(0xFFFF5252),
@ -85,8 +85,8 @@ class ExploreController extends GetxController {
targetRoute: Routes.chat, targetRoute: Routes.chat,
), ),
AIFeature( AIFeature(
title: '健康顾问', title: 'healthConsultant'.tr, // 健康顾问
description: '提供科学健康建议', description: 'healthConsultantInfo'.tr, // 提供科学健康建议
icon: Icons.healing, icon: Icons.healing,
startColor: const Color(0xFFFFC107), startColor: const Color(0xFFFFC107),
endColor: const Color(0xFFFF9800), endColor: const Color(0xFFFF9800),
@ -96,65 +96,65 @@ class ExploreController extends GetxController {
targetRoute: Routes.chat, targetRoute: Routes.chat,
), ),
]; ];
// 热门功能场景列表 // 热门功能场景列表
final List<FunctionScene> functionScenes = [ final List<FunctionScene> functionScenes = [
FunctionScene( FunctionScene(
title: '商务会议助手', title: 'conferenceAssistant'.tr, // 商务会议助手
description: '实时记录会议纪要,多语言翻译', description: 'conferenceAssistantInfo'.tr, // 实时记录会议纪要,多语言翻译
icon: Icons.people, icon: Icons.people,
startColor: const Color(0xFF3F51B5), startColor: const Color(0xFF3F51B5),
endColor: const Color(0xFF673AB7), endColor: const Color(0xFF673AB7),
iconColor: const Color(0xFF3F51B5), iconColor: const Color(0xFF3F51B5),
), ),
FunctionScene( FunctionScene(
title: '语言学习助手', title: 'studyAssistant'.tr, // 语言学习助手
description: '发音纠正,实时练习口语', description: 'studyAssistantInfo'.tr, // 发音纠正,实时练习口语
icon: Icons.school, icon: Icons.school,
startColor: const Color(0xFF4CAF50), startColor: const Color(0xFF4CAF50),
endColor: const Color(0xFF009688), endColor: const Color(0xFF009688),
iconColor: const Color(0xFF4CAF50), iconColor: const Color(0xFF4CAF50),
), ),
FunctionScene( FunctionScene(
title: '旅行翻译助手', title: 'travelTranslator'.tr, // 旅行翻译助手
description: '实时语音翻译,旅游景点介绍', description: 'travelTranslatorInfo'.tr, // 实时语音翻译,旅游景点介绍
icon: Icons.language, icon: Icons.language,
startColor: const Color(0xFFFFC107), startColor: const Color(0xFFFFC107),
endColor: const Color(0xFFFF9800), endColor: const Color(0xFFFF9800),
iconColor: const Color(0xFFFFC107), iconColor: const Color(0xFFFFC107),
), ),
]; ];
// 搜索功能 // 搜索功能
void search() { void search() {
if (searchController.text.isEmpty) return; if (searchController.text.isEmpty) return;
Get.snackbar( Get.snackbar(
'搜索', 'search'.tr, // 搜索
'正在搜索: ${searchController.text}', '${'beSearching'.tr} ${searchController.text}', // 正在搜索:
snackPosition: SnackPosition.TOP, snackPosition: SnackPosition.TOP,
); );
// 实际应用中,这里应该实现真正的搜索逻辑 // 实际应用中,这里应该实现真正的搜索逻辑
searchController.clear(); searchController.clear();
} }
// 点击AI功能 // 点击AI功能
void onAIFeatureTap(int index) { void onAIFeatureTap(int index) {
if (index < 0 || index >= aiFeatures.length) return; if (index < 0 || index >= aiFeatures.length) return;
final feature = aiFeatures[index]; final feature = aiFeatures[index];
final agent = AgentProvider.getAgentById(feature.agentId); final agent = AgentProvider.getAgentById(feature.agentId);
if (agent == null) { if (agent == null) {
Get.snackbar( Get.snackbar(
'错误', 'error'.tr, // 错误
'未找到对应的AI助手', 'noAssistantFound'.tr, // 未找到对应的AI助手
snackPosition: SnackPosition.TOP, snackPosition: SnackPosition.TOP,
); );
return; return;
} }
// 根据目标路由进行跳转 // 根据目标路由进行跳转
if (feature.targetRoute == Routes.chat) { if (feature.targetRoute == Routes.chat) {
// 跳转到聊天页面 // 跳转到聊天页面
@ -172,34 +172,34 @@ class ExploreController extends GetxController {
Get.toNamed(feature.targetRoute); Get.toNamed(feature.targetRoute);
} }
} }
// 点击功能场景 // 点击功能场景
void onFunctionSceneTap(int index) { void onFunctionSceneTap(int index) {
if (index < 0 || index >= functionScenes.length) return; if (index < 0 || index >= functionScenes.length) return;
Get.snackbar( Get.snackbar(
'场景', 'scene'.tr, // 场景
'您选择了: ${functionScenes[index].title}', '${'youHaveSelected'.tr} ${functionScenes[index].title}', // 您选择了:
snackPosition: SnackPosition.TOP, snackPosition: SnackPosition.TOP,
); );
// 实际应用中,这里应该跳转到对应的场景页面 // 实际应用中,这里应该跳转到对应的场景页面
} }
// 尝试新功能 // 尝试新功能
void tryNewFeature() { void tryNewFeature() {
Get.snackbar( Get.snackbar(
'新功能', 'newFunction'.tr, // 新功能
'您选择了尝试AI歌手模式', '${'youChoseTry'.tr}${'aiSinger'.tr}', // 您选择了尝试 // AI歌手模式
snackPosition: SnackPosition.TOP, snackPosition: SnackPosition.TOP,
); );
// 实际应用中,这里应该跳转到新功能页面 // 实际应用中,这里应该跳转到新功能页面
} }
@override @override
void onClose() { void onClose() {
searchController.dispose(); searchController.dispose();
super.onClose(); super.onClose();
} }
} }

60
lib/modules/explore/views/explore_view.dart

@ -11,7 +11,7 @@ class ExploreView extends GetView<ExploreController> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isDarkMode = Theme.of(context).brightness == Brightness.dark; final isDarkMode = Theme.of(context).brightness == Brightness.dark;
return Scaffold( return Scaffold(
backgroundColor: isDarkMode ? Colors.black : Colors.grey[50], backgroundColor: isDarkMode ? Colors.black : Colors.grey[50],
body: SafeArea( body: SafeArea(
@ -19,7 +19,7 @@ class ExploreView extends GetView<ExploreController> {
children: [ children: [
// 标题和搜索 // 标题和搜索
_buildHeaderAndSearch(isDarkMode), _buildHeaderAndSearch(isDarkMode),
// 主要内容(可滚动) // 主要内容(可滚动)
Expanded( Expanded(
child: SingleChildScrollView( child: SingleChildScrollView(
@ -31,15 +31,15 @@ class ExploreView extends GetView<ExploreController> {
children: [ children: [
// 特色功能 // 特色功能
_buildFeaturedCard(), _buildFeaturedCard(),
// AI功能列表 // AI功能列表
SizedBox(height: 24.h), SizedBox(height: 24.h),
_buildAIFeaturesSection(isDarkMode), _buildAIFeaturesSection(isDarkMode),
// 热门功能场景 // 热门功能场景
SizedBox(height: 24.h), SizedBox(height: 24.h),
_buildFunctionScenesSection(isDarkMode), _buildFunctionScenesSection(isDarkMode),
// 新功能推荐 // 新功能推荐
SizedBox(height: 24.h), SizedBox(height: 24.h),
_buildNewFeatureSection(isDarkMode), _buildNewFeatureSection(isDarkMode),
@ -66,7 +66,7 @@ class ExploreView extends GetView<ExploreController> {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Text(
'发现', 'explore'.tr, // 发现
style: TextStyle( style: TextStyle(
fontSize: 24.sp, fontSize: 24.sp,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
@ -77,12 +77,14 @@ class ExploreView extends GetView<ExploreController> {
width: 32.w, width: 32.w,
height: 32.w, height: 32.w,
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDarkMode ? Colors.white.withOpacity(0.1) : Colors.grey[200], color: isDarkMode
? Colors.white.withOpacity(0.1)
: Colors.grey[200],
shape: BoxShape.circle, shape: BoxShape.circle,
), ),
child: IconButton( child: IconButton(
icon: Icon( icon: Icon(
Icons.more_vert, Icons.more_vert,
size: 16.sp, size: 16.sp,
color: isDarkMode ? Colors.white : Colors.grey[700], color: isDarkMode ? Colors.white : Colors.grey[700],
), ),
@ -92,18 +94,19 @@ class ExploreView extends GetView<ExploreController> {
), ),
], ],
), ),
// 搜索框 // 搜索框
SizedBox(height: 16.h), SizedBox(height: 16.h),
Container( Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDarkMode ? Colors.white.withOpacity(0.1) : Colors.grey[100], color:
isDarkMode ? Colors.white.withOpacity(0.1) : Colors.grey[100],
borderRadius: BorderRadius.circular(12.r), borderRadius: BorderRadius.circular(12.r),
), ),
child: TextField( child: TextField(
controller: controller.searchController, controller: controller.searchController,
decoration: InputDecoration( decoration: InputDecoration(
hintText: '搜索AI功能...', hintText: 'searchAi'.tr, // 搜索AI功能...
hintStyle: TextStyle( hintStyle: TextStyle(
fontSize: 14.sp, fontSize: 14.sp,
color: isDarkMode ? Colors.grey[400] : Colors.grey[400], color: isDarkMode ? Colors.grey[400] : Colors.grey[400],
@ -169,7 +172,7 @@ class ExploreView extends GetView<ExploreController> {
), ),
), ),
), ),
// 渐变遮罩 // 渐变遮罩
Container( Container(
decoration: BoxDecoration( decoration: BoxDecoration(
@ -184,7 +187,7 @@ class ExploreView extends GetView<ExploreController> {
), ),
), ),
), ),
// 文本内容 // 文本内容
Padding( Padding(
padding: EdgeInsets.all(24.w), padding: EdgeInsets.all(24.w),
@ -193,7 +196,7 @@ class ExploreView extends GetView<ExploreController> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
'智能耳机,无限可能', 'exploreSlogan'.tr, // 智能耳机,无限可能
style: TextStyle( style: TextStyle(
fontSize: 22.sp, fontSize: 22.sp,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
@ -202,7 +205,7 @@ class ExploreView extends GetView<ExploreController> {
), ),
SizedBox(height: 4.h), SizedBox(height: 4.h),
Text( Text(
'探索DeepVoice提供的AI功能', 'exploreAi'.tr, // 探索DeepVoice提供的AI功能
style: TextStyle( style: TextStyle(
fontSize: 14.sp, fontSize: 14.sp,
color: Colors.white.withOpacity(0.8), color: Colors.white.withOpacity(0.8),
@ -222,7 +225,7 @@ class ExploreView extends GetView<ExploreController> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
'AI功能', 'aiFunction'.tr, // AI功能
style: TextStyle( style: TextStyle(
fontSize: 18.sp, fontSize: 18.sp,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@ -267,7 +270,7 @@ class ExploreView extends GetView<ExploreController> {
borderRadius: BorderRadius.circular(20.r), borderRadius: BorderRadius.circular(20.r),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: isDarkMode color: isDarkMode
? Colors.black.withOpacity(0.2) ? Colors.black.withOpacity(0.2)
: Colors.grey.withOpacity(0.1), : Colors.grey.withOpacity(0.1),
blurRadius: 8, blurRadius: 8,
@ -338,7 +341,7 @@ class ExploreView extends GetView<ExploreController> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
'热门功能场景', 'popularFunction'.tr, // 热门功能场景
style: TextStyle( style: TextStyle(
fontSize: 18.sp, fontSize: 18.sp,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@ -363,7 +366,8 @@ class ExploreView extends GetView<ExploreController> {
} }
// 功能场景项 // 功能场景项
Widget _buildFunctionSceneItem(FunctionScene scene, int index, bool isDarkMode) { Widget _buildFunctionSceneItem(
FunctionScene scene, int index, bool isDarkMode) {
return GestureDetector( return GestureDetector(
onTap: () => controller.onFunctionSceneTap(index), onTap: () => controller.onFunctionSceneTap(index),
child: Container( child: Container(
@ -373,7 +377,7 @@ class ExploreView extends GetView<ExploreController> {
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(16.r),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: isDarkMode color: isDarkMode
? Colors.black.withOpacity(0.2) ? Colors.black.withOpacity(0.2)
: Colors.grey.withOpacity(0.1), : Colors.grey.withOpacity(0.1),
blurRadius: 4, blurRadius: 4,
@ -405,7 +409,7 @@ class ExploreView extends GetView<ExploreController> {
), ),
), ),
SizedBox(width: 16.w), SizedBox(width: 16.w),
// 文本内容 // 文本内容
Expanded( Expanded(
child: Column( child: Column(
@ -478,10 +482,10 @@ class ExploreView extends GetView<ExploreController> {
), ),
), ),
SizedBox(height: 16.h), SizedBox(height: 16.h),
// 标题 // 标题
Text( Text(
'AI歌手模式', 'aiSinger'.tr, // AI歌手模式
style: TextStyle( style: TextStyle(
fontSize: 20.sp, fontSize: 20.sp,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
@ -489,17 +493,17 @@ class ExploreView extends GetView<ExploreController> {
), ),
), ),
SizedBox(height: 8.h), SizedBox(height: 8.h),
// 描述 // 描述
Text( Text(
'用你喜欢的歌手声音演唱任何歌曲,甚至是你自己创作的歌曲', 'aiSingerInfo'.tr, // 用你喜欢的歌手声音演唱任何歌曲,甚至是你自己创作的歌曲
style: TextStyle( style: TextStyle(
fontSize: 14.sp, fontSize: 14.sp,
color: isDarkMode ? Colors.grey[300] : Colors.grey[600], color: isDarkMode ? Colors.grey[300] : Colors.grey[600],
), ),
), ),
SizedBox(height: 16.h), SizedBox(height: 16.h),
// 按钮 // 按钮
ElevatedButton( ElevatedButton(
onPressed: controller.tryNewFeature, onPressed: controller.tryNewFeature,
@ -514,7 +518,7 @@ class ExploreView extends GetView<ExploreController> {
), ),
), ),
child: Text( child: Text(
'立即尝试', 'tryNow'.tr, // 立即尝试
style: TextStyle( style: TextStyle(
fontSize: 14.sp, fontSize: 14.sp,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
@ -526,4 +530,4 @@ class ExploreView extends GetView<ExploreController> {
), ),
); );
} }
} }

115
lib/modules/home/controllers/home_controller.dart

@ -6,76 +6,77 @@ import 'package:flutter_blue_plus/flutter_blue_plus.dart' hide BluetoothDevice;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../../data/services/voice_interaction_service.dart'; import '../../../data/services/voice_interaction_service.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
// 连接状态枚举 // 连接状态枚举
enum ConnectionStatus { enum ConnectionStatus {
disconnected, // 未连接 disconnected, // 未连接
connecting, // 连接中 connecting, // 连接中
connected, // 已连接 connected, // 已连接
failed // 连接失败 failed // 连接失败
} }
class HomeController extends GetxController { class HomeController extends GetxController {
static HomeController get to => Get.find(); static HomeController get to => Get.find();
// 经典蓝牙服务 // 经典蓝牙服务
late classic.ClassicBluetoothService _bluetoothService; late classic.ClassicBluetoothService _bluetoothService;
// 设备连接状态 // 设备连接状态
final _isConnected = false.obs; final _isConnected = false.obs;
bool get isConnected => _isConnected.value; bool get isConnected => _isConnected.value;
// 连接状态 // 连接状态
final _connectionStatus = ConnectionStatus.disconnected.obs; final _connectionStatus = ConnectionStatus.disconnected.obs;
ConnectionStatus get connectionStatus => _connectionStatus.value; ConnectionStatus get connectionStatus => _connectionStatus.value;
// 电池状态(模拟数据) // 电池状态(模拟数据)
final _deviceBattery = Rx<int?>(null); final _deviceBattery = Rx<int?>(null);
int? get deviceBattery => _deviceBattery.value; int? get deviceBattery => _deviceBattery.value;
// 固件版本(模拟数据) // 固件版本(模拟数据)
final String firmwareVersion = 'v2.4.1'; final String firmwareVersion = 'v2.4.1';
// 蓝牙设备信息 // 蓝牙设备信息
final _deviceInfo = <String, dynamic>{}.obs; final _deviceInfo = <String, dynamic>{}.obs;
Map<String, dynamic> get deviceInfo => _deviceInfo.value; Map<String, dynamic> get deviceInfo => _deviceInfo.value;
// 连接错误信息 // 连接错误信息
final _connectionErrorMsg = ''.obs; final _connectionErrorMsg = ''.obs;
String get connectionErrorMsg => _connectionErrorMsg.value; String get connectionErrorMsg => _connectionErrorMsg.value;
// 当前连接的经典蓝牙设备 // 当前连接的经典蓝牙设备
final _connectedClassicDevices = <classic.BluetoothDevice>[].obs; final _connectedClassicDevices = <classic.BluetoothDevice>[].obs;
List<classic.BluetoothDevice> get connectedClassicDevices => _connectedClassicDevices; List<classic.BluetoothDevice> get connectedClassicDevices =>
_connectedClassicDevices;
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
// 初始化蓝牙服务和状态监控 // 初始化蓝牙服务和状态监控
_initBluetoothService(); _initBluetoothService();
// 电池模拟更新(实际应用中可以通过蓝牙获取) // 电池模拟更新(实际应用中可以通过蓝牙获取)
_simulateBatteryUpdate(); _simulateBatteryUpdate();
} }
// 初始化蓝牙服务 // 初始化蓝牙服务
void _initBluetoothService() { void _initBluetoothService() {
try { try {
// 获取经典蓝牙服务实例 // 获取经典蓝牙服务实例
_bluetoothService = Get.find<classic.ClassicBluetoothService>(); _bluetoothService = Get.find<classic.ClassicBluetoothService>();
// 更新当前连接状态 // 更新当前连接状态
_updateConnectionStatus(); _updateConnectionStatus();
// 监听蓝牙连接状态变化 // 监听蓝牙连接状态变化
_setupBluetoothListeners(); _setupBluetoothListeners();
} catch (e) { } catch (e) {
Logger.error('初始化蓝牙服务失败: $e'); Logger.error('初始化蓝牙服务失败: $e');
_connectionStatus.value = ConnectionStatus.failed; _connectionStatus.value = ConnectionStatus.failed;
} }
} }
// 电池电量模拟更新 // 电池电量模拟更新
void _simulateBatteryUpdate() { void _simulateBatteryUpdate() {
// 每5分钟更新一次电池电量(模拟) // 每5分钟更新一次电池电量(模拟)
@ -86,17 +87,17 @@ class HomeController extends GetxController {
} else { } else {
_deviceBattery.value = null; _deviceBattery.value = null;
} }
// 递归调用实现定时更新 // 递归调用实现定时更新
_simulateBatteryUpdate(); _simulateBatteryUpdate();
}); });
} }
// 更新连接状态 // 更新连接状态
void _updateConnectionStatus() { void _updateConnectionStatus() {
// 检查当前设备 // 检查当前设备
final currentDevice = _bluetoothService.currentDevice.value; final currentDevice = _bluetoothService.currentDevice.value;
if (currentDevice != null) { if (currentDevice != null) {
_isConnected.value = true; _isConnected.value = true;
_connectionStatus.value = ConnectionStatus.connected; _connectionStatus.value = ConnectionStatus.connected;
@ -104,10 +105,10 @@ class HomeController extends GetxController {
_isConnected.value = false; _isConnected.value = false;
_connectionStatus.value = ConnectionStatus.disconnected; _connectionStatus.value = ConnectionStatus.disconnected;
} }
update(); // 通知UI刷新 update(); // 通知UI刷新
} }
// 设置蓝牙监听器 // 设置蓝牙监听器
void _setupBluetoothListeners() { void _setupBluetoothListeners() {
// 监听设备连接 // 监听设备连接
@ -117,7 +118,7 @@ class HomeController extends GetxController {
_connectionStatus.value = ConnectionStatus.connected; _connectionStatus.value = ConnectionStatus.connected;
update(); update();
}); });
// 监听设备断开 // 监听设备断开
_bluetoothService.onDeviceDisconnected((device) { _bluetoothService.onDeviceDisconnected((device) {
Logger.info('监听到蓝牙设备断开: ${device.name}'); Logger.info('监听到蓝牙设备断开: ${device.name}');
@ -126,11 +127,11 @@ class HomeController extends GetxController {
_deviceBattery.value = null; _deviceBattery.value = null;
update(); update();
}); });
// 监听蓝牙状态变化 // 监听蓝牙状态变化
_bluetoothService.onBluetoothStateChanged((state) { _bluetoothService.onBluetoothStateChanged((state) {
Logger.info('监听到蓝牙状态变化: $state'); Logger.info('监听到蓝牙状态变化: $state');
if (state == 'off') { if (state == 'off') {
_isConnected.value = false; _isConnected.value = false;
_connectionStatus.value = ConnectionStatus.disconnected; _connectionStatus.value = ConnectionStatus.disconnected;
@ -139,26 +140,26 @@ class HomeController extends GetxController {
} }
}); });
} }
// 刷新设备信息 // 刷新设备信息
Future<void> refreshDeviceInfo() async { Future<void> refreshDeviceInfo() async {
try { try {
// 设置状态为连接中 // 设置状态为连接中
_connectionStatus.value = ConnectionStatus.connecting; _connectionStatus.value = ConnectionStatus.connecting;
update(); update();
// 刷新设备列表 // 刷新设备列表
final hasDevices = await _bluetoothService.refreshConnectedDevices(); final hasDevices = await _bluetoothService.refreshConnectedDevices();
// 更新状态 // 更新状态
_updateConnectionStatus(); _updateConnectionStatus();
// 根据结果更新状态 // 根据结果更新状态
if (!hasDevices) { if (!hasDevices) {
Logger.info('刷新设备:未找到设备'); Logger.info('刷新设备:未找到设备');
// 这里可以设置状态变量而不是使用snackbar // 这里可以设置状态变量而不是使用snackbar
_connectionErrorMsg.value = _bluetoothService.errorMessage.value.isEmpty _connectionErrorMsg.value = _bluetoothService.errorMessage.value.isEmpty
? '未找到已连接的设备' ? 'noDeviceFound'.tr // 未找到已连接的设备
: _bluetoothService.errorMessage.value; : _bluetoothService.errorMessage.value;
} else { } else {
_connectionErrorMsg.value = ''; _connectionErrorMsg.value = '';
@ -167,32 +168,32 @@ class HomeController extends GetxController {
} catch (e) { } catch (e) {
Logger.error('刷新设备信息失败: $e'); Logger.error('刷新设备信息失败: $e');
_connectionStatus.value = ConnectionStatus.failed; _connectionStatus.value = ConnectionStatus.failed;
_connectionErrorMsg.value = '无法获取设备信息'; _connectionErrorMsg.value = 'failedDeviceInformation'.tr; // 无法获取设备信息
update(); update();
} }
} }
// 重新扫描设备 // 重新扫描设备
void rescanDevices() { void rescanDevices() {
Get.toNamed(Routes.pairing); Get.toNamed(Routes.pairing);
} }
// 打开翻译功能 // 打开翻译功能
void openTranslation() { void openTranslation() {
Get.toNamed(Routes.translation); Get.toNamed(Routes.translation);
} }
// 打开AI伴侣功能 // 打开AI伴侣功能
void openAIGirlfriend() { void openAIGirlfriend() {
Get.toNamed(Routes.chat); Get.toNamed(Routes.chat);
} }
// 展示AI功能 // 展示AI功能
void exploreAIFeatures() { void exploreAIFeatures() {
// 简单实现,直接跳转到AI聊天页面 // 简单实现,直接跳转到AI聊天页面
openAIGirlfriend(); openAIGirlfriend();
} }
// 获取连接状态对应的颜色 // 获取连接状态对应的颜色
Color getConnectionStatusColor() { Color getConnectionStatusColor() {
switch (_connectionStatus.value) { switch (_connectionStatus.value) {
@ -207,49 +208,49 @@ class HomeController extends GetxController {
return Colors.grey; return Colors.grey;
} }
} }
// 获取连接状态对应的文本 // 获取连接状态对应的文本
String getConnectionStatusText() { String getConnectionStatusText() {
switch (_connectionStatus.value) { switch (_connectionStatus.value) {
case ConnectionStatus.connected: case ConnectionStatus.connected:
return '已连接'; return 'connected'.tr; // 已连接
case ConnectionStatus.connecting: case ConnectionStatus.connecting:
return '连接中...'; return 'connecting'.tr; // 连接中...
case ConnectionStatus.failed: case ConnectionStatus.failed:
return '连接失败'; return 'connectionFailure'.tr; // 连接失败
case ConnectionStatus.disconnected: case ConnectionStatus.disconnected:
default: default:
return '未连接'; return 'unconnected'.tr; // 未连接
} }
} }
// 获取设备名称 // 获取设备名称
String getDeviceName() { String getDeviceName() {
final device = _bluetoothService.currentDevice.value; final device = _bluetoothService.currentDevice.value;
return device?.name ?? '未知设备'; return device?.name ?? 'unknownEquipment'.tr; // 未知设备
} }
// 获取信号强度(模拟数据) // 获取信号强度(模拟数据)
String getSignalStrength() { String getSignalStrength() {
if (!_isConnected.value) { if (!_isConnected.value) {
return '无信号'; return 'zeroSignal'.tr; // 无信号
} }
// 简单模拟信号强度 // 简单模拟信号强度
final random = DateTime.now().millisecondsSinceEpoch % 3; final random = DateTime.now().millisecondsSinceEpoch % 3;
switch (random) { switch (random) {
case 0: case 0:
return '良好'; return 'good'.tr; // 良好
case 1: case 1:
return '很好'; return 'fine'.tr; // 很好
default: default:
return '极佳'; return 'excellent'.tr; // 极佳
} }
} }
// 获取设备ID // 获取设备ID
String getDeviceId() { String getDeviceId() {
final device = _bluetoothService.currentDevice.value; final device = _bluetoothService.currentDevice.value;
return device?.address ?? '未知'; return device?.address ?? 'unknown'.tr; // 未知
} }
} }

414
lib/modules/home/views/home_view.dart

@ -16,7 +16,8 @@ class AnimatedPulsing extends StatefulWidget {
_AnimatedPulsingState createState() => _AnimatedPulsingState(); _AnimatedPulsingState createState() => _AnimatedPulsingState();
} }
class _AnimatedPulsingState extends State<AnimatedPulsing> with SingleTickerProviderStateMixin { class _AnimatedPulsingState extends State<AnimatedPulsing>
with SingleTickerProviderStateMixin {
late AnimationController _controller; late AnimationController _controller;
late Animation<double> _animation; late Animation<double> _animation;
@ -74,7 +75,7 @@ class HomeView extends GetView<HomeController> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final bool isDarkMode = Theme.of(context).brightness == Brightness.dark; final bool isDarkMode = Theme.of(context).brightness == Brightness.dark;
return Scaffold( return Scaffold(
backgroundColor: isDarkMode ? Colors.black : Color(0xFFF9FAFC), backgroundColor: isDarkMode ? Colors.black : Color(0xFFF9FAFC),
body: SafeArea( body: SafeArea(
@ -85,13 +86,13 @@ class HomeView extends GetView<HomeController> {
children: [ children: [
// 顶部标题栏 // 顶部标题栏
_buildAppBar(isDarkMode), _buildAppBar(isDarkMode),
// 顶部图片 // 顶部图片
Center( Center(
child: _buildEarbudsImage(), child: _buildEarbudsImage(),
), ),
SizedBox(height: 20.h), SizedBox(height: 20.h),
// 设备状态卡片 // 设备状态卡片
_buildDeviceStatusCard(isDarkMode), _buildDeviceStatusCard(isDarkMode),
SizedBox(height: 20.h), SizedBox(height: 20.h),
@ -111,43 +112,41 @@ class HomeView extends GetView<HomeController> {
// 顶部标题栏 // 顶部标题栏
Widget _buildAppBar(bool isDarkMode) { Widget _buildAppBar(bool isDarkMode) {
return Row( return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Text(
'DeepVoice', 'DeepVoice',
style: TextStyle( style: TextStyle(
fontSize: 20.sp, fontSize: 20.sp,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: isDarkMode ? Colors.white : Colors.grey[800], color: isDarkMode ? Colors.white : Colors.grey[800],
), ),
), ),
Container( Container(
width: 32.w, width: 32.w,
height: 32.w, height: 32.w,
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDarkMode ? Colors.grey[800] : Colors.grey[200], color: isDarkMode ? Colors.grey[800] : Colors.grey[200],
shape: BoxShape.circle, shape: BoxShape.circle,
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.grey.withOpacity(0.2), color: Colors.grey.withOpacity(0.2),
blurRadius: 4, blurRadius: 4,
offset: const Offset(0, 2), 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);
},
),
), ),
], ],
),
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);
},
),
),
],
); );
} }
@ -158,7 +157,9 @@ class HomeView extends GetView<HomeController> {
height: 220.h, height: 220.h,
alignment: Alignment.center, alignment: Alignment.center,
child: Image.asset( child: Image.asset(
isDarkMode ? 'assets/images/headphone_dark.png' : 'assets/images/headphone_light.png', isDarkMode
? 'assets/images/headphone_dark.png'
: 'assets/images/headphone_light.png',
height: 180.h, height: 180.h,
fit: BoxFit.contain, fit: BoxFit.contain,
), ),
@ -187,7 +188,7 @@ class HomeView extends GetView<HomeController> {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Text(
'设备状态', 'deviceStatus'.tr, // 设备状态
style: TextStyle( style: TextStyle(
fontSize: 14.sp, fontSize: 14.sp,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
@ -195,52 +196,57 @@ class HomeView extends GetView<HomeController> {
), ),
), ),
Obx(() => Row( Obx(() => Row(
children: [ children: [
Container( Container(
width: 8.w, width: 8.w,
height: 8.w, height: 8.w,
decoration: BoxDecoration( decoration: BoxDecoration(
color: controller.getConnectionStatusColor(), color: controller.getConnectionStatusColor(),
shape: BoxShape.circle, shape: BoxShape.circle,
), ),
), ),
SizedBox(width: 4.w), SizedBox(width: 4.w),
Text( Text(
controller.getConnectionStatusText(), controller.getConnectionStatusText(),
style: TextStyle( style: TextStyle(
fontSize: 12.sp, fontSize: 12.sp,
color: controller.getConnectionStatusColor(), color: controller.getConnectionStatusColor(),
), ),
), ),
// 添加刷新按钮 // 添加刷新按钮
SizedBox(width: 8.w), SizedBox(width: 8.w),
InkWell( InkWell(
onTap: controller.refreshDeviceInfo, onTap: controller.refreshDeviceInfo,
child: Icon( child: Icon(
Icons.refresh, Icons.refresh,
size: 16.sp, size: 16.sp,
color: isDarkMode ? Colors.grey[400] : Colors.grey[600], color:
), isDarkMode ? Colors.grey[400] : Colors.grey[600],
), ),
], ),
)), ],
)),
], ],
), ),
// 设备信息 // 设备信息
SizedBox(height: 12.h), SizedBox(height: 12.h),
Obx(() { Obx(() {
// 如果连接失败且有错误信息,显示错误提示 // 如果连接失败且有错误信息,显示错误提示
if (controller.connectionStatus == ConnectionStatus.failed && if (controller.connectionStatus == ConnectionStatus.failed &&
controller.connectionErrorMsg.isNotEmpty) { controller.connectionErrorMsg.isNotEmpty) {
return Column( return Column(
children: [ children: [
Container( Container(
padding: EdgeInsets.all(8.w), padding: EdgeInsets.all(8.w),
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDarkMode ? Colors.red[900]!.withOpacity(0.2) : Colors.red[50], color: isDarkMode
? Colors.red[900]!.withOpacity(0.2)
: Colors.red[50],
borderRadius: BorderRadius.circular(8.r), borderRadius: BorderRadius.circular(8.r),
border: Border.all(color: isDarkMode ? Colors.red[800]! : Colors.red[200]!), border: Border.all(
color:
isDarkMode ? Colors.red[800]! : Colors.red[200]!),
), ),
child: Row( child: Row(
children: [ children: [
@ -255,7 +261,9 @@ class HomeView extends GetView<HomeController> {
controller.connectionErrorMsg, controller.connectionErrorMsg,
style: TextStyle( style: TextStyle(
fontSize: 12.sp, fontSize: 12.sp,
color: isDarkMode ? Colors.red[300] : Colors.red[800], color: isDarkMode
? Colors.red[300]
: Colors.red[800],
), ),
), ),
), ),
@ -274,12 +282,12 @@ class HomeView extends GetView<HomeController> {
borderRadius: BorderRadius.circular(8.r), borderRadius: BorderRadius.circular(8.r),
), ),
), ),
child: Text('重新连接'), child: Text('reconnect'.tr), // 重新连接
), ),
], ],
); );
} }
// 如果正在连接中,显示连接中提示 // 如果正在连接中,显示连接中提示
if (controller.connectionStatus == ConnectionStatus.connecting) { if (controller.connectionStatus == ConnectionStatus.connecting) {
return Container( return Container(
@ -292,12 +300,13 @@ class HomeView extends GetView<HomeController> {
height: 24.w, height: 24.w,
child: CircularProgressIndicator( child: CircularProgressIndicator(
strokeWidth: 2.w, strokeWidth: 2.w,
valueColor: AlwaysStoppedAnimation<Color>(Colors.blue), valueColor:
AlwaysStoppedAnimation<Color>(Colors.blue),
), ),
), ),
SizedBox(height: 8.h), SizedBox(height: 8.h),
Text( Text(
'正在连接设备...', 'connectingDevice'.tr, // 正在连接设备...
style: TextStyle( style: TextStyle(
fontSize: 12.sp, fontSize: 12.sp,
color: Colors.blue, color: Colors.blue,
@ -308,119 +317,135 @@ class HomeView extends GetView<HomeController> {
), ),
); );
} }
return controller.isConnected return controller.isConnected
? Column( ? Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Expanded( Expanded(
child: Text( child: Text(
'已连接到:${controller.getDeviceName()}', '${'connectedTo'.tr}:${controller.getDeviceName()}', // 已连接到
style: TextStyle( style: TextStyle(
fontSize: 12.sp, fontSize: 12.sp,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: isDarkMode ? Colors.grey[300] : Colors.grey[700], color: isDarkMode
? Colors.grey[300]
: Colors.grey[700],
),
overflow: TextOverflow.ellipsis,
), ),
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( Container(
'信号: ${controller.getSignalStrength()}', padding: EdgeInsets.symmetric(
style: TextStyle( horizontal: 6.w, vertical: 2.h),
fontSize: 10.sp, decoration: BoxDecoration(
color: Colors.blue[700], color: Colors.blue.withOpacity(0.1),
borderRadius: BorderRadius.circular(4.r),
),
child: Text(
'${'signal'.tr}: ${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( SizedBox(height: 8.h),
crossAxisAlignment: CrossAxisAlignment.start, Container(
children: [ padding: EdgeInsets.symmetric(
Text( horizontal: 8.w, vertical: 6.h),
'设备ID:${controller.getDeviceId()}', decoration: BoxDecoration(
style: TextStyle( color:
fontSize: 10.sp, isDarkMode ? Colors.grey[900] : Colors.grey[50],
color: isDarkMode ? Colors.grey[400] : Colors.grey[500], borderRadius: BorderRadius.circular(8.r),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${'deviceId'.tr}:${controller.getDeviceId()}', // 设备ID
style: TextStyle(
fontSize: 10.sp,
color: isDarkMode
? Colors.grey[400]
: Colors.grey[500],
),
), ),
), SizedBox(height: 4.h),
SizedBox(height: 4.h), Text(
Text( '${'firmwareVersion'.tr}:${controller.firmwareVersion}', // 固件版本
'固件版本:${controller.firmwareVersion}', style: TextStyle(
style: TextStyle( fontSize: 10.sp,
fontSize: 10.sp, color: isDarkMode
color: isDarkMode ? Colors.grey[400] : Colors.grey[500], ? Colors.grey[400]
: Colors.grey[500],
),
), ),
), SizedBox(height: 4.h),
SizedBox(height: 4.h), // 显示设备状态(已连接/已配对)
// 显示设备状态(已连接/已配对) Row(
Row( children: [
children: [ Container(
Container( padding: EdgeInsets.symmetric(
padding: EdgeInsets.symmetric(horizontal: 6.w, vertical: 2.h), horizontal: 6.w, vertical: 2.h),
decoration: BoxDecoration( decoration: BoxDecoration(
color: controller.getConnectionStatusColor().withOpacity(0.1), color: controller
borderRadius: BorderRadius.circular(4.r), .getConnectionStatusColor()
), .withOpacity(0.1),
child: Text( borderRadius: BorderRadius.circular(4.r),
controller.getConnectionStatusText(), ),
style: TextStyle( child: Text(
fontSize: 10.sp, controller.getConnectionStatusText(),
color: controller.getConnectionStatusColor(), 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],
), ),
), ),
], ],
), )
); : 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(
'disconnectedDevice'.tr, // 未连接设备,点击下方按钮连接
style: TextStyle(
fontSize: 12.sp,
color: isDarkMode
? Colors.grey[400]
: Colors.grey[700],
),
),
],
),
);
}), }),
// 连接按钮(仅在未连接时显示) // 连接按钮(仅在未连接时显示)
SizedBox(height: 12.h), SizedBox(height: 12.h),
Obx(() { Obx(() {
@ -435,10 +460,10 @@ class HomeView extends GetView<HomeController> {
borderRadius: BorderRadius.circular(8.r), borderRadius: BorderRadius.circular(8.r),
), ),
), ),
child: Text('连接设备'), child: Text('connectDevice'.tr), // 连接设备
); );
} else { } else {
return SizedBox(); return const SizedBox();
} }
}), }),
], ],
@ -451,7 +476,7 @@ class HomeView extends GetView<HomeController> {
return Row( return Row(
children: [ children: [
Text( Text(
'热门AI体验', 'topAiExperiences'.tr, // 热门AI体验
style: TextStyle( style: TextStyle(
fontSize: 18.sp, fontSize: 18.sp,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
@ -478,8 +503,8 @@ class HomeView extends GetView<HomeController> {
Expanded( Expanded(
flex: 1, flex: 1,
child: _buildFeatureCard( child: _buildFeatureCard(
title: 'AI全能助手', title: 'aiAssistant'.tr, // AI全能助手
description: '24小时专属顾问,解答问题、提供建议', description: 'aiAssistantInfo'.tr, // 24小时专属顾问,解答问题、提供建议
icon: Icons.assistant, icon: Icons.assistant,
gradient: LinearGradient( gradient: LinearGradient(
begin: Alignment.topLeft, begin: Alignment.topLeft,
@ -496,9 +521,9 @@ class HomeView extends GetView<HomeController> {
iconColor: const Color(0xFF0066CC), iconColor: const Color(0xFF0066CC),
), ),
), ),
SizedBox(width: 16.w), SizedBox(width: 16.w),
// 右侧两个卡片 // 右侧两个卡片
Expanded( Expanded(
flex: 1, flex: 1,
@ -506,8 +531,9 @@ class HomeView extends GetView<HomeController> {
children: [ children: [
// 右上卡片 - 同声翻译 // 右上卡片 - 同声翻译
_buildFeatureCard( _buildFeatureCard(
title: '同声传译', title: 'simultaneousInterpretation'.tr, // 同声传译
description: '80+语言实时翻译,出国无忧', description:
'simultaneousInterpretationInfo'.tr, // 80+语言实时翻译,出国无忧
icon: Icons.translate, icon: Icons.translate,
gradient: LinearGradient( gradient: LinearGradient(
begin: Alignment.topLeft, begin: Alignment.topLeft,
@ -522,13 +548,13 @@ class HomeView extends GetView<HomeController> {
isDarkMode: isDarkMode, isDarkMode: isDarkMode,
iconColor: const Color(0xFF4CAF50), iconColor: const Color(0xFF4CAF50),
), ),
SizedBox(height: 5.h), // 减少间距 SizedBox(height: 5.h), // 减少间距
// 右下卡片 - AI女友 // 右下卡片 - AI女友
_buildFeatureCard( _buildFeatureCard(
title: 'AI伴侣', title: 'aiCompanion'.tr, // AI伴侣
description: '懂你心意,温暖陪伴', description: 'aiCompanionInfo'.tr, // 懂你心意,温暖陪伴
icon: Icons.favorite, icon: Icons.favorite,
gradient: LinearGradient( gradient: LinearGradient(
begin: Alignment.topLeft, begin: Alignment.topLeft,
@ -549,7 +575,7 @@ class HomeView extends GetView<HomeController> {
], ],
); );
} }
// 特性卡片构建方法 // 特性卡片构建方法
Widget _buildFeatureCard({ Widget _buildFeatureCard({
required String title, required String title,
@ -572,7 +598,7 @@ class HomeView extends GetView<HomeController> {
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(16.r),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: isDarkMode color: isDarkMode
? Colors.black.withOpacity(0.2) ? Colors.black.withOpacity(0.2)
: Colors.grey.withOpacity(0.1), : Colors.grey.withOpacity(0.1),
blurRadius: 8, blurRadius: 8,
@ -583,7 +609,7 @@ class HomeView extends GetView<HomeController> {
child: Stack( child: Stack(
children: [ children: [
// 图标 // 图标
if (iconOnTop) if (iconOnTop)
// 大卡片布局 // 大卡片布局
Align( Align(
alignment: Alignment.topLeft, alignment: Alignment.topLeft,
@ -619,7 +645,7 @@ class HomeView extends GetView<HomeController> {
), ),
), ),
), ),
// 文本内容 // 文本内容
if (iconOnTop) if (iconOnTop)
// 大卡片布局 - 文本 // 大卡片布局 - 文本
@ -688,4 +714,4 @@ class HomeView extends GetView<HomeController> {
), ),
); );
} }
} }

299
lib/modules/login/views/login_view.dart

@ -11,7 +11,7 @@ class LoginView extends GetView<LoginController> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isDarkMode = Theme.of(context).brightness == Brightness.dark; final isDarkMode = Theme.of(context).brightness == Brightness.dark;
return Scaffold( return Scaffold(
backgroundColor: isDarkMode ? Colors.black : Colors.grey[50], backgroundColor: isDarkMode ? Colors.black : Colors.grey[50],
body: SafeArea( body: SafeArea(
@ -33,11 +33,11 @@ class LoginView extends GetView<LoginController> {
// 应用标志和标题 // 应用标志和标题
SizedBox(height: 32.h), SizedBox(height: 32.h),
_buildAppLogo(isDarkMode), _buildAppLogo(isDarkMode),
// 欢迎文本 // 欢迎文本
SizedBox(height: 32.h), SizedBox(height: 32.h),
_buildWelcomeText(isDarkMode), _buildWelcomeText(isDarkMode),
// 输入框和继续按钮 // 输入框和继续按钮
SizedBox(height: 24.h), SizedBox(height: 24.h),
_buildInputField(isDarkMode), _buildInputField(isDarkMode),
@ -45,11 +45,11 @@ class LoginView extends GetView<LoginController> {
_buildVerificationInputField(isDarkMode), _buildVerificationInputField(isDarkMode),
SizedBox(height: 16.h), SizedBox(height: 16.h),
_buildContinueButton(isDarkMode), _buildContinueButton(isDarkMode),
// 社交媒体登录 // 社交媒体登录
SizedBox(height: 32.h), SizedBox(height: 32.h),
_buildSocialLoginSection(isDarkMode), _buildSocialLoginSection(isDarkMode),
// 用户协议 // 用户协议
SizedBox(height: 40.h), SizedBox(height: 40.h),
_buildTermsAndPrivacy(isDarkMode), _buildTermsAndPrivacy(isDarkMode),
@ -71,23 +71,23 @@ class LoginView extends GetView<LoginController> {
children: [ children: [
// 返回按钮 // 返回按钮
_buildBackButton(isDarkMode), _buildBackButton(isDarkMode),
// 验证码标题 // 验证码标题
SizedBox(height: 24.h), SizedBox(height: 24.h),
_buildVerificationTitle(isDarkMode), _buildVerificationTitle(isDarkMode),
// 验证码输入框 // 验证码输入框
SizedBox(height: 32.h), SizedBox(height: 32.h),
_buildVerificationInputs(context, isDarkMode), _buildVerificationInputs(context, isDarkMode),
// 重新发送验证码 // 重新发送验证码
SizedBox(height: 16.h), SizedBox(height: 16.h),
_buildResendButton(isDarkMode), _buildResendButton(isDarkMode),
// 登录按钮 // 登录按钮
SizedBox(height: 32.h), SizedBox(height: 32.h),
_buildLoginButton(isDarkMode), _buildLoginButton(isDarkMode),
// 使用密码登录 // 使用密码登录
SizedBox(height: 16.h), SizedBox(height: 16.h),
_buildUsePasswordButton(isDarkMode), _buildUsePasswordButton(isDarkMode),
@ -137,7 +137,7 @@ class LoginView extends GetView<LoginController> {
), ),
SizedBox(height: 8.h), SizedBox(height: 8.h),
Text( Text(
'连接世界,超越声音', 'slogan'.tr, // 连接世界,超越声音
style: TextStyle( style: TextStyle(
fontSize: 14.sp, fontSize: 14.sp,
color: isDarkMode ? Colors.grey[400] : Colors.black54, color: isDarkMode ? Colors.grey[400] : Colors.black54,
@ -152,7 +152,7 @@ class LoginView extends GetView<LoginController> {
return Column( return Column(
children: [ children: [
Text( Text(
'欢迎使用', 'welcome'.tr, // 欢迎使用
style: TextStyle( style: TextStyle(
fontSize: 20.sp, fontSize: 20.sp,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@ -161,7 +161,7 @@ class LoginView extends GetView<LoginController> {
), ),
SizedBox(height: 4.h), SizedBox(height: 4.h),
Text( Text(
'请使用手机号或邮箱登录/注册', 'loginTips'.tr, // 请使用手机号或邮箱登录/注册
style: TextStyle( style: TextStyle(
fontSize: 14.sp, fontSize: 14.sp,
color: isDarkMode ? Colors.grey[400] : Colors.black54, color: isDarkMode ? Colors.grey[400] : Colors.black54,
@ -175,7 +175,9 @@ class LoginView extends GetView<LoginController> {
Widget _buildInputField(bool isDarkMode) { Widget _buildInputField(bool isDarkMode) {
return Container( return Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDarkMode ? Colors.grey[900]!.withOpacity(0.4) : Colors.grey.withOpacity(0.1), color: isDarkMode
? Colors.grey[900]!.withOpacity(0.4)
: Colors.grey.withOpacity(0.1),
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(16.r),
), ),
child: Padding( child: Padding(
@ -192,7 +194,7 @@ class LoginView extends GetView<LoginController> {
child: TextField( child: TextField(
controller: controller.contactController, controller: controller.contactController,
decoration: InputDecoration( decoration: InputDecoration(
hintText: '手机号码或邮箱', hintText: 'accountNumber'.tr, // 手机号码或邮箱
hintStyle: TextStyle( hintStyle: TextStyle(
fontSize: 14.sp, fontSize: 14.sp,
color: isDarkMode ? Colors.grey[600] : Colors.black38, color: isDarkMode ? Colors.grey[600] : Colors.black38,
@ -217,7 +219,9 @@ class LoginView extends GetView<LoginController> {
Widget _buildVerificationInputField(bool isDarkMode) { Widget _buildVerificationInputField(bool isDarkMode) {
return Container( return Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDarkMode ? Colors.grey[900]!.withOpacity(0.4) : Colors.grey.withOpacity(0.1), color: isDarkMode
? Colors.grey[900]!.withOpacity(0.4)
: Colors.grey.withOpacity(0.1),
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(16.r),
), ),
child: Padding( child: Padding(
@ -236,7 +240,7 @@ class LoginView extends GetView<LoginController> {
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
maxLength: 6, maxLength: 6,
decoration: InputDecoration( decoration: InputDecoration(
hintText: '验证码', hintText: 'verificationCode'.tr, // 验证码
hintStyle: TextStyle( hintStyle: TextStyle(
fontSize: 14.sp, fontSize: 14.sp,
color: isDarkMode ? Colors.grey[600] : Colors.black38, color: isDarkMode ? Colors.grey[600] : Colors.black38,
@ -253,25 +257,26 @@ class LoginView extends GetView<LoginController> {
), ),
), ),
Obx(() => TextButton( Obx(() => TextButton(
onPressed: controller.countdownSeconds > 0 onPressed: controller.countdownSeconds > 0
? null ? null
: controller.getVerificationCode, : controller.getVerificationCode,
style: TextButton.styleFrom( style: TextButton.styleFrom(
padding: EdgeInsets.symmetric(horizontal: 16.w), padding: EdgeInsets.symmetric(horizontal: 16.w),
foregroundColor: const Color(0xFF3B82F6), foregroundColor: const Color(0xFF3B82F6),
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
disabledForegroundColor: isDarkMode ? Colors.grey[600] : Colors.grey[400], disabledForegroundColor:
), isDarkMode ? Colors.grey[600] : Colors.grey[400],
child: Text( ),
controller.countdownSeconds > 0 child: Text(
? '${controller.countdownSeconds}s后重新获取' controller.countdownSeconds > 0
: '获取验证码', ? '${controller.countdownSeconds}${'reacquireAfter'.tr}' // s后重新获取
style: TextStyle( : 'getCode'.tr, // 获取验证码
fontSize: 14.sp, style: TextStyle(
fontWeight: FontWeight.w500, fontSize: 14.sp,
), fontWeight: FontWeight.w500,
), ),
)), ),
)),
], ],
), ),
), ),
@ -283,35 +288,36 @@ class LoginView extends GetView<LoginController> {
return SizedBox( return SizedBox(
width: double.infinity, width: double.infinity,
child: Obx(() => ElevatedButton( child: Obx(() => ElevatedButton(
onPressed: controller.isLoading ? null : () => controller.login(), onPressed: controller.isLoading ? null : () => controller.login(),
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF0066CC), backgroundColor: const Color(0xFF0066CC),
foregroundColor: Colors.white, foregroundColor: Colors.white,
padding: EdgeInsets.symmetric(vertical: 14.h), padding: EdgeInsets.symmetric(vertical: 14.h),
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(16.r),
),
elevation: 0,
disabledBackgroundColor: const Color(0xFF0066CC).withOpacity(0.7),
disabledForegroundColor: Colors.white,
),
child: controller.isLoading
? SizedBox(
height: 20.h,
width: 20.h,
child: CircularProgressIndicator(
strokeWidth: 2.w,
valueColor: const AlwaysStoppedAnimation<Color>(Colors.white),
),
)
: Text(
'继续',
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w600,
),
), ),
)), elevation: 0,
disabledBackgroundColor: const Color(0xFF0066CC).withOpacity(0.7),
disabledForegroundColor: Colors.white,
),
child: controller.isLoading
? SizedBox(
height: 20.h,
width: 20.h,
child: CircularProgressIndicator(
strokeWidth: 2.w,
valueColor:
const AlwaysStoppedAnimation<Color>(Colors.white),
),
)
: Text(
'continue'.tr, // 继续
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w600,
),
),
)),
); );
} }
@ -321,18 +327,22 @@ class LoginView extends GetView<LoginController> {
children: [ children: [
Row( Row(
children: [ children: [
Expanded(child: Divider(color: isDarkMode ? Colors.grey[800] : Colors.grey[300])), Expanded(
child: Divider(
color: isDarkMode ? Colors.grey[800] : Colors.grey[300])),
Padding( Padding(
padding: EdgeInsets.symmetric(horizontal: 16.w), padding: EdgeInsets.symmetric(horizontal: 16.w),
child: Text( child: Text(
'或使用以下方式登录', 'otherWays'.tr, // 或使用以下方式登录
style: TextStyle( style: TextStyle(
fontSize: 12.sp, fontSize: 12.sp,
color: isDarkMode ? Colors.grey[500] : Colors.grey[600], color: isDarkMode ? Colors.grey[500] : Colors.grey[600],
), ),
), ),
), ),
Expanded(child: Divider(color: isDarkMode ? Colors.grey[800] : Colors.grey[300])), Expanded(
child: Divider(
color: isDarkMode ? Colors.grey[800] : Colors.grey[300])),
], ],
), ),
SizedBox(height: 24.h), SizedBox(height: 24.h),
@ -341,7 +351,8 @@ class LoginView extends GetView<LoginController> {
children: [ children: [
_buildSocialButton(FontAwesomeIcons.google, Colors.red, isDarkMode), _buildSocialButton(FontAwesomeIcons.google, Colors.red, isDarkMode),
SizedBox(width: 24.w), SizedBox(width: 24.w),
_buildSocialButton(FontAwesomeIcons.weixin, Colors.green, isDarkMode), _buildSocialButton(
FontAwesomeIcons.weixin, Colors.green, isDarkMode),
], ],
), ),
], ],
@ -356,13 +367,15 @@ class LoginView extends GetView<LoginController> {
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDarkMode ? Colors.grey[800] : Colors.white, color: isDarkMode ? Colors.grey[800] : Colors.white,
borderRadius: BorderRadius.circular(24.r), borderRadius: BorderRadius.circular(24.r),
boxShadow: isDarkMode ? [] : [ boxShadow: isDarkMode
BoxShadow( ? []
color: Colors.grey.withOpacity(0.2), : [
blurRadius: 4, BoxShadow(
offset: const Offset(0, 2), color: Colors.grey.withOpacity(0.2),
), blurRadius: 4,
], offset: const Offset(0, 2),
),
],
), ),
child: IconButton( child: IconButton(
icon: FaIcon( icon: FaIcon(
@ -380,7 +393,7 @@ class LoginView extends GetView<LoginController> {
return Column( return Column(
children: [ children: [
Text( Text(
'登录/注册即表示你同意', 'expressConsent'.tr, // 登录/注册即表示你同意
style: TextStyle( style: TextStyle(
fontSize: 12.sp, fontSize: 12.sp,
color: isDarkMode ? Colors.grey[500] : Colors.grey[600], color: isDarkMode ? Colors.grey[500] : Colors.grey[600],
@ -393,7 +406,7 @@ class LoginView extends GetView<LoginController> {
GestureDetector( GestureDetector(
onTap: () {}, onTap: () {},
child: Text( child: Text(
'《用户协议》', 'userAgreement'.tr, // 《用户协议》
style: TextStyle( style: TextStyle(
fontSize: 12.sp, fontSize: 12.sp,
color: const Color(0xFF3B82F6), color: const Color(0xFF3B82F6),
@ -401,7 +414,7 @@ class LoginView extends GetView<LoginController> {
), ),
), ),
Text( Text(
' 和 ', 'and'.tr, // 和
style: TextStyle( style: TextStyle(
fontSize: 12.sp, fontSize: 12.sp,
color: isDarkMode ? Colors.grey[500] : Colors.grey[600], color: isDarkMode ? Colors.grey[500] : Colors.grey[600],
@ -410,7 +423,7 @@ class LoginView extends GetView<LoginController> {
GestureDetector( GestureDetector(
onTap: () {}, onTap: () {},
child: Text( child: Text(
'《隐私政策》', 'privacyPolicy'.tr, // 《隐私政策》
style: TextStyle( style: TextStyle(
fontSize: 12.sp, fontSize: 12.sp,
color: const Color(0xFF3B82F6), color: const Color(0xFF3B82F6),
@ -458,12 +471,12 @@ class LoginView extends GetView<LoginController> {
), ),
SizedBox(height: 8.h), SizedBox(height: 8.h),
Obx(() => Text( Obx(() => Text(
'验证码已发送至 ${controller.displayContact}', '验证码已发送至 ${controller.displayContact}',
style: TextStyle( style: TextStyle(
fontSize: 14.sp, fontSize: 14.sp,
color: isDarkMode ? Colors.grey[400] : Colors.grey[600], color: isDarkMode ? Colors.grey[400] : Colors.grey[600],
), ),
)), )),
], ],
); );
} }
@ -472,17 +485,21 @@ class LoginView extends GetView<LoginController> {
Widget _buildVerificationInputs(BuildContext context, bool isDarkMode) { Widget _buildVerificationInputs(BuildContext context, bool isDarkMode) {
return Row( return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: List.generate(4, (index) => _buildVerificationInput(index, context, isDarkMode)), children: List.generate(
4, (index) => _buildVerificationInput(index, context, isDarkMode)),
); );
} }
// 单个验证码输入框 // 单个验证码输入框
Widget _buildVerificationInput(int index, BuildContext context, bool isDarkMode) { Widget _buildVerificationInput(
int index, BuildContext context, bool isDarkMode) {
return Container( return Container(
width: 60.w, width: 60.w,
height: 60.w, height: 60.w,
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDarkMode ? Colors.grey[900]!.withOpacity(0.3) : Colors.grey.withOpacity(0.1), color: isDarkMode
? Colors.grey[900]!.withOpacity(0.3)
: Colors.grey.withOpacity(0.1),
borderRadius: BorderRadius.circular(12.r), borderRadius: BorderRadius.circular(12.r),
border: Border.all( border: Border.all(
color: isDarkMode ? Colors.grey[700]! : Colors.grey[300]!, color: isDarkMode ? Colors.grey[700]! : Colors.grey[300]!,
@ -520,28 +537,28 @@ class LoginView extends GetView<LoginController> {
Widget _buildResendButton(bool isDarkMode) { Widget _buildResendButton(bool isDarkMode) {
return Center( return Center(
child: Obx(() => TextButton.icon( child: Obx(() => TextButton.icon(
onPressed: controller.countdownSeconds > 0 onPressed: controller.countdownSeconds > 0
? null ? null
: controller.resendVerificationCode, : controller.resendVerificationCode,
icon: Icon( icon: Icon(
Icons.refresh, Icons.refresh,
size: 14.sp, size: 14.sp,
color: controller.countdownSeconds > 0 color: controller.countdownSeconds > 0
? (isDarkMode ? Colors.grey[600] : Colors.grey[500]) ? (isDarkMode ? Colors.grey[600] : Colors.grey[500])
: const Color(0xFF3B82F6), : const Color(0xFF3B82F6),
), ),
label: Text( label: Text(
controller.countdownSeconds > 0 controller.countdownSeconds > 0
? '重新发送验证码 (${controller.countdownSeconds}s)' ? '重新发送验证码 (${controller.countdownSeconds}s)'
: '重新发送验证码', : '重新发送验证码',
style: TextStyle( style: TextStyle(
fontSize: 14.sp, fontSize: 14.sp,
color: controller.countdownSeconds > 0 color: controller.countdownSeconds > 0
? (isDarkMode ? Colors.grey[600] : Colors.grey[500]) ? (isDarkMode ? Colors.grey[600] : Colors.grey[500])
: const Color(0xFF3B82F6), : const Color(0xFF3B82F6),
), ),
), ),
)), )),
); );
} }
@ -550,35 +567,36 @@ class LoginView extends GetView<LoginController> {
return SizedBox( return SizedBox(
width: double.infinity, width: double.infinity,
child: Obx(() => ElevatedButton( child: Obx(() => ElevatedButton(
onPressed: controller.isLoading ? null : controller.login, onPressed: controller.isLoading ? null : controller.login,
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF3B82F6), backgroundColor: const Color(0xFF3B82F6),
foregroundColor: Colors.white, foregroundColor: Colors.white,
padding: EdgeInsets.symmetric(vertical: 14.h), padding: EdgeInsets.symmetric(vertical: 14.h),
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(16.r),
),
elevation: 0,
disabledBackgroundColor: const Color(0xFF3B82F6).withOpacity(0.7),
disabledForegroundColor: Colors.white,
),
child: controller.isLoading
? SizedBox(
height: 20.h,
width: 20.h,
child: CircularProgressIndicator(
strokeWidth: 2.w,
valueColor: const AlwaysStoppedAnimation<Color>(Colors.white),
),
)
: Text(
'登录',
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w600,
),
), ),
)), elevation: 0,
disabledBackgroundColor: const Color(0xFF3B82F6).withOpacity(0.7),
disabledForegroundColor: Colors.white,
),
child: controller.isLoading
? SizedBox(
height: 20.h,
width: 20.h,
child: CircularProgressIndicator(
strokeWidth: 2.w,
valueColor:
const AlwaysStoppedAnimation<Color>(Colors.white),
),
)
: Text(
'登录',
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w600,
),
),
)),
); );
} }
@ -591,10 +609,11 @@ class LoginView extends GetView<LoginController> {
'使用密码登录', '使用密码登录',
style: TextStyle( style: TextStyle(
fontSize: 14.sp, fontSize: 14.sp,
color: isDarkMode ? const Color(0xFF3B82F6) : const Color(0xFF3B82F6), color:
isDarkMode ? const Color(0xFF3B82F6) : const Color(0xFF3B82F6),
), ),
), ),
), ),
); );
} }
} }

118
lib/modules/pairing/controllers/pairing_controller.dart

@ -7,127 +7,124 @@ import '../../../core/utils/logger.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
/// 简化的配对控制器,仅用于显示已连接经典蓝牙设备和处理基本配对流程 /// 简化的配对控制器,仅用于显示已连接经典蓝牙设备和处理基本配对流程
class PairingController extends GetxController { class PairingController extends GetxController {
static PairingController get to => Get.find(); static PairingController get to => Get.find();
// 经典蓝牙服务 // 经典蓝牙服务
late classic.ClassicBluetoothService _bluetoothService; late classic.ClassicBluetoothService _bluetoothService;
// 是否开始搜索 // 是否开始搜索
final _isSearching = false.obs; final _isSearching = false.obs;
bool get isSearching => _isSearching.value; bool get isSearching => _isSearching.value;
// 设备连接状态 // 设备连接状态
final _isConnecting = false.obs; final _isConnecting = false.obs;
bool get isConnecting => _isConnecting.value; bool get isConnecting => _isConnecting.value;
// 蓝牙服务是否可用 // 蓝牙服务是否可用
final _isBluetoothServiceAvailable = true.obs; final _isBluetoothServiceAvailable = true.obs;
bool get isBluetoothServiceAvailable => _isBluetoothServiceAvailable.value; bool get isBluetoothServiceAvailable => _isBluetoothServiceAvailable.value;
// 当前连接设备 // 当前连接设备
Rx<classic.BluetoothDevice?> get currentDevice => _bluetoothService.currentDevice; Rx<classic.BluetoothDevice?> get currentDevice =>
_bluetoothService.currentDevice;
// 连接设备列表 // 连接设备列表
RxList<classic.BluetoothDevice> get connectedDevices => _bluetoothService.connectedDevices; RxList<classic.BluetoothDevice> get connectedDevices =>
_bluetoothService.connectedDevices;
// 错误信息 // 错误信息
RxString get errorMessage => _bluetoothService.errorMessage; RxString get errorMessage => _bluetoothService.errorMessage;
// 连接设备列表(兼容旧代码) // 连接设备列表(兼容旧代码)
List<classic.BluetoothDevice> get connectedClassicDevices => List<classic.BluetoothDevice> get connectedClassicDevices =>
_bluetoothService.currentDevice.value != null _bluetoothService.currentDevice.value != null
? [_bluetoothService.currentDevice.value!] ? [_bluetoothService.currentDevice.value!]
: []; : [];
// 状态描述(用于UI显示) // 状态描述(用于UI显示)
final statusDescription = '正在初始化...'.obs; final statusDescription = 'initializing'.tr.obs; // 正在初始化...
// 加载状态 // 加载状态
final isLoading = false.obs; final isLoading = false.obs;
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
_initialize(); _initialize();
} }
/// 完整的初始化流程 /// 完整的初始化流程
Future<void> _initialize() async { Future<void> _initialize() async {
try { try {
VoiceInteractionService.to.initialize(); VoiceInteractionService.to.initialize();
VoiceInteractionService.to.startService(); VoiceInteractionService.to.startService();
isLoading.value = true; isLoading.value = true;
statusDescription.value = '正在初始化...'; statusDescription.value = 'initializing'.tr; // 正在初始化...
// 1. 获取蓝牙服务 // 1. 获取蓝牙服务
_bluetoothService = Get.find<classic.ClassicBluetoothService>(); _bluetoothService = Get.find<classic.ClassicBluetoothService>();
// 确保蓝牙服务已初始化 // 确保蓝牙服务已初始化
if (!await _bluetoothService.initialize()) { if (!await _bluetoothService.initialize()) {
Logger.error('蓝牙服务初始化失败'); Logger.error('蓝牙服务初始化失败');
statusDescription.value = '蓝牙服务初始化失败'; statusDescription.value =
'bluetoothInitializationFailure'.tr; // 蓝牙服务初始化失败
_isBluetoothServiceAvailable.value = false; _isBluetoothServiceAvailable.value = false;
isLoading.value = false; isLoading.value = false;
return; return;
} }
// 设置连接监听器,确保在页面完全加载后设置,避免过早监听可能导致的问题 // 设置连接监听器,确保在页面完全加载后设置,避免过早监听可能导致的问题
_setupDeviceListeners(); _setupDeviceListeners();
// 2. 检查权限 // 2. 检查权限
statusDescription.value = '正在检查权限...'; statusDescription.value = 'checkingPermissions'.tr; // 正在检查权限...
final hasPermission = await _bluetoothService.checkAndRequestPermissions(); final hasPermission =
await _bluetoothService.checkAndRequestPermissions();
if (!hasPermission) { if (!hasPermission) {
statusDescription.value = '缺少蓝牙权限'; statusDescription.value = 'lackPermissions'.tr; // 缺少蓝牙权限
isLoading.value = false; isLoading.value = false;
return; return;
} }
// 3. 刷新设备列表 // 3. 刷新设备列表
statusDescription.value = '正在搜索设备...'; statusDescription.value = 'searchingDevice'.tr; // 正在搜索设备...
await refreshConnectedDevices(); await refreshConnectedDevices();
// 4. 处理结果 // 4. 处理结果
if (_bluetoothService.connectedDevices.isNotEmpty) { if (_bluetoothService.connectedDevices.isNotEmpty) {
statusDescription.value = '找到已连接的设备'; statusDescription.value = 'findConnectedDevice'.tr; // 找到已连接的设备
// 延迟跳转到主页 // 延迟跳转到主页
_autoNavigateToHome(); _autoNavigateToHome();
} else { } else {
// 没有找到设备 // 没有找到设备
statusDescription.value = '未找到已连接的蓝牙设备'; statusDescription.value = 'notFound'.tr; // 未找到已连接的蓝牙设备
} }
} catch (e) { } catch (e) {
Logger.error('初始化配对控制器失败: $e'); Logger.error('初始化配对控制器失败: $e');
statusDescription.value = '初始化失败'; statusDescription.value = 'initializationFailure'.tr; // 初始化失败
_isBluetoothServiceAvailable.value = false; _isBluetoothServiceAvailable.value = false;
} finally { } finally {
isLoading.value = false; isLoading.value = false;
} }
} }
/// 设置设备监听器 /// 设置设备监听器
void _setupDeviceListeners() { void _setupDeviceListeners() {
try { try {
if (!_isBluetoothServiceAvailable.value) return; if (!_isBluetoothServiceAvailable.value) return;
// 监听设备连接事件 // 监听设备连接事件
_bluetoothService.onDeviceConnected((device) { _bluetoothService.onDeviceConnected((device) {
Logger.info('检测到蓝牙设备连接: ${device.name}'); Logger.info('检测到蓝牙设备连接: ${device.name}');
// 更新状态并跳转到主页 // 更新状态并跳转到主页
statusDescription.value = '已连接到设备: ${device.name}'; statusDescription.value =
'${'connectedDevice'.tr} ${device.name}'; // 已连接到设备:
// 立即跳转到主页 // 立即跳转到主页
_navigateToHome(); _navigateToHome();
}); });
@ -135,60 +132,59 @@ class PairingController extends GetxController {
Logger.error('设置设备监听器失败: $e'); Logger.error('设置设备监听器失败: $e');
} }
} }
/// 立即跳转到主页 /// 立即跳转到主页
void _navigateToHome() { void _navigateToHome() {
Get.offAllNamed(Routes.home); Get.offAllNamed(Routes.home);
} }
/// 刷新设备 /// 刷新设备
Future<void> refreshConnectedDevices() async { Future<void> refreshConnectedDevices() async {
try { try {
isLoading.value = true; isLoading.value = true;
statusDescription.value = '正在搜索设备...'; statusDescription.value = 'searchingDevice'.tr; // 正在搜索设备...
final hasDevices = await _bluetoothService.refreshConnectedDevices(); final hasDevices = await _bluetoothService.refreshConnectedDevices();
if (hasDevices) { if (hasDevices) {
statusDescription.value = '找到已连接的设备'; statusDescription.value = 'findConnectedDevice'.tr; // 找到已连接的设备
// 延迟跳转到主页 // 延迟跳转到主页
_autoNavigateToHome(); _autoNavigateToHome();
} else { } else {
// 没有找到设备 // 没有找到设备
statusDescription.value = '未找到已连接的蓝牙设备'; statusDescription.value = 'notFound'.tr; // 未找到已连接的蓝牙设备
} }
} catch (e) { } catch (e) {
Logger.error('刷新设备失败: $e'); Logger.error('刷新设备失败: $e');
statusDescription.value = '刷新设备失败'; statusDescription.value = 'failedRefreshDevice'.tr; // 刷新设备失败
} finally { } finally {
isLoading.value = false; isLoading.value = false;
} }
} }
/// 自动跳转到主页 /// 自动跳转到主页
void _autoNavigateToHome() { void _autoNavigateToHome() {
if (_bluetoothService.connectedDevices.isEmpty) return; if (_bluetoothService.connectedDevices.isEmpty) return;
Logger.info('已找到连接的设备,自动跳转到主页'); Logger.info('已找到连接的设备,自动跳转到主页');
Future.delayed(const Duration(milliseconds: 500), () { Future.delayed(const Duration(milliseconds: 500), () {
Get.offAllNamed(Routes.home); Get.offAllNamed(Routes.home);
}); });
} }
/// 打开系统蓝牙设置 /// 打开系统蓝牙设置
void openBluetoothSettings() { void openBluetoothSettings() {
_bluetoothService.openBluetoothSettings(); _bluetoothService.openBluetoothSettings();
} }
/// 刷新设备(兼容旧代码) /// 刷新设备(兼容旧代码)
Future<void> refreshDevices() async { Future<void> refreshDevices() async {
await refreshConnectedDevices(); await refreshConnectedDevices();
} }
/// 跳过连接 /// 跳过连接
void skipConnecting() { void skipConnecting() {
Get.offAllNamed(Routes.home); Get.offAllNamed(Routes.home);
} }
} }

61
lib/modules/pairing/views/pairing_view.dart

@ -11,7 +11,7 @@ class PairingView extends GetView<PairingController> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final bool isDarkMode = Theme.of(context).brightness == Brightness.dark; final bool isDarkMode = Theme.of(context).brightness == Brightness.dark;
return Scaffold( return Scaffold(
backgroundColor: isDarkMode ? Colors.black : Colors.white, backgroundColor: isDarkMode ? Colors.black : Colors.white,
body: SafeArea( body: SafeArea(
@ -35,32 +35,33 @@ class PairingView extends GetView<PairingController> {
physics: BouncingScrollPhysics(), physics: BouncingScrollPhysics(),
child: ConstrainedBox( child: ConstrainedBox(
constraints: BoxConstraints( constraints: BoxConstraints(
minHeight: MediaQuery.of(Get.context!).size.height - 100.h, // 减去底部导航和状态栏高度 minHeight:
MediaQuery.of(Get.context!).size.height - 100.h, // 减去底部导航和状态栏高度
), ),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
SizedBox(height: 20.h), // 顶部间距 SizedBox(height: 20.h), // 顶部间距
// 标题部分 // 标题部分
_buildHeader(isDarkMode), _buildHeader(isDarkMode),
SizedBox(height: 20.h), SizedBox(height: 20.h),
// 设备动画 - 更大尺寸 // 设备动画 - 更大尺寸
_buildDeviceAnimation(), _buildDeviceAnimation(),
SizedBox(height: 20.h), SizedBox(height: 20.h),
// 状态信息 // 状态信息
_buildStatusInfo(isDarkMode), _buildStatusInfo(isDarkMode),
SizedBox(height: 40.h), SizedBox(height: 40.h),
// 底部按钮 // 底部按钮
_buildActionButtons(isDarkMode), _buildActionButtons(isDarkMode),
SizedBox(height: 20.h), SizedBox(height: 20.h),
], ],
), ),
@ -83,7 +84,7 @@ class PairingView extends GetView<PairingController> {
), ),
SizedBox(height: 8.h), SizedBox(height: 8.h),
Text( Text(
'连接世界,超越声音', 'homeSlogan'.tr, // 连接世界,超越声音
style: TextStyle( style: TextStyle(
fontSize: 16.sp, fontSize: 16.sp,
color: isDarkMode ? Colors.white60 : Colors.black54, color: isDarkMode ? Colors.white60 : Colors.black54,
@ -127,7 +128,7 @@ class PairingView extends GetView<PairingController> {
// 重新开始动画 // 重新开始动画
}, },
), ),
// 旋转边框 // 旋转边框
SizedBox( SizedBox(
width: 100.w, width: 100.w,
@ -140,7 +141,7 @@ class PairingView extends GetView<PairingController> {
backgroundColor: Colors.grey.withOpacity(0.2), backgroundColor: Colors.grey.withOpacity(0.2),
), ),
), ),
// 设备图标 // 设备图标
Container( Container(
width: 120.w, width: 120.w,
@ -166,13 +167,13 @@ class PairingView extends GetView<PairingController> {
), ),
); );
} }
// 状态信息 // 状态信息
Widget _buildStatusInfo(bool isDarkMode) { Widget _buildStatusInfo(bool isDarkMode) {
return Obx(() { return Obx(() {
final status = controller.statusDescription.value; final status = controller.statusDescription.value;
final isLoading = controller.isLoading.value; final isLoading = controller.isLoading.value;
return Column( return Column(
children: [ children: [
// 状态文本 - 更大更醒目 // 状态文本 - 更大更醒目
@ -185,19 +186,19 @@ class PairingView extends GetView<PairingController> {
), ),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
SizedBox(height: 10.h), SizedBox(height: 10.h),
// 子状态提示文本 // 子状态提示文本
Text( Text(
'请打开耳机充电盒,将耳机放入您的耳朵', 'turnEarphones'.tr, // 请打开耳机充电盒,将耳机放入您的耳朵
style: TextStyle( style: TextStyle(
fontSize: 14.sp, fontSize: 14.sp,
color: isDarkMode ? Colors.white60 : Colors.grey[600], color: isDarkMode ? Colors.white60 : Colors.grey[600],
), ),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
// 设备列表(有设备时才显示) // 设备列表(有设备时才显示)
if (controller.connectedClassicDevices.isNotEmpty) ...[ if (controller.connectedClassicDevices.isNotEmpty) ...[
SizedBox(height: 24.h), SizedBox(height: 24.h),
@ -207,23 +208,23 @@ class PairingView extends GetView<PairingController> {
); );
}); });
} }
// 已连接设备列表 // 已连接设备列表
Widget _buildConnectedDevicesList(bool isDarkMode) { Widget _buildConnectedDevicesList(bool isDarkMode) {
return Obx(() { return Obx(() {
final devices = controller.connectedClassicDevices; final devices = controller.connectedClassicDevices;
if (devices.isEmpty) { if (devices.isEmpty) {
return const SizedBox.shrink(); return const SizedBox.shrink();
} }
return Container( return Container(
width: double.infinity, width: double.infinity,
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
'当前已连接:', 'currentlyConnected'.tr, // 当前已连接:
style: TextStyle( style: TextStyle(
fontSize: 16.sp, fontSize: 16.sp,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@ -237,7 +238,7 @@ class PairingView extends GetView<PairingController> {
); );
}); });
} }
// 设备项 // 设备项
Widget _buildDeviceItem(device, bool isDarkMode) { Widget _buildDeviceItem(device, bool isDarkMode) {
return Container( return Container(
@ -287,7 +288,7 @@ class PairingView extends GetView<PairingController> {
), ),
); );
} }
// 操作按钮 // 操作按钮
Widget _buildActionButtons(bool isDarkMode) { Widget _buildActionButtons(bool isDarkMode) {
return Container( return Container(
@ -308,16 +309,16 @@ class PairingView extends GetView<PairingController> {
minimumSize: Size(double.infinity, 56.h), minimumSize: Size(double.infinity, 56.h),
), ),
child: Text( child: Text(
'去系统设置', 'unsystemSetup'.tr, // 去系统设置
style: TextStyle( style: TextStyle(
fontSize: 16.sp, fontSize: 16.sp,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
), ),
), ),
SizedBox(height: 12.h), SizedBox(height: 12.h),
// 次要按钮 // 次要按钮
TextButton( TextButton(
onPressed: () => controller.skipConnecting(), onPressed: () => controller.skipConnecting(),
@ -327,7 +328,7 @@ class PairingView extends GetView<PairingController> {
minimumSize: Size(double.infinity, 48.h), minimumSize: Size(double.infinity, 48.h),
), ),
child: Text( child: Text(
'稍后再说', 'talkLater'.tr, // 稍后再说
style: TextStyle( style: TextStyle(
fontSize: 16.sp, fontSize: 16.sp,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
@ -338,4 +339,4 @@ class PairingView extends GetView<PairingController> {
), ),
); );
} }
} }

98
lib/modules/settings/controllers/settings_controller.dart

@ -1,46 +1,81 @@
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:get_storage/get_storage.dart';
import '../../../core/themes/theme_service.dart'; import '../../../core/themes/theme_service.dart';
import '../../../data/services/speech_factory.dart'; import '../../../data/services/speech_factory.dart';
import 'package:get_storage/get_storage.dart'; import '../models/language_model.dart';
import '../../../data/models/user_model.dart'; import '../../../data/models/user_model.dart';
class SettingsController extends GetxController { class SettingsController extends GetxController {
// 语言设置
final List<LanguageModel> languageList = [
LanguageModel(
name: 'followingSystem', // 跟随系统.tr
alias: 'followingSystem', // 跟随系统.tr
value: 'system'),
LanguageModel(name: 'chinese', alias: '中文', value: 'zh_CN'), // 中文.tr
LanguageModel(name: 'english', alias: 'English', value: 'en_US'), // 英语.tr
];
Rx<LanguageModel> language = LanguageModel().obs;
final GetStorage _storage = GetStorage();
// 主题服务 // 主题服务
final ThemeService _themeService = ThemeService(); final ThemeService _themeService = ThemeService();
// 是否是暗黑模式 // 是否是暗黑模式
final RxBool isDarkMode = false.obs; final RxBool isDarkMode = false.obs;
// 翻译语言对 // 翻译语言对
final RxString sourceLanguage = '中文'.obs; final RxString sourceLanguage = '中文'.obs;
final RxString targetLanguage = '英文'.obs; final RxString targetLanguage = '英文'.obs;
final List<String> availableLanguages = ['中文', '英文', '日文', '韩文', '法文', '德文', '西班牙文', '俄文']; final List<String> availableLanguages = [
'中文',
'英文',
'日文',
'韩文',
'法文',
'德文',
'西班牙文',
'俄文'
];
// 语音服务类型 // 语音服务类型
final Rx<SpeechServiceType> speechServiceType = SpeechServiceType.azure.obs; final Rx<SpeechServiceType> speechServiceType = SpeechServiceType.azure.obs;
final List<Map<String, dynamic>> availableSpeechServices = [ final List<Map<String, dynamic>> availableSpeechServices = [
{'type': SpeechServiceType.azure, 'name': 'Azure 语音服务', 'description': '微软云端语音服务,支持高质量语音合成和识别'}, {
{'type': SpeechServiceType.flutter, 'name': 'Flutter 本地语音服务', 'description': '设备本地语音服务,无需联网即可使用'}, 'type': SpeechServiceType.azure,
'name': 'azureVoiceService', // Azure 语音服务.tr
'description': 'azureVoiceServiceInfo' // 微软云端语音服务,支持高质量语音合成和识别.tr
},
{
'type': SpeechServiceType.flutter,
'name': 'flutterVoiceService', // Flutter 本地语音服务.tr
'description': 'flutterVoiceServiceInfo' // 设备本地语音服务,无需联网即可使用.tr
},
]; ];
//用户信息 //用户信息
static const String userKey='user_info'; static const String userKey='user_info';
final _storage=GetStorage(); //final _storage=GetStorage();
final ImagePicker _picker=ImagePicker(); final ImagePicker _picker=ImagePicker();
final Rx<UserModel> userInfo=UserModel.defaultUser().obs; final Rx<UserModel> userInfo=UserModel.defaultUser().obs;
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
// 获取系统语言
_getLanguage();
// 从存储中加载上次保存的主题设置 // 从存储中加载上次保存的主题设置
final savedDarkMode = _themeService.isSavedDarkMode(); final savedDarkMode = _themeService.isSavedDarkMode();
// 设置当前主题状态 // 设置当前主题状态
isDarkMode.value = savedDarkMode; isDarkMode.value = savedDarkMode;
// 获取当前语音服务类型 // 获取当前语音服务类型
_loadCurrentSpeechServiceType(); _loadCurrentSpeechServiceType();
@ -97,7 +132,28 @@ class SettingsController extends GetxController {
// await saveUserInfo(); // await saveUserInfo();
// } // }
// } // }
void _getLanguage() {
String? languageValue = _storage.read('language');
LanguageModel data = languageList.firstWhere(
(element) => element.value == languageValue,
orElse: () => languageList[0],
);
language.value = data;
}
void setLanguage(LanguageModel data) {
if (data.value != 'system') {
List languageList = data.value.split('_');
Get.updateLocale(Locale(languageList[0], languageList[1]));
} else {
Get.updateLocale(Get.deviceLocale!);
}
language.value = data;
_storage.write('language', data.value);
Get.back();
}
// 加载当前语音服务类型 // 加载当前语音服务类型
void _loadCurrentSpeechServiceType() { void _loadCurrentSpeechServiceType() {
try { try {
@ -107,19 +163,19 @@ class SettingsController extends GetxController {
// 如果找不到SpeechFactory实例,使用默认值 // 如果找不到SpeechFactory实例,使用默认值
} }
} }
// 切换主题 // 切换主题
Future<void> toggleTheme() async { Future<void> toggleTheme() async {
// 切换暗黑模式状态 // 切换暗黑模式状态
isDarkMode.value = !isDarkMode.value; isDarkMode.value = !isDarkMode.value;
// 更新全局主题 // 更新全局主题
Get.changeThemeMode(isDarkMode.value ? ThemeMode.dark : ThemeMode.light); Get.changeThemeMode(isDarkMode.value ? ThemeMode.dark : ThemeMode.light);
// 保存用户选择的主题设置 - 确保立即写入存储 // 保存用户选择的主题设置 - 确保立即写入存储
await _themeService.saveThemeMode(isDarkMode.value); await _themeService.saveThemeMode(isDarkMode.value);
} }
// 设置语音服务类型 // 设置语音服务类型
void setSpeechServiceType(SpeechServiceType type) { void setSpeechServiceType(SpeechServiceType type) {
try { try {
@ -130,23 +186,23 @@ class SettingsController extends GetxController {
// 处理错误 // 处理错误
} }
} }
void setSourceLanguage(String language) { void setSourceLanguage(String language) {
sourceLanguage.value = language; sourceLanguage.value = language;
} }
void setTargetLanguage(String language) { void setTargetLanguage(String language) {
targetLanguage.value = language; targetLanguage.value = language;
} }
void swapLanguages() { void swapLanguages() {
final temp = sourceLanguage.value; final temp = sourceLanguage.value;
sourceLanguage.value = targetLanguage.value; sourceLanguage.value = targetLanguage.value;
targetLanguage.value = temp; targetLanguage.value = temp;
} }
void signOut() { void signOut() {
// 在此实现退出登录逻辑 // 在此实现退出登录逻辑
Get.offAllNamed('/login'); Get.offAllNamed('/login');
} }
} }

11
lib/modules/settings/models/language_model.dart

@ -0,0 +1,11 @@
class LanguageModel {
final String name;
final String alias;
final String value;
LanguageModel({
this.name = '',
this.alias = '',
this.value = '',
});
}

398
lib/modules/settings/views/settings_view.dart

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'dart:ui'; import 'dart:ui';
import '../../../core/theme/app_colors.dart';
import '../controllers/settings_controller.dart'; import '../controllers/settings_controller.dart';
import '../../../core/widgets/common_bottom_nav.dart'; import '../../../core/widgets/common_bottom_nav.dart';
import '../../../routes/app_routes.dart'; import '../../../routes/app_routes.dart';
@ -14,14 +15,14 @@ class SettingsView extends GetView<SettingsController> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
// 直接使用 Theme.of(context) 获取当前主题 // 直接使用 Theme.of(context) 获取当前主题
final isDarkMode = Theme.of(context).brightness == Brightness.dark; final isDarkMode = Theme.of(context).brightness == Brightness.dark;
return Scaffold( return Scaffold(
backgroundColor: isDarkMode ? Colors.black : Colors.grey[50], backgroundColor: isDarkMode ? Colors.black : Colors.grey[50],
appBar: AppBar( appBar: AppBar(
backgroundColor: isDarkMode ? Colors.black : Colors.white, backgroundColor: isDarkMode ? Colors.black : Colors.white,
elevation: 0.5, elevation: 0.5,
title: Text( title: Text(
'设置', 'settings'.tr, // 设置
style: TextStyle( style: TextStyle(
fontSize: 17.sp, fontSize: 17.sp,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@ -29,7 +30,6 @@ class SettingsView extends GetView<SettingsController> {
), ),
), ),
centerTitle: true, centerTitle: true,
), ),
body: ListView( body: ListView(
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 16.h), padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 16.h),
@ -52,22 +52,19 @@ class SettingsView extends GetView<SettingsController> {
padding: EdgeInsets.all(16.w), padding: EdgeInsets.all(16.w),
decoration: BoxDecoration( decoration: BoxDecoration(
gradient: LinearGradient( gradient: LinearGradient(
colors: isDarkMode colors: isDarkMode
? [ ? [
const Color(0xFF1A1A1A).withOpacity(0.7), const Color(0xFF1A1A1A).withOpacity(0.7),
const Color(0xFF2A2A2A).withOpacity(0.7) const Color(0xFF2A2A2A).withOpacity(0.7)
] ]
: [ : [const Color(0xFFE3F2FD), const Color(0xFFF3E5F5)],
const Color(0xFFE3F2FD),
const Color(0xFFF3E5F5)
],
begin: Alignment.topLeft, begin: Alignment.topLeft,
end: Alignment.bottomRight, end: Alignment.bottomRight,
), ),
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(16.r),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: isDarkMode color: isDarkMode
? Colors.black.withOpacity(0.2) ? Colors.black.withOpacity(0.2)
: Colors.black.withOpacity(0.05), : Colors.black.withOpacity(0.05),
blurRadius: 10, blurRadius: 10,
@ -77,8 +74,10 @@ class SettingsView extends GetView<SettingsController> {
), ),
child: ClipRRect(//裁剪圆角矩形 child: ClipRRect(//裁剪圆角矩形
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(16.r),
child: BackdropFilter(// 背景滤镜(实现毛玻璃效果) child: BackdropFilter(
filter: isDarkMode ? ImageFilter.blur(sigmaX: 10, sigmaY: 10) : ImageFilter.blur(sigmaX: 0, sigmaY: 0), filter: isDarkMode
? ImageFilter.blur(sigmaX: 10, sigmaY: 10)
: ImageFilter.blur(sigmaX: 0, sigmaY: 0),
child: Row( child: Row(
children: [ children: [
Obx(()=>GestureDetector( Obx(()=>GestureDetector(
@ -127,8 +126,9 @@ class SettingsView extends GetView<SettingsController> {
SizedBox(height: 4.h), SizedBox(height: 4.h),
Row( Row(
children: [ children: [
Container(// 会员等级 Container(
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 2.h), padding: EdgeInsets.symmetric(
horizontal: 8.w, vertical: 2.h),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.blue[600], color: Colors.blue[600],
borderRadius: BorderRadius.circular(10.r), borderRadius: BorderRadius.circular(10.r),
@ -144,10 +144,11 @@ class SettingsView extends GetView<SettingsController> {
SizedBox(width: 8.w), SizedBox(width: 8.w),
Flexible( Flexible(
child: Text( child: Text(
'已连接设备: DeepVoice Pro', '${'joinDevice'.tr} DeepVoice Pro', // 已连接设备:
style: TextStyle( style: TextStyle(
fontSize: 12.sp, fontSize: 12.sp,
color: isDarkMode ? Colors.white70 : Colors.black54, color:
isDarkMode ? Colors.white70 : Colors.black54,
), ),
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
@ -169,7 +170,7 @@ class SettingsView extends GetView<SettingsController> {
width: 32.w, width: 32.w,
height: 32.w, height: 32.w,
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDarkMode color: isDarkMode
? Colors.white.withOpacity(0.1) ? Colors.white.withOpacity(0.1)
: Colors.white.withOpacity(0.7), : Colors.white.withOpacity(0.7),
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(16.r),
@ -206,7 +207,7 @@ class SettingsView extends GetView<SettingsController> {
Padding( Padding(
padding: EdgeInsets.only(left: 4.w, bottom: 8.h), padding: EdgeInsets.only(left: 4.w, bottom: 8.h),
child: Text( child: Text(
'系统设置', 'systemSetting'.tr, // 系统设置
style: TextStyle( style: TextStyle(
fontSize: 12.sp, fontSize: 12.sp,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
@ -221,7 +222,7 @@ class SettingsView extends GetView<SettingsController> {
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(16.r),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: isDarkMode color: isDarkMode
? Colors.black.withOpacity(0.2) ? Colors.black.withOpacity(0.2)
: Colors.black.withOpacity(0.05), : Colors.black.withOpacity(0.05),
blurRadius: 10, blurRadius: 10,
@ -232,52 +233,70 @@ class SettingsView extends GetView<SettingsController> {
child: ClipRRect( child: ClipRRect(
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(16.r),
child: BackdropFilter( child: BackdropFilter(
filter: isDarkMode ? ImageFilter.blur(sigmaX: 5, sigmaY: 5) : ImageFilter.blur(sigmaX: 0, sigmaY: 0), filter: isDarkMode
? ImageFilter.blur(sigmaX: 5, sigmaY: 5)
: ImageFilter.blur(sigmaX: 0, sigmaY: 0),
child: Column( child: Column(
children: [ children: [
// 通知设置 // 语言设置
_buildSimpleNavigationSetting( Obx(
title: '通知设置', () => _buildSimpleNavigationSetting(
subtitle: '管理应用推送和提醒', title: 'languageSetting'.tr, // 语言设置
icon: Icons.notifications_none, subtitle: controller.language.value.name.tr,
iconBgColor: isDarkMode ? Colors.indigo[900]!.withOpacity(0.3) : Colors.indigo[100]!, icon: Icons.language,
iconColor: isDarkMode ? Colors.indigo[300]! : Colors.indigo[600]!, iconBgColor: isDarkMode
titleColor: isDarkMode ? Colors.white : null, ? Colors.indigo[900]!.withOpacity(0.3)
subtitleColor: isDarkMode ? Colors.white70 : null, : Colors.indigo[100]!,
onTap: () { iconColor: isDarkMode
// 打开通知设置页面 ? Colors.indigo[300]!
}, : Colors.indigo[600]!,
isDarkMode: isDarkMode, titleColor: isDarkMode ? Colors.white : null,
subtitleColor: isDarkMode ? Colors.white70 : null,
onTap: () {
_showLanguageSelectionDialog(isDarkMode);
},
isDarkMode: isDarkMode,
),
), ),
Divider(height: 1, color: isDarkMode ? Colors.white.withOpacity(0.1) : Colors.grey[200]), Divider(
height: 1,
color: isDarkMode
? Colors.white.withOpacity(0.1)
: Colors.grey[200]),
// 主题设置 // 主题设置
_buildToggleSetting( _buildToggleSetting(
title: '暗黑模式', title: 'darkMode'.tr, // 暗黑模式
subtitle: '切换应用的明暗主题', subtitle: 'darkModeInfo'.tr, // 切换应用的明暗主题
icon: isDarkMode ? Icons.dark_mode : Icons.light_mode, icon: isDarkMode ? Icons.dark_mode : Icons.light_mode,
iconBgColor: isDarkMode iconBgColor: isDarkMode
? Colors.indigo[900]!.withOpacity(0.3) ? Colors.indigo[900]!.withOpacity(0.3)
: Colors.amber[100]!, : Colors.amber[100]!,
iconColor: isDarkMode iconColor:
? Colors.indigo[300]! isDarkMode ? Colors.indigo[300]! : Colors.amber[600]!,
: Colors.amber[600]!,
value: isDarkMode, value: isDarkMode,
onChanged: (value) { onChanged: (value) {
controller.toggleTheme(); controller.toggleTheme();
}, },
isDarkMode: isDarkMode, isDarkMode: isDarkMode,
), ),
Divider(height: 1, color: isDarkMode ? Colors.white.withOpacity(0.1) : Colors.grey[200]), Divider(
height: 1,
color: isDarkMode
? Colors.white.withOpacity(0.1)
: Colors.grey[200]),
// 备份与恢复 // 备份与恢复
_buildNavigationSetting( _buildNavigationSetting(
title: '备份与恢复', title: 'backups'.tr, // 备份与恢复
subtitle: '备份设置和AI互动历史', subtitle: 'backupsInfo'.tr, // 备份设置和AI互动历史
icon: Icons.cloud_upload_outlined, icon: Icons.cloud_upload_outlined,
iconBgColor: isDarkMode ? Colors.cyan[900]!.withOpacity(0.3) : Colors.cyan[100]!, iconBgColor: isDarkMode
iconColor: isDarkMode ? Colors.cyan[300]! : Colors.cyan[600]!, ? Colors.cyan[900]!.withOpacity(0.3)
value: '上次备份: 今天', : Colors.cyan[100]!,
iconColor:
isDarkMode ? Colors.cyan[300]! : Colors.cyan[600]!,
value: '${'lastBackup'.tr} 今天', // 上次备份:
valueStyle: TextStyle( valueStyle: TextStyle(
fontSize: 12.sp, fontSize: 12.sp,
color: isDarkMode ? Colors.white70 : Colors.grey[500], color: isDarkMode ? Colors.white70 : Colors.grey[500],
@ -287,20 +306,31 @@ class SettingsView extends GetView<SettingsController> {
}, },
isDarkMode: isDarkMode, isDarkMode: isDarkMode,
), ),
Divider(height: 1, color: isDarkMode ? Colors.white.withOpacity(0.1) : Colors.grey[200]), Divider(
height: 1,
color: isDarkMode
? Colors.white.withOpacity(0.1)
: Colors.grey[200]),
// 语音服务选择 // 语音服务选择
_buildSpeechServiceSetting(isDarkMode), _buildSpeechServiceSetting(isDarkMode),
Divider(height: 1, color: isDarkMode ? Colors.white.withOpacity(0.1) : Colors.grey[200]), Divider(
height: 1,
color: isDarkMode
? Colors.white.withOpacity(0.1)
: Colors.grey[200]),
// 隐私设置 // 隐私设置
_buildSimpleNavigationSetting( _buildSimpleNavigationSetting(
title: '隐私设置', title: 'privacySettings'.tr, // 隐私设置
subtitle: '管理数据收集和权限', subtitle: 'privacySettingsInfo'.tr, // 管理数据收集和权限
icon: Icons.shield_outlined, icon: Icons.shield_outlined,
iconBgColor: isDarkMode ? Colors.purple[900]!.withOpacity(0.3) : Colors.purple[100]!, iconBgColor: isDarkMode
iconColor: isDarkMode ? Colors.purple[300]! : Colors.purple[600]!, ? Colors.purple[900]!.withOpacity(0.3)
: Colors.purple[100]!,
iconColor:
isDarkMode ? Colors.purple[300]! : Colors.purple[600]!,
titleColor: isDarkMode ? Colors.white : null, titleColor: isDarkMode ? Colors.white : null,
subtitleColor: isDarkMode ? Colors.white70 : null, subtitleColor: isDarkMode ? Colors.white70 : null,
onTap: () { onTap: () {
@ -308,39 +338,50 @@ class SettingsView extends GetView<SettingsController> {
}, },
isDarkMode: isDarkMode, isDarkMode: isDarkMode,
), ),
Divider(height: 1, color: isDarkMode ? Colors.white.withOpacity(0.1) : Colors.grey[200]), Divider(
height: 1,
color: isDarkMode
? Colors.white.withOpacity(0.1)
: Colors.grey[200]),
// 退出登录 // 退出登录
_buildSimpleNavigationSetting( _buildSimpleNavigationSetting(
title: '退出登录', title: 'logout'.tr, // 退出登录
icon: Icons.logout, icon: Icons.logout,
iconBgColor: isDarkMode ? Colors.red[900]!.withOpacity(0.3) : Colors.red[50]!, iconBgColor: isDarkMode
? Colors.red[900]!.withOpacity(0.3)
: Colors.red[50]!,
iconColor: isDarkMode ? Colors.red[300]! : Colors.red[600]!, iconColor: isDarkMode ? Colors.red[300]! : Colors.red[600]!,
titleColor: isDarkMode ? Colors.red[300]! : Colors.red[600]!, titleColor:
isDarkMode ? Colors.red[300]! : Colors.red[600]!,
onTap: () { onTap: () {
// 退出登录确认对话框 // 退出登录确认对话框
Get.dialog( Get.dialog(
AlertDialog( AlertDialog(
backgroundColor: isDarkMode ? Colors.grey[900] : Colors.white, backgroundColor:
isDarkMode ? Colors.grey[900] : Colors.white,
title: Text( title: Text(
'退出登录', 'logout'.tr, // 退出登录
style: TextStyle( style: TextStyle(
color: isDarkMode ? Colors.white : Colors.black87, color: isDarkMode ? Colors.white : Colors.black87,
), ),
), ),
content: Text( content: Text(
'确定要退出当前账号吗?', 'logoutTips'.tr, // 确定要退出当前账号吗?
style: TextStyle( style: TextStyle(
color: isDarkMode ? Colors.white70 : Colors.black54, color:
isDarkMode ? Colors.white70 : Colors.black54,
), ),
), ),
actions: [ actions: [
TextButton( TextButton(
onPressed: () => Get.back(), onPressed: () => Get.back(),
child: Text( child: Text(
'取消', 'cancel'.tr, // 取消
style: TextStyle( style: TextStyle(
color: isDarkMode ? Colors.white70 : Colors.black54, color: isDarkMode
? Colors.white70
: Colors.black54,
), ),
), ),
), ),
@ -350,9 +391,11 @@ class SettingsView extends GetView<SettingsController> {
controller.signOut(); controller.signOut();
}, },
child: Text( child: Text(
'确定', 'confirm'.tr, // 确定
style: TextStyle( style: TextStyle(
color: isDarkMode ? Colors.red[300] : Colors.red[600], color: isDarkMode
? Colors.red[300]
: Colors.red[600],
), ),
), ),
), ),
@ -379,7 +422,7 @@ class SettingsView extends GetView<SettingsController> {
Padding( Padding(
padding: EdgeInsets.only(left: 4.w, bottom: 8.h), padding: EdgeInsets.only(left: 4.w, bottom: 8.h),
child: Text( child: Text(
'关于应用', 'aboutApplication'.tr, // 关于应用
style: TextStyle( style: TextStyle(
fontSize: 12.sp, fontSize: 12.sp,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
@ -395,7 +438,7 @@ class SettingsView extends GetView<SettingsController> {
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(16.r),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: isDarkMode color: isDarkMode
? Colors.black.withOpacity(0.2) ? Colors.black.withOpacity(0.2)
: Colors.black.withOpacity(0.05), : Colors.black.withOpacity(0.05),
blurRadius: 10, blurRadius: 10,
@ -406,7 +449,9 @@ class SettingsView extends GetView<SettingsController> {
child: ClipRRect( child: ClipRRect(
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(16.r),
child: BackdropFilter( child: BackdropFilter(
filter: isDarkMode ? ImageFilter.blur(sigmaX: 5, sigmaY: 5) : ImageFilter.blur(sigmaX: 0, sigmaY: 0), filter: isDarkMode
? ImageFilter.blur(sigmaX: 5, sigmaY: 5)
: ImageFilter.blur(sigmaX: 0, sigmaY: 0),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
@ -418,8 +463,10 @@ class SettingsView extends GetView<SettingsController> {
decoration: BoxDecoration( decoration: BoxDecoration(
gradient: LinearGradient( gradient: LinearGradient(
colors: [ colors: [
const Color(0xFF2196F3).withOpacity(isDarkMode ? 0.7 : 1.0), const Color(0xFF2196F3)
const Color(0xFF9C27B0).withOpacity(isDarkMode ? 0.7 : 1.0) .withOpacity(isDarkMode ? 0.7 : 1.0),
const Color(0xFF9C27B0)
.withOpacity(isDarkMode ? 0.7 : 1.0)
], ],
begin: Alignment.topLeft, begin: Alignment.topLeft,
end: Alignment.bottomRight, end: Alignment.bottomRight,
@ -446,10 +493,11 @@ class SettingsView extends GetView<SettingsController> {
), ),
SizedBox(height: 2.h), SizedBox(height: 2.h),
Text( Text(
'版本 2.4.1 (Build 87)', '${'versions'.tr} 2.4.1 (Build 87)', // 版本
style: TextStyle( style: TextStyle(
fontSize: 12.sp, fontSize: 12.sp,
color: isDarkMode ? Colors.white70 : Colors.black54, color:
isDarkMode ? Colors.white70 : Colors.black54,
), ),
), ),
], ],
@ -457,13 +505,16 @@ class SettingsView extends GetView<SettingsController> {
], ],
), ),
Container( Container(
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 6.h), padding:
EdgeInsets.symmetric(horizontal: 12.w, vertical: 6.h),
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDarkMode ? Colors.blue[900]!.withOpacity(0.3) : Colors.blue[100], color: isDarkMode
? Colors.blue[900]!.withOpacity(0.3)
: Colors.blue[100],
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(16.r),
), ),
child: Text( child: Text(
'检查更新', 'checkUpdates'.tr, // 检查更新
style: TextStyle( style: TextStyle(
fontSize: 12.sp, fontSize: 12.sp,
color: isDarkMode ? Colors.blue[300] : Colors.blue[600], color: isDarkMode ? Colors.blue[300] : Colors.blue[600],
@ -488,7 +539,7 @@ class SettingsView extends GetView<SettingsController> {
), ),
SizedBox(height: 4.h), SizedBox(height: 4.h),
Text( Text(
'连接世界,超越声音', 'homeSlogan'.tr, // 连接世界,超越声音
style: TextStyle( style: TextStyle(
fontSize: 12.sp, fontSize: 12.sp,
color: isDarkMode ? Colors.grey[400] : Colors.grey[500], color: isDarkMode ? Colors.grey[400] : Colors.grey[500],
@ -547,8 +598,7 @@ class SettingsView extends GetView<SettingsController> {
), ),
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
if (subtitle != null) if (subtitle != null) SizedBox(height: 2.h),
SizedBox(height: 2.h),
if (subtitle != null) if (subtitle != null)
Text( Text(
subtitle, subtitle,
@ -623,14 +673,14 @@ class SettingsView extends GetView<SettingsController> {
), ),
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
if (subtitle != null) if (subtitle != null) SizedBox(height: 2.h),
SizedBox(height: 2.h),
if (subtitle != null) if (subtitle != null)
Text( Text(
subtitle, subtitle,
style: TextStyle( style: TextStyle(
fontSize: 12.sp, fontSize: 12.sp,
color: isDarkMode ? Colors.white70 : Colors.black54, color:
isDarkMode ? Colors.white70 : Colors.black54,
), ),
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
@ -646,10 +696,11 @@ class SettingsView extends GetView<SettingsController> {
Flexible( Flexible(
child: Text( child: Text(
value, value,
style: valueStyle ?? TextStyle( style: valueStyle ??
fontSize: 14.sp, TextStyle(
color: isDarkMode ? Colors.white : Colors.black87, fontSize: 14.sp,
), color: isDarkMode ? Colors.white : Colors.black87,
),
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
), ),
@ -658,7 +709,9 @@ class SettingsView extends GetView<SettingsController> {
width: 32.w, width: 32.w,
height: 32.w, height: 32.w,
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDarkMode ? Colors.white.withOpacity(0.05) : Colors.grey[100], color: isDarkMode
? Colors.white.withOpacity(0.05)
: Colors.grey[100],
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(16.r),
), ),
child: Icon( child: Icon(
@ -720,18 +773,21 @@ class SettingsView extends GetView<SettingsController> {
style: TextStyle( style: TextStyle(
fontSize: 16.sp, fontSize: 16.sp,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: titleColor ?? (isDarkMode ? Colors.white : Colors.black87), color: titleColor ??
(isDarkMode ? Colors.white : Colors.black87),
), ),
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
if (subtitle != null) if (subtitle != null) SizedBox(height: 2.h),
SizedBox(height: 2.h),
if (subtitle != null) if (subtitle != null)
Text( Text(
subtitle, subtitle,
style: TextStyle( style: TextStyle(
fontSize: 12.sp, fontSize: 12.sp,
color: subtitleColor ?? (isDarkMode ? Colors.white70 : Colors.black54), color: subtitleColor ??
(isDarkMode
? Colors.white70
: Colors.black54),
), ),
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
@ -745,7 +801,9 @@ class SettingsView extends GetView<SettingsController> {
width: 32.w, width: 32.w,
height: 32.w, height: 32.w,
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDarkMode ? Colors.white.withOpacity(0.05) : Colors.grey[100], color: isDarkMode
? Colors.white.withOpacity(0.05)
: Colors.grey[100],
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(16.r),
), ),
child: Icon( child: Icon(
@ -778,7 +836,9 @@ class SettingsView extends GetView<SettingsController> {
width: 32.w, width: 32.w,
height: 32.w, height: 32.w,
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDarkMode ? Colors.teal[900]!.withOpacity(0.3) : Colors.teal[100]!, color: isDarkMode
? Colors.teal[900]!.withOpacity(0.3)
: Colors.teal[100]!,
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(16.r),
), ),
child: Icon( child: Icon(
@ -793,7 +853,7 @@ class SettingsView extends GetView<SettingsController> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
'语音服务', 'voiceService'.tr, // 语音服务
style: TextStyle( style: TextStyle(
fontSize: 16.sp, fontSize: 16.sp,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
@ -802,7 +862,7 @@ class SettingsView extends GetView<SettingsController> {
), ),
SizedBox(height: 2.h), SizedBox(height: 2.h),
Text( Text(
'选择语音识别和合成服务', 'voiceServiceInfo'.tr, // 选择语音识别和合成服务
style: TextStyle( style: TextStyle(
fontSize: 12.sp, fontSize: 12.sp,
color: isDarkMode ? Colors.white70 : Colors.black54, color: isDarkMode ? Colors.white70 : Colors.black54,
@ -821,11 +881,12 @@ class SettingsView extends GetView<SettingsController> {
Obx(() { Obx(() {
final serviceType = controller.speechServiceType.value; final serviceType = controller.speechServiceType.value;
final serviceName = controller.availableSpeechServices final serviceName = controller.availableSpeechServices
.firstWhere((service) => service['type'] == serviceType)['name']; .firstWhere(
(service) => service['type'] == serviceType)['name'];
return Flexible( return Flexible(
child: Text( child: Text(
serviceName, '$serviceName'.tr,
style: TextStyle( style: TextStyle(
fontSize: 12.sp, fontSize: 12.sp,
color: isDarkMode ? Colors.white70 : Colors.grey[600], color: isDarkMode ? Colors.white70 : Colors.grey[600],
@ -839,7 +900,9 @@ class SettingsView extends GetView<SettingsController> {
width: 32.w, width: 32.w,
height: 32.w, height: 32.w,
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDarkMode ? Colors.white.withOpacity(0.05) : Colors.grey[100], color: isDarkMode
? Colors.white.withOpacity(0.05)
: Colors.grey[100],
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(16.r),
), ),
child: Icon( child: Icon(
@ -855,7 +918,7 @@ class SettingsView extends GetView<SettingsController> {
), ),
); );
} }
// 显示语音服务选择对话框 // 显示语音服务选择对话框
void _showSpeechServiceSelectionDialog(bool isDarkMode) { void _showSpeechServiceSelectionDialog(bool isDarkMode) {
Get.dialog( Get.dialog(
@ -871,7 +934,7 @@ class SettingsView extends GetView<SettingsController> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
'选择语音服务', 'selectVoiceService'.tr, // 选择语音服务
style: TextStyle( style: TextStyle(
fontSize: 18.sp, fontSize: 18.sp,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@ -881,8 +944,9 @@ class SettingsView extends GetView<SettingsController> {
SizedBox(height: 16.h), SizedBox(height: 16.h),
...controller.availableSpeechServices.map((service) { ...controller.availableSpeechServices.map((service) {
return Obx(() { return Obx(() {
final isSelected = controller.speechServiceType.value == service['type']; final isSelected =
controller.speechServiceType.value == service['type'];
return InkWell( return InkWell(
onTap: () { onTap: () {
controller.setSpeechServiceType(service['type']); controller.setSpeechServiceType(service['type']);
@ -893,12 +957,18 @@ class SettingsView extends GetView<SettingsController> {
padding: EdgeInsets.all(12.w), padding: EdgeInsets.all(12.w),
decoration: BoxDecoration( decoration: BoxDecoration(
color: isSelected color: isSelected
? (isDarkMode ? Colors.teal[900]!.withOpacity(0.3) : Colors.teal[50]!) ? (isDarkMode
: (isDarkMode ? Colors.white.withOpacity(0.05) : Colors.grey[100]!), ? Colors.teal[900]!.withOpacity(0.3)
: Colors.teal[50]!)
: (isDarkMode
? Colors.white.withOpacity(0.05)
: Colors.grey[100]!),
borderRadius: BorderRadius.circular(12.r), borderRadius: BorderRadius.circular(12.r),
border: Border.all( border: Border.all(
color: isSelected color: isSelected
? (isDarkMode ? Colors.teal[300]! : Colors.teal[600]!) ? (isDarkMode
? Colors.teal[300]!
: Colors.teal[600]!)
: Colors.transparent, : Colors.transparent,
width: 1, width: 1,
), ),
@ -910,8 +980,12 @@ class SettingsView extends GetView<SettingsController> {
height: 24.w, height: 24.w,
decoration: BoxDecoration( decoration: BoxDecoration(
color: isSelected color: isSelected
? (isDarkMode ? Colors.teal[300] : Colors.teal[600]) ? (isDarkMode
: (isDarkMode ? Colors.white.withOpacity(0.1) : Colors.grey[300]), ? Colors.teal[300]
: Colors.teal[600])
: (isDarkMode
? Colors.white.withOpacity(0.1)
: Colors.grey[300]),
shape: BoxShape.circle, shape: BoxShape.circle,
), ),
child: isSelected child: isSelected
@ -928,20 +1002,24 @@ class SettingsView extends GetView<SettingsController> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
service['name'], '${service['name']}'.tr,
style: TextStyle( style: TextStyle(
fontSize: 16.sp, fontSize: 16.sp,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: isDarkMode ? Colors.white : Colors.black87, color: isDarkMode
? Colors.white
: Colors.black87,
), ),
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
SizedBox(height: 4.h), SizedBox(height: 4.h),
Text( Text(
service['description'], '${service['description']}'.tr,
style: TextStyle( style: TextStyle(
fontSize: 12.sp, fontSize: 12.sp,
color: isDarkMode ? Colors.white70 : Colors.black54, color: isDarkMode
? Colors.white70
: Colors.black54,
), ),
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
maxLines: 2, maxLines: 2,
@ -962,7 +1040,7 @@ class SettingsView extends GetView<SettingsController> {
TextButton( TextButton(
onPressed: () => Get.back(), onPressed: () => Get.back(),
child: Text( child: Text(
'取消', 'cancel'.tr, // 取消
style: TextStyle( style: TextStyle(
color: isDarkMode ? Colors.white70 : Colors.black54, color: isDarkMode ? Colors.white70 : Colors.black54,
), ),
@ -976,4 +1054,86 @@ class SettingsView extends GetView<SettingsController> {
), ),
); );
} }
}
// 显示语言选择对话框
void _showLanguageSelectionDialog(bool isDarkMode) {
Get.dialog(
Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24.r),
),
insetPadding: EdgeInsets.symmetric(horizontal: 20.w),
backgroundColor: isDarkMode ? AppColors.cardBackground : Colors.white,
child: Container(
width: 0.9.sw,
padding: EdgeInsets.only(top: 32.h, bottom: 16.h),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.symmetric(horizontal: 24.w),
child: Text(
'selectLanguage'.tr, // 选择语言
style: TextStyle(
fontSize: 20.sp,
fontWeight: FontWeight.bold,
color: isDarkMode ? Colors.white : Colors.black,
),
),
),
SizedBox(height: 16.h),
Container(
constraints: BoxConstraints(maxHeight: 0.5.sh),
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: controller.languageList.map((language) {
return ListTile(
title: Text(
language.name.tr,
style: TextStyle(
color: isDarkMode ? Colors.white : Colors.black,
),
),
trailing:
controller.language.value.value == language.value
? Icon(
Icons.check_circle,
color: isDarkMode
? AppColors.primary
: Colors.blue,
)
: const SizedBox.shrink(),
onTap: () {
controller.setLanguage(language);
},
);
}).toList(),
),
),
),
SizedBox(height: 16.h),
Align(
alignment: Alignment.centerRight,
child: Padding(
padding: EdgeInsets.only(right: 24.w),
child: TextButton(
onPressed: () => Get.back(),
child: Text(
'cancel'.tr, // 取消
style: TextStyle(
color: isDarkMode ? AppColors.primary : Colors.blue,
fontSize: 16.sp,
),
),
),
),
),
],
),
),
),
);
}
}

30
lib/modules/splash/controllers/splash_controller.dart

@ -8,31 +8,31 @@ class SplashController extends GetxController {
// 进度条动画控制 // 进度条动画控制
final _loadingProgress = 0.0.obs; final _loadingProgress = 0.0.obs;
double get loadingProgress => _loadingProgress.value; double get loadingProgress => _loadingProgress.value;
// 版本号 // 版本号
final String version = 'v1.0.0'; final String version = 'v1.0.0';
// 资源加载状态 // 资源加载状态
final _isResourceLoaded = false.obs; final _isResourceLoaded = false.obs;
bool get isResourceLoaded => _isResourceLoaded.value; bool get isResourceLoaded => _isResourceLoaded.value;
// 主题服务 // 主题服务
final ThemeService _themeService = ThemeService(); final ThemeService _themeService = ThemeService();
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
// 确保页面先渲染,再执行其他操作 // 确保页面先渲染,再执行其他操作
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
// 启动进度条动画 // 启动进度条动画
_startLoadingAnimation(); _startLoadingAnimation();
// 异步加载应用资源 // 异步加载应用资源
_loadAppResources(); _loadAppResources();
}); });
} }
// 进度条动画 // 进度条动画
void _startLoadingAnimation() { void _startLoadingAnimation() {
// 模拟0.5秒的加载进度 // 模拟0.5秒的加载进度
@ -48,21 +48,21 @@ class SplashController extends GetxController {
} }
}); });
} }
// 异步加载应用资源 // 异步加载应用资源
Future<void> _loadAppResources() async { Future<void> _loadAppResources() async {
try { try {
// 在这里可以添加应用初始化需要的资源加载 // 在这里可以添加应用初始化需要的资源加载
// 例如:预加载图片、初始化数据库、加载配置等 // 例如:预加载图片、初始化数据库、加载配置等
// 模拟耗时操作 // 模拟耗时操作
await Future.delayed(const Duration(milliseconds: 100)); await Future.delayed(const Duration(milliseconds: 100));
// TODO: 在此处添加实际的资源加载代码 // TODO: 在此处添加实际的资源加载代码
// 标记资源加载完成 // 标记资源加载完成
_isResourceLoaded.value = true; _isResourceLoaded.value = true;
// 如果进度条已经走完,则跳转到登录页面 // 如果进度条已经走完,则跳转到登录页面
if (_loadingProgress.value >= 1.0) { if (_loadingProgress.value >= 1.0) {
_navigateToLogin(); _navigateToLogin();
@ -71,12 +71,12 @@ class SplashController extends GetxController {
// 处理资源加载错误 // 处理资源加载错误
// 即使加载失败,也应该允许用户继续使用应用 // 即使加载失败,也应该允许用户继续使用应用
_isResourceLoaded.value = true; _isResourceLoaded.value = true;
// 记录错误日志 // 记录错误日志
print('资源加载错误: $e'); print('资源加载错误: $e');
} }
} }
// 跳转到登录页面 // 跳转到登录页面
void _navigateToLogin() { void _navigateToLogin() {
// 添加一个小延迟,确保UI完全渲染 // 添加一个小延迟,确保UI完全渲染
@ -84,4 +84,4 @@ class SplashController extends GetxController {
Get.offAllNamed(Routes.login); Get.offAllNamed(Routes.login);
}); });
} }
} }

20
lib/modules/splash/views/splash_view.dart

@ -9,7 +9,7 @@ class SplashView extends GetView<SplashController> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isDarkMode = Theme.of(context).brightness == Brightness.dark; final isDarkMode = Theme.of(context).brightness == Brightness.dark;
return Scaffold( return Scaffold(
backgroundColor: isDarkMode ? Colors.black : const Color(0xFFF5F5F7), backgroundColor: isDarkMode ? Colors.black : const Color(0xFFF5F5F7),
body: Stack( body: Stack(
@ -21,24 +21,24 @@ class SplashView extends GetView<SplashController> {
children: [ children: [
// Logo动画 // Logo动画
_buildAnimatedLogo(isDarkMode), _buildAnimatedLogo(isDarkMode),
// 应用名称 // 应用名称
_buildAppTitle(isDarkMode), _buildAppTitle(isDarkMode),
// 标语 // 标语
_buildSlogan(isDarkMode), _buildSlogan(isDarkMode),
// 加载进度条 // 加载进度条
SizedBox(height: 40.h), SizedBox(height: 40.h),
_buildLoadingBar(isDarkMode), _buildLoadingBar(isDarkMode),
// 额外信息 // 额外信息
SizedBox(height: 16.h), SizedBox(height: 16.h),
_buildExtraInfo(isDarkMode), _buildExtraInfo(isDarkMode),
], ],
), ),
), ),
// 版本号 // 版本号
_buildVersionInfo(isDarkMode), _buildVersionInfo(isDarkMode),
], ],
@ -55,7 +55,7 @@ class SplashView extends GetView<SplashController> {
builder: (context, value, child) { builder: (context, value, child) {
// 确保opacity值不超过1.0 // 确保opacity值不超过1.0
final opacity = value <= 1.0 ? value : 1.0; final opacity = value <= 1.0 ? value : 1.0;
return Transform.scale( return Transform.scale(
scale: value, scale: value,
child: AnimatedOpacity( child: AnimatedOpacity(
@ -130,7 +130,7 @@ class SplashView extends GetView<SplashController> {
child: Padding( child: Padding(
padding: EdgeInsets.only(top: 8.h), padding: EdgeInsets.only(top: 8.h),
child: Text( child: Text(
'连接世界,岂止声音', 'slogan'.tr, // 连接世界,岂止声音
style: TextStyle( style: TextStyle(
fontSize: 14.sp, fontSize: 14.sp,
color: isDarkMode ? Colors.grey[400] : Colors.grey[600], color: isDarkMode ? Colors.grey[400] : Colors.grey[600],
@ -189,7 +189,7 @@ class SplashView extends GetView<SplashController> {
return Opacity( return Opacity(
opacity: value, opacity: value,
child: Text( child: Text(
'AI语音专家 · 随时随地', 'sloganInfo'.tr, // AI语音专家 · 随时随地
style: TextStyle( style: TextStyle(
fontSize: 12.sp, fontSize: 12.sp,
color: isDarkMode ? Colors.grey[500] : Colors.grey[400], color: isDarkMode ? Colors.grey[500] : Colors.grey[400],
@ -217,4 +217,4 @@ class SplashView extends GetView<SplashController> {
), ),
); );
} }
} }

164
lib/modules/translation/views/translation_view.dart

@ -13,7 +13,7 @@ class TranslationView extends GetView<TranslationController> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isDarkMode = Theme.of(context).brightness == Brightness.dark; final isDarkMode = Theme.of(context).brightness == Brightness.dark;
return Scaffold( return Scaffold(
backgroundColor: isDarkMode ? AppColors.darkBackground : Colors.grey[50], backgroundColor: isDarkMode ? AppColors.darkBackground : Colors.grey[50],
appBar: _buildAppBar(isDarkMode), appBar: _buildAppBar(isDarkMode),
@ -47,22 +47,25 @@ class TranslationView extends GetView<TranslationController> {
child: Container( child: Container(
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h), padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h),
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDarkMode ? Colors.white.withOpacity(0.1) : Colors.grey[100], color: isDarkMode
? Colors.white.withOpacity(0.1)
: Colors.grey[100],
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(16.r),
), ),
child: Obx(() => Text( child: Obx(() => Text(
controller.sourceLanguage.value.split(' ')[0], // Just show the Chinese part controller.sourceLanguage.value
style: TextStyle( .split(' ')[0], // Just show the Chinese part
fontSize: 16.sp, style: TextStyle(
fontWeight: FontWeight.w600, fontSize: 16.sp,
color: isDarkMode ? Colors.white : Colors.black, fontWeight: FontWeight.w600,
), color: isDarkMode ? Colors.white : Colors.black,
overflow: TextOverflow.ellipsis, ),
)), overflow: TextOverflow.ellipsis,
)),
), ),
), ),
), ),
// 语言互换按钮 // 语言互换按钮
GestureDetector( GestureDetector(
onTap: controller.swapLanguages, onTap: controller.swapLanguages,
@ -70,7 +73,9 @@ class TranslationView extends GetView<TranslationController> {
margin: EdgeInsets.symmetric(horizontal: 8.w), margin: EdgeInsets.symmetric(horizontal: 8.w),
padding: EdgeInsets.all(4.r), padding: EdgeInsets.all(4.r),
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDarkMode ? AppColors.primary.withOpacity(0.2) : Colors.blue[50], color: isDarkMode
? AppColors.primary.withOpacity(0.2)
: Colors.blue[50],
shape: BoxShape.circle, shape: BoxShape.circle,
), ),
child: Icon( child: Icon(
@ -80,7 +85,7 @@ class TranslationView extends GetView<TranslationController> {
), ),
), ),
), ),
// 目标语言选择 // 目标语言选择
Flexible( Flexible(
child: GestureDetector( child: GestureDetector(
@ -88,18 +93,21 @@ class TranslationView extends GetView<TranslationController> {
child: Container( child: Container(
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h), padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h),
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDarkMode ? Colors.white.withOpacity(0.1) : Colors.grey[100], color: isDarkMode
? Colors.white.withOpacity(0.1)
: Colors.grey[100],
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(16.r),
), ),
child: Obx(() => Text( child: Obx(() => Text(
controller.targetLanguage.value.split(' ')[0], // Just show the English part controller.targetLanguage.value
style: TextStyle( .split(' ')[0], // Just show the English part
fontSize: 16.sp, style: TextStyle(
fontWeight: FontWeight.w600, fontSize: 16.sp,
color: isDarkMode ? Colors.white : Colors.black, fontWeight: FontWeight.w600,
), color: isDarkMode ? Colors.white : Colors.black,
overflow: TextOverflow.ellipsis, ),
)), overflow: TextOverflow.ellipsis,
)),
), ),
), ),
), ),
@ -109,15 +117,17 @@ class TranslationView extends GetView<TranslationController> {
actions: [ actions: [
// TTS toggle button // TTS toggle button
Obx(() => IconButton( Obx(() => IconButton(
icon: Icon( icon: Icon(
controller.isTtsEnabled.value ? Icons.volume_up : Icons.volume_off, controller.isTtsEnabled.value
color: isDarkMode ? Colors.white : Colors.black, ? Icons.volume_up
size: 24.sp, : Icons.volume_off,
), color: isDarkMode ? Colors.white : Colors.black,
onPressed: controller.toggleTts, size: 24.sp,
tooltip: '自动播放翻译结果', ),
)), onPressed: controller.toggleTts,
tooltip: '自动播放翻译结果',
)),
// 历史记录按钮 // 历史记录按钮
IconButton( IconButton(
icon: Icon( icon: Icon(
@ -129,10 +139,10 @@ class TranslationView extends GetView<TranslationController> {
// 停止语音识别和TTS,释放资源 // 停止语音识别和TTS,释放资源
await controller.stopAll(); await controller.stopAll();
await Get.to( await Get.to(
() => const TranslationHistoryView(), () => const TranslationHistoryView(),
binding: TranslationHistoryBinding(), binding: TranslationHistoryBinding(),
); );
// 返回时不自动开始翻译,只需重新设置滚动监听器 // 返回时不自动开始翻译,只需重新设置滚动监听器
controller.setupScrollListener(); controller.setupScrollListener();
}, },
@ -156,22 +166,19 @@ class TranslationView extends GetView<TranslationController> {
Widget _buildTranslationContent(bool isDarkMode) { Widget _buildTranslationContent(bool isDarkMode) {
return Obx(() { return Obx(() {
final itemCount = controller.translationHistory.length; final itemCount = controller.translationHistory.length;
// 使用 currentItem 来判断是否显示当前项 // 使用 currentItem 来判断是否显示当前项
final hasCurrentItem = controller.currentItem.value != null && final hasCurrentItem = controller.currentItem.value != null &&
controller.currentItem.value!.sourceText.isNotEmpty; // 只有当有实际内容时才显示当前项 controller
.currentItem.value!.sourceText.isNotEmpty; // 只有当有实际内容时才显示当前项
// 计算底部内边距,确保内容不会被底部框遮住 // 计算底部内边距,确保内容不会被底部框遮住
final bottomPadding = controller.bottomBarHeight + 20.h; // 使用控制栏高度加上额外间距 final bottomPadding = controller.bottomBarHeight + 20.h; // 使用控制栏高度加上额外间距
return ListView.builder( return ListView.builder(
controller: controller.scrollController, controller: controller.scrollController,
padding: EdgeInsets.only( padding: EdgeInsets.only(
left: 16.w, left: 16.w, right: 16.w, top: 16.h, bottom: bottomPadding),
right: 16.w,
top: 16.h,
bottom: bottomPadding
),
itemCount: itemCount + (hasCurrentItem ? 1 : 0), itemCount: itemCount + (hasCurrentItem ? 1 : 0),
itemBuilder: (context, index) { itemBuilder: (context, index) {
// Current item (being translated) // Current item (being translated)
@ -184,7 +191,7 @@ class TranslationView extends GetView<TranslationController> {
isDarkMode: isDarkMode, isDarkMode: isDarkMode,
); );
} }
// History items // History items
final historyItem = controller.translationHistory[index]; final historyItem = controller.translationHistory[index];
return _buildTranslationItemWidget( return _buildTranslationItemWidget(
@ -201,15 +208,13 @@ class TranslationView extends GetView<TranslationController> {
} }
Widget _buildTranslationItemWidget( Widget _buildTranslationItemWidget(
String sourceText, String sourceText,
String translatedText, String translatedText,
bool isCurrent, bool isCurrent, {
{ TranslationItem? item,
TranslationItem? item, bool isIntermediate = false,
bool isIntermediate = false, required bool isDarkMode,
required bool isDarkMode, }) {
}
) {
// 使用AnimatedContainer实现平滑过渡,但恢复原来的视觉样式 // 使用AnimatedContainer实现平滑过渡,但恢复原来的视觉样式
return AnimatedContainer( return AnimatedContainer(
duration: const Duration(milliseconds: 300), duration: const Duration(milliseconds: 300),
@ -231,17 +236,20 @@ class TranslationView extends GetView<TranslationController> {
sourceText.isEmpty && isCurrent ? '等待语音输入...' : sourceText, sourceText.isEmpty && isCurrent ? '等待语音输入...' : sourceText,
style: TextStyle( style: TextStyle(
fontSize: 16.sp, fontSize: 16.sp,
color: sourceText.isEmpty && isCurrent color: sourceText.isEmpty && isCurrent
? (isDarkMode ? Colors.grey[400] : Colors.grey) ? (isDarkMode ? Colors.grey[400] : Colors.grey)
: (isDarkMode ? Colors.white : Colors.black), : (isDarkMode ? Colors.white : Colors.black),
fontWeight: FontWeight.normal, fontWeight: FontWeight.normal,
), ),
), ),
), ),
// Divider // Divider
Divider(height: 1, thickness: 1, color: isDarkMode ? Colors.grey[800] : Colors.grey[200]), Divider(
height: 1,
thickness: 1,
color: isDarkMode ? Colors.grey[800] : Colors.grey[200]),
// Translated text // Translated text
AnimatedContainer( AnimatedContainer(
duration: const Duration(milliseconds: 300), duration: const Duration(milliseconds: 300),
@ -252,11 +260,13 @@ class TranslationView extends GetView<TranslationController> {
children: [ children: [
Expanded( Expanded(
child: Text( child: Text(
translatedText.isEmpty && isCurrent ? '翻译结果将显示在这里...' : translatedText, translatedText.isEmpty && isCurrent
? '翻译结果将显示在这里...'
: translatedText,
style: TextStyle( style: TextStyle(
fontSize: 16.sp, fontSize: 16.sp,
color: translatedText.isEmpty && isCurrent color: translatedText.isEmpty && isCurrent
? (isDarkMode ? Colors.grey[400] : Colors.grey) ? (isDarkMode ? Colors.grey[400] : Colors.grey)
: (isDarkMode ? Colors.white : Colors.black), : (isDarkMode ? Colors.white : Colors.black),
fontStyle: FontStyle.normal, fontStyle: FontStyle.normal,
), ),
@ -288,16 +298,17 @@ class TranslationView extends GetView<TranslationController> {
Widget _buildControlBar(bool isDarkMode) { Widget _buildControlBar(bool isDarkMode) {
// 使用GlobalKey来获取控制栏的高度 // 使用GlobalKey来获取控制栏的高度
final controlBarKey = GlobalKey(); final controlBarKey = GlobalKey();
// 在构建完成后获取控制栏的高度并设置到控制器中 // 在构建完成后获取控制栏的高度并设置到控制器中
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
if (controlBarKey.currentContext != null) { if (controlBarKey.currentContext != null) {
final RenderBox renderBox = controlBarKey.currentContext!.findRenderObject() as RenderBox; final RenderBox renderBox =
controlBarKey.currentContext!.findRenderObject() as RenderBox;
final height = renderBox.size.height; final height = renderBox.size.height;
controller.setBottomBarHeight(height); controller.setBottomBarHeight(height);
} }
}); });
return Container( return Container(
key: controlBarKey, key: controlBarKey,
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 16.h), padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 16.h),
@ -314,8 +325,6 @@ class TranslationView extends GetView<TranslationController> {
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
// 主要控制按钮 - 开始/暂停语音识别 // 主要控制按钮 - 开始/暂停语音识别
Obx(() { Obx(() {
final isRecognizing = controller.isRecognizing.value; final isRecognizing = controller.isRecognizing.value;
@ -331,8 +340,8 @@ class TranslationView extends GetView<TranslationController> {
width: 60.w, width: 60.w,
height: 60.w, height: 60.w,
decoration: BoxDecoration( decoration: BoxDecoration(
color: isRecognizing color: isRecognizing
? (isDarkMode ? Colors.red[700] : Colors.red) ? (isDarkMode ? Colors.red[700] : Colors.red)
: (isDarkMode ? AppColors.primary : Colors.blue), : (isDarkMode ? AppColors.primary : Colors.blue),
shape: BoxShape.circle, shape: BoxShape.circle,
), ),
@ -368,7 +377,9 @@ class TranslationView extends GetView<TranslationController> {
Padding( Padding(
padding: EdgeInsets.symmetric(horizontal: 24.w), padding: EdgeInsets.symmetric(horizontal: 24.w),
child: Text( child: Text(
isSource ? '选择源语言' : '选择目标语言', isSource
? 'selectSourceLanguage'.tr // 选择源语言
: 'selectTargetLanguage'.tr, // 选择目标语言
style: TextStyle( style: TextStyle(
fontSize: 20.sp, fontSize: 20.sp,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
@ -382,7 +393,8 @@ class TranslationView extends GetView<TranslationController> {
child: SingleChildScrollView( child: SingleChildScrollView(
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: controller.supportedLanguages.keys.map((language) { children:
controller.supportedLanguages.keys.map((language) {
return ListTile( return ListTile(
title: Text( title: Text(
language, language,
@ -391,13 +403,15 @@ class TranslationView extends GetView<TranslationController> {
), ),
), ),
trailing: Obx(() { trailing: Obx(() {
final currentLanguage = isSource final currentLanguage = isSource
? controller.sourceLanguage.value ? controller.sourceLanguage.value
: controller.targetLanguage.value; : controller.targetLanguage.value;
return currentLanguage == language return currentLanguage == language
? Icon( ? Icon(
Icons.check_circle, Icons.check_circle,
color: isDarkMode ? AppColors.primary : Colors.blue, color: isDarkMode
? AppColors.primary
: Colors.blue,
) )
: const SizedBox.shrink(); : const SizedBox.shrink();
}), }),
@ -422,7 +436,7 @@ class TranslationView extends GetView<TranslationController> {
child: TextButton( child: TextButton(
onPressed: () => Get.back(), onPressed: () => Get.back(),
child: Text( child: Text(
'取消', 'cancel'.tr, // 取消
style: TextStyle( style: TextStyle(
color: isDarkMode ? AppColors.primary : Colors.blue, color: isDarkMode ? AppColors.primary : Colors.blue,
fontSize: 16.sp, fontSize: 16.sp,
@ -437,4 +451,4 @@ class TranslationView extends GetView<TranslationController> {
), ),
); );
} }
} }

2
pubspec.yaml

@ -9,6 +9,8 @@ environment:
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
flutter_localizations:
sdk: flutter
cupertino_icons: ^1.0.6 cupertino_icons: ^1.0.6
get: ^4.6.6 get: ^4.6.6
get_storage: ^2.1.1 get_storage: ^2.1.1

Loading…
Cancel
Save