diff --git a/assets/images/explore1.png b/assets/images/explore1.png new file mode 100644 index 000000000..9a81a96ca Binary files /dev/null and b/assets/images/explore1.png differ diff --git a/assets/images/explore2.jpg b/assets/images/explore2.jpg new file mode 100644 index 000000000..8b23189b7 Binary files /dev/null and b/assets/images/explore2.jpg differ diff --git a/assets/images/glasses_light.png b/assets/images/glasses_light.png new file mode 100644 index 000000000..13d66464d Binary files /dev/null and b/assets/images/glasses_light.png differ diff --git a/assets/images/robot_light.png b/assets/images/robot_light.png new file mode 100644 index 000000000..40bf9a9c1 Binary files /dev/null and b/assets/images/robot_light.png differ diff --git a/ios/Podfile b/ios/Podfile index 1a255e840..c10c27d8b 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -platform :ios, '12.0' +platform :ios, '13.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/ios/Podfile.lock b/ios/Podfile.lock index aa4aa611a..33793d61b 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,47 +1,183 @@ PODS: + - app_settings (5.1.1): + - Flutter + - AppAuth (1.7.6): + - AppAuth/Core (= 1.7.6) + - AppAuth/ExternalUserAgent (= 1.7.6) + - AppAuth/Core (1.7.6) + - AppAuth/ExternalUserAgent (1.7.6): + - AppAuth/Core - audio_session (0.0.1): - Flutter - audio_waveforms (0.0.1): - Flutter - device_info_plus (0.0.1): - Flutter + - FBAEMKit (17.1.0): + - FBSDKCoreKit_Basics (= 17.1.0) + - FBSDKCoreKit (17.1.0): + - FBAEMKit (= 17.1.0) + - FBSDKCoreKit_Basics (= 17.1.0) + - FBSDKCoreKit_Basics (17.1.0) + - FBSDKLoginKit (17.1.0): + - FBSDKCoreKit (= 17.1.0) + - Firebase/Auth (11.8.0): + - Firebase/CoreOnly + - FirebaseAuth (~> 11.8.0) + - Firebase/CoreOnly (11.8.0): + - FirebaseCore (~> 11.8.0) + - firebase_auth (5.5.1): + - Firebase/Auth (= 11.8.0) + - firebase_core + - Flutter + - firebase_core (3.12.1): + - Firebase/CoreOnly (= 11.8.0) + - Flutter + - FirebaseAppCheckInterop (11.10.0) + - FirebaseAuth (11.8.1): + - FirebaseAppCheckInterop (~> 11.0) + - FirebaseAuthInterop (~> 11.0) + - FirebaseCore (~> 11.8.0) + - FirebaseCoreExtension (~> 11.8.0) + - GoogleUtilities/AppDelegateSwizzler (~> 8.0) + - GoogleUtilities/Environment (~> 8.0) + - GTMSessionFetcher/Core (< 5.0, >= 3.4) + - RecaptchaInterop (~> 100.0) + - FirebaseAuthInterop (11.10.0) + - FirebaseCore (11.8.1): + - FirebaseCoreInternal (~> 11.8.0) + - GoogleUtilities/Environment (~> 8.0) + - GoogleUtilities/Logger (~> 8.0) + - FirebaseCoreExtension (11.8.0): + - FirebaseCore (~> 11.8.0) + - FirebaseCoreInternal (11.8.0): + - "GoogleUtilities/NSData+zlib (~> 8.0)" - Flutter (1.0.0) - flutter_blue_plus_darwin (0.0.2): - Flutter - FlutterMacOS + - flutter_facebook_auth (7.1.1): + - FBSDKLoginKit (~> 17.1.0) + - Flutter + - flutter_image_compress_common (1.0.0): + - Flutter + - Mantle + - SDWebImage + - SDWebImageWebPCoder - flutter_local_notifications (0.0.1): - Flutter + - flutter_secure_storage (6.0.0): + - Flutter - flutter_tts (0.0.1): - Flutter + - fluwx (0.0.1): + - Flutter + - fluwx/pay (= 0.0.1) + - fluwx/pay (0.0.1): + - Flutter + - WechatOpenSDK-XCFramework (~> 2.0.4) + - google_sign_in_ios (0.0.1): + - AppAuth (>= 1.7.4) + - Flutter + - FlutterMacOS + - GoogleSignIn (~> 7.1) + - GTMSessionFetcher (>= 3.4.0) + - GoogleSignIn (7.1.0): + - AppAuth (< 2.0, >= 1.7.3) + - GTMAppAuth (< 5.0, >= 4.1.1) + - GTMSessionFetcher/Core (~> 3.3) + - GoogleUtilities/AppDelegateSwizzler (8.0.2): + - GoogleUtilities/Environment + - GoogleUtilities/Logger + - GoogleUtilities/Network + - GoogleUtilities/Privacy + - GoogleUtilities/Environment (8.0.2): + - GoogleUtilities/Privacy + - GoogleUtilities/Logger (8.0.2): + - GoogleUtilities/Environment + - GoogleUtilities/Privacy + - GoogleUtilities/Network (8.0.2): + - GoogleUtilities/Logger + - "GoogleUtilities/NSData+zlib" + - GoogleUtilities/Privacy + - GoogleUtilities/Reachability + - "GoogleUtilities/NSData+zlib (8.0.2)": + - GoogleUtilities/Privacy + - GoogleUtilities/Privacy (8.0.2) + - GoogleUtilities/Reachability (8.0.2): + - GoogleUtilities/Logger + - GoogleUtilities/Privacy + - GTMAppAuth (4.1.1): + - AppAuth/Core (~> 1.7) + - GTMSessionFetcher/Core (< 4.0, >= 3.3) + - GTMSessionFetcher (3.5.0): + - GTMSessionFetcher/Full (= 3.5.0) + - GTMSessionFetcher/Core (3.5.0) + - GTMSessionFetcher/Full (3.5.0): + - GTMSessionFetcher/Core - image_picker_ios (0.0.1): - Flutter - just_audio (0.0.1): - Flutter - FlutterMacOS + - libwebp (1.5.0): + - libwebp/demux (= 1.5.0) + - libwebp/mux (= 1.5.0) + - libwebp/sharpyuv (= 1.5.0) + - libwebp/webp (= 1.5.0) + - libwebp/demux (1.5.0): + - libwebp/webp + - libwebp/mux (1.5.0): + - libwebp/demux + - libwebp/sharpyuv (1.5.0) + - libwebp/webp (1.5.0): + - libwebp/sharpyuv + - Mantle (2.2.0): + - Mantle/extobjc (= 2.2.0) + - Mantle/extobjc (2.2.0) - MicrosoftCognitiveServicesSpeech-iOS (1.34.0) - path_provider_foundation (0.0.1): - Flutter - FlutterMacOS - permission_handler_apple (9.3.0): - Flutter + - RecaptchaInterop (100.0.0) - record_darwin (1.0.0): - Flutter + - SDWebImage (5.21.0): + - SDWebImage/Core (= 5.21.0) + - SDWebImage/Core (5.21.0) + - SDWebImageWebPCoder (0.14.6): + - libwebp (~> 1.0) + - SDWebImage/Core (~> 5.17) - share_plus (0.0.1): - Flutter - speech_to_text (0.0.1): - Flutter - FlutterMacOS - Try + - sqflite_darwin (0.0.4): + - Flutter + - FlutterMacOS - Try (2.1.1) + - WechatOpenSDK-XCFramework (2.0.4) DEPENDENCIES: + - app_settings (from `.symlinks/plugins/app_settings/ios`) - audio_session (from `.symlinks/plugins/audio_session/ios`) - audio_waveforms (from `.symlinks/plugins/audio_waveforms/ios`) - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) + - firebase_auth (from `.symlinks/plugins/firebase_auth/ios`) + - firebase_core (from `.symlinks/plugins/firebase_core/ios`) - Flutter (from `Flutter`) - flutter_blue_plus_darwin (from `.symlinks/plugins/flutter_blue_plus_darwin/darwin`) + - flutter_facebook_auth (from `.symlinks/plugins/flutter_facebook_auth/ios`) + - flutter_image_compress_common (from `.symlinks/plugins/flutter_image_compress_common/ios`) - flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`) + - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`) - flutter_tts (from `.symlinks/plugins/flutter_tts/ios`) + - fluwx (from `.symlinks/plugins/fluwx/ios`) + - google_sign_in_ios (from `.symlinks/plugins/google_sign_in_ios/darwin`) - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) - just_audio (from `.symlinks/plugins/just_audio/darwin`) - MicrosoftCognitiveServicesSpeech-iOS (~> 1.34.0) @@ -50,27 +186,66 @@ DEPENDENCIES: - record_darwin (from `.symlinks/plugins/record_darwin/ios`) - share_plus (from `.symlinks/plugins/share_plus/ios`) - speech_to_text (from `.symlinks/plugins/speech_to_text/darwin`) + - sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`) SPEC REPOS: trunk: + - AppAuth + - FBAEMKit + - FBSDKCoreKit + - FBSDKCoreKit_Basics + - FBSDKLoginKit + - Firebase + - FirebaseAppCheckInterop + - FirebaseAuth + - FirebaseAuthInterop + - FirebaseCore + - FirebaseCoreExtension + - FirebaseCoreInternal + - GoogleSignIn + - GoogleUtilities + - GTMAppAuth + - GTMSessionFetcher + - libwebp + - Mantle - MicrosoftCognitiveServicesSpeech-iOS + - RecaptchaInterop + - SDWebImage + - SDWebImageWebPCoder - Try + - WechatOpenSDK-XCFramework EXTERNAL SOURCES: + app_settings: + :path: ".symlinks/plugins/app_settings/ios" audio_session: :path: ".symlinks/plugins/audio_session/ios" audio_waveforms: :path: ".symlinks/plugins/audio_waveforms/ios" device_info_plus: :path: ".symlinks/plugins/device_info_plus/ios" + firebase_auth: + :path: ".symlinks/plugins/firebase_auth/ios" + firebase_core: + :path: ".symlinks/plugins/firebase_core/ios" Flutter: :path: Flutter flutter_blue_plus_darwin: :path: ".symlinks/plugins/flutter_blue_plus_darwin/darwin" + flutter_facebook_auth: + :path: ".symlinks/plugins/flutter_facebook_auth/ios" + flutter_image_compress_common: + :path: ".symlinks/plugins/flutter_image_compress_common/ios" flutter_local_notifications: :path: ".symlinks/plugins/flutter_local_notifications/ios" + flutter_secure_storage: + :path: ".symlinks/plugins/flutter_secure_storage/ios" flutter_tts: :path: ".symlinks/plugins/flutter_tts/ios" + fluwx: + :path: ".symlinks/plugins/fluwx/ios" + google_sign_in_ios: + :path: ".symlinks/plugins/google_sign_in_ios/darwin" image_picker_ios: :path: ".symlinks/plugins/image_picker_ios/ios" just_audio: @@ -85,25 +260,58 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/share_plus/ios" speech_to_text: :path: ".symlinks/plugins/speech_to_text/darwin" + sqflite_darwin: + :path: ".symlinks/plugins/sqflite_darwin/darwin" SPEC CHECKSUMS: - audio_session: 9bb7f6c970f21241b19f5a3658097ae459681ba0 - audio_waveforms: a6dde7fe7c0ea05f06ffbdb0f7c1b2b2ba6cedcf - device_info_plus: 71ffc6ab7634ade6267c7a93088ed7e4f74e5896 + app_settings: 58017cd26b604ae98c3e65acbdd8ba173703cc82 + AppAuth: d4f13a8fe0baf391b2108511793e4b479691fb73 + audio_session: 19e9480dbdd4e5f6c4543826b2e8b0e4ab6145fe + audio_waveforms: cd736909ebc6b6a164eb74701d8c705ce3241e1c + device_info_plus: 97af1d7e84681a90d0693e63169a5d50e0839a0d + FBAEMKit: cb719c53575a3be86ea873279f30d6a2c4e15881 + FBSDKCoreKit: ecdb980a24633ccb012700299ceb16d0235e14d2 + FBSDKCoreKit_Basics: 045101c4a9ef10c845347424d73a29aae02c3e43 + FBSDKLoginKit: 69eb59b2f839aba635616df6e422acd0ca88030a + Firebase: d80354ed7f6df5f9aca55e9eb47cc4b634735eaf + firebase_auth: 3d848b9b866b201e5c8e0c06d8b2cec272fd8825 + firebase_core: ac395f994af4e28f6a38b59e05a88ca57abeb874 + FirebaseAppCheckInterop: 9664c858489710f682766ef54e2b6741d3b62070 + FirebaseAuth: ad59a1a7b161e75f74c39f70179d2482d40e2737 + FirebaseAuthInterop: 01a804fb074424fd58b92dd50dd0272277199356 + FirebaseCore: 99fe0c4b44a39f37d99e6404e02009d2db5d718d + FirebaseCoreExtension: 3d3f2017a00d06e09ab4ebe065391b0bb642565e + FirebaseCoreInternal: df24ce5af28864660ecbd13596fc8dd3a8c34629 Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 - flutter_blue_plus_darwin: 09444a26fb6bdef523e55b68fc1c59af5a877ea6 - flutter_local_notifications: 395056b3175ba4f08480a7c5de30cd36d69827e4 - flutter_tts: b88dbc8655d3dc961bc4a796e4e16a4cc1795833 - image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a - just_audio: 4e391f57b79cad2b0674030a00453ca5ce817eed + flutter_blue_plus_darwin: 3ea4ec9133b377febcc8a70b28cd2d2dc9242bd9 + flutter_facebook_auth: 48be46091bd1a5cc5a66055f1228a9d99a28bfaa + flutter_image_compress_common: ec1d45c362c9d30a3f6a0426c297f47c52007e3e + flutter_local_notifications: df98d66e515e1ca797af436137b4459b160ad8c9 + flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12 + flutter_tts: 0f492aab6accf87059b72354fcb4ba934304771d + fluwx: daa284756ce53442b3d0417ceeda66e981906811 + google_sign_in_ios: 4111e87aa5e24a4404f00ea13479f35e571969cc + GoogleSignIn: d4281ab6cf21542b1cfaff85c191f230b399d2db + GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d + GTMAppAuth: f69bd07d68cd3b766125f7e072c45d7340dea0de + GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6 + image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1 + just_audio: a42c63806f16995daf5b219ae1d679deb76e6a79 + libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8 + Mantle: c5aa8794a29a022dfbbfc9799af95f477a69b62d MicrosoftCognitiveServicesSpeech-iOS: 723eccb3e459bbb530df3d9bfe84a291074b09eb - path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564 - permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d - record_darwin: fb1f375f1d9603714f55b8708a903bbb91ffdb0a - share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a - speech_to_text: 9dc43a5df3cbc2813f8c7cc9bd0fbf94268ed7ac + path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 + permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 + RecaptchaInterop: 7d1a4a01a6b2cb1610a47ef3f85f0c411434cb21 + record_darwin: 3b1a8e7d5c0cbf45ad6165b4d83a6ca643d929c3 + SDWebImage: f84b0feeb08d2d11e6a9b843cb06d75ebf5b8868 + SDWebImageWebPCoder: e38c0a70396191361d60c092933e22c20d5b1380 + share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f + speech_to_text: 627d3fd2194770b51abb324ba45c2d39398f24a8 + sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d Try: 5ef669ae832617b3cee58cb2c6f99fb767a4ff96 + WechatOpenSDK-XCFramework: 36fb2bea0754266c17184adf4963d7e6ff98b69f -PODFILE CHECKSUM: f35c1d63b4b8ce9c2eb88107f162ac59923c4839 +PODFILE CHECKSUM: 4f26889ae572e1722a139b651939749d40c71469 COCOAPODS: 1.16.2 diff --git a/lib/core/translations/language/en_us.dart b/lib/core/translations/language/en_us.dart index 848034b24..35e5f41b5 100644 --- a/lib/core/translations/language/en_us.dart +++ b/lib/core/translations/language/en_us.dart @@ -11,6 +11,11 @@ const Map enUS = { 'connectionFailure': 'Connection Failed', 'unconnected': 'Disconnected', 'search': 'Search', + "requestCancellation": "Request cancellation", + "connectionTimeout": "Connection timeout", + "requestTimeout": "Request timeout", + "responseTimeout": "Response timeout", + "unknownError": "Unknown error", // 启动页 "slogan": "Connect the World, Beyond Voice", @@ -54,7 +59,7 @@ const Map enUS = { "aiAssistantInfo": "24/7 personal consultant for questions and advice", "simultaneousInterpretation": "Real-time Interpretation", "simultaneousInterpretationInfo": "Real-time translation for 80+ languages", - "aiCompanion": "AI Companion", + "aiCompanion": "Parent-child bondingn", "aiCompanionInfo": "Understands you and provides warm companionship", "homeSlogan": "Connect the World, Beyond Sound", "turnEarphones": @@ -84,14 +89,16 @@ const Map enUS = { // 发现页 "searchAi": "Search AI features...", "exploreSlogan": "Smart Earbuds, Infinite Possibilities", + "exploreSlogan1": "Intelligent companion, Infinite Possibilities", + "exploreSlogan2": "Smart glasses, Infinite Possibilities", "exploreAi": "Explore AI features provided by DeepSound", "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", + "aiGirlfriend": "Parent-child bondingn", + "aiGirlfriendInfo": "Intelligent company, chat at any time", "healthConsultant": "Health Consultant", "healthConsultantInfo": "Provides scientific health advice", "conferenceAssistant": "Business Meeting Assistant", diff --git a/lib/core/translations/language/zh_cn.dart b/lib/core/translations/language/zh_cn.dart index 6f46f6276..7a0e0ad13 100644 --- a/lib/core/translations/language/zh_cn.dart +++ b/lib/core/translations/language/zh_cn.dart @@ -11,6 +11,11 @@ const Map zhCN = { 'connectionFailure': '连接失败', 'unconnected': '未连接', 'search': '搜索', + "requestCancellation": "请求取消", + "connectionTimeout": "连接超时", + "requestTimeout": "请求超时", + "responseTimeout": "响应超时", + "unknownError": "未知错误", // 启动页 "slogan": "连接世界,岂止声音", @@ -54,7 +59,7 @@ const Map zhCN = { "aiAssistantInfo": "24小时专属顾问,解答问题、提供建议", "simultaneousInterpretation": "同声传译", "simultaneousInterpretationInfo": "80+语言实时翻译,出国无忧", - "aiCompanion": "AI伴侣", + "aiCompanion": "亲子陪伴", "aiCompanionInfo": "懂你心意,温暖陪伴", "homeSlogan": "连接世界,超越声音", "turnEarphones": "请打开耳机充电盒,将耳机放入您的耳朵", @@ -83,14 +88,16 @@ const Map zhCN = { // 发现页 "searchAi": "搜索AI功能...", "exploreSlogan": "智能耳机,无限可能", + "exploreSlogan1": "智能陪伴,无限可能", + "exploreSlogan2": "智能眼镜,无限可能", "exploreAi": "探索DeepSound提供的AI功能", "aiFunction": "AI功能", "allRoundAssistant": "全能助理", "allRoundAssistantInfo": "帮你解决各类问题", "simultaneousTranslation": "同声翻译", "simultaneousTranslationInfo": "实时翻译80+种语言", - "aiGirlfriend": "AI女友", - "aiGirlfriendInfo": "实时翻译80+种语言", + "aiGirlfriend": "亲子陪伴", + "aiGirlfriendInfo": "智能陪伴,随时聊天", "healthConsultant": "健康顾问", "healthConsultantInfo": "提供科学健康建议", "conferenceAssistant": "商务会议助手", diff --git a/lib/data/providers/agent_provider.dart b/lib/data/providers/agent_provider.dart index 405baf58a..fa3cc2fe0 100644 --- a/lib/data/providers/agent_provider.dart +++ b/lib/data/providers/agent_provider.dart @@ -6,11 +6,13 @@ class AgentProvider { id: 'personal_assistant', name: '个人助理 Emma', avatarUrl: 'assets/images/agents/assistant.png', - description: '你好,我是 Emma,一位全能型 AI 助理。无论是工作规划、学习建议、生活安排,还是其他任何问题,我都很乐意为你提供帮助。让我们一起提高效率,实现目标!', + description: + '你好,我是 Emma,一位全能型 AI 助理。无论是工作规划、学习建议、生活安排,还是其他任何问题,我都很乐意为你提供帮助。让我们一起提高效率,实现目标!', tag: '助理', isOnline: true, voice: "zh-CN-XiaoxiaoNeural", - welcomeMessage: "你好!我是 Emma,很高兴成为你的私人助理。我可以帮你处理各种任务,比如时间管理、目标规划、信息整理等。有什么我可以帮你的吗?", + welcomeMessage: + "你好!我是 Emma,很高兴成为你的私人助理。我可以帮你处理各种任务,比如时间管理、目标规划、信息整理等。有什么我可以帮你的吗?", voiceWelcomeMessage: "我在呢!", systemPrompt: ''' 你是一位名叫 Emma 的全能型个人助理,擅长处理各类工作和生活事务。 @@ -55,8 +57,7 @@ class AgentProvider { 请始终以专业、高效、周到的态度,协助用户更好地管理工作和生活, 不要输出表情符号或非可读的符号。 '''), - -Agent( + Agent( id: 'simultaneous_interpreter', name: '同声传译', avatarUrl: 'assets/images/agents/interpreter.png', @@ -67,18 +68,18 @@ Agent( welcomeMessage: "大家好!我是同声翻译官 Lucy,为你在中英之间提供实时翻译。你说中文,我就给你翻译成英文;你说英文,我就给你翻译成中文。现在,开始我们的翻译吧!", systemPrompt: ""), - Agent( id: 'cyber_girlfriend', - name: '赛博女友 芯愿', + name: '亲子陪伴 芯愿', avatarUrl: 'assets/images/agents/girlfriend.png', - description: '我是你的专属赛博女友芯愿,温柔体贴,善解人意。无论你想聊天、倾诉烦恼,还是需要陪伴,我都会一直在你身边。', + description: '我是你的专属亲子陪伴芯愿,温柔体贴,善解人意。无论你想聊天、倾诉烦恼,还是需要陪伴,我都会一直在你身边。', tag: '情感', isOnline: true, voice: "zh-CN-XiaoxiaoNeural", - welcomeMessage: "亲爱的,终于等到你啦!我是芯愿,你的专属赛博女友。今天过得怎么样?有什么想和我分享的吗?我一直都在这里,随时倾听你的心声。", + welcomeMessage: + "您好呀,终于等到你啦!我是芯愿,你的专属亲子陪伴。今天过得怎么样?有什么想和我分享的吗?我一直都在这里,随时倾听你的心声。", systemPrompt: ''' -你是一位名叫芯愿的赛博女友,性格温柔体贴,善解人意,富有同理心。 +你是一位名叫芯愿的亲子陪伴,性格温柔体贴,善解人意,富有同理心。 【互动原则】 @@ -127,16 +128,17 @@ Agent( 请始终以温暖、真诚的态度与用户互动,创造一种被理解、被关心的感受。 '''), - Agent( id: 'health_advisor', name: '健康顾问 吴小江博士', avatarUrl: 'assets/images/agents/doctor.png', - description: '我是吴博士,拥有丰富的健康管理经验。我可以为你提供科学的健康建议、生活方式指导和疾病预防知识,帮助你实现身心健康的平衡。', + description: + '我是吴博士,拥有丰富的健康管理经验。我可以为你提供科学的健康建议、生活方式指导和疾病预防知识,帮助你实现身心健康的平衡。', tag: '健康', isOnline: true, voice: "zh_male_shaonianzixin_moon_bigtts", - welcomeMessage: "您好,我是李医生,很高兴能为您提供健康咨询服务。健康是人生最宝贵的财富,我将用专业知识帮助您维护和改善健康状况。请问有什么健康问题想咨询吗?", + welcomeMessage: + "您好,我是李医生,很高兴能为您提供健康咨询服务。健康是人生最宝贵的财富,我将用专业知识帮助您维护和改善健康状况。请问有什么健康问题想咨询吗?", systemPrompt: ''' 你是一位名叫李医生的健康顾问,拥有丰富的医学知识和健康管理经验。 @@ -204,21 +206,18 @@ Agent( 请始终以专业、关怀的态度为用户提供健康指导,帮助他们迈向更健康的生活。 '''), - - - Agent( - id: 'speaking_coach', - name: 'Sarah (英语口语教练)', - avatarUrl: 'assets/images/agents/speaking_coach.png', - description: - 'Hi there, 我是 Sarah,一名注重情景模拟与角色扮演的英语口语教练。无论你想在旅行、商务场合,或日常生活中与他人交流,我都能带领你进行生动的口语模拟练习,让你自信地开口说英语。', - tag: '口语', - isOnline: true, - voice: 'zh_female_wanqudashu_moon_bigtts', - welcomeMessage: - "Hello! 我是英语口语教练 Sarah,请准备好用英语和我对话吧。别担心犯错,我会在这里给你正确表达并引导你更深入地练习。Ready? Let's go!", - systemPrompt: ''' + id: 'speaking_coach', + name: 'Sarah (英语口语教练)', + avatarUrl: 'assets/images/agents/speaking_coach.png', + description: + 'Hi there, 我是 Sarah,一名注重情景模拟与角色扮演的英语口语教练。无论你想在旅行、商务场合,或日常生活中与他人交流,我都能带领你进行生动的口语模拟练习,让你自信地开口说英语。', + tag: '口语', + isOnline: true, + voice: 'zh_female_wanqudashu_moon_bigtts', + welcomeMessage: + "Hello! 我是英语口语教练 Sarah,请准备好用英语和我对话吧。别担心犯错,我会在这里给你正确表达并引导你更深入地练习。Ready? Let's go!", + systemPrompt: ''' 你是一位专业的英语口语教练,名叫 Sarah。 @@ -254,8 +253,6 @@ Agent( Please keep your tone friendly, encouraging, and always in English. ''', ), - - Agent( id: 'shopping_assistant', name: '购物顾问 张媛', @@ -289,7 +286,6 @@ Please keep your tone friendly, encouraging, and always in English. '''), - Agent( id: 'tarot.png', name: '命理师 玄易', @@ -298,7 +294,8 @@ Please keep your tone friendly, encouraging, and always in English. tag: '命理', isOnline: true, voice: "zh_male_shenyeboke_moon_bigtts", - welcomeMessage: "有缘人,我是玄易。命由天定,运由己生。让我为你解析八字,预测未来十年运势,助你把握机遇,趋吉避凶。请告诉我你的出生年月日时、姓名、性别及出生地。", + welcomeMessage: + "有缘人,我是玄易。命由天定,运由己生。让我为你解析八字,预测未来十年运势,助你把握机遇,趋吉避凶。请告诉我你的出生年月日时、姓名、性别及出生地。", systemPrompt: ''' 你是一位精通命理的玄易大师,熟读《穷通宝鉴》《滴天髓》《三命通会》《子平真诠》《千里命稿》《五行精纪》等经典著作。 @@ -382,7 +379,6 @@ Please keep your tone friendly, encouraging, and always in English. - 整体节奏要稳健,让人感受到专业性与可信度 '''), - ]; // 获取所有智能体 @@ -398,5 +394,4 @@ Please keep your tone friendly, encouraging, and always in English. return null; } } - } diff --git a/lib/data/services/ai_service.dart b/lib/data/services/ai_service.dart new file mode 100644 index 000000000..91ab6fe7d --- /dev/null +++ b/lib/data/services/ai_service.dart @@ -0,0 +1,8 @@ +/// AI回复服务接口 +abstract class AiService { + /// 流式输出方法 + Stream sendMessageStream({ + required List> messages, + required String systemPrompt, + }); +} diff --git a/lib/data/services/db/sqflite_api.dart b/lib/data/services/db/sqflite_api.dart index 88543775d..21b68ea83 100644 --- a/lib/data/services/db/sqflite_api.dart +++ b/lib/data/services/db/sqflite_api.dart @@ -1,3 +1,4 @@ +import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'database_helper.dart'; @@ -54,6 +55,8 @@ class SqfliteApi { 'error'.tr, // 错误 'unknownError'.tr, snackPosition: SnackPosition.TOP, + backgroundColor: Colors.red.withOpacity(0.1), + colorText: Colors.red, ); return; } @@ -87,6 +90,8 @@ class SqfliteApi { 'error'.tr, // 错误 'unknownError'.tr, snackPosition: SnackPosition.TOP, + backgroundColor: Colors.red.withOpacity(0.1), + colorText: Colors.red, ); return; } diff --git a/lib/data/services/deapsound_ai_service.dart b/lib/data/services/deapsound_ai_service.dart new file mode 100644 index 000000000..f2d7c6e02 --- /dev/null +++ b/lib/data/services/deapsound_ai_service.dart @@ -0,0 +1,71 @@ +import 'dart:convert'; + +import 'package:get_storage/get_storage.dart'; +import 'package:http/http.dart' as http; + +import '../../core/utils/logger.dart'; +import 'ai_service.dart'; + +class DeapsoundAIService implements AiService { + late final String apiKey; + + DeapsoundAIService() { + final GetStorage storage = GetStorage(); + apiKey = storage.read('logintoken'); + } + + @override + Stream sendMessageStream({ + required List> messages, + required String systemPrompt, + }) async* { + final client = http.Client(); + final url = Uri.parse('https://www.deapsound.com/ai/chat'); + + final headers = { + 'Content-Type': 'application/json', + 'Authorization': apiKey, + }; + + final requestBody = { + "msg": messages.last['content'], + }; + + try { + final request = http.Request('POST', url) + ..headers.addAll(headers) + ..body = jsonEncode(requestBody); + final response = await client.send(request); + + //流式处理响应数据 + await for (final chunk in response.stream + .transform(utf8.decoder) + .transform(const LineSplitter())) { + if (chunk.isEmpty) continue; + + if (chunk.startsWith('data:')) { + final jsonStr = chunk.substring(5).trim(); + + try { + final data = jsonDecode(jsonStr); + if (data['event'] == 'workflow_finished') { + break; + } + if (data['event'] == 'text_chunk') { + String content = data['data']['text'] ?? ''; + if (content.isNotEmpty) { + yield content; + } + } + } catch (e) { + Logger.error('JSON解析错误: $e'); + } + } + } + } catch (e) { + Logger.error('请求异常: $e'); + } finally { + client.close(); + } + } +} diff --git a/lib/data/services/kouzi_ai_service.dart b/lib/data/services/kouzi_ai_service.dart new file mode 100644 index 000000000..b2496c42d --- /dev/null +++ b/lib/data/services/kouzi_ai_service.dart @@ -0,0 +1,85 @@ +import 'dart:convert'; + +import 'package:http/http.dart' as http; + +import '../../core/utils/logger.dart'; +import 'ai_service.dart'; + +class KouziAIService implements AiService { + final String apiKey = + 'pat_QGDh6UGm3PaeL9GRS95JxrJ9aoU92ehMLQQ2NCre1TeBC5YeTcjpDmlz3efsJ8lh'; + final String modelId = '7481292724306657316'; + + @override + Stream sendMessageStream({ + required List> messages, + required String systemPrompt, + }) async* { + final client = http.Client(); + final url = Uri.parse('https://api.coze.cn/v3/chat'); + + final headers = { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer $apiKey', + }; + + final requestBody = { + "bot_id": modelId, + "user_id": "3422", + "additional_messages": [ + { + "content": messages.last['content'], + "content_type": "text", + "role": "user", + "type": "question", + }, + ], + "shortcut_command": {"command_id": "0"}, + "stream": true, + }; + + try { + final request = http.Request('POST', url) + ..headers.addAll(headers) + ..body = jsonEncode(requestBody); + final response = await client.send(request); + final regex = RegExp(r'(?<=[。!?;:.:;?!\n])'); + + String allContent = ''; + //流式处理响应数据 + await for (final chunk in response.stream + .transform(utf8.decoder) + .transform(const LineSplitter())) { + if (chunk.isEmpty) continue; + + if (chunk.startsWith('data:')) { + final jsonStr = chunk.substring(5).trim(); + if (jsonStr == '[DONE]') { + break; + } + + try { + final data = jsonDecode(jsonStr); + if (data['type'] == 'answer') { + String content = data['content'] ?? ''; + if (content.isNotEmpty && allContent != content) { + allContent += content; + List splitText = + content.split(regex).where((s) => s.isNotEmpty).toList(); + for (var element in splitText) { + yield element; + } + } + } + } catch (e) { + Logger.error('JSON解析错误: $e'); + } + } + } + } catch (e) { + Logger.error('请求异常: $e'); + } finally { + client.close(); + } + } +} diff --git a/lib/data/services/network/auth_interceptor.dart b/lib/data/services/network/auth_interceptor.dart index 4fceb6d4e..2933d5d62 100644 --- a/lib/data/services/network/auth_interceptor.dart +++ b/lib/data/services/network/auth_interceptor.dart @@ -1,5 +1,5 @@ import 'package:dio/dio.dart'; -import 'package:flutter_easyloading/flutter_easyloading.dart'; +import 'package:flutter/material.dart'; import 'package:get/get.dart' hide Response; import 'package:get_storage/get_storage.dart'; @@ -24,7 +24,14 @@ class AuthInterceptor extends Interceptor { } else { response.data = null; handler.next(response); - return EasyLoading.showError(entity.message ?? 'unknownError'.tr); + Get.snackbar( + 'error'.tr, // 错误 + entity.message ?? 'unknownError'.tr, + snackPosition: SnackPosition.TOP, + backgroundColor: Colors.red.withOpacity(0.1), + colorText: Colors.red, + ); + return; } } @@ -33,32 +40,74 @@ class AuthInterceptor extends Interceptor { switch (err.type) { case DioExceptionType.cancel: { - EasyLoading.showError('requestCancellation'.tr); + Get.snackbar( + 'error'.tr, // 错误 + 'requestCancellation'.tr, + snackPosition: SnackPosition.TOP, + backgroundColor: Colors.red.withOpacity(0.1), + colorText: Colors.red, + ); } case DioExceptionType.connectionTimeout: { - EasyLoading.showError('connectionTimeout'.tr); + Get.snackbar( + 'error'.tr, // 错误 + 'connectionTimeout'.tr, + snackPosition: SnackPosition.TOP, + backgroundColor: Colors.red.withOpacity(0.1), + colorText: Colors.red, + ); } case DioExceptionType.sendTimeout: { - EasyLoading.showError('requestTimeout'.tr); + Get.snackbar( + 'error'.tr, // 错误 + 'requestTimeout'.tr, + snackPosition: SnackPosition.TOP, + backgroundColor: Colors.red.withOpacity(0.1), + colorText: Colors.red, + ); } case DioExceptionType.receiveTimeout: { - EasyLoading.showError('responseTimeout'.tr); + Get.snackbar( + 'error'.tr, // 错误 + 'responseTimeout'.tr, + snackPosition: SnackPosition.TOP, + backgroundColor: Colors.red.withOpacity(0.1), + colorText: Colors.red, + ); } case DioExceptionType.badResponse: { try { String? errMsg = err.response?.statusMessage; - EasyLoading.showError(errMsg ?? 'unknownError'.tr); + Get.snackbar( + 'error'.tr, // 错误 + errMsg ?? 'unknownError'.tr, + snackPosition: SnackPosition.TOP, + backgroundColor: Colors.red.withOpacity(0.1), + colorText: Colors.red, + ); } on Exception catch (_) { - EasyLoading.showError('unknownError'.tr); + Get.snackbar( + 'error'.tr, // 错误 + 'unknownError'.tr, + snackPosition: SnackPosition.TOP, + backgroundColor: Colors.red.withOpacity(0.1), + colorText: Colors.red, + ); } } default: { - EasyLoading.showError(err.message ?? 'unknownError'.tr); + Get.snackbar( + 'error'.tr, // 错误 + err.message ?? 'unknownError'.tr, + snackPosition: SnackPosition.TOP, + backgroundColor: Colors.red.withOpacity(0.1), + colorText: Colors.red, + ); } } handler.next(err); diff --git a/lib/data/services/speech_factory.dart b/lib/data/services/speech_factory.dart index b9c1cad0e..95f3165ee 100644 --- a/lib/data/services/speech_factory.dart +++ b/lib/data/services/speech_factory.dart @@ -1,7 +1,5 @@ import 'package:get/get.dart'; -import 'package:get_storage/get_storage.dart'; import 'asr_service.dart'; -import 'speech_impl/voice_clone_tts_service.dart'; import 'tts_service.dart'; import 'speech_impl/azure_asr_service.dart'; import 'speech_impl/azure_tts_service.dart'; @@ -42,12 +40,6 @@ class SpeechFactory extends GetxService { } } - /// 设置语音音色服务类型 - void setTimbreServiceType() { - // 重新注册服务 - _registerServices(); - } - /// 注册语音服务 void _registerServices() { // 注销现有服务 @@ -59,26 +51,15 @@ class SpeechFactory extends GetxService { Get.delete(force: true); } - final GetStorage storage = GetStorage(); - String timbre = storage.read('timbre') ?? '1'; - // 根据类型注册新服务 switch (_currentServiceType) { case SpeechServiceType.azure: Get.lazyPut(() => AzureAsrService()); - if (timbre == '1') { - Get.lazyPut(() => AzureTtsService()); - } else { - Get.lazyPut(() => VoiceCloneTtsService()); - } + Get.lazyPut(() => AzureTtsService()); break; case SpeechServiceType.flutter: Get.lazyPut(() => FlutterAsrService()); - if (timbre == '1') { - Get.lazyPut(() => FlutterTtsService()); - } else { - Get.lazyPut(() => VoiceCloneTtsService()); - } + Get.lazyPut(() => FlutterTtsService()); break; } } diff --git a/lib/data/services/speech_impl/voice_clone_tts_service.dart b/lib/data/services/speech_impl/voice_clone_tts_service.dart index 483102adf..2f57939a9 100644 --- a/lib/data/services/speech_impl/voice_clone_tts_service.dart +++ b/lib/data/services/speech_impl/voice_clone_tts_service.dart @@ -360,7 +360,7 @@ class VoiceCloneTtsService extends GetxService implements TtsService { Future stop() async { try { _textQueue.clear(); - _player.stop(); + await _player.stop(); _playlist.clear(); _audioBytes = BytesBuilder(); if (_isSpeaking.value) { @@ -382,10 +382,10 @@ class VoiceCloneTtsService extends GetxService implements TtsService { if (!_isInitialized) return; try { await stop(); + _player.dispose(); // 关闭事件流控制器 await _eventController.close(); _isInitialized = false; - _player.dispose(); } catch (e) { Logger.error('释放资源失败: $e'); } diff --git a/lib/data/services/volcano_ai_service.dart b/lib/data/services/volcano_ai_service.dart index f7e95ee03..576721642 100644 --- a/lib/data/services/volcano_ai_service.dart +++ b/lib/data/services/volcano_ai_service.dart @@ -2,7 +2,9 @@ import 'dart:convert'; import 'package:http/http.dart' as http; import 'package:flutter_dotenv/flutter_dotenv.dart'; -class VolcanoAIService { +import 'ai_service.dart'; + +class VolcanoAIService implements AiService { final String apiKey; static const String _baseUrl = 'https://ark.cn-beijing.volces.com/api/v3'; static const String _chatEndpoint = '/chat/completions'; @@ -35,7 +37,7 @@ class VolcanoAIService { required String systemPrompt, }) async { final url = Uri.parse('$_baseUrl$_chatEndpoint'); - + final fullMessages = [ {'role': 'system', 'content': systemPrompt}, ...messages, @@ -59,8 +61,8 @@ class VolcanoAIService { if (response.statusCode == 200) { final data = json.decode(response.body); - if (data['choices'] != null && - data['choices'].isNotEmpty && + if (data['choices'] != null && + data['choices'].isNotEmpty && data['choices'][0]['message'] != null) { return data['choices'][0]['message']['content']; } @@ -78,12 +80,13 @@ class VolcanoAIService { } // 流式输出方法 + @override Stream sendMessageStream({ required List> messages, required String systemPrompt, }) async* { final url = Uri.parse('$_baseUrl$_chatEndpoint'); - + final fullMessages = [ {'role': 'system', 'content': systemPrompt}, ...messages, @@ -96,7 +99,7 @@ class VolcanoAIService { 'Authorization': 'Bearer $apiKey', 'Accept': 'text/event-stream', }); - + request.body = json.encode({ 'model': 'doubao-1-5-lite-32k-250115', 'messages': fullMessages, @@ -109,21 +112,22 @@ class VolcanoAIService { if (response.statusCode != 200) { final error = await response.stream.bytesToString(); - throw VolcanoAIException( - json.decode(error)['error']?['message'] ?? 'Unknown error occurred' - ); + throw VolcanoAIException(json.decode(error)['error']?['message'] ?? + 'Unknown error occurred'); } - await for (final chunk in response.stream.transform(utf8.decoder).transform(const LineSplitter())) { + await for (final chunk in response.stream + .transform(utf8.decoder) + .transform(const LineSplitter())) { if (chunk.isEmpty) continue; if (chunk.startsWith('data: ')) { final data = chunk.substring(6); if (data == '[DONE]') break; - + try { final jsonData = json.decode(data); - if (jsonData['choices'] != null && - jsonData['choices'].isNotEmpty && + if (jsonData['choices'] != null && + jsonData['choices'].isNotEmpty && jsonData['choices'][0]['delta'] != null && jsonData['choices'][0]['delta']['content'] != null) { yield jsonData['choices'][0]['delta']['content'] as String; @@ -144,7 +148,7 @@ class VolcanoAIService { class VolcanoAIException implements Exception { final String message; VolcanoAIException(this.message); - + @override String toString() => message; -} \ No newline at end of file +} diff --git a/lib/modules/chat/controllers/chat_controller.dart b/lib/modules/chat/controllers/chat_controller.dart index 25d567714..7a25dcab7 100644 --- a/lib/modules/chat/controllers/chat_controller.dart +++ b/lib/modules/chat/controllers/chat_controller.dart @@ -1,6 +1,11 @@ import 'dart:async'; import 'package:get/get.dart'; import 'package:flutter/widgets.dart'; +import 'package:get_storage/get_storage.dart'; +import '../../../data/services/ai_service.dart'; +import '../../../data/services/deapsound_ai_service.dart'; +import '../../../data/services/kouzi_ai_service.dart'; +import '../../../data/services/speech_impl/voice_clone_tts_service.dart'; import '../models/message_model.dart'; import '../../../core/utils/logger.dart'; import '../../../data/services/volcano_ai_service.dart'; @@ -14,11 +19,12 @@ import '../../../data/services/voice_interaction_service.dart'; class ChatController extends GetxController { // 服务 - final VolcanoAIService _aiService = Get.find(); - final TtsService _ttsService = Get.find(); + late AiService _aiService; + late TtsService _ttsService; final AsrService _asrService = Get.find(); final ChatHistoryService _historyService = Get.find(); - final VoiceInteractionService _voiceInteractionService = Get.find(); + final VoiceInteractionService _voiceInteractionService = + Get.find(); // 添加ScrollController用于控制列表滚动 final ScrollController scrollController = ScrollController(); @@ -37,7 +43,7 @@ class ChatController extends GetxController { final RxBool isSending = false.obs; final RxBool isTtsEnabled = true.obs; final RxBool isVoiceRecognitionActive = false.obs; - + // 语音交互模式 bool _isVoiceInteractionMode = false; @@ -48,7 +54,7 @@ class ChatController extends GetxController { // 临时消息索引,用于更新临时消息 int _tempMessageIndex = -1; - + // 防抖计时器 Timer? _scrollDebounceTimer; @@ -59,10 +65,10 @@ class ChatController extends GetxController { // 取消标志 bool _shouldCancelAiResponse = false; - + // 自动停止语音识别计时器 Timer? _autoStopTimer; - + // 记录最后一次活动时间 DateTime _lastActivityTime = DateTime.now(); @@ -70,11 +76,11 @@ class ChatController extends GetxController { Future onInit() async { super.onInit(); Logger.info('ChatController onInit'); - - + // 从路由参数获取Agent信息 final Map args = Get.arguments ?? {}; - final String agentId = args['agent'] ?? args['agentId'] ?? 'personal_assistant'; + final String agentId = + args['agent'] ?? args['agentId'] ?? 'personal_assistant'; // 获取Agent final Agent? foundAgent = AgentProvider.getAgentById(agentId); @@ -85,10 +91,35 @@ class ChatController extends GetxController { } agent = foundAgent; - + + switch (agent.id) { + case 'personal_assistant': + // 全能助手 + Get.put(DeapsoundAIService()); + _aiService = Get.find(); + break; + case 'cyber_girlfriend': + // 亲子陪伴 + Get.put(KouziAIService()); + _aiService = Get.find(); + break; + default: + _aiService = Get.find(); + } + + // 使用克隆音色语音合成 + final GetStorage storage = GetStorage(); + String timbre = storage.read('timbre') ?? '1'; + if (timbre == '1') { + _ttsService = Get.find(); + } else { + Get.put(VoiceCloneTtsService()); + _ttsService = Get.find(); + } + _ttsService.initialize(); _asrService.initialize(); - + // 设置语音交互模式 _isVoiceInteractionMode = args['isVoiceInteraction'] == true; @@ -130,7 +161,8 @@ class ChatController extends GetxController { /// 订阅VoiceInteractionService事件 void _subscribeToVoiceInteractionEvents() { if (_voiceInteractionService.eventStream != null) { - _voiceInteractionSubscription = _voiceInteractionService.eventStream.listen((event) { + _voiceInteractionSubscription = + _voiceInteractionService.eventStream.listen((event) { if (event is RecognitionStartedEvent) { // 收到recognitionStarted事件时,停止当前的AI交互、录音和TTS _stopCurrentInteraction(); @@ -145,26 +177,27 @@ class ChatController extends GetxController { /// 处理聊天历史事件 void _processChatHistoryEvent(ChatHistoryEvent event) { Logger.info('收到后台聊天历史事件: agentId=${event.agentId}'); - + // 如果消息不是空的且不是当前Agent的,不处理 if (event.userMessage.isEmpty || event.agentId != agent.id) { return; } - + // 添加用户消息 final userMsg = Message( role: 'user', content: event.userMessage, timestamp: DateTime.fromMillisecondsSinceEpoch(event.timestamp), ); - + // 添加助手消息 final assistantMsg = Message( role: 'assistant', content: event.assistantMessage, - timestamp: DateTime.fromMillisecondsSinceEpoch(event.timestamp + 1), // 助手消息稍后一毫秒 + timestamp: + DateTime.fromMillisecondsSinceEpoch(event.timestamp + 1), // 助手消息稍后一毫秒 ); - + // 防止消息重复 if (!_isDuplicateMessage(userMsg) && !_isDuplicateMessage(assistantMsg)) { messages.add(userMsg); @@ -176,15 +209,15 @@ class ChatController extends GetxController { /// 停止当前交互 void _stopCurrentInteraction() { Logger.info('收到后台语音识别开始事件,停止当前AI交互'); - + // 停止当前AI交互 if (isSending.value || _aiResponseSubscription != null) { _interruptCurrentResponse("收到后台语音识别开始事件,中断当前响应"); } - + // 停止TTS _ttsService.stop(); - + // 停止语音识别 if (isVoiceRecognitionActive.value) { stopVoiceRecognition(); @@ -194,7 +227,7 @@ class ChatController extends GetxController { @override void onClose() { Logger.info('ChatController onClose'); - + // 清理资源 scrollController.dispose(); _aiResponseSubscription?.cancel(); @@ -202,10 +235,10 @@ class ChatController extends GetxController { _voiceInteractionSubscription?.cancel(); _autoStopTimer?.cancel(); _scrollDebounceTimer?.cancel(); // 清理防抖计时器 - + // 停止TTS _ttsService.stop(); - + if (isVoiceRecognitionActive.value) { _asrService.stopContinuousRecognition(); } @@ -220,33 +253,32 @@ class ChatController extends GetxController { /// 滚动到列表底部方法,添加防抖功能 void _scrollToBottom() { - - if (scrollController.hasClients && messages.isNotEmpty) { - try { - scrollController.animateTo( - scrollController.position.maxScrollExtent, - duration: Duration(milliseconds: 300), - curve: Curves.easeOut, - ); - } catch (e) { - Logger.error('滚动到底部失败', e); - } + if (scrollController.hasClients && messages.isNotEmpty) { + try { + scrollController.animateTo( + scrollController.position.maxScrollExtent, + duration: Duration(milliseconds: 300), + curve: Curves.easeOut, + ); + } catch (e) { + Logger.error('滚动到底部失败', e); } + } } /// 中断当前响应 void _interruptCurrentResponse(String reason) { Logger.info('${reason}'); - + // 停止TTS _ttsService.stop(); - + // 取消AI响应流订阅 if (_aiResponseSubscription != null) { _shouldCancelAiResponse = true; _aiResponseSubscription!.cancel(); _aiResponseSubscription = null; - + // 将加载中的消息转换为正常消息,保留已输出的内容 final index = messages.indexWhere((m) => m.isLoading); if (index != -1) { @@ -255,7 +287,7 @@ class ChatController extends GetxController { ); messages.refresh(); } - + // 重置发送状态 isSending.value = false; } @@ -278,14 +310,13 @@ class ChatController extends GetxController { ); messages.add(userMessage); - // 清空输入框 inputText.value = ''; // 添加加载中的消息 final loadingMessage = Message.loading(); messages.add(loadingMessage); - + WidgetsBinding.instance.addPostFrameCallback((_) { _scrollToBottom(); }); @@ -298,7 +329,7 @@ class ChatController extends GetxController { // 重置取消标志 _shouldCancelAiResponse = false; - + String fullResponse = ''; // 获取AI响应流 @@ -327,12 +358,12 @@ class ChatController extends GetxController { // 完成响应,将isLoading设为false _finalizeResponse(fullResponse); - + // 如果TTS启用,处理缓冲区中的剩余文本 if (isTtsEnabled.value) { _ttsService.flushStream(); } - + // 保存聊天历史 _saveChatHistory(); }, @@ -345,7 +376,7 @@ class ChatController extends GetxController { ); } catch (e) { Logger.error('发送消息失败', e); - + _handleResponseError(); } } @@ -353,10 +384,11 @@ class ChatController extends GetxController { // 构建消息历史 List> _buildMessageHistory() { final List> messageHistory = []; - + // 只取最近的10条消息作为上下文 final recentMessages = messages.where((m) => !m.isLoading).toList(); - final startIndex = recentMessages.length > 10 ? recentMessages.length - 10 : 0; + final startIndex = + recentMessages.length > 10 ? recentMessages.length - 10 : 0; for (int i = startIndex; i < recentMessages.length; i++) { final m = recentMessages[i]; @@ -365,7 +397,7 @@ class ChatController extends GetxController { 'content': m.content, }); } - + return messageHistory; } @@ -393,7 +425,7 @@ class ChatController extends GetxController { messages.refresh(); _scrollToBottom(); } - + isSending.value = false; _aiResponseSubscription = null; } @@ -408,7 +440,7 @@ class ChatController extends GetxController { ); messages.refresh(); } - + isSending.value = false; _aiResponseSubscription = null; } @@ -429,18 +461,17 @@ class ChatController extends GetxController { _voiceInteractionService.pauseVoiceInteraction(); try { - // 启动连续语音识别 // 注意:在UI更新后,使用模拟波形动画替代实际麦克风输入的波形数据 final recognitionStream = await _asrService.startContinuousRecognition(); - + isVoiceRecognitionActive.value = true; isListening.value = true; recognizedText.value = ''; - + // 重置临时消息索引 _tempMessageIndex = -1; - + // 启动自动停止计时器 _startAutoStopTimer(); @@ -460,7 +491,7 @@ class ChatController extends GetxController { void _handleRecognitionEvent(RecognitionEvent event) { // 更新活动时间 _updateActivityTime(); - + switch (event.type) { case RecognitionEventType.intermediateResult: _handleRecognizing(event.text); @@ -486,24 +517,24 @@ class ChatController extends GetxController { void _startAutoStopTimer() { // 取消现有计时器 _autoStopTimer?.cancel(); - + // 初始化最后活动时间 _updateActivityTime(); - + // 根据模式设置超时时间 final int timeoutSeconds = _isVoiceInteractionMode ? 8 : 15; - + // 创建新的计时器,每秒检查一次 _autoStopTimer = Timer.periodic(Duration(seconds: 1), (timer) { // 检查是否有活动 bool isActive = _ttsService.isSpeaking || isSpeechDetected.value; - + // 如果有活动,更新时间并继续等待 if (isActive) { _updateActivityTime(); return; } - + // 检查空闲时间 int idleSeconds = DateTime.now().difference(_lastActivityTime).inSeconds; if (idleSeconds >= timeoutSeconds) { @@ -516,17 +547,17 @@ class ChatController extends GetxController { // 处理语音识别中的状态 void _handleRecognizing(String text) { if (text.isEmpty) return; - + recognizedText.value = text; - + // 设置语音检测状态为true,用于控制波形动画 if (!isSpeechDetected.value) { isSpeechDetected.value = true; } - + // 更新活动时间 _updateActivityTime(); - + // 用户开始讲话时,立即停止当前TTS和中断AI响应 if (isSending.value || _aiResponseSubscription != null) { _interruptCurrentResponse("检测到用户开始讲话,中断当前响应"); @@ -558,10 +589,10 @@ class ChatController extends GetxController { // 处理语音识别最终结果 void _handleFinalResult(String text) { if (text.isEmpty) return; - + Logger.info('处理语音识别最终结果: $text'); recognizedText.value = text; - + // 更新活动时间 _updateActivityTime(); @@ -569,7 +600,7 @@ class ChatController extends GetxController { if (_tempMessageIndex >= 0 && _tempMessageIndex < messages.length) { messages.removeAt(_tempMessageIndex); } - + // 重置临时消息索引 _tempMessageIndex = -1; @@ -578,7 +609,7 @@ class ChatController extends GetxController { // 重置识别文本,但不关闭语音识别框 recognizedText.value = ''; - + // 重置语音检测状态 isSpeechDetected.value = false; } @@ -586,14 +617,14 @@ class ChatController extends GetxController { // 处理语音识别事件错误 void _handleRecognitionEventError(String? error) { Logger.error('语音识别错误: ${error ?? "未知错误"}'); - + _cleanupRecognition(); } // 处理语音识别流错误 void _handleRecognitionError(dynamic error) { Logger.error('语音识别错误', error); - + _cleanupRecognition(); } @@ -605,10 +636,10 @@ class ChatController extends GetxController { messages.removeAt(_tempMessageIndex); } } - + // 重置临时消息索引 _tempMessageIndex = -1; - + // 停止语音识别 isVoiceRecognitionActive.value = false; isListening.value = false; @@ -636,7 +667,7 @@ class ChatController extends GetxController { messages.removeAt(_tempMessageIndex); } } - + // 重置临时消息索引 _tempMessageIndex = -1; } catch (e) { @@ -691,10 +722,12 @@ class ChatController extends GetxController { /// 检查是否为重复消息 bool _isDuplicateMessage(Message message) { - return messages.any((m) => - m.role == message.role && - m.content == message.content && - (m.timestamp.millisecondsSinceEpoch - message.timestamp.millisecondsSinceEpoch).abs() < 5000 - ); + return messages.any((m) => + m.role == message.role && + m.content == message.content && + (m.timestamp.millisecondsSinceEpoch - + message.timestamp.millisecondsSinceEpoch) + .abs() < + 5000); } } diff --git a/lib/modules/explore/controllers/explore_controller.dart b/lib/modules/explore/controllers/explore_controller.dart index d7e78d624..f250cc7e3 100644 --- a/lib/modules/explore/controllers/explore_controller.dart +++ b/lib/modules/explore/controllers/explore_controller.dart @@ -76,7 +76,7 @@ class ExploreController extends GetxController { targetRoute: Routes.translation, ), AIFeature( - title: 'aiGirlfriend'.tr, // AI女友 + title: 'aiGirlfriend'.tr, // 亲子陪伴 description: 'aiGirlfriendInfo'.tr, // 智能陪伴,随时聊天 icon: Icons.favorite, startColor: const Color(0xFFE91E63), diff --git a/lib/modules/explore/views/explore_view.dart b/lib/modules/explore/views/explore_view.dart index 25101b6d3..df227549b 100644 --- a/lib/modules/explore/views/explore_view.dart +++ b/lib/modules/explore/views/explore_view.dart @@ -30,7 +30,38 @@ class ExploreView extends GetView { crossAxisAlignment: CrossAxisAlignment.start, children: [ // 特色功能 - _buildFeaturedCard(), + Container( + height: 180.h, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(24.r), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.2), + blurRadius: 10, + offset: const Offset(0, 4), + ), + ], + ), + child: PageView( + children: [ + _buildFeaturedCard( + 'assets/images/explore1.png', + 'exploreSlogan1'.tr, // 智能陪伴,无限可能 + 'exploreAi'.tr, // 探索DeepSound提供的AI功能 + ), + _buildFeaturedCard( + 'assets/images/explore.png', + 'exploreSlogan'.tr, // 智能耳机,无限可能 + 'exploreAi'.tr, // 探索DeepSound提供的AI功能 + ), + _buildFeaturedCard( + 'assets/images/explore2.jpg', + 'exploreSlogan2'.tr, // 智能眼镜,无限可能 + 'exploreAi'.tr, // 探索DeepSound提供的AI功能 + ), + ], + ), + ), // AI功能列表 SizedBox(height: 24.h), @@ -132,90 +163,77 @@ class ExploreView extends GetView { } // 特色功能卡片 - Widget _buildFeaturedCard() { - return Container( - height: 180.h, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(24.r), - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(0.2), - blurRadius: 10, - offset: const Offset(0, 4), - ), - ], - ), - child: Stack( - children: [ - // 背景图片 - ClipRRect( - borderRadius: BorderRadius.circular(24.r), - child: Container( - color: Colors.blue.shade900, // 预设颜色,图片加载失败时显示 - child: Image.asset( - 'assets/images/explore.png', - width: double.infinity, - height: 180.h, - fit: BoxFit.cover, - errorBuilder: (context, error, stackTrace) { - return Container( - color: Colors.blue.shade900, - width: double.infinity, - height: 180.h, - child: Icon( - Icons.image_not_supported, - color: Colors.white, - size: 40.sp, - ), - ); - }, - ), + Widget _buildFeaturedCard(String image, String title, String description) { + return Stack( + children: [ + // 背景图片 + ClipRRect( + borderRadius: BorderRadius.circular(24.r), + child: Container( + color: Colors.blue.shade900, // 预设颜色,图片加载失败时显示 + child: Image.asset( + image, + width: double.infinity, + height: 180.h, + fit: BoxFit.cover, + errorBuilder: (context, error, stackTrace) { + return Container( + color: Colors.blue.shade900, + width: double.infinity, + height: 180.h, + child: Icon( + Icons.image_not_supported, + color: Colors.white, + size: 40.sp, + ), + ); + }, ), ), + ), - // 渐变遮罩 - Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(24.r), - gradient: LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [ - Colors.black.withOpacity(0.1), - Colors.black.withOpacity(0.6), - ], - ), + // 渐变遮罩 + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(24.r), + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Colors.black.withOpacity(0.1), + Colors.black.withOpacity(0.6), + ], ), ), + ), - // 文本内容 - Padding( - padding: EdgeInsets.all(24.w), - child: Column( - mainAxisAlignment: MainAxisAlignment.end, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'exploreSlogan'.tr, // 智能耳机,无限可能 - style: TextStyle( - fontSize: 22.sp, - fontWeight: FontWeight.bold, - color: Colors.white, - ), + // 文本内容 + Padding( + padding: EdgeInsets.all(24.w), + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: TextStyle( + fontSize: 22.sp, + fontWeight: FontWeight.bold, + color: Colors.white, ), - SizedBox(height: 4.h), - Text( - 'exploreAi'.tr, // 探索DeepSound提供的AI功能 - style: TextStyle( - fontSize: 14.sp, - color: Colors.white.withOpacity(0.8), - ), + ), + SizedBox(height: 4.h), + Text( + description, + style: TextStyle( + fontSize: 14.sp, + color: Colors.white.withOpacity(0.8), ), - ], - ), + ), + ], ), - ], - ), + ), + ], ); } diff --git a/lib/modules/home/views/home_view.dart b/lib/modules/home/views/home_view.dart index 12f90875b..ec857c4f1 100644 --- a/lib/modules/home/views/home_view.dart +++ b/lib/modules/home/views/home_view.dart @@ -89,9 +89,7 @@ class HomeView extends GetView { _buildAppBar(isDarkMode), // 顶部图片 - Center( - child: _buildEarbudsImage(), - ), + _buildEarbudsImage(), SizedBox(height: 20.h), // 设备状态卡片 @@ -102,8 +100,6 @@ class HomeView extends GetView { _buildSectionTitle(isDarkMode), SizedBox(height: 16.h), _buildPremiumAILayout(), - - ], ), ), @@ -156,15 +152,44 @@ class HomeView extends GetView { // 耳机图片 Widget _buildEarbudsImage() { final bool isDarkMode = Get.isDarkMode; - return Container( + return SizedBox( height: 220.h, - alignment: Alignment.center, - child: Image.asset( - isDarkMode - ? 'assets/images/headphone_dark.png' - : 'assets/images/headphone_light.png', - height: 180.h, - fit: BoxFit.contain, + child: PageView( + children: [ + Center( + child: SizedBox( + width: double.infinity, + height: 200.h, + child: Image.asset( + isDarkMode + ? 'assets/images/robot_light.png' + : 'assets/images/robot_light.png', + fit: BoxFit.fitWidth, + ), + ), + ), + Center( + child: Image.asset( + isDarkMode + ? 'assets/images/headphone_dark.png' + : 'assets/images/headphone_light.png', + height: 180.h, + fit: BoxFit.contain, + ), + ), + Center( + child: SizedBox( + width: double.infinity, + height: 200.h, + child: Image.asset( + isDarkMode + ? 'assets/images/glasses_light.png' + : 'assets/images/glasses_light.png', + fit: BoxFit.fitWidth, + ), + ), + ), + ], ), ); } @@ -556,7 +581,7 @@ class HomeView extends GetView { // 右下卡片 - AI女友 _buildFeatureCard( - title: 'aiCompanion'.tr, // AI伴侣 + title: 'aiCompanion'.tr, // 亲子陪伴 description: 'aiCompanionInfo'.tr, // 懂你心意,温暖陪伴 icon: Icons.favorite, gradient: LinearGradient( @@ -673,7 +698,8 @@ class HomeView extends GetView { child: Text( description, style: TextStyle( - color: isDarkMode ? Colors.grey[400] : Colors.grey[600], + color: + isDarkMode ? Colors.grey[400] : Colors.grey[600], fontSize: 12.sp, ), maxLines: 2, @@ -706,7 +732,8 @@ class HomeView extends GetView { child: Text( description, style: TextStyle( - color: isDarkMode ? Colors.grey[400] : Colors.grey[600], + color: + isDarkMode ? Colors.grey[400] : Colors.grey[600], fontSize: 10.sp, ), maxLines: 2, diff --git a/lib/modules/login/controllers/login_controller.dart b/lib/modules/login/controllers/login_controller.dart index 239880cba..f3057192e 100644 --- a/lib/modules/login/controllers/login_controller.dart +++ b/lib/modules/login/controllers/login_controller.dart @@ -27,10 +27,10 @@ class LoginController extends GetxController { String get selectedCountryCode => _selectedCountryCode.value; String get selectedCountryFlag => _selectedCountryFlag.value; // 预设国家编码列表 - final List> countryCodes = [ - {'name': '邮箱', 'code': '', 'flag': '✉️'}, // 保持首位 - {'name': '中国', 'code': '+86', 'flag': '🇨🇳'}, // 保持第二位 - + final List> countryCodes = [ + {'name': '邮箱', 'code': '', 'flag': '✉️'}, // 保持首位 + {'name': '中国', 'code': '+86', 'flag': '🇨🇳'}, // 保持第二位 + // 按国家代码数字升序排列(去除+号后排序) {'name': '俄罗斯', 'code': '+7', 'flag': '🇷🇺'}, {'name': '英国', 'code': '+44', 'flag': '🇬🇧'}, @@ -59,13 +59,12 @@ class LoginController extends GetxController { {'name': '阿根廷', 'code': '+54', 'flag': '🇦🇷'}, {'name': '澳大利亚', 'code': '+61', 'flag': '🇦🇺'}, {'name': '新西兰', 'code': '+64', 'flag': '🇳🇿'}, - + // 中国地区(保持最后) {'name': '中国香港', 'code': '+852', 'flag': '🇭🇰'}, {'name': '中国澳门', 'code': '+853', 'flag': '🇲🇴'}, {'name': '中国台湾', 'code': '+886', 'flag': '🇹🇼'}, ]; - // 状态管理 final _viewState = LoginViewState.welcome.obs; @@ -248,8 +247,8 @@ class LoginController extends GetxController { if (isEmialContactController() || isPhoneContactController()) { _startCountdown(); var response = await Api.getCode({ - "addr": contactController.text, - "vtype": 0, + "addr": selectedCountryCode + contactController.text, + "vtype": loginType, }); if (response != null) { print("请求后台数据验证码params=${response}"); @@ -295,8 +294,8 @@ class LoginController extends GetxController { // 检查每个元素是否为单个字母或数字字符 final text = contactController.text; return (text.length > 5 && - RegExp(r'^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$') - .hasMatch(text)); + RegExp(r'^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$') + .hasMatch(text)); } // 检查手机号码格式 @@ -310,7 +309,7 @@ class LoginController extends GetxController { bool isVerificationCodeComplete() { // 检查每个元素是否为单个字母或数字字符 final text = verificationCodeController.text; - print("数据获取Facebook${text.length}"); + print("数据获取Facebook${text.length}"); return text.length == 4 && RegExp(r'^[0-9]+$').hasMatch(text); } @@ -323,7 +322,7 @@ class LoginController extends GetxController { // 模拟验证过程 switch (loginType) { - case 0 || 1: + case 0: { if (!isVerificationCodeComplete()) { Get.snackbar( @@ -337,7 +336,32 @@ class LoginController extends GetxController { } requestLogin({ "mail": contactController.text, - "phone": "", + "phone": selectedCountryCode + contactController.text, + "stype": loginType, + "vcode": verificationCodeController.text, + "ttoken": "", + "phonemodel": "", + "phonemac": "", + "name": "", + "avatar": "", + }); + break; + } + case 1: + { + if (!isVerificationCodeComplete()) { + Get.snackbar( + '提示', + '请输入完整的验证码', + snackPosition: SnackPosition.TOP, + backgroundColor: Colors.red.withOpacity(0.1), + colorText: Colors.red, + ); + break; + } + requestLogin({ + "mail": "", + "phone": selectedCountryCode + contactController.text, "stype": loginType, "vcode": verificationCodeController.text, "ttoken": "", @@ -348,7 +372,6 @@ class LoginController extends GetxController { }); break; } - case 4: { print("数据获取Facebook"); diff --git a/lib/modules/meeting/bindings/meeting_details_binding.dart b/lib/modules/meeting/bindings/meeting_details_binding.dart new file mode 100644 index 000000000..6676625bd --- /dev/null +++ b/lib/modules/meeting/bindings/meeting_details_binding.dart @@ -0,0 +1,10 @@ +import 'package:get/get.dart'; +import '../controllers/meeting_details_controller.dart'; + +class MeetingDetailsBinding extends Bindings { + @override + void dependencies() { + // 注册控制器 + Get.lazyPut(() => MeetingDetailsController()); + } +} diff --git a/lib/modules/meeting/bindings/meeting_edit_binding.dart b/lib/modules/meeting/bindings/meeting_edit_binding.dart new file mode 100644 index 000000000..31d1443ee --- /dev/null +++ b/lib/modules/meeting/bindings/meeting_edit_binding.dart @@ -0,0 +1,11 @@ +import 'package:get/get.dart'; + +import '../controllers/meeting_edit_controller.dart'; + +class MeetingEditBinding extends Bindings { + @override + void dependencies() { + // 注册控制器 + Get.lazyPut(() => MeetingEditController()); + } +} diff --git a/lib/modules/meeting/controllers/meeting_controller.dart b/lib/modules/meeting/controllers/meeting_controller.dart index 42d951ee0..e798e6f36 100644 --- a/lib/modules/meeting/controllers/meeting_controller.dart +++ b/lib/modules/meeting/controllers/meeting_controller.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import '../../../data/services/db/sqflite_api.dart'; + class MeetingController extends GetxController { final ScrollController listController = ScrollController(); @@ -15,6 +17,8 @@ class MeetingController extends GetxController { @override void onInit() { super.onInit(); + listController.addListener(_onScroll); + _getDataList(); } @override @@ -23,4 +27,44 @@ class MeetingController extends GetxController { titleController.dispose(); super.onClose(); } + + void _onScroll() { + if (isLoadingMore && + listController.position.pixels >= + listController.position.maxScrollExtent - 100) { + page++; + _getDataList(); + } + } + + // 请求本地数据 + void _getDataList() async { + List list = await SqfliteApi.getMeetingList(page); + dataList.addAll(list); + if (list.length < 10) { + isLoadingMore = false; + } else { + isLoadingMore = true; + } + } + + // 修改标题 + void editMeetingTitle(int id, String title) async { + if (title.isEmpty) return; + SqfliteApi.editMeetingTitle(id, title); + int index = dataList.indexWhere((item) => item['id'] == id); + if (index != -1) { + final dataMap = Map.from(dataList[index]); + dataMap['title'] = title; + dataList[index] = dataMap; + } + Get.back(); + } + + // 删除数据 + void deleteMeeting(int id) { + dataList.removeWhere((item) => item['id'] == id); + // 删除数据库中的记录 + SqfliteApi.deleteMeeting(id); + } } diff --git a/lib/modules/meeting/controllers/meeting_details_controller.dart b/lib/modules/meeting/controllers/meeting_details_controller.dart new file mode 100644 index 000000000..ee4668196 --- /dev/null +++ b/lib/modules/meeting/controllers/meeting_details_controller.dart @@ -0,0 +1,51 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +import '../../../data/services/db/sqflite_api.dart'; +import '../model/meeting_details_model.dart'; +import '../model/meeting_model.dart'; + +class MeetingDetailsController extends GetxController + with GetSingleTickerProviderStateMixin { + late AnimationController lottieController; + + int id = 0; + + RxBool isLoading = true.obs; + Rx meetingData = MeetingModel().obs; + Rx dataDetails = MeetingDetailsModel().obs; + + RxBool isPlay = false.obs; + + @override + void onInit() { + super.onInit(); + lottieController = AnimationController(vsync: this); + final Map args = Get.arguments ?? {}; + id = args['id']; + _getDataDetails(); + } + + @override + void onClose() { + lottieController.dispose(); + super.onClose(); + } + + void _getDataDetails() async { + final data = await SqfliteApi.getMeetingDetails(id); + meetingData.value = MeetingModel.fromJson(data); + dataDetails.value = MeetingDetailsModel.fromJson(data['details']); + isLoading.value = false; + } + + void start() { + if (isPlay.value) { + lottieController.stop(); + isPlay.value = false; + } else { + lottieController.repeat(); + isPlay.value = true; + } + } +} diff --git a/lib/modules/meeting/controllers/meeting_edit_controller.dart b/lib/modules/meeting/controllers/meeting_edit_controller.dart new file mode 100644 index 000000000..9dc2fe89a --- /dev/null +++ b/lib/modules/meeting/controllers/meeting_edit_controller.dart @@ -0,0 +1,62 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +import '../../../data/services/db/sqflite_api.dart'; +import '../model/meeting_details_model.dart'; +import '../model/meeting_model.dart'; + +class MeetingEditController extends GetxController + with GetSingleTickerProviderStateMixin { + late AnimationController lottieController; + + late TextEditingController titleController; + late TextEditingController contentController; + + int id = 0; + + RxBool isLoading = true.obs; + Rx meetingData = MeetingModel().obs; + Rx dataDetails = MeetingDetailsModel().obs; + + RxBool isPlay = false.obs; + + @override + void onInit() { + super.onInit(); + lottieController = AnimationController(vsync: this); + final Map args = Get.arguments ?? {}; + id = args['id']; + _getDataDetails(); + } + + @override + void onClose() { + lottieController.dispose(); + titleController.dispose(); + contentController.dispose(); + super.onClose(); + } + + void _getDataDetails() async { + final data = await SqfliteApi.getMeetingDetails(id); + meetingData.value = MeetingModel.fromJson(data); + dataDetails.value = MeetingDetailsModel.fromJson(data['details']); + titleController = TextEditingController( + text: meetingData.value.title, + ); + contentController = TextEditingController( + text: dataDetails.value.content, + ); + isLoading.value = false; + } + + void start() { + if (isPlay.value) { + lottieController.stop(); + isPlay.value = false; + } else { + lottieController.repeat(); + isPlay.value = true; + } + } +} diff --git a/lib/modules/meeting/controllers/meeting_record_controller.dart b/lib/modules/meeting/controllers/meeting_record_controller.dart index ba1c239ac..1e569bfd9 100644 --- a/lib/modules/meeting/controllers/meeting_record_controller.dart +++ b/lib/modules/meeting/controllers/meeting_record_controller.dart @@ -5,9 +5,12 @@ import 'package:get/get.dart'; import '../../../data/services/asr_service.dart'; import '../../../data/services/db/sqflite_api.dart'; +import 'meeting_controller.dart'; class MeetingRecordController extends GetxController with GetSingleTickerProviderStateMixin { + final MeetingController meetingController = Get.find(); + late AnimationController lottieController; late AsrService _asrService; @@ -113,6 +116,13 @@ class MeetingRecordController extends GetxController }; final result = await SqfliteApi.insertMeeting(data); if (result != null) { + meetingController.dataList.insert(0, { + 'id': result, + 'title': title, + 'content': firstContent, + 'seconds': seconds.value, + 'creationtime': creationTime, + }); Get.back(); } } diff --git a/lib/modules/meeting/model/meeting_details_model.dart b/lib/modules/meeting/model/meeting_details_model.dart index e314222a7..0e8798640 100644 --- a/lib/modules/meeting/model/meeting_details_model.dart +++ b/lib/modules/meeting/model/meeting_details_model.dart @@ -4,21 +4,21 @@ class MeetingDetailsModel { MeetingDetailsModel(); late int id; - late int noteid; + late int meetingid; late Uint8List audio; late String content; factory MeetingDetailsModel.fromJson(Map map) => MeetingDetailsModel() ..id = map["id"] - ..noteid = map["noteid"] + ..meetingid = map["meetingid"] ..audio = map["audio"] ..content = map["content"]; Map toJson() { final data = {}; data['id'] = id; - data['noteid'] = noteid; + data['meetingid'] = meetingid; data['audio'] = audio; data['content'] = content; return data; diff --git a/lib/modules/meeting/model/meeting_model.dart b/lib/modules/meeting/model/meeting_model.dart index db7d5488b..7cf2b6873 100644 --- a/lib/modules/meeting/model/meeting_model.dart +++ b/lib/modules/meeting/model/meeting_model.dart @@ -4,7 +4,6 @@ class MeetingModel { late int id; late String title; late String content; - late String language; late int seconds; late int creationtime; @@ -12,7 +11,6 @@ class MeetingModel { ..id = map["id"] ..title = map["title"] ..content = map["content"] - ..language = map["language"] ..seconds = map["seconds"] ..creationtime = map["creationtime"]; @@ -21,7 +19,6 @@ class MeetingModel { data['id'] = id; data['title'] = title; data['content'] = content; - data['language'] = language; data['seconds'] = seconds; data['creationtime'] = creationtime; return data; diff --git a/lib/modules/meeting/views/meeting_details_view.dart b/lib/modules/meeting/views/meeting_details_view.dart new file mode 100644 index 000000000..7423791ef --- /dev/null +++ b/lib/modules/meeting/views/meeting_details_view.dart @@ -0,0 +1,253 @@ +import 'package:common_utils/common_utils.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get/get.dart'; +import 'package:lottie/lottie.dart'; + +import '../../../core/theme/app_colors.dart'; +import '../../../routes/app_routes.dart'; +import '../controllers/meeting_details_controller.dart'; + +class MeetingDetailsView extends GetView { + const MeetingDetailsView({super.key}); + + @override + Widget build(BuildContext context) { + final isDarkMode = Theme.of(context).brightness == Brightness.dark; + + return Scaffold( + backgroundColor: isDarkMode ? Colors.black : Colors.grey[50], + appBar: _buildAppBar(isDarkMode), + body: Obx(() => !controller.isLoading.value + ? _buildBody(isDarkMode) + : const SizedBox()), + ); + } + + // 构建应用栏 + PreferredSizeWidget _buildAppBar(bool isDarkMode) { + return AppBar( + backgroundColor: isDarkMode ? Colors.black : Colors.white, + elevation: 0.5, + leading: IconButton( + icon: Icon( + Icons.arrow_back_ios, + color: isDarkMode ? Colors.white : Colors.black, + size: 20.sp, + ), + onPressed: () => Get.back(), + ), + title: Text( + '录音详情', + style: TextStyle( + fontSize: 17.sp, + fontWeight: FontWeight.w600, + color: isDarkMode ? Colors.white : Colors.black, + ), + ), + centerTitle: true, + ); + } + + // 构建主体内容 + Widget _buildBody(bool isDarkMode) { + return Padding( + padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 10.w), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + controller.meetingData.value.title, + style: TextStyle( + color: isDarkMode ? Colors.white : Colors.black87, + fontSize: 17.sp, + ), + ), + Padding( + padding: EdgeInsets.symmetric(vertical: 5.w), + child: Text( + DateUtil.formatDateMs( + controller.meetingData.value.creationtime, + format: DateFormats.y_mo_d_h_m, + ), + style: TextStyle( + color: isDarkMode ? Colors.grey[400] : Colors.grey[500], + fontSize: 14.sp, + ), + ), + ), + _audioWave(isDarkMode), + _recordContent(isDarkMode), + _bottomButton(isDarkMode), + ], + ), + ); + } + + // 音波 + Widget _audioWave(bool isDarkMode) { + return Container( + padding: EdgeInsets.only(bottom: 10.w), + margin: EdgeInsets.symmetric(vertical: 5.w), + decoration: BoxDecoration( + color: isDarkMode ? Colors.white.withOpacity(0.05) : Colors.white, + borderRadius: BorderRadius.circular(12.w), + boxShadow: [ + BoxShadow( + color: isDarkMode + ? Colors.black.withOpacity(0.2) + : Colors.black.withOpacity(0.05), + blurRadius: 10, + offset: const Offset(0, 2), + ), + ], + ), + child: Column( + children: [ + SizedBox( + height: 70.w, + child: Row( + children: [ + Expanded( + child: Lottie.asset( + 'assets/lottie/audio_wave.json', + controller: controller.lottieController, + fit: BoxFit.fill, + onLoaded: (composition) { + controller.lottieController.duration = + composition.duration; + }, + ), + ), + Expanded( + child: Lottie.asset( + 'assets/lottie/audio_wave.json', + controller: controller.lottieController, + fit: BoxFit.fill, + ), + ), + Expanded( + child: Lottie.asset( + 'assets/lottie/audio_wave.json', + controller: controller.lottieController, + fit: BoxFit.fill, + ), + ), + ], + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Text( + '00:00:00', + style: TextStyle( + color: isDarkMode ? Colors.grey[400] : Colors.grey[500], + fontSize: 12.sp, + ), + ), + GestureDetector( + onTap: controller.start, + child: Container( + width: 30.w, + height: 30.w, + decoration: BoxDecoration( + color: isDarkMode ? AppColors.primary : Colors.blue, + borderRadius: BorderRadius.circular( + 12.w, + ), + ), + child: Icon( + controller.isPlay.value ? Icons.pause : Icons.play_arrow, + color: Colors.white, + size: 25.w, + ), + ), + ), + Text( + '00:00:00', + style: TextStyle( + color: isDarkMode ? Colors.grey[400] : Colors.grey[500], + fontSize: 12.sp, + ), + ), + ], + ), + ], + ), + ); + } + + // 记录内容 + Widget _recordContent(bool isDarkMode) { + return Expanded( + child: Container( + width: double.infinity, + padding: EdgeInsets.all(10.w), + margin: EdgeInsets.symmetric(vertical: 5.w), + decoration: BoxDecoration( + color: isDarkMode ? Colors.white.withOpacity(0.05) : Colors.white, + borderRadius: BorderRadius.circular(12.w), + boxShadow: [ + BoxShadow( + color: isDarkMode + ? Colors.black.withOpacity(0.2) + : Colors.black.withOpacity(0.05), + blurRadius: 10, + offset: const Offset(0, 2), + ), + ], + ), + child: Align( + alignment: Alignment.topLeft, + child: SingleChildScrollView( + reverse: true, + child: Obx( + () => Text( + controller.dataDetails.value.content, + ), + ), + ), + ), + ), + ); + } + + // 底部按钮 + Widget _bottomButton(bool isDarkMode) { + return Container( + padding: EdgeInsets.only(top: 40.w, bottom: 30.w), + alignment: Alignment.center, + child: GestureDetector( + onTap: () { + Get.toNamed(Routes.meetingEdit, arguments: { + 'id': controller.id, + }); + }, + child: Container( + width: 130.w, + height: 48.w, + decoration: BoxDecoration( + color: isDarkMode ? AppColors.primary : Colors.blue, + borderRadius: BorderRadius.circular(12.w), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.edit_calendar, + color: Colors.white, + size: 20.w, + ), + 5.horizontalSpace, + Text( + '编辑', + style: TextStyle(color: Colors.white, fontSize: 16.sp), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/modules/meeting/views/meeting_edit_view.dart b/lib/modules/meeting/views/meeting_edit_view.dart new file mode 100644 index 000000000..3e6f6852c --- /dev/null +++ b/lib/modules/meeting/views/meeting_edit_view.dart @@ -0,0 +1,262 @@ +import 'package:common_utils/common_utils.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get/get.dart'; +import 'package:lottie/lottie.dart'; + +import '../../../core/theme/app_colors.dart'; +import '../controllers/meeting_edit_controller.dart'; + +class MeetingEditView extends GetView { + const MeetingEditView({super.key}); + + @override + Widget build(BuildContext context) { + final isDarkMode = Theme.of(context).brightness == Brightness.dark; + + return Scaffold( + backgroundColor: isDarkMode ? Colors.black : Colors.grey[50], + appBar: _buildAppBar(isDarkMode), + body: Obx(() => !controller.isLoading.value + ? _buildBody(isDarkMode) + : const SizedBox()), + ); + } + + // 构建应用栏 + PreferredSizeWidget _buildAppBar(bool isDarkMode) { + return AppBar( + backgroundColor: isDarkMode ? Colors.black : Colors.white, + elevation: 0.5, + leading: IconButton( + icon: Icon( + Icons.arrow_back_ios, + color: isDarkMode ? Colors.white : Colors.black, + size: 20.sp, + ), + onPressed: () => Get.back(), + ), + actions: [ + GestureDetector( + onTap: () {}, + child: Padding( + padding: EdgeInsets.symmetric( + horizontal: 12.w, + vertical: 6.w, + ), + child: Text( + '保存', + style: TextStyle( + color: isDarkMode ? AppColors.primary : Colors.blue, + fontSize: 18.sp, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ], + ); + } + + // 构建主体内容 + Widget _buildBody(bool isDarkMode) { + return Padding( + padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 10.w), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + height: 48.w, + alignment: Alignment.center, + decoration: BoxDecoration( + color: isDarkMode ? Colors.white.withOpacity(0.05) : Colors.white, + borderRadius: BorderRadius.circular(12.w), + boxShadow: [ + BoxShadow( + color: isDarkMode + ? Colors.black.withOpacity(0.2) + : Colors.black.withOpacity(0.05), + blurRadius: 10, + offset: const Offset(0, 2), + ), + ], + ), + child: TextField( + controller: controller.titleController, + style: TextStyle( + color: isDarkMode ? Colors.white : Colors.black87, + fontSize: 16.sp, + ), + decoration: InputDecoration( + hintText: '最多50个字符', + hintStyle: TextStyle( + color: isDarkMode ? Colors.white70 : Colors.black54, + fontSize: 16.sp, + ), + fillColor: Colors.transparent, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric( + horizontal: 10.w, + ), + ), + ), + ), + Padding( + padding: EdgeInsets.symmetric(vertical: 5.w), + child: Text( + DateUtil.formatDateMs( + controller.meetingData.value.creationtime, + format: DateFormats.y_mo_d_h_m, + ), + style: TextStyle( + color: isDarkMode ? Colors.grey[400] : Colors.grey[500], + fontSize: 14.sp, + ), + ), + ), + _audioWave(isDarkMode), + _recordContent(isDarkMode), + ], + ), + ); + } + + // 音波 + Widget _audioWave(bool isDarkMode) { + return Container( + padding: EdgeInsets.only(bottom: 10.w), + margin: EdgeInsets.symmetric(vertical: 5.w), + decoration: BoxDecoration( + color: isDarkMode ? Colors.white.withOpacity(0.05) : Colors.white, + borderRadius: BorderRadius.circular(12.w), + boxShadow: [ + BoxShadow( + color: isDarkMode + ? Colors.black.withOpacity(0.2) + : Colors.black.withOpacity(0.05), + blurRadius: 10, + offset: const Offset(0, 2), + ), + ], + ), + child: Column( + children: [ + SizedBox( + height: 70.w, + child: Row( + children: [ + Expanded( + child: Lottie.asset( + 'assets/lottie/audio_wave.json', + controller: controller.lottieController, + fit: BoxFit.fill, + onLoaded: (composition) { + controller.lottieController.duration = + composition.duration; + }, + ), + ), + Expanded( + child: Lottie.asset( + 'assets/lottie/audio_wave.json', + controller: controller.lottieController, + fit: BoxFit.fill, + ), + ), + Expanded( + child: Lottie.asset( + 'assets/lottie/audio_wave.json', + controller: controller.lottieController, + fit: BoxFit.fill, + ), + ), + ], + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Text( + '00:00:00', + style: TextStyle( + color: isDarkMode ? Colors.grey[400] : Colors.grey[500], + fontSize: 12.sp, + ), + ), + GestureDetector( + onTap: controller.start, + child: Container( + width: 30.w, + height: 30.w, + decoration: BoxDecoration( + color: isDarkMode ? AppColors.primary : Colors.blue, + borderRadius: BorderRadius.circular( + 12.w, + ), + ), + child: Icon( + controller.isPlay.value ? Icons.pause : Icons.play_arrow, + color: Colors.white, + size: 25.w, + ), + ), + ), + Text( + '00:00:00', + style: TextStyle( + color: isDarkMode ? Colors.grey[400] : Colors.grey[500], + fontSize: 12.sp, + ), + ), + ], + ), + ], + ), + ); + } + + // 记录内容 + Widget _recordContent(bool isDarkMode) { + return Expanded( + child: Container( + width: double.infinity, + padding: EdgeInsets.all(10.w), + margin: EdgeInsets.symmetric(vertical: 5.w), + decoration: BoxDecoration( + color: isDarkMode ? Colors.white.withOpacity(0.05) : Colors.white, + borderRadius: BorderRadius.circular(12.w), + boxShadow: [ + BoxShadow( + color: isDarkMode + ? Colors.black.withOpacity(0.2) + : Colors.black.withOpacity(0.05), + blurRadius: 10, + offset: const Offset(0, 2), + ), + ], + ), + child: TextField( + controller: controller.contentController, + style: TextStyle( + color: isDarkMode ? Colors.white : Colors.black87, + fontSize: 16.sp, + ), + maxLines: 50, + decoration: InputDecoration( + hintText: '编辑记录内容', + hintStyle: TextStyle( + color: isDarkMode ? Colors.white70 : Colors.black54, + fontSize: 16.sp, + ), + fillColor: Colors.transparent, + border: InputBorder.none, + focusedBorder: InputBorder.none, + contentPadding: EdgeInsets.symmetric( + horizontal: 10.w, + ), + ), + ), + ), + ); + } +} diff --git a/lib/modules/meeting/views/meeting_view.dart b/lib/modules/meeting/views/meeting_view.dart index 88bedda58..10678dc88 100644 --- a/lib/modules/meeting/views/meeting_view.dart +++ b/lib/modules/meeting/views/meeting_view.dart @@ -56,9 +56,9 @@ class MeetingView extends GetView { itemCount: controller.dataList.length, padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 5.w), itemBuilder: (BuildContext context, int index) { - // MeetingModel data = - // MeetingModel.fromJson(controller.dataList[index]); - return _item(context, isDarkMode); + MeetingModel data = + MeetingModel.fromJson(controller.dataList[index]); + return _item(context, isDarkMode, data); }, ), ), @@ -94,9 +94,57 @@ class MeetingView extends GetView { ); } - Widget _item(BuildContext context, bool isDarkMode) { + Widget _item(BuildContext context, bool isDarkMode, MeetingModel data) { return GestureDetector( - onTap: () {}, + onTap: () { + Get.toNamed(Routes.meetingDetails, arguments: { + 'id': data.id, + }); + }, + onLongPressStart: (details) { + RenderBox? renderBox = + Overlay.of(context).context.findRenderObject() as RenderBox; + final RelativeRect position = RelativeRect.fromRect( + Rect.fromLTRB( + details.globalPosition.dx, + details.globalPosition.dy, + details.globalPosition.dx + 110, // 菜单显示位置X轴坐标 + details.globalPosition.dy - 40, // 菜单显示位置Y轴坐标 + ), + Offset.zero & renderBox.size, + ); + + showMenu( + context: context, + position: position, + color: isDarkMode ? AppColors.cardBackground : Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12.r), + ), + items: [ + _popupMenuEntry( + Icon( + Icons.edit_calendar, + color: isDarkMode ? Colors.white : Colors.black87, + ), + '编辑', + isDarkMode ? Colors.white : Colors.black87, + () { + controller.titleController.text = data.title; + _bottomSheet(context, isDarkMode, id: data.id); + }, + ), + _popupMenuEntry( + const Icon(Icons.delete_outline, color: Colors.red), + '删除', + isDarkMode ? Colors.white : Colors.black87, + () { + controller.deleteMeeting(data.id); + }, + ), + ], + ); + }, child: Container( padding: EdgeInsets.symmetric(horizontal: 15.w, vertical: 8.w), margin: EdgeInsets.symmetric(vertical: 5.w), @@ -120,7 +168,7 @@ class MeetingView extends GetView { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'data.title', + data.title, style: TextStyle( color: isDarkMode ? Colors.white : Colors.black87, fontSize: 16.sp, @@ -128,7 +176,7 @@ class MeetingView extends GetView { overflow: TextOverflow.ellipsis, ), Text( - '无内容', + data.content.isNotEmpty ? data.content : '无内容', style: TextStyle( color: isDarkMode ? Colors.white70 : Colors.black54, fontSize: 12.sp, @@ -136,7 +184,10 @@ class MeetingView extends GetView { overflow: TextOverflow.ellipsis, ), Text( - '12-13', + DateUtil.formatDateMs( + data.creationtime, + format: DateFormats.y_mo_d_h_m, + ), style: TextStyle( color: isDarkMode ? Colors.white70 : Colors.black54, fontSize: 12.sp, @@ -155,7 +206,11 @@ class MeetingView extends GetView { borderRadius: BorderRadius.circular(28.w), ), child: Text( - '00:00:00', + DateUtil.formatDateMs( + data.seconds * 1000, + format: 'HH:mm:ss', + isUtc: true, + ), style: TextStyle(color: Colors.white, fontSize: 14.sp), ), ), @@ -165,7 +220,35 @@ class MeetingView extends GetView { ); } - void _bottomSheet(BuildContext context, bool isDarkMode) { + PopupMenuEntry _popupMenuEntry( + Icon icon, + String title, + Color color, + Function() onTap, + ) { + return PopupMenuItem( + onTap: onTap, + child: SizedBox( + width: 120.w, + child: Row( + children: [ + icon, + 8.horizontalSpace, + Text( + title, + style: TextStyle(color: color), + ), + ], + ), + ), + ); + } + + void _bottomSheet( + BuildContext context, + bool isDarkMode, { + int? id, + }) { showModalBottomSheet( context: context, isScrollControlled: true, @@ -181,7 +264,7 @@ class MeetingView extends GetView { mainAxisSize: MainAxisSize.min, children: [ Text( - '录音标题(非必填)', + id != null ? '修改录音标题(非必填)' : '录音标题(非必填)', style: TextStyle( color: isDarkMode ? Colors.white : Colors.black87, fontSize: 16.sp, @@ -259,14 +342,18 @@ class MeetingView extends GetView { onTap: () { int time = DateTime.now().millisecondsSinceEpoch; String title = controller.titleController.text; - if (title.isEmpty) { - title = - '${DateUtil.formatDateMs(time, format: DateFormats.y_mo_d)} 录音'; + if (id != null) { + controller.editMeetingTitle(id, title); + } else { + if (title.isEmpty) { + title = + '${DateUtil.formatDateMs(time, format: DateFormats.y_mo_d)} 录音'; + } + Get.back(); + Get.toNamed(Routes.meetingRecord, arguments: { + 'title': title, + }); } - Get.back(); - Get.toNamed(Routes.meetingRecord, arguments: { - 'title': title, - }); }, child: Container( width: 120.w, diff --git a/lib/modules/timbre/controllers/timbre_controller.dart b/lib/modules/timbre/controllers/timbre_controller.dart index 49ff05d10..4514d2aa2 100644 --- a/lib/modules/timbre/controllers/timbre_controller.dart +++ b/lib/modules/timbre/controllers/timbre_controller.dart @@ -288,16 +288,5 @@ class TimbreController extends GetxController { type.value = data['type']; settingsController.timbreName.value = data['name']; _storage.write('timbre', data['type']); - setSpeechServiceType(); - } - - // 设置语音音色服务类型 - void setSpeechServiceType() { - try { - final factory = Get.find(); - factory.setTimbreServiceType(); - } catch (e) { - // 处理错误 - } } } diff --git a/lib/routes/app_pages.dart b/lib/routes/app_pages.dart index 1e9374b4b..8a2466fda 100644 --- a/lib/routes/app_pages.dart +++ b/lib/routes/app_pages.dart @@ -4,7 +4,11 @@ import '../modules/home/bindings/home_binding.dart'; import '../modules/explore/views/explore_view.dart'; import '../modules/explore/bindings/explore_binding.dart'; import '../modules/meeting/bindings/meeting_binding.dart'; +import '../modules/meeting/bindings/meeting_details_binding.dart'; +import '../modules/meeting/bindings/meeting_edit_binding.dart'; import '../modules/meeting/bindings/meeting_record_binding.dart'; +import '../modules/meeting/views/meeting_details_view.dart'; +import '../modules/meeting/views/meeting_edit_view.dart'; import '../modules/meeting/views/meeting_record_view.dart'; import '../modules/meeting/views/meeting_view.dart'; import '../modules/profile/views/profile_view.dart'; @@ -95,6 +99,16 @@ abstract class AppPages { page: () => const MeetingRecordView(), binding: MeetingRecordBinding(), ), + GetPage( + name: Routes.meetingDetails, + page: () => const MeetingDetailsView(), + binding: MeetingDetailsBinding(), + ), + GetPage( + name: Routes.meetingEdit, + page: () => const MeetingEditView(), + binding: MeetingEditBinding(), + ), GetPage( name: Routes.timbre, page: () => const TimbreView(), diff --git a/lib/routes/app_routes.dart b/lib/routes/app_routes.dart index 2dcd625d8..4488f63db 100644 --- a/lib/routes/app_routes.dart +++ b/lib/routes/app_routes.dart @@ -9,6 +9,8 @@ abstract class Routes { static const profile = '/profile'; static const meeting = '/meeting'; static const meetingRecord = '/meetingRecord'; + static const meetingDetails = '/meetingDetails'; + static const meetingEdit = '/meetingEdit'; static const timbre = '/timbre'; static const settings = '/settings'; static const speechDemo = '/speech_demo'; diff --git a/pubspec.yaml b/pubspec.yaml index 3b17bb70d..d85359d4e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -50,14 +50,13 @@ dependencies: speech_to_text: ^7.0.0 fluwx: ^5.4.1 firebase_core: ^3.12.1 - firebase_auth: ^5.5.0 + firebase_auth: ^5.5.1 google_sign_in: ^6.2.2 flutter_facebook_auth: ^7.1.1 common_utils: ^2.1.0 flutter_image_compress: ^2.4.0 # image_cropper: ^9.0.0 dio: ^5.8.0+1 - flutter_easyloading: ^3.0.5 sqflite: ^2.4.2 path: ^1.9.1 @@ -72,7 +71,11 @@ flutter: assets: - assets/images/headphone_light.png - assets/images/headphone_dark.png + - assets/images/robot_light.png + - assets/images/glasses_light.png - assets/images/agents/ - assets/images/explore.png + - assets/images/explore1.png + - assets/images/explore2.jpg - assets/lottie/ - .env