From 27a79d28ca5807be528b4480437641cd8a6076c7 Mon Sep 17 00:00:00 2001 From: tanlongsheng <252620078@qq.com> Date: Thu, 20 Mar 2025 11:16:46 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=A4=9A=E8=AF=AD=E8=A8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/core/translations/app_translations.dart | 102 ++--- lib/core/translations/language/en_us.dart | 155 +++++++ lib/core/translations/language/zh_cn.dart | 150 +++++++ lib/core/widgets/common_bottom_nav.dart | 19 +- lib/main.dart | 26 +- lib/modules/chat/views/chat_view.dart | 207 +++++---- .../controllers/explore_controller.dart | 80 ++-- lib/modules/explore/views/explore_view.dart | 60 +-- .../home/controllers/home_controller.dart | 115 ++--- lib/modules/home/views/home_view.dart | 414 ++++++++++-------- lib/modules/login/views/login_view.dart | 299 +++++++------ .../controllers/pairing_controller.dart | 118 +++-- lib/modules/pairing/views/pairing_view.dart | 61 +-- .../controllers/settings_controller.dart | 97 +++- .../settings/models/language_model.dart | 11 + lib/modules/settings/views/settings_view.dart | 398 ++++++++++++----- .../splash/controllers/splash_controller.dart | 30 +- lib/modules/splash/views/splash_view.dart | 20 +- .../translation/views/translation_view.dart | 164 +++---- pubspec.yaml | 2 + 20 files changed, 1554 insertions(+), 974 deletions(-) create mode 100644 lib/core/translations/language/en_us.dart create mode 100644 lib/core/translations/language/zh_cn.dart create mode 100644 lib/modules/settings/models/language_model.dart diff --git a/lib/core/translations/app_translations.dart b/lib/core/translations/app_translations.dart index 71bf8b7a0..39ad9e62c 100644 --- a/lib/core/translations/app_translations.dart +++ b/lib/core/translations/app_translations.dart @@ -1,81 +1,29 @@ +import 'dart:ui'; + 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 { + 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 - Map> get keys => { - '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', - }, - }; -} \ No newline at end of file + Map> get keys => {'en_US': enUS, 'zh_CN': zhCN}; +} diff --git a/lib/core/translations/language/en_us.dart b/lib/core/translations/language/en_us.dart new file mode 100644 index 000000000..1df8d03eb --- /dev/null +++ b/lib/core/translations/language/en_us.dart @@ -0,0 +1,155 @@ +const Map 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", +}; diff --git a/lib/core/translations/language/zh_cn.dart b/lib/core/translations/language/zh_cn.dart new file mode 100644 index 000000000..657a6880c --- /dev/null +++ b/lib/core/translations/language/zh_cn.dart @@ -0,0 +1,150 @@ +const Map 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": "选择目标语言", +}; diff --git a/lib/core/widgets/common_bottom_nav.dart b/lib/core/widgets/common_bottom_nav.dart index 9f8c2ec08..4a7e0dbad 100644 --- a/lib/core/widgets/common_bottom_nav.dart +++ b/lib/core/widgets/common_bottom_nav.dart @@ -4,7 +4,7 @@ import '../../routes/app_routes.dart'; class CommonBottomNav extends StatelessWidget { final int currentIndex; - + const CommonBottomNav({ Key? key, required this.currentIndex, @@ -13,7 +13,7 @@ class CommonBottomNav extends StatelessWidget { @override Widget build(BuildContext context) { final isDarkMode = Theme.of(context).brightness == Brightness.dark; - + return BottomNavigationBar( currentIndex: currentIndex, onTap: (index) { @@ -30,25 +30,28 @@ class CommonBottomNav extends StatelessWidget { break; } }, - backgroundColor: isDarkMode ? Colors.black : const Color(0xFFF9FAFB), // 暗黑模式黑色背景,浅色模式浅灰色背景 + backgroundColor: isDarkMode + ? Colors.black + : const Color(0xFFF9FAFB), // 暗黑模式黑色背景,浅色模式浅灰色背景 selectedItemColor: const Color(0xFF3B82F6), // 蓝色选中项保持不变 - unselectedItemColor: isDarkMode ? Colors.grey[500] : const Color(0xFF6B7280), // 暗黑模式灰色调整 + unselectedItemColor: + isDarkMode ? Colors.grey[500] : const Color(0xFF6B7280), // 暗黑模式灰色调整 elevation: isDarkMode ? 0 : 8, // 暗黑模式无阴影,浅色模式保持阴影 type: BottomNavigationBarType.fixed, items: [ BottomNavigationBarItem( icon: const Icon(Icons.home), - label: 'home'.tr, + label: 'home'.tr, // 首页 ), BottomNavigationBarItem( icon: const Icon(Icons.explore), - label: 'explore'.tr, + label: 'explore'.tr, // 发现 ), BottomNavigationBarItem( icon: const Icon(Icons.settings), - label: 'settings'.tr, + label: 'settings'.tr, // 设置 ), ], ); } -} \ No newline at end of file +} diff --git a/lib/main.dart b/lib/main.dart index e374b1554..135794747 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:get/get.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:get_storage/get_storage.dart'; @@ -15,22 +16,22 @@ import 'core/themes/theme_service.dart'; void main() async { // 确保Flutter绑定初始化 WidgetsFlutterBinding.ensureInitialized(); - + // 初始化存储 - 在创建应用之前初始化 await GetStorage.init(); - + // 初始化环境变量 await dotenv.load(); - + // 设置窗口大小(仅在 macOS 下生效) if (Platform.isMacOS) { const double width = 375.0; const double height = 812.0; - + setWindowTitle('Deep Voice'); setWindowMinSize(const Size(width, height)); setWindowMaxSize(const Size(width, height)); - + // 居中显示窗口 final screen = await getCurrentScreen(); if (screen != null) { @@ -43,7 +44,7 @@ void main() async { setWindowFrame(windowFrame); } } - + // 运行应用 runApp(const MainApp()); } @@ -55,7 +56,7 @@ class MainApp extends StatelessWidget { Widget build(BuildContext context) { // 获取保存的主题设置 final themeMode = ThemeService().getThemeMode(); - + return ScreenUtilInit( designSize: const Size(375, 812), minTextAdapt: true, @@ -71,9 +72,14 @@ class MainApp extends StatelessWidget { getPages: AppPages.pages, debugShowCheckedModeBanner: false, translations: AppTranslations(), - locale: const Locale('zh', 'CN'), - fallbackLocale: const Locale('en', 'US'), - + localizationsDelegates: const [ + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + locale: AppTranslations.deviceLocale(), + fallbackLocale: AppTranslations.fallbackLocale, + supportedLocales: AppTranslations.supportedLocales, ); }, ); diff --git a/lib/modules/chat/views/chat_view.dart b/lib/modules/chat/views/chat_view.dart index 4abeb6d55..cddb49257 100644 --- a/lib/modules/chat/views/chat_view.dart +++ b/lib/modules/chat/views/chat_view.dart @@ -14,25 +14,22 @@ class ChatView extends GetView { @override Widget build(BuildContext context) { final isDarkMode = Theme.of(context).brightness == Brightness.dark; - + return Scaffold( backgroundColor: isDarkMode ? AppColors.darkBackground : Colors.grey[50], appBar: _buildAppBar(isDarkMode), body: _buildBody(isDarkMode), ); } - + // 构建AppBar PreferredSizeWidget _buildAppBar(bool isDarkMode) { return AppBar( elevation: 1, backgroundColor: isDarkMode ? AppColors.cardBackground : Colors.white, leading: IconButton( - icon: Icon( - Icons.arrow_back_ios, - color: isDarkMode ? Colors.white : Colors.black87, - size: 20.r - ), + icon: Icon(Icons.arrow_back_ios, + color: isDarkMode ? Colors.white : Colors.black87, size: 20.r), onPressed: () => Get.back(), ), titleSpacing: 0, @@ -101,7 +98,7 @@ class ChatView extends GetView { ), SizedBox(width: 4.w), Text( - '在线', + 'online'.tr, // 在线 style: TextStyle( fontSize: 12.sp, color: Colors.green, @@ -123,7 +120,9 @@ class ChatView extends GetView { : Icons.volume_off, color: controller.isTtsEnabled.value ? AppColors.primary - : isDarkMode ? Colors.grey[400] : Colors.grey, + : isDarkMode + ? Colors.grey[400] + : Colors.grey, ), onPressed: controller.toggleTts, )), @@ -133,7 +132,8 @@ class ChatView extends GetView { height: 34.r, margin: EdgeInsets.only(right: 8.w), 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, boxShadow: [ if (!isDarkMode) @@ -145,11 +145,8 @@ class ChatView extends GetView { ], ), child: PopupMenuButton( - icon: Icon( - Icons.more_vert, - color: isDarkMode ? Colors.white : Colors.black87, - size: 20.r - ), + icon: Icon(Icons.more_vert, + color: isDarkMode ? Colors.white : Colors.black87, size: 20.r), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12.r), ), @@ -162,7 +159,7 @@ class ChatView extends GetView { Icon(Icons.delete_outline, color: Colors.red), SizedBox(width: 8.w), Text( - '清空聊天记录', + 'clearChatHistory'.tr, // 清空聊天记录 style: TextStyle( color: isDarkMode ? Colors.white : Colors.black87, ), @@ -183,7 +180,7 @@ class ChatView extends GetView { ], ); } - + // 构建主体内容 Widget _buildBody(bool isDarkMode) { return Column( @@ -198,13 +195,13 @@ class ChatView extends GetView { } }), ), - + // 底部输入区域 _buildInputArea(isDarkMode), ], ); } - + // 构建空状态 Widget _buildEmptyState(bool isDarkMode) { return Center( @@ -215,13 +212,15 @@ class ChatView extends GetView { width: 80.w, height: 80.w, 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, ), child: Icon( Icons.chat_bubble_outline, 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), @@ -250,7 +249,7 @@ class ChatView extends GetView { ), ); } - + // 构建聊天列表 Widget _buildChatList(bool isDarkMode) { return ListView.builder( @@ -266,32 +265,34 @@ class ChatView extends GetView { }, ); } - + // 构建消息项 Widget _buildMessageItem(Message message, bool isDarkMode) { final isUser = message.role == 'user'; - + return Padding( padding: EdgeInsets.only(bottom: 16.h), child: Row( - mainAxisAlignment: isUser ? MainAxisAlignment.end : MainAxisAlignment.start, + mainAxisAlignment: + isUser ? MainAxisAlignment.end : MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ if (!isUser) _buildAvatar(false, isDarkMode), - SizedBox(width: 8.w), - Flexible( child: Column( - crossAxisAlignment: isUser ? CrossAxisAlignment.end : CrossAxisAlignment.start, + crossAxisAlignment: + isUser ? CrossAxisAlignment.end : CrossAxisAlignment.start, children: [ // 消息气泡 Container( padding: EdgeInsets.all(12.r), decoration: BoxDecoration( - color: isUser - ? AppColors.primary - : isDarkMode ? AppColors.cardBackground : Colors.grey[200], + color: isUser + ? AppColors.primary + : isDarkMode + ? AppColors.cardBackground + : Colors.grey[200], borderRadius: BorderRadius.circular(16.r), ), child: Column( @@ -302,14 +303,16 @@ class ChatView extends GetView { SelectableText( message.content, style: TextStyle( - color: isUser - ? Colors.white - : isDarkMode ? Colors.white : Colors.black, + color: isUser + ? Colors.white + : isDarkMode + ? Colors.white + : Colors.black, fontSize: 16.sp, height: 1.5, ), ), - + // 加载中动画 if (message.isLoading) Padding( @@ -325,7 +328,7 @@ class ChatView extends GetView { ], ), ), - + // 时间戳 Padding( padding: EdgeInsets.only(top: 4.h, left: 4.w, right: 4.w), @@ -340,9 +343,7 @@ class ChatView extends GetView { ], ), ), - SizedBox(width: 8.w), - if (isUser) _buildAvatar(true, isDarkMode), ], ), @@ -351,8 +352,9 @@ class ChatView extends GetView { // 构建输入区域 Widget _buildInputArea(bool isDarkMode) { - final textController = TextEditingController(text: controller.inputText.value); - + final textController = + TextEditingController(text: controller.inputText.value); + // 检查是否处于语音识别模式 return Obx(() { if (controller.isVoiceRecognitionActive.value) { @@ -362,9 +364,10 @@ class ChatView extends GetView { } }); } - + // 构建文本输入面板 - Widget _buildTextInputPanel(bool isDarkMode, TextEditingController textController) { + Widget _buildTextInputPanel( + bool isDarkMode, TextEditingController textController) { return Container( padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h), decoration: BoxDecoration( @@ -385,7 +388,9 @@ class ChatView extends GetView { width: 40.w, height: 40.w, 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, ), child: IconButton( @@ -398,20 +403,21 @@ class ChatView extends GetView { onPressed: controller.startVoiceRecognition, ), ), - + SizedBox(width: 8.w), - + // 输入框 Expanded( child: TextField( controller: textController, decoration: InputDecoration( - hintText: '输入消息...', + hintText: 'inputMessage'.tr, // 输入消息... hintStyle: TextStyle( - color: isDarkMode ? Colors.grey[500] : Colors.grey[500] - ), + color: isDarkMode ? Colors.grey[500] : Colors.grey[500]), filled: true, - fillColor: isDarkMode ? Colors.white.withOpacity(0.1) : Colors.grey[100], + fillColor: isDarkMode + ? Colors.white.withOpacity(0.1) + : Colors.grey[100], border: OutlineInputBorder( borderRadius: BorderRadius.circular(20.r), borderSide: BorderSide.none, @@ -435,9 +441,9 @@ class ChatView extends GetView { }, ), ), - + SizedBox(width: 8.w), - + // 发送按钮 Obx(() { final hasText = controller.inputText.value.isNotEmpty; @@ -445,17 +451,21 @@ class ChatView extends GetView { width: 40.w, height: 40.w, decoration: BoxDecoration( - color: hasText - ? AppColors.primary - : isDarkMode ? Colors.white.withOpacity(0.1) : Colors.grey[200], + color: hasText + ? AppColors.primary + : isDarkMode + ? Colors.white.withOpacity(0.1) + : Colors.grey[200], shape: BoxShape.circle, ), child: IconButton( icon: Icon( Icons.send, - color: hasText - ? Colors.white - : isDarkMode ? Colors.white.withOpacity(0.5) : Colors.grey[400], + color: hasText + ? Colors.white + : isDarkMode + ? Colors.white.withOpacity(0.5) + : Colors.grey[400], size: 18.r, ), padding: EdgeInsets.zero, @@ -473,7 +483,7 @@ class ChatView extends GetView { ), ); } - + // 构建语音输入面板 Widget _buildVoiceInputPanel(bool isDarkMode) { return Container( @@ -501,7 +511,9 @@ class ChatView extends GetView { width: 60.r, height: 60.r, decoration: BoxDecoration( - color: isDarkMode ? Colors.white.withOpacity(0.1) : Colors.white, + color: isDarkMode + ? Colors.white.withOpacity(0.1) + : Colors.white, shape: BoxShape.circle, boxShadow: [ BoxShadow( @@ -513,13 +525,17 @@ class ChatView extends GetView { ), child: Center( child: Obx(() => Icon( - controller.isListening.value ? Icons.mic : Icons.mic_none, - color: controller.isListening.value ? AppColors.primary : Colors.grey.shade400, - size: 28.r, - )), + controller.isListening.value + ? Icons.mic + : Icons.mic_none, + color: controller.isListening.value + ? AppColors.primary + : Colors.grey.shade400, + size: 28.r, + )), ), ), - + // 中间音频波形和状态文本 Expanded( child: Column( @@ -542,10 +558,10 @@ class ChatView extends GetView { 23, (index) { // 随机高度模拟波形 - double height = isSpeaking - ? (Random().nextDouble() * 20.0 + 5.0).h + double height = isSpeaking + ? (Random().nextDouble() * 20.0 + 5.0).h : 2.h; - + return SizedBox( width: 6.w, child: Center( @@ -553,12 +569,16 @@ class ChatView extends GetView { duration: Duration(milliseconds: 500), width: 2.5.w, height: height, - margin: EdgeInsets.symmetric(horizontal: 1.5.w), + margin: EdgeInsets.symmetric( + horizontal: 1.5.w), decoration: BoxDecoration( - color: isSpeaking - ? AppColors.primary - : isDarkMode ? Colors.grey.shade600 : Colors.grey.shade400, - borderRadius: BorderRadius.circular(2.r), + color: isSpeaking + ? AppColors.primary + : isDarkMode + ? Colors.grey.shade600 + : Colors.grey.shade400, + borderRadius: + BorderRadius.circular(2.r), ), ), ), @@ -571,26 +591,32 @@ class ChatView extends GetView { }), ), SizedBox(height: 8.h), - + // 语音状态文本 Obx(() => Text( - controller.isSpeechDetected.value ? '正在聆听...' : '你可以开始说话', - style: TextStyle( - fontSize: 14.sp, - color: isDarkMode ? Colors.grey[400] : Colors.grey[700], - fontWeight: FontWeight.w500, - ), - )), + controller.isSpeechDetected.value + ? 'imListening'.tr // 正在聆听... + : 'canStartTalking'.tr, // 你可以开始说话 + style: TextStyle( + fontSize: 14.sp, + color: isDarkMode + ? Colors.grey[400] + : Colors.grey[700], + fontWeight: FontWeight.w500, + ), + )), ], ), ), - + // 关闭按钮 Container( width: 60.r, height: 60.r, decoration: BoxDecoration( - color: isDarkMode ? Colors.white.withOpacity(0.1) : Colors.white, + color: isDarkMode + ? Colors.white.withOpacity(0.1) + : Colors.white, shape: BoxShape.circle, boxShadow: [ BoxShadow( @@ -619,15 +645,15 @@ class ChatView extends GetView { ), ); } - + // 构建头像 Widget _buildAvatar(bool isUser, bool isDarkMode) { return Container( width: 36.r, height: 36.r, decoration: BoxDecoration( - color: isUser - ? (isDarkMode ? Colors.white.withOpacity(0.1) : Colors.grey[200]) + color: isUser + ? (isDarkMode ? Colors.white.withOpacity(0.1) : Colors.grey[200]) : AppColors.primary, shape: BoxShape.circle, boxShadow: [ @@ -668,7 +694,7 @@ class ChatView extends GetView { ), ); } - + // 构建加载动画点 Widget _buildLoadingDot(int index, bool isDarkMode) { return AnimatedBuilder( @@ -679,21 +705,22 @@ class ChatView extends GetView { width: 8.r, height: 8.r, 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, ), ); }, ); } - + // 格式化时间 String _formatTime(DateTime time) { final now = DateTime.now(); final today = DateTime(now.year, now.month, now.day); final yesterday = today.subtract(const Duration(days: 1)); final messageDate = DateTime(time.year, time.month, time.day); - + if (messageDate == today) { return '今天 ${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}'; } else if (messageDate == yesterday) { @@ -702,4 +729,4 @@ class ChatView extends GetView { return '${time.month}月${time.day}日 ${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}'; } } -} \ No newline at end of file +} diff --git a/lib/modules/explore/controllers/explore_controller.dart b/lib/modules/explore/controllers/explore_controller.dart index a452df264..86a7fe05d 100644 --- a/lib/modules/explore/controllers/explore_controller.dart +++ b/lib/modules/explore/controllers/explore_controller.dart @@ -13,7 +13,7 @@ class AIFeature { final Color iconColor; final String agentId; final String targetRoute; - + AIFeature({ required this.title, required this.description, @@ -34,7 +34,7 @@ class FunctionScene { final Color startColor; final Color endColor; final Color iconColor; - + FunctionScene({ required this.title, required this.description, @@ -48,12 +48,12 @@ class FunctionScene { class ExploreController extends GetxController { // 搜索关键词 final searchController = TextEditingController(); - + // AI功能列表 final List aiFeatures = [ AIFeature( - title: '全能助理', - description: '帮你解决各类问题', + title: 'allRoundAssistant'.tr, // 全能助理 + description: 'allRoundAssistantInfo'.tr, // 帮你解决各类问题 icon: Icons.assistant, startColor: const Color(0xFF0066CC), endColor: const Color(0xFF5E5CE6), @@ -63,8 +63,8 @@ class ExploreController extends GetxController { targetRoute: Routes.chat, ), AIFeature( - title: '同声翻译', - description: '实时翻译80+种语言', + title: 'simultaneousTranslation'.tr, // 同声翻译 + description: 'simultaneousTranslationInfo.tr', // 实时翻译80+种语言 icon: Icons.translate, startColor: const Color(0xFF4CAF50), endColor: const Color(0xFF009688), @@ -74,8 +74,8 @@ class ExploreController extends GetxController { targetRoute: Routes.translation, ), AIFeature( - title: 'AI女友', - description: '智能陪伴,随时聊天', + title: 'aiGirlfriend'.tr, // AI女友 + description: 'aiGirlfriendInfo'.tr, // 智能陪伴,随时聊天 icon: Icons.favorite, startColor: const Color(0xFFE91E63), endColor: const Color(0xFFFF5252), @@ -85,8 +85,8 @@ class ExploreController extends GetxController { targetRoute: Routes.chat, ), AIFeature( - title: '健康顾问', - description: '提供科学健康建议', + title: 'healthConsultant'.tr, // 健康顾问 + description: 'healthConsultantInfo'.tr, // 提供科学健康建议 icon: Icons.healing, startColor: const Color(0xFFFFC107), endColor: const Color(0xFFFF9800), @@ -96,65 +96,65 @@ class ExploreController extends GetxController { targetRoute: Routes.chat, ), ]; - + // 热门功能场景列表 final List functionScenes = [ FunctionScene( - title: '商务会议助手', - description: '实时记录会议纪要,多语言翻译', + title: 'conferenceAssistant'.tr, // 商务会议助手 + description: 'conferenceAssistantInfo'.tr, // 实时记录会议纪要,多语言翻译 icon: Icons.people, startColor: const Color(0xFF3F51B5), endColor: const Color(0xFF673AB7), iconColor: const Color(0xFF3F51B5), ), FunctionScene( - title: '语言学习助手', - description: '发音纠正,实时练习口语', + title: 'studyAssistant'.tr, // 语言学习助手 + description: 'studyAssistantInfo'.tr, // 发音纠正,实时练习口语 icon: Icons.school, startColor: const Color(0xFF4CAF50), endColor: const Color(0xFF009688), iconColor: const Color(0xFF4CAF50), ), FunctionScene( - title: '旅行翻译助手', - description: '实时语音翻译,旅游景点介绍', + title: 'travelTranslator'.tr, // 旅行翻译助手 + description: 'travelTranslatorInfo'.tr, // 实时语音翻译,旅游景点介绍 icon: Icons.language, startColor: const Color(0xFFFFC107), endColor: const Color(0xFFFF9800), iconColor: const Color(0xFFFFC107), ), ]; - + // 搜索功能 void search() { if (searchController.text.isEmpty) return; - + Get.snackbar( - '搜索', - '正在搜索: ${searchController.text}', + 'search'.tr, // 搜索 + '${'beSearching'.tr} ${searchController.text}', // 正在搜索: snackPosition: SnackPosition.TOP, ); - + // 实际应用中,这里应该实现真正的搜索逻辑 searchController.clear(); } - + // 点击AI功能 void onAIFeatureTap(int index) { if (index < 0 || index >= aiFeatures.length) return; - + final feature = aiFeatures[index]; final agent = AgentProvider.getAgentById(feature.agentId); - + if (agent == null) { Get.snackbar( - '错误', - '未找到对应的AI助手', + 'error'.tr, // 错误 + 'noAssistantFound'.tr, // 未找到对应的AI助手 snackPosition: SnackPosition.TOP, ); return; } - + // 根据目标路由进行跳转 if (feature.targetRoute == Routes.chat) { // 跳转到聊天页面 @@ -172,34 +172,34 @@ class ExploreController extends GetxController { Get.toNamed(feature.targetRoute); } } - + // 点击功能场景 void onFunctionSceneTap(int index) { if (index < 0 || index >= functionScenes.length) return; - + Get.snackbar( - '场景', - '您选择了: ${functionScenes[index].title}', + 'scene'.tr, // 场景 + '${'youHaveSelected'.tr} ${functionScenes[index].title}', // 您选择了: snackPosition: SnackPosition.TOP, ); - + // 实际应用中,这里应该跳转到对应的场景页面 } - + // 尝试新功能 void tryNewFeature() { Get.snackbar( - '新功能', - '您选择了尝试AI歌手模式', + 'newFunction'.tr, // 新功能 + '${'youChoseTry'.tr}${'aiSinger'.tr}', // 您选择了尝试 // AI歌手模式 snackPosition: SnackPosition.TOP, ); - + // 实际应用中,这里应该跳转到新功能页面 } - + @override void onClose() { searchController.dispose(); super.onClose(); } -} \ No newline at end of file +} diff --git a/lib/modules/explore/views/explore_view.dart b/lib/modules/explore/views/explore_view.dart index f68d1c392..997f6f1f7 100644 --- a/lib/modules/explore/views/explore_view.dart +++ b/lib/modules/explore/views/explore_view.dart @@ -11,7 +11,7 @@ class ExploreView extends GetView { @override Widget build(BuildContext context) { final isDarkMode = Theme.of(context).brightness == Brightness.dark; - + return Scaffold( backgroundColor: isDarkMode ? Colors.black : Colors.grey[50], body: SafeArea( @@ -19,7 +19,7 @@ class ExploreView extends GetView { children: [ // 标题和搜索 _buildHeaderAndSearch(isDarkMode), - + // 主要内容(可滚动) Expanded( child: SingleChildScrollView( @@ -31,15 +31,15 @@ class ExploreView extends GetView { children: [ // 特色功能 _buildFeaturedCard(), - + // AI功能列表 SizedBox(height: 24.h), _buildAIFeaturesSection(isDarkMode), - + // 热门功能场景 SizedBox(height: 24.h), _buildFunctionScenesSection(isDarkMode), - + // 新功能推荐 SizedBox(height: 24.h), _buildNewFeatureSection(isDarkMode), @@ -66,7 +66,7 @@ class ExploreView extends GetView { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - '发现', + 'explore'.tr, // 发现 style: TextStyle( fontSize: 24.sp, fontWeight: FontWeight.bold, @@ -77,12 +77,14 @@ class ExploreView extends GetView { width: 32.w, height: 32.w, 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, ), child: IconButton( icon: Icon( - Icons.more_vert, + Icons.more_vert, size: 16.sp, color: isDarkMode ? Colors.white : Colors.grey[700], ), @@ -92,18 +94,19 @@ class ExploreView extends GetView { ), ], ), - + // 搜索框 SizedBox(height: 16.h), Container( 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), ), child: TextField( controller: controller.searchController, decoration: InputDecoration( - hintText: '搜索AI功能...', + hintText: 'searchAi'.tr, // 搜索AI功能... hintStyle: TextStyle( fontSize: 14.sp, color: isDarkMode ? Colors.grey[400] : Colors.grey[400], @@ -169,7 +172,7 @@ class ExploreView extends GetView { ), ), ), - + // 渐变遮罩 Container( decoration: BoxDecoration( @@ -184,7 +187,7 @@ class ExploreView extends GetView { ), ), ), - + // 文本内容 Padding( padding: EdgeInsets.all(24.w), @@ -193,7 +196,7 @@ class ExploreView extends GetView { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - '智能耳机,无限可能', + 'exploreSlogan'.tr, // 智能耳机,无限可能 style: TextStyle( fontSize: 22.sp, fontWeight: FontWeight.bold, @@ -202,7 +205,7 @@ class ExploreView extends GetView { ), SizedBox(height: 4.h), Text( - '探索DeepVoice提供的AI功能', + 'exploreAi'.tr, // 探索DeepVoice提供的AI功能 style: TextStyle( fontSize: 14.sp, color: Colors.white.withOpacity(0.8), @@ -222,7 +225,7 @@ class ExploreView extends GetView { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'AI功能', + 'aiFunction'.tr, // AI功能 style: TextStyle( fontSize: 18.sp, fontWeight: FontWeight.w600, @@ -267,7 +270,7 @@ class ExploreView extends GetView { borderRadius: BorderRadius.circular(20.r), boxShadow: [ BoxShadow( - color: isDarkMode + color: isDarkMode ? Colors.black.withOpacity(0.2) : Colors.grey.withOpacity(0.1), blurRadius: 8, @@ -338,7 +341,7 @@ class ExploreView extends GetView { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - '热门功能场景', + 'popularFunction'.tr, // 热门功能场景 style: TextStyle( fontSize: 18.sp, fontWeight: FontWeight.w600, @@ -363,7 +366,8 @@ class ExploreView extends GetView { } // 功能场景项 - Widget _buildFunctionSceneItem(FunctionScene scene, int index, bool isDarkMode) { + Widget _buildFunctionSceneItem( + FunctionScene scene, int index, bool isDarkMode) { return GestureDetector( onTap: () => controller.onFunctionSceneTap(index), child: Container( @@ -373,7 +377,7 @@ class ExploreView extends GetView { borderRadius: BorderRadius.circular(16.r), boxShadow: [ BoxShadow( - color: isDarkMode + color: isDarkMode ? Colors.black.withOpacity(0.2) : Colors.grey.withOpacity(0.1), blurRadius: 4, @@ -405,7 +409,7 @@ class ExploreView extends GetView { ), ), SizedBox(width: 16.w), - + // 文本内容 Expanded( child: Column( @@ -478,10 +482,10 @@ class ExploreView extends GetView { ), ), SizedBox(height: 16.h), - + // 标题 Text( - 'AI歌手模式', + 'aiSinger'.tr, // AI歌手模式 style: TextStyle( fontSize: 20.sp, fontWeight: FontWeight.bold, @@ -489,17 +493,17 @@ class ExploreView extends GetView { ), ), SizedBox(height: 8.h), - + // 描述 Text( - '用你喜欢的歌手声音演唱任何歌曲,甚至是你自己创作的歌曲', + 'aiSingerInfo'.tr, // 用你喜欢的歌手声音演唱任何歌曲,甚至是你自己创作的歌曲 style: TextStyle( fontSize: 14.sp, color: isDarkMode ? Colors.grey[300] : Colors.grey[600], ), ), SizedBox(height: 16.h), - + // 按钮 ElevatedButton( onPressed: controller.tryNewFeature, @@ -514,7 +518,7 @@ class ExploreView extends GetView { ), ), child: Text( - '立即尝试', + 'tryNow'.tr, // 立即尝试 style: TextStyle( fontSize: 14.sp, fontWeight: FontWeight.w500, @@ -526,4 +530,4 @@ class ExploreView extends GetView { ), ); } -} \ No newline at end of file +} diff --git a/lib/modules/home/controllers/home_controller.dart b/lib/modules/home/controllers/home_controller.dart index 6b82d1892..8b6bc7716 100644 --- a/lib/modules/home/controllers/home_controller.dart +++ b/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 '../../../data/services/voice_interaction_service.dart'; import 'package:permission_handler/permission_handler.dart'; + // 连接状态枚举 enum ConnectionStatus { - disconnected, // 未连接 - connecting, // 连接中 - connected, // 已连接 - failed // 连接失败 + disconnected, // 未连接 + connecting, // 连接中 + connected, // 已连接 + failed // 连接失败 } class HomeController extends GetxController { static HomeController get to => Get.find(); - + // 经典蓝牙服务 late classic.ClassicBluetoothService _bluetoothService; - + // 设备连接状态 final _isConnected = false.obs; bool get isConnected => _isConnected.value; - + // 连接状态 final _connectionStatus = ConnectionStatus.disconnected.obs; ConnectionStatus get connectionStatus => _connectionStatus.value; - + // 电池状态(模拟数据) final _deviceBattery = Rx(null); int? get deviceBattery => _deviceBattery.value; - + // 固件版本(模拟数据) final String firmwareVersion = 'v2.4.1'; - + // 蓝牙设备信息 final _deviceInfo = {}.obs; Map get deviceInfo => _deviceInfo.value; - + // 连接错误信息 final _connectionErrorMsg = ''.obs; String get connectionErrorMsg => _connectionErrorMsg.value; - + // 当前连接的经典蓝牙设备 final _connectedClassicDevices = [].obs; - List get connectedClassicDevices => _connectedClassicDevices; - + List get connectedClassicDevices => + _connectedClassicDevices; + @override void onInit() { super.onInit(); - + // 初始化蓝牙服务和状态监控 _initBluetoothService(); - + // 电池模拟更新(实际应用中可以通过蓝牙获取) _simulateBatteryUpdate(); } - + // 初始化蓝牙服务 void _initBluetoothService() { try { // 获取经典蓝牙服务实例 _bluetoothService = Get.find(); - + // 更新当前连接状态 _updateConnectionStatus(); - + // 监听蓝牙连接状态变化 _setupBluetoothListeners(); - } catch (e) { Logger.error('初始化蓝牙服务失败: $e'); _connectionStatus.value = ConnectionStatus.failed; } } - + // 电池电量模拟更新 void _simulateBatteryUpdate() { // 每5分钟更新一次电池电量(模拟) @@ -86,17 +87,17 @@ class HomeController extends GetxController { } else { _deviceBattery.value = null; } - + // 递归调用实现定时更新 _simulateBatteryUpdate(); }); } - + // 更新连接状态 void _updateConnectionStatus() { // 检查当前设备 final currentDevice = _bluetoothService.currentDevice.value; - + if (currentDevice != null) { _isConnected.value = true; _connectionStatus.value = ConnectionStatus.connected; @@ -104,10 +105,10 @@ class HomeController extends GetxController { _isConnected.value = false; _connectionStatus.value = ConnectionStatus.disconnected; } - + update(); // 通知UI刷新 } - + // 设置蓝牙监听器 void _setupBluetoothListeners() { // 监听设备连接 @@ -117,7 +118,7 @@ class HomeController extends GetxController { _connectionStatus.value = ConnectionStatus.connected; update(); }); - + // 监听设备断开 _bluetoothService.onDeviceDisconnected((device) { Logger.info('监听到蓝牙设备断开: ${device.name}'); @@ -126,11 +127,11 @@ class HomeController extends GetxController { _deviceBattery.value = null; update(); }); - + // 监听蓝牙状态变化 _bluetoothService.onBluetoothStateChanged((state) { Logger.info('监听到蓝牙状态变化: $state'); - + if (state == 'off') { _isConnected.value = false; _connectionStatus.value = ConnectionStatus.disconnected; @@ -139,26 +140,26 @@ class HomeController extends GetxController { } }); } - + // 刷新设备信息 Future refreshDeviceInfo() async { try { // 设置状态为连接中 _connectionStatus.value = ConnectionStatus.connecting; update(); - + // 刷新设备列表 final hasDevices = await _bluetoothService.refreshConnectedDevices(); - + // 更新状态 _updateConnectionStatus(); - + // 根据结果更新状态 if (!hasDevices) { Logger.info('刷新设备:未找到设备'); // 这里可以设置状态变量而不是使用snackbar - _connectionErrorMsg.value = _bluetoothService.errorMessage.value.isEmpty - ? '未找到已连接的设备' + _connectionErrorMsg.value = _bluetoothService.errorMessage.value.isEmpty + ? 'noDeviceFound'.tr // 未找到已连接的设备 : _bluetoothService.errorMessage.value; } else { _connectionErrorMsg.value = ''; @@ -167,32 +168,32 @@ class HomeController extends GetxController { } catch (e) { Logger.error('刷新设备信息失败: $e'); _connectionStatus.value = ConnectionStatus.failed; - _connectionErrorMsg.value = '无法获取设备信息'; + _connectionErrorMsg.value = 'failedDeviceInformation'.tr; // 无法获取设备信息 update(); } } - + // 重新扫描设备 void rescanDevices() { Get.toNamed(Routes.pairing); } - + // 打开翻译功能 void openTranslation() { Get.toNamed(Routes.translation); } - + // 打开AI伴侣功能 void openAIGirlfriend() { Get.toNamed(Routes.chat); } - + // 展示AI功能 void exploreAIFeatures() { // 简单实现,直接跳转到AI聊天页面 openAIGirlfriend(); } - + // 获取连接状态对应的颜色 Color getConnectionStatusColor() { switch (_connectionStatus.value) { @@ -207,49 +208,49 @@ class HomeController extends GetxController { return Colors.grey; } } - + // 获取连接状态对应的文本 String getConnectionStatusText() { switch (_connectionStatus.value) { case ConnectionStatus.connected: - return '已连接'; + return 'connected'.tr; // 已连接 case ConnectionStatus.connecting: - return '连接中...'; + return 'connecting'.tr; // 连接中... case ConnectionStatus.failed: - return '连接失败'; + return 'connectionFailure'.tr; // 连接失败 case ConnectionStatus.disconnected: default: - return '未连接'; + return 'unconnected'.tr; // 未连接 } } - + // 获取设备名称 String getDeviceName() { final device = _bluetoothService.currentDevice.value; - return device?.name ?? '未知设备'; + return device?.name ?? 'unknownEquipment'.tr; // 未知设备 } - + // 获取信号强度(模拟数据) String getSignalStrength() { if (!_isConnected.value) { - return '无信号'; + return 'zeroSignal'.tr; // 无信号 } - + // 简单模拟信号强度 final random = DateTime.now().millisecondsSinceEpoch % 3; switch (random) { case 0: - return '良好'; + return 'good'.tr; // 良好 case 1: - return '很好'; + return 'fine'.tr; // 很好 default: - return '极佳'; + return 'excellent'.tr; // 极佳 } } - + // 获取设备ID String getDeviceId() { final device = _bluetoothService.currentDevice.value; - return device?.address ?? '未知'; + return device?.address ?? 'unknown'.tr; // 未知 } -} \ No newline at end of file +} diff --git a/lib/modules/home/views/home_view.dart b/lib/modules/home/views/home_view.dart index 71ae638a2..da2a1aa3c 100644 --- a/lib/modules/home/views/home_view.dart +++ b/lib/modules/home/views/home_view.dart @@ -16,7 +16,8 @@ class AnimatedPulsing extends StatefulWidget { _AnimatedPulsingState createState() => _AnimatedPulsingState(); } -class _AnimatedPulsingState extends State with SingleTickerProviderStateMixin { +class _AnimatedPulsingState extends State + with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation _animation; @@ -74,7 +75,7 @@ class HomeView extends GetView { @override Widget build(BuildContext context) { final bool isDarkMode = Theme.of(context).brightness == Brightness.dark; - + return Scaffold( backgroundColor: isDarkMode ? Colors.black : Color(0xFFF9FAFC), body: SafeArea( @@ -85,13 +86,13 @@ class HomeView extends GetView { children: [ // 顶部标题栏 _buildAppBar(isDarkMode), - + // 顶部图片 Center( child: _buildEarbudsImage(), ), SizedBox(height: 20.h), - + // 设备状态卡片 _buildDeviceStatusCard(isDarkMode), SizedBox(height: 20.h), @@ -111,43 +112,41 @@ class HomeView extends GetView { // 顶部标题栏 Widget _buildAppBar(bool isDarkMode) { return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( 'DeepVoice', - style: TextStyle( - fontSize: 20.sp, - fontWeight: FontWeight.bold, + style: TextStyle( + fontSize: 20.sp, + fontWeight: FontWeight.bold, color: isDarkMode ? Colors.white : Colors.grey[800], - ), - ), - Container( - width: 32.w, - height: 32.w, - decoration: BoxDecoration( + ), + ), + Container( + width: 32.w, + height: 32.w, + decoration: BoxDecoration( color: isDarkMode ? Colors.grey[800] : Colors.grey[200], - shape: BoxShape.circle, - boxShadow: [ - BoxShadow( - color: Colors.grey.withOpacity(0.2), - blurRadius: 4, - offset: const Offset(0, 2), - ), - ], - ), - child: IconButton( - icon: Icon( - Icons.settings_outlined, - size: 16.sp, - color: isDarkMode ? Colors.white70 : Colors.grey[600] - ), - padding: EdgeInsets.zero, - onPressed: () { - Get.toNamed(Routes.settings); - }, - ), + shape: BoxShape.circle, + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.2), + blurRadius: 4, + offset: const Offset(0, 2), ), ], + ), + child: IconButton( + icon: Icon(Icons.settings_outlined, + size: 16.sp, + color: isDarkMode ? Colors.white70 : Colors.grey[600]), + padding: EdgeInsets.zero, + onPressed: () { + Get.toNamed(Routes.settings); + }, + ), + ), + ], ); } @@ -158,7 +157,9 @@ class HomeView extends GetView { height: 220.h, alignment: Alignment.center, 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, fit: BoxFit.contain, ), @@ -187,7 +188,7 @@ class HomeView extends GetView { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - '设备状态', + 'deviceStatus'.tr, // 设备状态 style: TextStyle( fontSize: 14.sp, fontWeight: FontWeight.w500, @@ -195,52 +196,57 @@ class HomeView extends GetView { ), ), Obx(() => Row( - children: [ - Container( - width: 8.w, - height: 8.w, - decoration: BoxDecoration( - color: controller.getConnectionStatusColor(), - shape: BoxShape.circle, - ), - ), - SizedBox(width: 4.w), - Text( - controller.getConnectionStatusText(), - style: TextStyle( - fontSize: 12.sp, - color: controller.getConnectionStatusColor(), - ), - ), - // 添加刷新按钮 - SizedBox(width: 8.w), - InkWell( - onTap: controller.refreshDeviceInfo, - child: Icon( - Icons.refresh, - size: 16.sp, - color: isDarkMode ? Colors.grey[400] : Colors.grey[600], - ), - ), - ], - )), + children: [ + Container( + width: 8.w, + height: 8.w, + decoration: BoxDecoration( + color: controller.getConnectionStatusColor(), + shape: BoxShape.circle, + ), + ), + SizedBox(width: 4.w), + Text( + controller.getConnectionStatusText(), + style: TextStyle( + fontSize: 12.sp, + color: controller.getConnectionStatusColor(), + ), + ), + // 添加刷新按钮 + SizedBox(width: 8.w), + InkWell( + onTap: controller.refreshDeviceInfo, + child: Icon( + Icons.refresh, + size: 16.sp, + color: + isDarkMode ? Colors.grey[400] : Colors.grey[600], + ), + ), + ], + )), ], ), - + // 设备信息 SizedBox(height: 12.h), Obx(() { // 如果连接失败且有错误信息,显示错误提示 - if (controller.connectionStatus == ConnectionStatus.failed && + if (controller.connectionStatus == ConnectionStatus.failed && controller.connectionErrorMsg.isNotEmpty) { return Column( children: [ Container( padding: EdgeInsets.all(8.w), decoration: BoxDecoration( - color: isDarkMode ? Colors.red[900]!.withOpacity(0.2) : Colors.red[50], + color: isDarkMode + ? Colors.red[900]!.withOpacity(0.2) + : Colors.red[50], borderRadius: BorderRadius.circular(8.r), - border: Border.all(color: isDarkMode ? Colors.red[800]! : Colors.red[200]!), + border: Border.all( + color: + isDarkMode ? Colors.red[800]! : Colors.red[200]!), ), child: Row( children: [ @@ -255,7 +261,9 @@ class HomeView extends GetView { controller.connectionErrorMsg, style: TextStyle( 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 { borderRadius: BorderRadius.circular(8.r), ), ), - child: Text('重新连接'), + child: Text('reconnect'.tr), // 重新连接 ), ], ); } - + // 如果正在连接中,显示连接中提示 if (controller.connectionStatus == ConnectionStatus.connecting) { return Container( @@ -292,12 +300,13 @@ class HomeView extends GetView { height: 24.w, child: CircularProgressIndicator( strokeWidth: 2.w, - valueColor: AlwaysStoppedAnimation(Colors.blue), + valueColor: + AlwaysStoppedAnimation(Colors.blue), ), ), SizedBox(height: 8.h), Text( - '正在连接设备...', + 'connectingDevice'.tr, // 正在连接设备... style: TextStyle( fontSize: 12.sp, color: Colors.blue, @@ -308,119 +317,135 @@ class HomeView extends GetView { ), ); } - - return controller.isConnected - ? Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: Text( - '已连接到:${controller.getDeviceName()}', - style: TextStyle( - fontSize: 12.sp, - fontWeight: FontWeight.w500, - color: isDarkMode ? Colors.grey[300] : Colors.grey[700], + + return controller.isConnected + ? Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + '${'connectedTo'.tr}:${controller.getDeviceName()}', // 已连接到 + style: TextStyle( + fontSize: 12.sp, + fontWeight: FontWeight.w500, + 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( - '信号: ${controller.getSignalStrength()}', - style: TextStyle( - fontSize: 10.sp, - color: Colors.blue[700], + 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( + '${'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( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - '设备ID:${controller.getDeviceId()}', - style: TextStyle( - fontSize: 10.sp, - color: isDarkMode ? Colors.grey[400] : Colors.grey[500], + SizedBox(height: 8.h), + Container( + padding: EdgeInsets.symmetric( + horizontal: 8.w, vertical: 6.h), + decoration: BoxDecoration( + color: + isDarkMode ? Colors.grey[900] : Colors.grey[50], + borderRadius: BorderRadius.circular(8.r), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '${'deviceId'.tr}:${controller.getDeviceId()}', // 设备ID + style: TextStyle( + fontSize: 10.sp, + color: isDarkMode + ? Colors.grey[400] + : Colors.grey[500], + ), ), - ), - SizedBox(height: 4.h), - Text( - '固件版本:${controller.firmwareVersion}', - style: TextStyle( - fontSize: 10.sp, - color: isDarkMode ? Colors.grey[400] : Colors.grey[500], + SizedBox(height: 4.h), + Text( + '${'firmwareVersion'.tr}:${controller.firmwareVersion}', // 固件版本 + style: TextStyle( + fontSize: 10.sp, + color: isDarkMode + ? Colors.grey[400] + : Colors.grey[500], + ), ), - ), - SizedBox(height: 4.h), - // 显示设备状态(已连接/已配对) - Row( - children: [ - Container( - padding: EdgeInsets.symmetric(horizontal: 6.w, vertical: 2.h), - decoration: BoxDecoration( - color: controller.getConnectionStatusColor().withOpacity(0.1), - borderRadius: BorderRadius.circular(4.r), - ), - child: Text( - controller.getConnectionStatusText(), - style: TextStyle( - fontSize: 10.sp, - color: controller.getConnectionStatusColor(), + SizedBox(height: 4.h), + // 显示设备状态(已连接/已配对) + Row( + children: [ + Container( + padding: EdgeInsets.symmetric( + horizontal: 6.w, vertical: 2.h), + decoration: BoxDecoration( + color: controller + .getConnectionStatusColor() + .withOpacity(0.1), + borderRadius: BorderRadius.circular(4.r), + ), + child: Text( + controller.getConnectionStatusText(), + style: TextStyle( + fontSize: 10.sp, + color: + controller.getConnectionStatusColor(), + ), ), ), - ), - ], - ), - ], - ), - ), - ], - ) - : Container( - padding: EdgeInsets.symmetric(vertical: 8.h), - alignment: Alignment.center, - decoration: BoxDecoration( - color: isDarkMode ? Colors.grey[900] : Colors.grey[100], - borderRadius: BorderRadius.circular(8.r), - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - Icons.bluetooth_disabled, - size: 14.sp, - color: isDarkMode ? Colors.grey[400] : Colors.grey[700], - ), - SizedBox(width: 4.w), - Text( - '未连接设备,点击下方按钮连接', - style: TextStyle( - fontSize: 12.sp, - color: isDarkMode ? Colors.grey[400] : Colors.grey[700], + ], + ), + ], ), ), ], - ), - ); + ) + : 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), Obx(() { @@ -435,10 +460,10 @@ class HomeView extends GetView { borderRadius: BorderRadius.circular(8.r), ), ), - child: Text('连接设备'), + child: Text('connectDevice'.tr), // 连接设备 ); } else { - return SizedBox(); + return const SizedBox(); } }), ], @@ -451,7 +476,7 @@ class HomeView extends GetView { return Row( children: [ Text( - '热门AI体验', + 'topAiExperiences'.tr, // 热门AI体验 style: TextStyle( fontSize: 18.sp, fontWeight: FontWeight.bold, @@ -478,8 +503,8 @@ class HomeView extends GetView { Expanded( flex: 1, child: _buildFeatureCard( - title: 'AI全能助手', - description: '24小时专属顾问,解答问题、提供建议', + title: 'aiAssistant'.tr, // AI全能助手 + description: 'aiAssistantInfo'.tr, // 24小时专属顾问,解答问题、提供建议 icon: Icons.assistant, gradient: LinearGradient( begin: Alignment.topLeft, @@ -496,9 +521,9 @@ class HomeView extends GetView { iconColor: const Color(0xFF0066CC), ), ), - + SizedBox(width: 16.w), - + // 右侧两个卡片 Expanded( flex: 1, @@ -506,8 +531,9 @@ class HomeView extends GetView { children: [ // 右上卡片 - 同声翻译 _buildFeatureCard( - title: '同声传译', - description: '80+语言实时翻译,出国无忧', + title: 'simultaneousInterpretation'.tr, // 同声传译 + description: + 'simultaneousInterpretationInfo'.tr, // 80+语言实时翻译,出国无忧 icon: Icons.translate, gradient: LinearGradient( begin: Alignment.topLeft, @@ -522,13 +548,13 @@ class HomeView extends GetView { isDarkMode: isDarkMode, iconColor: const Color(0xFF4CAF50), ), - + SizedBox(height: 5.h), // 减少间距 - + // 右下卡片 - AI女友 _buildFeatureCard( - title: 'AI伴侣', - description: '懂你心意,温暖陪伴', + title: 'aiCompanion'.tr, // AI伴侣 + description: 'aiCompanionInfo'.tr, // 懂你心意,温暖陪伴 icon: Icons.favorite, gradient: LinearGradient( begin: Alignment.topLeft, @@ -549,7 +575,7 @@ class HomeView extends GetView { ], ); } - + // 特性卡片构建方法 Widget _buildFeatureCard({ required String title, @@ -572,7 +598,7 @@ class HomeView extends GetView { borderRadius: BorderRadius.circular(16.r), boxShadow: [ BoxShadow( - color: isDarkMode + color: isDarkMode ? Colors.black.withOpacity(0.2) : Colors.grey.withOpacity(0.1), blurRadius: 8, @@ -583,7 +609,7 @@ class HomeView extends GetView { child: Stack( children: [ // 图标 - if (iconOnTop) + if (iconOnTop) // 大卡片布局 Align( alignment: Alignment.topLeft, @@ -619,7 +645,7 @@ class HomeView extends GetView { ), ), ), - + // 文本内容 if (iconOnTop) // 大卡片布局 - 文本 @@ -688,4 +714,4 @@ class HomeView extends GetView { ), ); } -} \ No newline at end of file +} diff --git a/lib/modules/login/views/login_view.dart b/lib/modules/login/views/login_view.dart index 9fda142f7..cc4faa1c0 100644 --- a/lib/modules/login/views/login_view.dart +++ b/lib/modules/login/views/login_view.dart @@ -11,7 +11,7 @@ class LoginView extends GetView { @override Widget build(BuildContext context) { final isDarkMode = Theme.of(context).brightness == Brightness.dark; - + return Scaffold( backgroundColor: isDarkMode ? Colors.black : Colors.grey[50], body: SafeArea( @@ -33,11 +33,11 @@ class LoginView extends GetView { // 应用标志和标题 SizedBox(height: 32.h), _buildAppLogo(isDarkMode), - + // 欢迎文本 SizedBox(height: 32.h), _buildWelcomeText(isDarkMode), - + // 输入框和继续按钮 SizedBox(height: 24.h), _buildInputField(isDarkMode), @@ -45,11 +45,11 @@ class LoginView extends GetView { _buildVerificationInputField(isDarkMode), SizedBox(height: 16.h), _buildContinueButton(isDarkMode), - + // 社交媒体登录 SizedBox(height: 32.h), _buildSocialLoginSection(isDarkMode), - + // 用户协议 SizedBox(height: 40.h), _buildTermsAndPrivacy(isDarkMode), @@ -71,23 +71,23 @@ class LoginView extends GetView { children: [ // 返回按钮 _buildBackButton(isDarkMode), - + // 验证码标题 SizedBox(height: 24.h), _buildVerificationTitle(isDarkMode), - + // 验证码输入框 SizedBox(height: 32.h), _buildVerificationInputs(context, isDarkMode), - + // 重新发送验证码 SizedBox(height: 16.h), _buildResendButton(isDarkMode), - + // 登录按钮 SizedBox(height: 32.h), _buildLoginButton(isDarkMode), - + // 使用密码登录 SizedBox(height: 16.h), _buildUsePasswordButton(isDarkMode), @@ -137,7 +137,7 @@ class LoginView extends GetView { ), SizedBox(height: 8.h), Text( - '连接世界,超越声音', + 'slogan'.tr, // 连接世界,超越声音 style: TextStyle( fontSize: 14.sp, color: isDarkMode ? Colors.grey[400] : Colors.black54, @@ -152,7 +152,7 @@ class LoginView extends GetView { return Column( children: [ Text( - '欢迎使用', + 'welcome'.tr, // 欢迎使用 style: TextStyle( fontSize: 20.sp, fontWeight: FontWeight.w600, @@ -161,7 +161,7 @@ class LoginView extends GetView { ), SizedBox(height: 4.h), Text( - '请使用手机号或邮箱登录/注册', + 'loginTips'.tr, // 请使用手机号或邮箱登录/注册 style: TextStyle( fontSize: 14.sp, color: isDarkMode ? Colors.grey[400] : Colors.black54, @@ -175,7 +175,9 @@ class LoginView extends GetView { Widget _buildInputField(bool isDarkMode) { return Container( 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), ), child: Padding( @@ -192,7 +194,7 @@ class LoginView extends GetView { child: TextField( controller: controller.contactController, decoration: InputDecoration( - hintText: '手机号码或邮箱', + hintText: 'accountNumber'.tr, // 手机号码或邮箱 hintStyle: TextStyle( fontSize: 14.sp, color: isDarkMode ? Colors.grey[600] : Colors.black38, @@ -217,7 +219,9 @@ class LoginView extends GetView { Widget _buildVerificationInputField(bool isDarkMode) { return Container( 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), ), child: Padding( @@ -236,7 +240,7 @@ class LoginView extends GetView { keyboardType: TextInputType.number, maxLength: 6, decoration: InputDecoration( - hintText: '验证码', + hintText: 'verificationCode'.tr, // 验证码 hintStyle: TextStyle( fontSize: 14.sp, color: isDarkMode ? Colors.grey[600] : Colors.black38, @@ -253,25 +257,26 @@ class LoginView extends GetView { ), ), Obx(() => TextButton( - onPressed: controller.countdownSeconds > 0 - ? null - : controller.getVerificationCode, - style: TextButton.styleFrom( - padding: EdgeInsets.symmetric(horizontal: 16.w), - foregroundColor: const Color(0xFF3B82F6), - backgroundColor: Colors.transparent, - disabledForegroundColor: isDarkMode ? Colors.grey[600] : Colors.grey[400], - ), - child: Text( - controller.countdownSeconds > 0 - ? '${controller.countdownSeconds}s后重新获取' - : '获取验证码', - style: TextStyle( - fontSize: 14.sp, - fontWeight: FontWeight.w500, - ), - ), - )), + onPressed: controller.countdownSeconds > 0 + ? null + : controller.getVerificationCode, + style: TextButton.styleFrom( + padding: EdgeInsets.symmetric(horizontal: 16.w), + foregroundColor: const Color(0xFF3B82F6), + backgroundColor: Colors.transparent, + disabledForegroundColor: + isDarkMode ? Colors.grey[600] : Colors.grey[400], + ), + child: Text( + controller.countdownSeconds > 0 + ? '${controller.countdownSeconds}${'reacquireAfter'.tr}' // s后重新获取 + : 'getCode'.tr, // 获取验证码 + style: TextStyle( + fontSize: 14.sp, + fontWeight: FontWeight.w500, + ), + ), + )), ], ), ), @@ -283,35 +288,36 @@ class LoginView extends GetView { return SizedBox( width: double.infinity, child: Obx(() => ElevatedButton( - onPressed: controller.isLoading ? null : () => controller.login(), - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFF0066CC), - foregroundColor: Colors.white, - padding: EdgeInsets.symmetric(vertical: 14.h), - shape: RoundedRectangleBorder( - 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(Colors.white), - ), - ) - : Text( - '继续', - style: TextStyle( - fontSize: 16.sp, - fontWeight: FontWeight.w600, - ), + onPressed: controller.isLoading ? null : () => controller.login(), + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFF0066CC), + foregroundColor: Colors.white, + padding: EdgeInsets.symmetric(vertical: 14.h), + shape: RoundedRectangleBorder( + 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(Colors.white), + ), + ) + : Text( + 'continue'.tr, // 继续 + style: TextStyle( + fontSize: 16.sp, + fontWeight: FontWeight.w600, + ), + ), + )), ); } @@ -321,18 +327,22 @@ class LoginView extends GetView { children: [ Row( 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: EdgeInsets.symmetric(horizontal: 16.w), child: Text( - '或使用以下方式登录', + 'otherWays'.tr, // 或使用以下方式登录 style: TextStyle( fontSize: 12.sp, 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), @@ -341,7 +351,8 @@ class LoginView extends GetView { children: [ _buildSocialButton(FontAwesomeIcons.google, Colors.red, isDarkMode), SizedBox(width: 24.w), - _buildSocialButton(FontAwesomeIcons.weixin, Colors.green, isDarkMode), + _buildSocialButton( + FontAwesomeIcons.weixin, Colors.green, isDarkMode), ], ), ], @@ -356,13 +367,15 @@ class LoginView extends GetView { decoration: BoxDecoration( color: isDarkMode ? Colors.grey[800] : Colors.white, borderRadius: BorderRadius.circular(24.r), - boxShadow: isDarkMode ? [] : [ - BoxShadow( - color: Colors.grey.withOpacity(0.2), - blurRadius: 4, - offset: const Offset(0, 2), - ), - ], + boxShadow: isDarkMode + ? [] + : [ + BoxShadow( + color: Colors.grey.withOpacity(0.2), + blurRadius: 4, + offset: const Offset(0, 2), + ), + ], ), child: IconButton( icon: FaIcon( @@ -380,7 +393,7 @@ class LoginView extends GetView { return Column( children: [ Text( - '登录/注册即表示你同意', + 'expressConsent'.tr, // 登录/注册即表示你同意 style: TextStyle( fontSize: 12.sp, color: isDarkMode ? Colors.grey[500] : Colors.grey[600], @@ -393,7 +406,7 @@ class LoginView extends GetView { GestureDetector( onTap: () {}, child: Text( - '《用户协议》', + 'userAgreement'.tr, // 《用户协议》 style: TextStyle( fontSize: 12.sp, color: const Color(0xFF3B82F6), @@ -401,7 +414,7 @@ class LoginView extends GetView { ), ), Text( - ' 和 ', + 'and'.tr, // 和 style: TextStyle( fontSize: 12.sp, color: isDarkMode ? Colors.grey[500] : Colors.grey[600], @@ -410,7 +423,7 @@ class LoginView extends GetView { GestureDetector( onTap: () {}, child: Text( - '《隐私政策》', + 'privacyPolicy'.tr, // 《隐私政策》 style: TextStyle( fontSize: 12.sp, color: const Color(0xFF3B82F6), @@ -458,12 +471,12 @@ class LoginView extends GetView { ), SizedBox(height: 8.h), Obx(() => Text( - '验证码已发送至 ${controller.displayContact}', - style: TextStyle( - fontSize: 14.sp, - color: isDarkMode ? Colors.grey[400] : Colors.grey[600], - ), - )), + '验证码已发送至 ${controller.displayContact}', + style: TextStyle( + fontSize: 14.sp, + color: isDarkMode ? Colors.grey[400] : Colors.grey[600], + ), + )), ], ); } @@ -472,17 +485,21 @@ class LoginView extends GetView { Widget _buildVerificationInputs(BuildContext context, bool isDarkMode) { return Row( 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( width: 60.w, height: 60.w, 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), border: Border.all( color: isDarkMode ? Colors.grey[700]! : Colors.grey[300]!, @@ -520,28 +537,28 @@ class LoginView extends GetView { Widget _buildResendButton(bool isDarkMode) { return Center( child: Obx(() => TextButton.icon( - onPressed: controller.countdownSeconds > 0 - ? null - : controller.resendVerificationCode, - icon: Icon( - Icons.refresh, - size: 14.sp, - color: controller.countdownSeconds > 0 - ? (isDarkMode ? Colors.grey[600] : Colors.grey[500]) - : const Color(0xFF3B82F6), - ), - label: Text( - controller.countdownSeconds > 0 - ? '重新发送验证码 (${controller.countdownSeconds}s)' - : '重新发送验证码', - style: TextStyle( - fontSize: 14.sp, - color: controller.countdownSeconds > 0 - ? (isDarkMode ? Colors.grey[600] : Colors.grey[500]) - : const Color(0xFF3B82F6), - ), - ), - )), + onPressed: controller.countdownSeconds > 0 + ? null + : controller.resendVerificationCode, + icon: Icon( + Icons.refresh, + size: 14.sp, + color: controller.countdownSeconds > 0 + ? (isDarkMode ? Colors.grey[600] : Colors.grey[500]) + : const Color(0xFF3B82F6), + ), + label: Text( + controller.countdownSeconds > 0 + ? '重新发送验证码 (${controller.countdownSeconds}s)' + : '重新发送验证码', + style: TextStyle( + fontSize: 14.sp, + color: controller.countdownSeconds > 0 + ? (isDarkMode ? Colors.grey[600] : Colors.grey[500]) + : const Color(0xFF3B82F6), + ), + ), + )), ); } @@ -550,35 +567,36 @@ class LoginView extends GetView { return SizedBox( width: double.infinity, child: Obx(() => ElevatedButton( - onPressed: controller.isLoading ? null : controller.login, - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFF3B82F6), - foregroundColor: Colors.white, - padding: EdgeInsets.symmetric(vertical: 14.h), - shape: RoundedRectangleBorder( - 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(Colors.white), - ), - ) - : Text( - '登录', - style: TextStyle( - fontSize: 16.sp, - fontWeight: FontWeight.w600, - ), + onPressed: controller.isLoading ? null : controller.login, + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFF3B82F6), + foregroundColor: Colors.white, + padding: EdgeInsets.symmetric(vertical: 14.h), + shape: RoundedRectangleBorder( + 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(Colors.white), + ), + ) + : Text( + '登录', + style: TextStyle( + fontSize: 16.sp, + fontWeight: FontWeight.w600, + ), + ), + )), ); } @@ -591,10 +609,11 @@ class LoginView extends GetView { '使用密码登录', style: TextStyle( fontSize: 14.sp, - color: isDarkMode ? const Color(0xFF3B82F6) : const Color(0xFF3B82F6), + color: + isDarkMode ? const Color(0xFF3B82F6) : const Color(0xFF3B82F6), ), ), ), ); } -} \ No newline at end of file +} diff --git a/lib/modules/pairing/controllers/pairing_controller.dart b/lib/modules/pairing/controllers/pairing_controller.dart index 0d71aa111..3ca16f47f 100644 --- a/lib/modules/pairing/controllers/pairing_controller.dart +++ b/lib/modules/pairing/controllers/pairing_controller.dart @@ -7,127 +7,124 @@ import '../../../core/utils/logger.dart'; import 'package:flutter/material.dart'; import 'package:permission_handler/permission_handler.dart'; - /// 简化的配对控制器,仅用于显示已连接经典蓝牙设备和处理基本配对流程 class PairingController extends GetxController { static PairingController get to => Get.find(); - + // 经典蓝牙服务 late classic.ClassicBluetoothService _bluetoothService; - + // 是否开始搜索 final _isSearching = false.obs; bool get isSearching => _isSearching.value; - + // 设备连接状态 final _isConnecting = false.obs; bool get isConnecting => _isConnecting.value; - + // 蓝牙服务是否可用 final _isBluetoothServiceAvailable = true.obs; bool get isBluetoothServiceAvailable => _isBluetoothServiceAvailable.value; - + // 当前连接设备 - Rx get currentDevice => _bluetoothService.currentDevice; - + Rx get currentDevice => + _bluetoothService.currentDevice; + // 连接设备列表 - RxList get connectedDevices => _bluetoothService.connectedDevices; - + RxList get connectedDevices => + _bluetoothService.connectedDevices; + // 错误信息 RxString get errorMessage => _bluetoothService.errorMessage; - + // 连接设备列表(兼容旧代码) - List get connectedClassicDevices => - _bluetoothService.currentDevice.value != null - ? [_bluetoothService.currentDevice.value!] + List get connectedClassicDevices => + _bluetoothService.currentDevice.value != null + ? [_bluetoothService.currentDevice.value!] : []; - + // 状态描述(用于UI显示) - final statusDescription = '正在初始化...'.obs; - + final statusDescription = 'initializing'.tr.obs; // 正在初始化... + // 加载状态 final isLoading = false.obs; - + @override void onInit() { super.onInit(); _initialize(); } - /// 完整的初始化流程 Future _initialize() async { try { - VoiceInteractionService.to.initialize(); VoiceInteractionService.to.startService(); - - - isLoading.value = true; - statusDescription.value = '正在初始化...'; - + statusDescription.value = 'initializing'.tr; // 正在初始化... + // 1. 获取蓝牙服务 _bluetoothService = Get.find(); - + // 确保蓝牙服务已初始化 if (!await _bluetoothService.initialize()) { Logger.error('蓝牙服务初始化失败'); - statusDescription.value = '蓝牙服务初始化失败'; + statusDescription.value = + 'bluetoothInitializationFailure'.tr; // 蓝牙服务初始化失败 _isBluetoothServiceAvailable.value = false; isLoading.value = false; return; } - - // 设置连接监听器,确保在页面完全加载后设置,避免过早监听可能导致的问题 + + // 设置连接监听器,确保在页面完全加载后设置,避免过早监听可能导致的问题 _setupDeviceListeners(); - + // 2. 检查权限 - statusDescription.value = '正在检查权限...'; - final hasPermission = await _bluetoothService.checkAndRequestPermissions(); + statusDescription.value = 'checkingPermissions'.tr; // 正在检查权限... + final hasPermission = + await _bluetoothService.checkAndRequestPermissions(); if (!hasPermission) { - statusDescription.value = '缺少蓝牙权限'; + statusDescription.value = 'lackPermissions'.tr; // 缺少蓝牙权限 isLoading.value = false; return; } - + // 3. 刷新设备列表 - statusDescription.value = '正在搜索设备...'; + statusDescription.value = 'searchingDevice'.tr; // 正在搜索设备... await refreshConnectedDevices(); - + // 4. 处理结果 if (_bluetoothService.connectedDevices.isNotEmpty) { - statusDescription.value = '找到已连接的设备'; + statusDescription.value = 'findConnectedDevice'.tr; // 找到已连接的设备 // 延迟跳转到主页 _autoNavigateToHome(); } else { // 没有找到设备 - statusDescription.value = '未找到已连接的蓝牙设备'; + statusDescription.value = 'notFound'.tr; // 未找到已连接的蓝牙设备 } - } catch (e) { Logger.error('初始化配对控制器失败: $e'); - statusDescription.value = '初始化失败'; + statusDescription.value = 'initializationFailure'.tr; // 初始化失败 _isBluetoothServiceAvailable.value = false; } finally { isLoading.value = false; } } - /// 设置设备监听器 void _setupDeviceListeners() { try { if (!_isBluetoothServiceAvailable.value) return; - + // 监听设备连接事件 _bluetoothService.onDeviceConnected((device) { Logger.info('检测到蓝牙设备连接: ${device.name}'); - + // 更新状态并跳转到主页 - statusDescription.value = '已连接到设备: ${device.name}'; - + statusDescription.value = + '${'connectedDevice'.tr} ${device.name}'; // 已连接到设备: + // 立即跳转到主页 _navigateToHome(); }); @@ -135,60 +132,59 @@ class PairingController extends GetxController { Logger.error('设置设备监听器失败: $e'); } } - + /// 立即跳转到主页 void _navigateToHome() { Get.offAllNamed(Routes.home); } - - + /// 刷新设备 Future refreshConnectedDevices() async { try { isLoading.value = true; - statusDescription.value = '正在搜索设备...'; - + statusDescription.value = 'searchingDevice'.tr; // 正在搜索设备... + final hasDevices = await _bluetoothService.refreshConnectedDevices(); - + if (hasDevices) { - statusDescription.value = '找到已连接的设备'; + statusDescription.value = 'findConnectedDevice'.tr; // 找到已连接的设备 // 延迟跳转到主页 _autoNavigateToHome(); } else { // 没有找到设备 - statusDescription.value = '未找到已连接的蓝牙设备'; + statusDescription.value = 'notFound'.tr; // 未找到已连接的蓝牙设备 } } catch (e) { Logger.error('刷新设备失败: $e'); - statusDescription.value = '刷新设备失败'; + statusDescription.value = 'failedRefreshDevice'.tr; // 刷新设备失败 } finally { isLoading.value = false; } } - + /// 自动跳转到主页 void _autoNavigateToHome() { if (_bluetoothService.connectedDevices.isEmpty) return; - + Logger.info('已找到连接的设备,自动跳转到主页'); - + Future.delayed(const Duration(milliseconds: 500), () { Get.offAllNamed(Routes.home); }); } - + /// 打开系统蓝牙设置 void openBluetoothSettings() { _bluetoothService.openBluetoothSettings(); } - + /// 刷新设备(兼容旧代码) Future refreshDevices() async { await refreshConnectedDevices(); } - + /// 跳过连接 void skipConnecting() { Get.offAllNamed(Routes.home); } -} \ No newline at end of file +} diff --git a/lib/modules/pairing/views/pairing_view.dart b/lib/modules/pairing/views/pairing_view.dart index 40de49a0c..97638299b 100644 --- a/lib/modules/pairing/views/pairing_view.dart +++ b/lib/modules/pairing/views/pairing_view.dart @@ -11,7 +11,7 @@ class PairingView extends GetView { @override Widget build(BuildContext context) { final bool isDarkMode = Theme.of(context).brightness == Brightness.dark; - + return Scaffold( backgroundColor: isDarkMode ? Colors.black : Colors.white, body: SafeArea( @@ -35,32 +35,33 @@ class PairingView extends GetView { physics: BouncingScrollPhysics(), child: ConstrainedBox( constraints: BoxConstraints( - minHeight: MediaQuery.of(Get.context!).size.height - 100.h, // 减去底部导航和状态栏高度 + minHeight: + MediaQuery.of(Get.context!).size.height - 100.h, // 减去底部导航和状态栏高度 ), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ SizedBox(height: 20.h), // 顶部间距 - + // 标题部分 _buildHeader(isDarkMode), - + SizedBox(height: 20.h), - + // 设备动画 - 更大尺寸 _buildDeviceAnimation(), - + SizedBox(height: 20.h), - + // 状态信息 _buildStatusInfo(isDarkMode), - + SizedBox(height: 40.h), - + // 底部按钮 _buildActionButtons(isDarkMode), - + SizedBox(height: 20.h), ], ), @@ -83,7 +84,7 @@ class PairingView extends GetView { ), SizedBox(height: 8.h), Text( - '连接世界,超越声音', + 'homeSlogan'.tr, // 连接世界,超越声音 style: TextStyle( fontSize: 16.sp, color: isDarkMode ? Colors.white60 : Colors.black54, @@ -127,7 +128,7 @@ class PairingView extends GetView { // 重新开始动画 }, ), - + // 旋转边框 SizedBox( width: 100.w, @@ -140,7 +141,7 @@ class PairingView extends GetView { backgroundColor: Colors.grey.withOpacity(0.2), ), ), - + // 设备图标 Container( width: 120.w, @@ -166,13 +167,13 @@ class PairingView extends GetView { ), ); } - + // 状态信息 Widget _buildStatusInfo(bool isDarkMode) { return Obx(() { final status = controller.statusDescription.value; final isLoading = controller.isLoading.value; - + return Column( children: [ // 状态文本 - 更大更醒目 @@ -185,19 +186,19 @@ class PairingView extends GetView { ), textAlign: TextAlign.center, ), - + SizedBox(height: 10.h), - + // 子状态提示文本 Text( - '请打开耳机充电盒,将耳机放入您的耳朵', + 'turnEarphones'.tr, // 请打开耳机充电盒,将耳机放入您的耳朵 style: TextStyle( fontSize: 14.sp, color: isDarkMode ? Colors.white60 : Colors.grey[600], ), textAlign: TextAlign.center, ), - + // 设备列表(有设备时才显示) if (controller.connectedClassicDevices.isNotEmpty) ...[ SizedBox(height: 24.h), @@ -207,23 +208,23 @@ class PairingView extends GetView { ); }); } - + // 已连接设备列表 Widget _buildConnectedDevicesList(bool isDarkMode) { return Obx(() { final devices = controller.connectedClassicDevices; - + if (devices.isEmpty) { return const SizedBox.shrink(); } - + return Container( width: double.infinity, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - '当前已连接:', + 'currentlyConnected'.tr, // 当前已连接: style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w600, @@ -237,7 +238,7 @@ class PairingView extends GetView { ); }); } - + // 设备项 Widget _buildDeviceItem(device, bool isDarkMode) { return Container( @@ -287,7 +288,7 @@ class PairingView extends GetView { ), ); } - + // 操作按钮 Widget _buildActionButtons(bool isDarkMode) { return Container( @@ -308,16 +309,16 @@ class PairingView extends GetView { minimumSize: Size(double.infinity, 56.h), ), child: Text( - '去系统设置', + 'unsystemSetup'.tr, // 去系统设置 style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w600, ), ), ), - + SizedBox(height: 12.h), - + // 次要按钮 TextButton( onPressed: () => controller.skipConnecting(), @@ -327,7 +328,7 @@ class PairingView extends GetView { minimumSize: Size(double.infinity, 48.h), ), child: Text( - '稍后再说', + 'talkLater'.tr, // 稍后再说 style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w500, @@ -338,4 +339,4 @@ class PairingView extends GetView { ), ); } -} \ No newline at end of file +} diff --git a/lib/modules/settings/controllers/settings_controller.dart b/lib/modules/settings/controllers/settings_controller.dart index 1ab68e829..349bf9962 100644 --- a/lib/modules/settings/controllers/settings_controller.dart +++ b/lib/modules/settings/controllers/settings_controller.dart @@ -1,41 +1,98 @@ import 'package:get/get.dart'; import 'package:flutter/material.dart'; +import 'package:get_storage/get_storage.dart'; import '../../../core/themes/theme_service.dart'; import '../../../data/services/speech_factory.dart'; +import '../models/language_model.dart'; class SettingsController extends GetxController { + // 语言设置 + final List 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 language = LanguageModel().obs; + + final GetStorage _storage = GetStorage(); + // 主题服务 final ThemeService _themeService = ThemeService(); - + // 是否是暗黑模式 final RxBool isDarkMode = false.obs; - + // 翻译语言对 final RxString sourceLanguage = '中文'.obs; final RxString targetLanguage = '英文'.obs; - final List availableLanguages = ['中文', '英文', '日文', '韩文', '法文', '德文', '西班牙文', '俄文']; - + final List availableLanguages = [ + '中文', + '英文', + '日文', + '韩文', + '法文', + '德文', + '西班牙文', + '俄文' + ]; + // 语音服务类型 final Rx speechServiceType = SpeechServiceType.azure.obs; final List> 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 + }, ]; - + @override void onInit() { super.onInit(); - + + // 获取系统语言 + _getLanguage(); + // 从存储中加载上次保存的主题设置 final savedDarkMode = _themeService.isSavedDarkMode(); - + // 设置当前主题状态 isDarkMode.value = savedDarkMode; - + // 获取当前语音服务类型 _loadCurrentSpeechServiceType(); } - + + 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() { try { @@ -45,19 +102,19 @@ class SettingsController extends GetxController { // 如果找不到SpeechFactory实例,使用默认值 } } - + // 切换主题 Future toggleTheme() async { // 切换暗黑模式状态 isDarkMode.value = !isDarkMode.value; - + // 更新全局主题 Get.changeThemeMode(isDarkMode.value ? ThemeMode.dark : ThemeMode.light); - + // 保存用户选择的主题设置 - 确保立即写入存储 await _themeService.saveThemeMode(isDarkMode.value); } - + // 设置语音服务类型 void setSpeechServiceType(SpeechServiceType type) { try { @@ -68,23 +125,23 @@ class SettingsController extends GetxController { // 处理错误 } } - + void setSourceLanguage(String language) { sourceLanguage.value = language; } - + void setTargetLanguage(String language) { targetLanguage.value = language; } - + void swapLanguages() { final temp = sourceLanguage.value; sourceLanguage.value = targetLanguage.value; targetLanguage.value = temp; } - + void signOut() { // 在此实现退出登录逻辑 Get.offAllNamed('/login'); } -} \ No newline at end of file +} diff --git a/lib/modules/settings/models/language_model.dart b/lib/modules/settings/models/language_model.dart new file mode 100644 index 000000000..8b635e18c --- /dev/null +++ b/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 = '', + }); +} diff --git a/lib/modules/settings/views/settings_view.dart b/lib/modules/settings/views/settings_view.dart index d66e6e9c9..bc0a981e5 100644 --- a/lib/modules/settings/views/settings_view.dart +++ b/lib/modules/settings/views/settings_view.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'dart:ui'; +import '../../../core/theme/app_colors.dart'; import '../controllers/settings_controller.dart'; import '../../../core/widgets/common_bottom_nav.dart'; import '../../../routes/app_routes.dart'; @@ -13,14 +14,14 @@ class SettingsView extends GetView { Widget build(BuildContext context) { // 直接使用 Theme.of(context) 获取当前主题 final isDarkMode = Theme.of(context).brightness == Brightness.dark; - + return Scaffold( backgroundColor: isDarkMode ? Colors.black : Colors.grey[50], appBar: AppBar( backgroundColor: isDarkMode ? Colors.black : Colors.white, elevation: 0.5, title: Text( - '设置', + 'settings'.tr, // 设置 style: TextStyle( fontSize: 17.sp, fontWeight: FontWeight.w600, @@ -28,7 +29,6 @@ class SettingsView extends GetView { ), ), centerTitle: true, - ), body: ListView( padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 16.h), @@ -51,22 +51,19 @@ class SettingsView extends GetView { padding: EdgeInsets.all(16.w), decoration: BoxDecoration( 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(0xFFE3F2FD), - const Color(0xFFF3E5F5) - ], + : [const Color(0xFFE3F2FD), const Color(0xFFF3E5F5)], begin: Alignment.topLeft, end: Alignment.bottomRight, ), borderRadius: BorderRadius.circular(16.r), boxShadow: [ BoxShadow( - color: isDarkMode + color: isDarkMode ? Colors.black.withOpacity(0.2) : Colors.black.withOpacity(0.05), blurRadius: 10, @@ -77,7 +74,9 @@ class SettingsView extends GetView { child: ClipRRect( borderRadius: BorderRadius.circular(16.r), 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( children: [ Container( @@ -119,13 +118,14 @@ class SettingsView extends GetView { Row( children: [ Container( - padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 2.h), + padding: EdgeInsets.symmetric( + horizontal: 8.w, vertical: 2.h), decoration: BoxDecoration( color: Colors.blue[600], borderRadius: BorderRadius.circular(10.r), ), child: Text( - '高级会员', + 'premiumMember'.tr, // 高级会员 style: TextStyle( fontSize: 10.sp, color: Colors.white, @@ -135,10 +135,11 @@ class SettingsView extends GetView { SizedBox(width: 8.w), Flexible( child: Text( - '已连接设备: DeepVoice Pro', + '${'joinDevice'.tr} DeepVoice Pro', // 已连接设备: style: TextStyle( fontSize: 12.sp, - color: isDarkMode ? Colors.white70 : Colors.black54, + color: + isDarkMode ? Colors.white70 : Colors.black54, ), overflow: TextOverflow.ellipsis, ), @@ -147,7 +148,7 @@ class SettingsView extends GetView { ), SizedBox(height: 4.h), Text( - '账号ID: deepvoice_834291', + '${'accountId'.tr} deepvoice_834291', // 账号ID: style: TextStyle( fontSize: 12.sp, color: isDarkMode ? Colors.white70 : Colors.black54, @@ -160,7 +161,7 @@ class SettingsView extends GetView { width: 32.w, height: 32.w, decoration: BoxDecoration( - color: isDarkMode + color: isDarkMode ? Colors.white.withOpacity(0.1) : Colors.white.withOpacity(0.7), borderRadius: BorderRadius.circular(16.r), @@ -193,7 +194,7 @@ class SettingsView extends GetView { Padding( padding: EdgeInsets.only(left: 4.w, bottom: 8.h), child: Text( - '系统设置', + 'systemSetting'.tr, // 系统设置 style: TextStyle( fontSize: 12.sp, fontWeight: FontWeight.w500, @@ -208,7 +209,7 @@ class SettingsView extends GetView { borderRadius: BorderRadius.circular(16.r), boxShadow: [ BoxShadow( - color: isDarkMode + color: isDarkMode ? Colors.black.withOpacity(0.2) : Colors.black.withOpacity(0.05), blurRadius: 10, @@ -219,52 +220,70 @@ class SettingsView extends GetView { child: ClipRRect( borderRadius: BorderRadius.circular(16.r), 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( children: [ - // 通知设置 - _buildSimpleNavigationSetting( - title: '通知设置', - subtitle: '管理应用推送和提醒', - icon: Icons.notifications_none, - iconBgColor: isDarkMode ? Colors.indigo[900]!.withOpacity(0.3) : Colors.indigo[100]!, - iconColor: isDarkMode ? Colors.indigo[300]! : Colors.indigo[600]!, - titleColor: isDarkMode ? Colors.white : null, - subtitleColor: isDarkMode ? Colors.white70 : null, - onTap: () { - // 打开通知设置页面 - }, - isDarkMode: isDarkMode, + // 语言设置 + Obx( + () => _buildSimpleNavigationSetting( + title: 'languageSetting'.tr, // 语言设置 + subtitle: controller.language.value.name.tr, + icon: Icons.language, + iconBgColor: isDarkMode + ? Colors.indigo[900]!.withOpacity(0.3) + : Colors.indigo[100]!, + iconColor: isDarkMode + ? Colors.indigo[300]! + : Colors.indigo[600]!, + 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( - title: '暗黑模式', - subtitle: '切换应用的明暗主题', + title: 'darkMode'.tr, // 暗黑模式 + subtitle: 'darkModeInfo'.tr, // 切换应用的明暗主题 icon: isDarkMode ? Icons.dark_mode : Icons.light_mode, - iconBgColor: isDarkMode - ? Colors.indigo[900]!.withOpacity(0.3) + iconBgColor: isDarkMode + ? Colors.indigo[900]!.withOpacity(0.3) : Colors.amber[100]!, - iconColor: isDarkMode - ? Colors.indigo[300]! - : Colors.amber[600]!, + iconColor: + isDarkMode ? Colors.indigo[300]! : Colors.amber[600]!, value: isDarkMode, onChanged: (value) { controller.toggleTheme(); }, 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( - title: '备份与恢复', - subtitle: '备份设置和AI互动历史', + title: 'backups'.tr, // 备份与恢复 + subtitle: 'backupsInfo'.tr, // 备份设置和AI互动历史 icon: Icons.cloud_upload_outlined, - iconBgColor: isDarkMode ? Colors.cyan[900]!.withOpacity(0.3) : Colors.cyan[100]!, - iconColor: isDarkMode ? Colors.cyan[300]! : Colors.cyan[600]!, - value: '上次备份: 今天', + iconBgColor: isDarkMode + ? Colors.cyan[900]!.withOpacity(0.3) + : Colors.cyan[100]!, + iconColor: + isDarkMode ? Colors.cyan[300]! : Colors.cyan[600]!, + value: '${'lastBackup'.tr} 今天', // 上次备份: valueStyle: TextStyle( fontSize: 12.sp, color: isDarkMode ? Colors.white70 : Colors.grey[500], @@ -274,20 +293,31 @@ class SettingsView extends GetView { }, 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), - - 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( - title: '隐私设置', - subtitle: '管理数据收集和权限', + title: 'privacySettings'.tr, // 隐私设置 + subtitle: 'privacySettingsInfo'.tr, // 管理数据收集和权限 icon: Icons.shield_outlined, - iconBgColor: isDarkMode ? Colors.purple[900]!.withOpacity(0.3) : Colors.purple[100]!, - iconColor: isDarkMode ? Colors.purple[300]! : Colors.purple[600]!, + iconBgColor: isDarkMode + ? Colors.purple[900]!.withOpacity(0.3) + : Colors.purple[100]!, + iconColor: + isDarkMode ? Colors.purple[300]! : Colors.purple[600]!, titleColor: isDarkMode ? Colors.white : null, subtitleColor: isDarkMode ? Colors.white70 : null, onTap: () { @@ -295,39 +325,50 @@ class SettingsView extends GetView { }, 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( - title: '退出登录', + title: 'logout'.tr, // 退出登录 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]!, - titleColor: isDarkMode ? Colors.red[300]! : Colors.red[600]!, + titleColor: + isDarkMode ? Colors.red[300]! : Colors.red[600]!, onTap: () { // 退出登录确认对话框 Get.dialog( AlertDialog( - backgroundColor: isDarkMode ? Colors.grey[900] : Colors.white, + backgroundColor: + isDarkMode ? Colors.grey[900] : Colors.white, title: Text( - '退出登录', + 'logout'.tr, // 退出登录 style: TextStyle( color: isDarkMode ? Colors.white : Colors.black87, ), ), content: Text( - '确定要退出当前账号吗?', + 'logoutTips'.tr, // 确定要退出当前账号吗? style: TextStyle( - color: isDarkMode ? Colors.white70 : Colors.black54, + color: + isDarkMode ? Colors.white70 : Colors.black54, ), ), actions: [ TextButton( onPressed: () => Get.back(), child: Text( - '取消', + 'cancel'.tr, // 取消 style: TextStyle( - color: isDarkMode ? Colors.white70 : Colors.black54, + color: isDarkMode + ? Colors.white70 + : Colors.black54, ), ), ), @@ -337,9 +378,11 @@ class SettingsView extends GetView { controller.signOut(); }, child: Text( - '确定', + 'confirm'.tr, // 确定 style: TextStyle( - color: isDarkMode ? Colors.red[300] : Colors.red[600], + color: isDarkMode + ? Colors.red[300] + : Colors.red[600], ), ), ), @@ -366,7 +409,7 @@ class SettingsView extends GetView { Padding( padding: EdgeInsets.only(left: 4.w, bottom: 8.h), child: Text( - '关于应用', + 'aboutApplication'.tr, // 关于应用 style: TextStyle( fontSize: 12.sp, fontWeight: FontWeight.w500, @@ -382,7 +425,7 @@ class SettingsView extends GetView { borderRadius: BorderRadius.circular(16.r), boxShadow: [ BoxShadow( - color: isDarkMode + color: isDarkMode ? Colors.black.withOpacity(0.2) : Colors.black.withOpacity(0.05), blurRadius: 10, @@ -393,7 +436,9 @@ class SettingsView extends GetView { child: ClipRRect( borderRadius: BorderRadius.circular(16.r), 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( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -405,8 +450,10 @@ class SettingsView extends GetView { decoration: BoxDecoration( gradient: LinearGradient( colors: [ - const Color(0xFF2196F3).withOpacity(isDarkMode ? 0.7 : 1.0), - const Color(0xFF9C27B0).withOpacity(isDarkMode ? 0.7 : 1.0) + const Color(0xFF2196F3) + .withOpacity(isDarkMode ? 0.7 : 1.0), + const Color(0xFF9C27B0) + .withOpacity(isDarkMode ? 0.7 : 1.0) ], begin: Alignment.topLeft, end: Alignment.bottomRight, @@ -433,10 +480,11 @@ class SettingsView extends GetView { ), SizedBox(height: 2.h), Text( - '版本 2.4.1 (Build 87)', + '${'versions'.tr} 2.4.1 (Build 87)', // 版本 style: TextStyle( fontSize: 12.sp, - color: isDarkMode ? Colors.white70 : Colors.black54, + color: + isDarkMode ? Colors.white70 : Colors.black54, ), ), ], @@ -444,13 +492,16 @@ class SettingsView extends GetView { ], ), Container( - padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 6.h), + padding: + EdgeInsets.symmetric(horizontal: 12.w, vertical: 6.h), 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), ), child: Text( - '检查更新', + 'checkUpdates'.tr, // 检查更新 style: TextStyle( fontSize: 12.sp, color: isDarkMode ? Colors.blue[300] : Colors.blue[600], @@ -475,7 +526,7 @@ class SettingsView extends GetView { ), SizedBox(height: 4.h), Text( - '连接世界,超越声音', + 'homeSlogan'.tr, // 连接世界,超越声音 style: TextStyle( fontSize: 12.sp, color: isDarkMode ? Colors.grey[400] : Colors.grey[500], @@ -534,8 +585,7 @@ class SettingsView extends GetView { ), overflow: TextOverflow.ellipsis, ), - if (subtitle != null) - SizedBox(height: 2.h), + if (subtitle != null) SizedBox(height: 2.h), if (subtitle != null) Text( subtitle, @@ -610,14 +660,14 @@ class SettingsView extends GetView { ), overflow: TextOverflow.ellipsis, ), - if (subtitle != null) - SizedBox(height: 2.h), + if (subtitle != null) SizedBox(height: 2.h), if (subtitle != null) Text( subtitle, style: TextStyle( fontSize: 12.sp, - color: isDarkMode ? Colors.white70 : Colors.black54, + color: + isDarkMode ? Colors.white70 : Colors.black54, ), overflow: TextOverflow.ellipsis, ), @@ -633,10 +683,11 @@ class SettingsView extends GetView { Flexible( child: Text( value, - style: valueStyle ?? TextStyle( - fontSize: 14.sp, - color: isDarkMode ? Colors.white : Colors.black87, - ), + style: valueStyle ?? + TextStyle( + fontSize: 14.sp, + color: isDarkMode ? Colors.white : Colors.black87, + ), overflow: TextOverflow.ellipsis, ), ), @@ -645,7 +696,9 @@ class SettingsView extends GetView { width: 32.w, height: 32.w, 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), ), child: Icon( @@ -707,18 +760,21 @@ class SettingsView extends GetView { style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w500, - color: titleColor ?? (isDarkMode ? Colors.white : Colors.black87), + color: titleColor ?? + (isDarkMode ? Colors.white : Colors.black87), ), overflow: TextOverflow.ellipsis, ), - if (subtitle != null) - SizedBox(height: 2.h), + if (subtitle != null) SizedBox(height: 2.h), if (subtitle != null) Text( subtitle, style: TextStyle( fontSize: 12.sp, - color: subtitleColor ?? (isDarkMode ? Colors.white70 : Colors.black54), + color: subtitleColor ?? + (isDarkMode + ? Colors.white70 + : Colors.black54), ), overflow: TextOverflow.ellipsis, ), @@ -732,7 +788,9 @@ class SettingsView extends GetView { width: 32.w, height: 32.w, 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), ), child: Icon( @@ -765,7 +823,9 @@ class SettingsView extends GetView { width: 32.w, height: 32.w, 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), ), child: Icon( @@ -780,7 +840,7 @@ class SettingsView extends GetView { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - '语音服务', + 'voiceService'.tr, // 语音服务 style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w500, @@ -789,7 +849,7 @@ class SettingsView extends GetView { ), SizedBox(height: 2.h), Text( - '选择语音识别和合成服务', + 'voiceServiceInfo'.tr, // 选择语音识别和合成服务 style: TextStyle( fontSize: 12.sp, color: isDarkMode ? Colors.white70 : Colors.black54, @@ -808,11 +868,12 @@ class SettingsView extends GetView { Obx(() { final serviceType = controller.speechServiceType.value; final serviceName = controller.availableSpeechServices - .firstWhere((service) => service['type'] == serviceType)['name']; - + .firstWhere( + (service) => service['type'] == serviceType)['name']; + return Flexible( child: Text( - serviceName, + '$serviceName'.tr, style: TextStyle( fontSize: 12.sp, color: isDarkMode ? Colors.white70 : Colors.grey[600], @@ -826,7 +887,9 @@ class SettingsView extends GetView { width: 32.w, height: 32.w, 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), ), child: Icon( @@ -842,7 +905,7 @@ class SettingsView extends GetView { ), ); } - + // 显示语音服务选择对话框 void _showSpeechServiceSelectionDialog(bool isDarkMode) { Get.dialog( @@ -858,7 +921,7 @@ class SettingsView extends GetView { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - '选择语音服务', + 'selectVoiceService'.tr, // 选择语音服务 style: TextStyle( fontSize: 18.sp, fontWeight: FontWeight.w600, @@ -868,8 +931,9 @@ class SettingsView extends GetView { SizedBox(height: 16.h), ...controller.availableSpeechServices.map((service) { return Obx(() { - final isSelected = controller.speechServiceType.value == service['type']; - + final isSelected = + controller.speechServiceType.value == service['type']; + return InkWell( onTap: () { controller.setSpeechServiceType(service['type']); @@ -880,12 +944,18 @@ class SettingsView extends GetView { padding: EdgeInsets.all(12.w), decoration: BoxDecoration( color: isSelected - ? (isDarkMode ? Colors.teal[900]!.withOpacity(0.3) : Colors.teal[50]!) - : (isDarkMode ? Colors.white.withOpacity(0.05) : Colors.grey[100]!), + ? (isDarkMode + ? Colors.teal[900]!.withOpacity(0.3) + : Colors.teal[50]!) + : (isDarkMode + ? Colors.white.withOpacity(0.05) + : Colors.grey[100]!), borderRadius: BorderRadius.circular(12.r), border: Border.all( color: isSelected - ? (isDarkMode ? Colors.teal[300]! : Colors.teal[600]!) + ? (isDarkMode + ? Colors.teal[300]! + : Colors.teal[600]!) : Colors.transparent, width: 1, ), @@ -897,8 +967,12 @@ class SettingsView extends GetView { height: 24.w, decoration: BoxDecoration( color: isSelected - ? (isDarkMode ? Colors.teal[300] : Colors.teal[600]) - : (isDarkMode ? Colors.white.withOpacity(0.1) : Colors.grey[300]), + ? (isDarkMode + ? Colors.teal[300] + : Colors.teal[600]) + : (isDarkMode + ? Colors.white.withOpacity(0.1) + : Colors.grey[300]), shape: BoxShape.circle, ), child: isSelected @@ -915,20 +989,24 @@ class SettingsView extends GetView { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - service['name'], + '${service['name']}'.tr, style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w500, - color: isDarkMode ? Colors.white : Colors.black87, + color: isDarkMode + ? Colors.white + : Colors.black87, ), overflow: TextOverflow.ellipsis, ), SizedBox(height: 4.h), Text( - service['description'], + '${service['description']}'.tr, style: TextStyle( fontSize: 12.sp, - color: isDarkMode ? Colors.white70 : Colors.black54, + color: isDarkMode + ? Colors.white70 + : Colors.black54, ), overflow: TextOverflow.ellipsis, maxLines: 2, @@ -949,7 +1027,7 @@ class SettingsView extends GetView { TextButton( onPressed: () => Get.back(), child: Text( - '取消', + 'cancel'.tr, // 取消 style: TextStyle( color: isDarkMode ? Colors.white70 : Colors.black54, ), @@ -963,4 +1041,86 @@ class SettingsView extends GetView { ), ); } -} \ No newline at end of file + + // 显示语言选择对话框 + 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, + ), + ), + ), + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/modules/splash/controllers/splash_controller.dart b/lib/modules/splash/controllers/splash_controller.dart index b12ae7050..c0b4cf9fe 100644 --- a/lib/modules/splash/controllers/splash_controller.dart +++ b/lib/modules/splash/controllers/splash_controller.dart @@ -8,31 +8,31 @@ class SplashController extends GetxController { // 进度条动画控制 final _loadingProgress = 0.0.obs; double get loadingProgress => _loadingProgress.value; - + // 版本号 final String version = 'v1.0.0'; - + // 资源加载状态 final _isResourceLoaded = false.obs; bool get isResourceLoaded => _isResourceLoaded.value; - + // 主题服务 final ThemeService _themeService = ThemeService(); - + @override void onInit() { super.onInit(); - + // 确保页面先渲染,再执行其他操作 WidgetsBinding.instance.addPostFrameCallback((_) { // 启动进度条动画 _startLoadingAnimation(); - + // 异步加载应用资源 _loadAppResources(); }); } - + // 进度条动画 void _startLoadingAnimation() { // 模拟0.5秒的加载进度 @@ -48,21 +48,21 @@ class SplashController extends GetxController { } }); } - + // 异步加载应用资源 Future _loadAppResources() async { try { // 在这里可以添加应用初始化需要的资源加载 // 例如:预加载图片、初始化数据库、加载配置等 - + // 模拟耗时操作 await Future.delayed(const Duration(milliseconds: 100)); - + // TODO: 在此处添加实际的资源加载代码 - + // 标记资源加载完成 _isResourceLoaded.value = true; - + // 如果进度条已经走完,则跳转到登录页面 if (_loadingProgress.value >= 1.0) { _navigateToLogin(); @@ -71,12 +71,12 @@ class SplashController extends GetxController { // 处理资源加载错误 // 即使加载失败,也应该允许用户继续使用应用 _isResourceLoaded.value = true; - + // 记录错误日志 print('资源加载错误: $e'); } } - + // 跳转到登录页面 void _navigateToLogin() { // 添加一个小延迟,确保UI完全渲染 @@ -84,4 +84,4 @@ class SplashController extends GetxController { Get.offAllNamed(Routes.login); }); } -} \ No newline at end of file +} diff --git a/lib/modules/splash/views/splash_view.dart b/lib/modules/splash/views/splash_view.dart index 0f2fb0e4f..fba98eedb 100644 --- a/lib/modules/splash/views/splash_view.dart +++ b/lib/modules/splash/views/splash_view.dart @@ -9,7 +9,7 @@ class SplashView extends GetView { @override Widget build(BuildContext context) { final isDarkMode = Theme.of(context).brightness == Brightness.dark; - + return Scaffold( backgroundColor: isDarkMode ? Colors.black : const Color(0xFFF5F5F7), body: Stack( @@ -21,24 +21,24 @@ class SplashView extends GetView { children: [ // Logo动画 _buildAnimatedLogo(isDarkMode), - + // 应用名称 _buildAppTitle(isDarkMode), - + // 标语 _buildSlogan(isDarkMode), - + // 加载进度条 SizedBox(height: 40.h), _buildLoadingBar(isDarkMode), - + // 额外信息 SizedBox(height: 16.h), _buildExtraInfo(isDarkMode), ], ), ), - + // 版本号 _buildVersionInfo(isDarkMode), ], @@ -55,7 +55,7 @@ class SplashView extends GetView { builder: (context, value, child) { // 确保opacity值不超过1.0 final opacity = value <= 1.0 ? value : 1.0; - + return Transform.scale( scale: value, child: AnimatedOpacity( @@ -130,7 +130,7 @@ class SplashView extends GetView { child: Padding( padding: EdgeInsets.only(top: 8.h), child: Text( - '连接世界,岂止声音', + 'slogan'.tr, // 连接世界,岂止声音 style: TextStyle( fontSize: 14.sp, color: isDarkMode ? Colors.grey[400] : Colors.grey[600], @@ -189,7 +189,7 @@ class SplashView extends GetView { return Opacity( opacity: value, child: Text( - 'AI语音专家 · 随时随地', + 'sloganInfo'.tr, // AI语音专家 · 随时随地 style: TextStyle( fontSize: 12.sp, color: isDarkMode ? Colors.grey[500] : Colors.grey[400], @@ -217,4 +217,4 @@ class SplashView extends GetView { ), ); } -} \ No newline at end of file +} diff --git a/lib/modules/translation/views/translation_view.dart b/lib/modules/translation/views/translation_view.dart index 727eb72b2..5e209d453 100644 --- a/lib/modules/translation/views/translation_view.dart +++ b/lib/modules/translation/views/translation_view.dart @@ -13,7 +13,7 @@ class TranslationView extends GetView { @override Widget build(BuildContext context) { final isDarkMode = Theme.of(context).brightness == Brightness.dark; - + return Scaffold( backgroundColor: isDarkMode ? AppColors.darkBackground : Colors.grey[50], appBar: _buildAppBar(isDarkMode), @@ -47,22 +47,25 @@ class TranslationView extends GetView { child: Container( padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h), 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), ), child: Obx(() => Text( - controller.sourceLanguage.value.split(' ')[0], // Just show the Chinese part - style: TextStyle( - fontSize: 16.sp, - fontWeight: FontWeight.w600, - color: isDarkMode ? Colors.white : Colors.black, - ), - overflow: TextOverflow.ellipsis, - )), + controller.sourceLanguage.value + .split(' ')[0], // Just show the Chinese part + style: TextStyle( + fontSize: 16.sp, + fontWeight: FontWeight.w600, + color: isDarkMode ? Colors.white : Colors.black, + ), + overflow: TextOverflow.ellipsis, + )), ), ), ), - + // 语言互换按钮 GestureDetector( onTap: controller.swapLanguages, @@ -70,7 +73,9 @@ class TranslationView extends GetView { margin: EdgeInsets.symmetric(horizontal: 8.w), padding: EdgeInsets.all(4.r), 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, ), child: Icon( @@ -80,7 +85,7 @@ class TranslationView extends GetView { ), ), ), - + // 目标语言选择 Flexible( child: GestureDetector( @@ -88,18 +93,21 @@ class TranslationView extends GetView { child: Container( padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h), 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), ), child: Obx(() => Text( - controller.targetLanguage.value.split(' ')[0], // Just show the English part - style: TextStyle( - fontSize: 16.sp, - fontWeight: FontWeight.w600, - color: isDarkMode ? Colors.white : Colors.black, - ), - overflow: TextOverflow.ellipsis, - )), + controller.targetLanguage.value + .split(' ')[0], // Just show the English part + style: TextStyle( + fontSize: 16.sp, + fontWeight: FontWeight.w600, + color: isDarkMode ? Colors.white : Colors.black, + ), + overflow: TextOverflow.ellipsis, + )), ), ), ), @@ -109,15 +117,17 @@ class TranslationView extends GetView { actions: [ // TTS toggle button Obx(() => IconButton( - icon: Icon( - controller.isTtsEnabled.value ? Icons.volume_up : Icons.volume_off, - color: isDarkMode ? Colors.white : Colors.black, - size: 24.sp, - ), - onPressed: controller.toggleTts, - tooltip: '自动播放翻译结果', - )), - + icon: Icon( + controller.isTtsEnabled.value + ? Icons.volume_up + : Icons.volume_off, + color: isDarkMode ? Colors.white : Colors.black, + size: 24.sp, + ), + onPressed: controller.toggleTts, + tooltip: '自动播放翻译结果', + )), + // 历史记录按钮 IconButton( icon: Icon( @@ -129,10 +139,10 @@ class TranslationView extends GetView { // 停止语音识别和TTS,释放资源 await controller.stopAll(); await Get.to( - () => const TranslationHistoryView(), + () => const TranslationHistoryView(), binding: TranslationHistoryBinding(), ); - + // 返回时不自动开始翻译,只需重新设置滚动监听器 controller.setupScrollListener(); }, @@ -156,22 +166,19 @@ class TranslationView extends GetView { Widget _buildTranslationContent(bool isDarkMode) { return Obx(() { final itemCount = controller.translationHistory.length; - + // 使用 currentItem 来判断是否显示当前项 - final hasCurrentItem = controller.currentItem.value != null && - controller.currentItem.value!.sourceText.isNotEmpty; // 只有当有实际内容时才显示当前项 - + final hasCurrentItem = controller.currentItem.value != null && + controller + .currentItem.value!.sourceText.isNotEmpty; // 只有当有实际内容时才显示当前项 + // 计算底部内边距,确保内容不会被底部框遮住 final bottomPadding = controller.bottomBarHeight + 20.h; // 使用控制栏高度加上额外间距 - + return ListView.builder( controller: controller.scrollController, padding: EdgeInsets.only( - left: 16.w, - right: 16.w, - top: 16.h, - bottom: bottomPadding - ), + left: 16.w, right: 16.w, top: 16.h, bottom: bottomPadding), itemCount: itemCount + (hasCurrentItem ? 1 : 0), itemBuilder: (context, index) { // Current item (being translated) @@ -184,7 +191,7 @@ class TranslationView extends GetView { isDarkMode: isDarkMode, ); } - + // History items final historyItem = controller.translationHistory[index]; return _buildTranslationItemWidget( @@ -201,15 +208,13 @@ class TranslationView extends GetView { } Widget _buildTranslationItemWidget( - String sourceText, - String translatedText, - bool isCurrent, - { - TranslationItem? item, - bool isIntermediate = false, - required bool isDarkMode, - } - ) { + String sourceText, + String translatedText, + bool isCurrent, { + TranslationItem? item, + bool isIntermediate = false, + required bool isDarkMode, + }) { // 使用AnimatedContainer实现平滑过渡,但恢复原来的视觉样式 return AnimatedContainer( duration: const Duration(milliseconds: 300), @@ -231,17 +236,20 @@ class TranslationView extends GetView { sourceText.isEmpty && isCurrent ? '等待语音输入...' : sourceText, style: TextStyle( fontSize: 16.sp, - color: sourceText.isEmpty && isCurrent - ? (isDarkMode ? Colors.grey[400] : Colors.grey) + color: sourceText.isEmpty && isCurrent + ? (isDarkMode ? Colors.grey[400] : Colors.grey) : (isDarkMode ? Colors.white : Colors.black), fontWeight: FontWeight.normal, ), ), ), - + // 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 AnimatedContainer( duration: const Duration(milliseconds: 300), @@ -252,11 +260,13 @@ class TranslationView extends GetView { children: [ Expanded( child: Text( - translatedText.isEmpty && isCurrent ? '翻译结果将显示在这里...' : translatedText, + translatedText.isEmpty && isCurrent + ? '翻译结果将显示在这里...' + : translatedText, style: TextStyle( fontSize: 16.sp, - color: translatedText.isEmpty && isCurrent - ? (isDarkMode ? Colors.grey[400] : Colors.grey) + color: translatedText.isEmpty && isCurrent + ? (isDarkMode ? Colors.grey[400] : Colors.grey) : (isDarkMode ? Colors.white : Colors.black), fontStyle: FontStyle.normal, ), @@ -288,16 +298,17 @@ class TranslationView extends GetView { Widget _buildControlBar(bool isDarkMode) { // 使用GlobalKey来获取控制栏的高度 final controlBarKey = GlobalKey(); - + // 在构建完成后获取控制栏的高度并设置到控制器中 WidgetsBinding.instance.addPostFrameCallback((_) { 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; controller.setBottomBarHeight(height); } }); - + return Container( key: controlBarKey, padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 16.h), @@ -314,8 +325,6 @@ class TranslationView extends GetView { child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - - // 主要控制按钮 - 开始/暂停语音识别 Obx(() { final isRecognizing = controller.isRecognizing.value; @@ -331,8 +340,8 @@ class TranslationView extends GetView { width: 60.w, height: 60.w, decoration: BoxDecoration( - color: isRecognizing - ? (isDarkMode ? Colors.red[700] : Colors.red) + color: isRecognizing + ? (isDarkMode ? Colors.red[700] : Colors.red) : (isDarkMode ? AppColors.primary : Colors.blue), shape: BoxShape.circle, ), @@ -368,7 +377,9 @@ class TranslationView extends GetView { Padding( padding: EdgeInsets.symmetric(horizontal: 24.w), child: Text( - isSource ? '选择源语言' : '选择目标语言', + isSource + ? 'selectSourceLanguage'.tr // 选择源语言 + : 'selectTargetLanguage'.tr, // 选择目标语言 style: TextStyle( fontSize: 20.sp, fontWeight: FontWeight.bold, @@ -382,7 +393,8 @@ class TranslationView extends GetView { child: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, - children: controller.supportedLanguages.keys.map((language) { + children: + controller.supportedLanguages.keys.map((language) { return ListTile( title: Text( language, @@ -391,13 +403,15 @@ class TranslationView extends GetView { ), ), trailing: Obx(() { - final currentLanguage = isSource - ? controller.sourceLanguage.value + final currentLanguage = isSource + ? controller.sourceLanguage.value : controller.targetLanguage.value; return currentLanguage == language ? Icon( Icons.check_circle, - color: isDarkMode ? AppColors.primary : Colors.blue, + color: isDarkMode + ? AppColors.primary + : Colors.blue, ) : const SizedBox.shrink(); }), @@ -422,7 +436,7 @@ class TranslationView extends GetView { child: TextButton( onPressed: () => Get.back(), child: Text( - '取消', + 'cancel'.tr, // 取消 style: TextStyle( color: isDarkMode ? AppColors.primary : Colors.blue, fontSize: 16.sp, @@ -437,4 +451,4 @@ class TranslationView extends GetView { ), ); } -} \ No newline at end of file +} diff --git a/pubspec.yaml b/pubspec.yaml index 245cb88bd..ba0a93491 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -9,6 +9,8 @@ environment: dependencies: flutter: sdk: flutter + flutter_localizations: + sdk: flutter cupertino_icons: ^1.0.6 get: ^4.6.6 get_storage: ^2.1.1