From db959315e213da92ac3983a153b768470935cf33 Mon Sep 17 00:00:00 2001 From: fdp <1286779656@qq.com> Date: Thu, 20 Mar 2025 17:17:42 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BE=AE=E4=BF=A1=EF=BC=8C=E8=B0=B7=E6=AD=8C?= =?UTF-8?q?=EF=BC=8Cfacebook=E7=99=BB=E9=99=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- GoogleService-Info.plist | 36 + android/app/build.gradle.kts | 36 +- android/app/google-services.json | 47 + android/app/sign/DeepSound.properties | 7 + android/app/src/main/AndroidManifest.xml | 5 +- .../com/example/deep_voice/AzureAsrHelper.kt | 4 +- .../com/example/deep_voice/AzureTtsHelper.kt | 4 +- .../deep_voice/ClassicBluetoothHelper.kt | 2 +- .../com/example/deep_voice/MainActivity.kt | 4 +- .../deep_voice/VoiceInteractionService.kt | 10 +- .../example/deep_voice/VolcanoAIService.kt | 2 +- .../deep_voice/core/utils/FileLogger.kt | 2 +- android/app/src/main/res/values/strings.xml | 6 + android/build.gradle.kts | 1 + firebase.json | 1 + lib/core/translations/app_translations.dart | 153 +-- lib/data/services/audio_service.dart | 29 +- lib/firebase_options.dart | 92 ++ lib/main.dart | 39 +- .../login/controllers/facebookLogin.dart | 39 + .../login/controllers/googleLogin.dart | 72 ++ .../login/controllers/weixinLogin.dart | 889 ++++++++++++++++++ lib/modules/login/views/login_view.dart | 343 ++++--- lib/modules/settings/views/settings_view.dart | 171 ++-- linux/CMakeLists.txt | 2 +- linux/flutter/generated_plugin_registrant.cc | 4 + linux/flutter/generated_plugins.cmake | 1 + pubspec.yaml | 6 +- .../flutter/generated_plugin_registrant.cc | 9 + windows/flutter/generated_plugins.cmake | 3 + 30 files changed, 1700 insertions(+), 319 deletions(-) create mode 100644 GoogleService-Info.plist create mode 100644 android/app/google-services.json create mode 100644 android/app/sign/DeepSound.properties create mode 100644 android/app/src/main/res/values/strings.xml create mode 100644 firebase.json create mode 100644 lib/firebase_options.dart create mode 100644 lib/modules/login/controllers/facebookLogin.dart create mode 100644 lib/modules/login/controllers/googleLogin.dart create mode 100644 lib/modules/login/controllers/weixinLogin.dart diff --git a/GoogleService-Info.plist b/GoogleService-Info.plist new file mode 100644 index 000000000..c90ada3a3 --- /dev/null +++ b/GoogleService-Info.plist @@ -0,0 +1,36 @@ + + + + + CLIENT_ID + 907754492821-oc9e77svgmmlgouo0op7vv00e1hb8278.apps.googleusercontent.com + REVERSED_CLIENT_ID + com.googleusercontent.apps.907754492821-oc9e77svgmmlgouo0op7vv00e1hb8278 + ANDROID_CLIENT_ID + 907754492821-9jakqvathu61e6b8g95i41ssi7o4dnka.apps.googleusercontent.com + API_KEY + AIzaSyAvyBQcOXuO_q851vevqZIufsPRTMCe808 + GCM_SENDER_ID + 907754492821 + PLIST_VERSION + 1 + BUNDLE_ID + com.yunqiinnovation.deepsound + PROJECT_ID + deepsound-e0e3d + STORAGE_BUCKET + deepsound-e0e3d.firebasestorage.app + IS_ADS_ENABLED + + IS_ANALYTICS_ENABLED + + IS_APPINVITE_ENABLED + + IS_GCM_ENABLED + + IS_SIGNIN_ENABLED + + GOOGLE_APP_ID + 1:907754492821:ios:246794fc44b23174145c9c + + \ No newline at end of file diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index 6879ee469..9fe85f007 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -1,3 +1,5 @@ +import java.util.Properties +import java.io.FileInputStream repositories { google() mavenCentral() @@ -11,10 +13,14 @@ plugins { id("kotlin-android") // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. id("dev.flutter.flutter-gradle-plugin") + id("com.google.gms.google-services")version "4.3.13" +} +val keystorePropertiesFile = rootProject.file("app/sign/DeepSound.properties") +val keystoreProperties =Properties().apply { + load(FileInputStream(keystorePropertiesFile)) // 使用 Kotlin 扩展方法 } - android { - namespace = "com.example.deep_voice" + namespace = "com.yunqiinnovation.deepsound" compileSdk = 35 ndkVersion = "28.0.13004108" @@ -31,7 +37,7 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId = "com.example.deep_voice" + applicationId = "com.yunqiinnovation.deepsound" // You can update the following values to match your application needs. // For more information, see: https://flutter.dev/to/review-gradle-config. minSdk = 24 // Updated to meet azure_speech_recognition_null_safety requirements @@ -39,12 +45,34 @@ android { versionCode = flutter.versionCode versionName = flutter.versionName } + signingConfigs { + +create("release") { + +keyAlias = keystoreProperties.getProperty("keyAlias") + +keyPassword = keystoreProperties.getProperty("keyPassword") + +storeFile = file(keystoreProperties.getProperty("storeFile")) + +storePassword = keystoreProperties.getProperty("storePassword") +enableV1Signing = true + +enableV2Signing = true + +} + +} buildTypes { release { // TODO: Add your own signing config for the release build. // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig = signingConfigs.getByName("debug") + isMinifyEnabled = false + isShrinkResources = false + + signingConfig = signingConfigs.getByName("release") + //signingConfig = signingConfigs.getByName("debug") } } } diff --git a/android/app/google-services.json b/android/app/google-services.json new file mode 100644 index 000000000..4cbe172f9 --- /dev/null +++ b/android/app/google-services.json @@ -0,0 +1,47 @@ +{ + "project_info": { + "project_number": "907754492821", + "project_id": "deepsound-e0e3d", + "storage_bucket": "deepsound-e0e3d.firebasestorage.app" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:907754492821:android:2deffc5aa1037ae2145c9c", + "android_client_info": { + "package_name": "com.yunqiinnovation.deepsound" + } + }, + "oauth_client": [ + { + "client_id": "907754492821-9jakqvathu61e6b8g95i41ssi7o4dnka.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "com.yunqiinnovation.deepsound", + "certificate_hash": "5a9520045bc16fb4f09fb97668a8b0fc4babd376" + } + }, + { + "client_id": "907754492821-15btgolbd7thaa8fpt7s8hs8hq3jl0lg.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyAj0_-nS3ygc8VFe1azotSE3-HGZYClkTs" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "907754492821-15btgolbd7thaa8fpt7s8hs8hq3jl0lg.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/android/app/sign/DeepSound.properties b/android/app/sign/DeepSound.properties new file mode 100644 index 000000000..43f28dcac --- /dev/null +++ b/android/app/sign/DeepSound.properties @@ -0,0 +1,7 @@ + storePassword=13071376716 + + keyPassword=13071376716 + + keyAlias=DeepSound + + storeFile=sign/DeepSound.keystore \ No newline at end of file diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 88f1fef92..f120cc1f7 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -29,7 +29,7 @@ - + + + + 3829022037361855 + fb3829022037361855 + 193d0a2b1757c41cfa557ea8e8f21c33 + \ No newline at end of file diff --git a/android/build.gradle.kts b/android/build.gradle.kts index 89176ef44..35365df1a 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -3,6 +3,7 @@ allprojects { google() mavenCentral() } + } val newBuildDir: Directory = rootProject.layout.buildDirectory.dir("../../build").get() diff --git a/firebase.json b/firebase.json new file mode 100644 index 000000000..a2b0f41a5 --- /dev/null +++ b/firebase.json @@ -0,0 +1 @@ +{"flutter":{"platforms":{"android":{"default":{"projectId":"deepsound-e0e3d","appId":"1:907754492821:android:2deffc5aa1037ae2145c9c","fileOutput":"android/app/google-services.json"}},"dart":{"lib/firebase_options.dart":{"projectId":"deepsound-e0e3d","configurations":{"android":"1:907754492821:android:2deffc5aa1037ae2145c9c","ios":"1:907754492821:ios:8c1d7d1b583aec22145c9c","macos":"1:907754492821:ios:8c1d7d1b583aec22145c9c","web":"1:907754492821:web:99451e7efd7d120a145c9c","windows":"1:907754492821:web:e3e8040111db6871145c9c"}}}}}} \ No newline at end of file diff --git a/lib/core/translations/app_translations.dart b/lib/core/translations/app_translations.dart index 71bf8b7a0..2d498993a 100644 --- a/lib/core/translations/app_translations.dart +++ b/lib/core/translations/app_translations.dart @@ -3,79 +3,80 @@ import 'package:get/get.dart'; class AppTranslations extends Translations { @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 + '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', + }, + }; +} diff --git a/lib/data/services/audio_service.dart b/lib/data/services/audio_service.dart index a9ad58a4c..398695ea7 100644 --- a/lib/data/services/audio_service.dart +++ b/lib/data/services/audio_service.dart @@ -13,13 +13,13 @@ class AudioServiceManager extends GetxService { // 添加一个标志,表示是否已经尝试过初始化 bool _hasAttemptedInit = false; - + // 添加一个计时器,用于定期检查服务状态 Timer? _serviceCheckTimer; Future init() async { print('初始化 AudioServiceManager'); - + if (_isInitialized) { print('AudioServiceManager 已经初始化'); return this; @@ -38,12 +38,13 @@ class AudioServiceManager extends GetxService { // 使用锁确保初始化的原子性 await _lock.synchronized(() async { if (_isInitialized) return; - + // 使用 AudioService.init 初始化音频服务 _audioHandler = await AudioService.init( builder: () => MyAudioHandler(), config: AudioServiceConfig( - androidNotificationChannelId: 'com.example.deep_voice.channel.audio', + androidNotificationChannelId: + 'com.yunqiinnovation.deepsound.channel.audio', androidNotificationChannelName: 'Deep Voice Audio Service', // 使用应用图标作为通知图标,确保这个图标存在 androidNotificationIcon: 'mipmap/ic_launcher', @@ -58,13 +59,13 @@ class AudioServiceManager extends GetxService { preloadArtwork: false, ), ); - + // 注册自定义音频处理器 await Get.put(_audioHandler!, permanent: true); - + _isInitialized = true; print('AudioServiceManager 初始化成功'); - + // 启动定期检查服务状态的计时器 _startServiceCheckTimer(); }); @@ -73,18 +74,18 @@ class AudioServiceManager extends GetxService { } catch (e) { print('AudioServiceManager初始化失败: $e'); _isInitialized = false; // 确保失败时重置状态 - + // 延迟5秒后尝试重新初始化 Future.delayed(Duration(seconds: 5), () { print('尝试重新初始化 AudioServiceManager'); _hasAttemptedInit = false; init(); }); - + return this; // 即使失败也返回实例,避免空指针异常 } } - + // 启动定期检查服务状态的计时器 void _startServiceCheckTimer() { _serviceCheckTimer?.cancel(); @@ -92,7 +93,7 @@ class AudioServiceManager extends GetxService { _checkAndRestartServiceIfNeeded(); }); } - + // 检查服务状态,如果需要则重启服务 Future _checkAndRestartServiceIfNeeded() async { if (!_isInitialized || _audioHandler == null) { @@ -115,7 +116,7 @@ class AudioServiceManager extends GetxService { await _audioHandler!.customAction('media_button'); } catch (e) { print('发送媒体按钮事件失败: $e'); - + // 如果发送失败,尝试重新初始化服务 _isInitialized = false; _hasAttemptedInit = false; @@ -129,7 +130,7 @@ class AudioServiceManager extends GetxService { await init(); } } - + // 手动触发媒体按钮事件(用于测试) Future triggerMediaButtonEvent() async { print('手动触发媒体按钮事件'); @@ -153,4 +154,4 @@ class AudioServiceManager extends GetxService { super.onClose(); } -} \ No newline at end of file +} diff --git a/lib/firebase_options.dart b/lib/firebase_options.dart new file mode 100644 index 000000000..1f8b9593e --- /dev/null +++ b/lib/firebase_options.dart @@ -0,0 +1,92 @@ +// File generated by FlutterFire CLI. +// ignore_for_file: type=lint +import 'package:firebase_core/firebase_core.dart' show FirebaseOptions; +import 'package:flutter/foundation.dart' + show defaultTargetPlatform, kIsWeb, TargetPlatform; + +/// Default [FirebaseOptions] for use with your Firebase apps. +/// +/// Example: +/// ```dart +/// import 'firebase_options.dart'; +/// // ... +/// await Firebase.initializeApp( +/// options: DefaultFirebaseOptions.currentPlatform, +/// ); +/// ``` +class DefaultFirebaseOptions { + static FirebaseOptions get currentPlatform { + if (kIsWeb) { + return web; + } + switch (defaultTargetPlatform) { + case TargetPlatform.android: + return android; + case TargetPlatform.iOS: + return ios; + case TargetPlatform.macOS: + return macos; + case TargetPlatform.windows: + return windows; + case TargetPlatform.linux: + throw UnsupportedError( + 'DefaultFirebaseOptions have not been configured for linux - ' + 'you can reconfigure this by running the FlutterFire CLI again.', + ); + default: + throw UnsupportedError( + 'DefaultFirebaseOptions are not supported for this platform.', + ); + } + } + + static const FirebaseOptions web = FirebaseOptions( + apiKey: 'AIzaSyBJXQTXsEmY_XA7ZEOusxG6VF5CaKtjHCA', + appId: '1:907754492821:web:99451e7efd7d120a145c9c', + messagingSenderId: '907754492821', + projectId: 'deepsound-e0e3d', + authDomain: 'deepsound-e0e3d.firebaseapp.com', + storageBucket: 'deepsound-e0e3d.firebasestorage.app', + measurementId: 'G-ETX6DF35J3', + ); + + static const FirebaseOptions android = FirebaseOptions( + apiKey: 'AIzaSyAj0_-nS3ygc8VFe1azotSE3-HGZYClkTs', + appId: '1:907754492821:android:2deffc5aa1037ae2145c9c', + messagingSenderId: '907754492821', + projectId: 'deepsound-e0e3d', + storageBucket: 'deepsound-e0e3d.firebasestorage.app', + ); + + static const FirebaseOptions ios = FirebaseOptions( + apiKey: 'AIzaSyAvyBQcOXuO_q851vevqZIufsPRTMCe808', + appId: '1:907754492821:ios:8c1d7d1b583aec22145c9c', + messagingSenderId: '907754492821', + projectId: 'deepsound-e0e3d', + storageBucket: 'deepsound-e0e3d.firebasestorage.app', + androidClientId: '907754492821-9jakqvathu61e6b8g95i41ssi7o4dnka.apps.googleusercontent.com', + iosClientId: '907754492821-ej6j6pfto66jfhpj47b9bjs9amvo9dde.apps.googleusercontent.com', + iosBundleId: 'com.example.client', + ); + + static const FirebaseOptions macos = FirebaseOptions( + apiKey: 'AIzaSyAvyBQcOXuO_q851vevqZIufsPRTMCe808', + appId: '1:907754492821:ios:8c1d7d1b583aec22145c9c', + messagingSenderId: '907754492821', + projectId: 'deepsound-e0e3d', + storageBucket: 'deepsound-e0e3d.firebasestorage.app', + androidClientId: '907754492821-9jakqvathu61e6b8g95i41ssi7o4dnka.apps.googleusercontent.com', + iosClientId: '907754492821-ej6j6pfto66jfhpj47b9bjs9amvo9dde.apps.googleusercontent.com', + iosBundleId: 'com.example.client', + ); + + static const FirebaseOptions windows = FirebaseOptions( + apiKey: 'AIzaSyBJXQTXsEmY_XA7ZEOusxG6VF5CaKtjHCA', + appId: '1:907754492821:web:e3e8040111db6871145c9c', + messagingSenderId: '907754492821', + projectId: 'deepsound-e0e3d', + authDomain: 'deepsound-e0e3d.firebaseapp.com', + storageBucket: 'deepsound-e0e3d.firebasestorage.app', + measurementId: 'G-SKGXEK5TJM', + ); +} diff --git a/lib/main.dart b/lib/main.dart index e374b1554..69f1f51ad 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -11,26 +11,50 @@ import 'package:window_size/window_size.dart'; import 'core/translations/app_translations.dart'; import 'core/themes/app_theme.dart'; import 'core/themes/theme_service.dart'; +import 'modules/login/controllers/weixinLogin.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'firebase_options.dart'; void main() async { // 确保Flutter绑定初始化 WidgetsFlutterBinding.ensureInitialized(); - + // 初始化存储 - 在创建应用之前初始化 await GetStorage.init(); - + // 初始化环境变量 await dotenv.load(); - + + FlWX().register( + appSecret: '4cfde33bc22b6cc6b1f8b225dd97d255', + appId: 'wx5bbb7be9324f3e2d', + params: FlWXCallbackParams(onHttp: (url) async { + return ''; + })); + var err = FlWX().isInstalled; + String msg = err.toString() == 'false' ? '请先下载微信app' : '请授权登录'; + + print(msg); + + if (Firebase.apps.isEmpty) { + await Firebase.initializeApp( + name: 'DeepSound', + options: DefaultFirebaseOptions.currentPlatform, + //demoProjectId:'1:907754492821:android:2deffc5aa1037ae2145c9c' + ).then((value) { + // logger.i('Firebase Initialized:${value.options}'); + print('Firebase Initialized:${value.options}'); + }); + } // 设置窗口大小(仅在 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 +67,7 @@ void main() async { setWindowFrame(windowFrame); } } - + // 运行应用 runApp(const MainApp()); } @@ -55,7 +79,7 @@ class MainApp extends StatelessWidget { Widget build(BuildContext context) { // 获取保存的主题设置 final themeMode = ThemeService().getThemeMode(); - + return ScreenUtilInit( designSize: const Size(375, 812), minTextAdapt: true, @@ -73,7 +97,6 @@ class MainApp extends StatelessWidget { translations: AppTranslations(), locale: const Locale('zh', 'CN'), fallbackLocale: const Locale('en', 'US'), - ); }, ); diff --git a/lib/modules/login/controllers/facebookLogin.dart b/lib/modules/login/controllers/facebookLogin.dart new file mode 100644 index 000000000..d77ae2177 --- /dev/null +++ b/lib/modules/login/controllers/facebookLogin.dart @@ -0,0 +1,39 @@ +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:flutter_facebook_auth/flutter_facebook_auth.dart'; +import 'package:get/get.dart'; + +class Facebook { + String token = ''; + // FaceBook登录 + // 修改后的 signInWithFacebook + Future signInWithFacebook() async { + try { + print("开始 Facebook 登录"); + final LoginResult result = await FacebookAuth.instance.login(); + print("Facebook 登录结果:$result"); + + if (result.status == LoginStatus.success) { + token = result.accessToken!.tokenString; + print("Facebook Token: $token"); + + final credential = FacebookAuthProvider.credential(token); + print("Facebook Token: $credential"); + final userData = await FacebookAuth.instance.getUserData(); + print("user login facebook ${userData..values}"); + + return true; + } else { + print("登录失败,状态:${result.status},消息:${result.message}"); + return false; + } + } catch (e) { + print("Facebook 登录异常:$e"); + return false; + } + } + + // 退出Google登录 + Future signOutFacebook() async { + await FacebookAuth.instance.logOut(); + } +} diff --git a/lib/modules/login/controllers/googleLogin.dart b/lib/modules/login/controllers/googleLogin.dart new file mode 100644 index 000000000..3e118e6f8 --- /dev/null +++ b/lib/modules/login/controllers/googleLogin.dart @@ -0,0 +1,72 @@ +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:google_sign_in/google_sign_in.dart'; +import 'package:get/get.dart'; + +class Google { + String? token = ''; + String? email = ''; + String? displayName = ''; + String? id = ''; + String? accessToken = ''; + + // 登录并返回 + Future signInWithGoogle() async { + try { + // 确保用户已经登录,并获取当前用户的身份验证令牌 + final currentUser = FirebaseAuth.instance.currentUser; + print('Future响应数据: $currentUser'); + if (currentUser != null) { + // 这里处理您需要使用这个JWT令牌的逻辑,例如将它存储到本地存储中作为凭据。 + + token != await currentUser.getIdToken(); + + print('Future响应数据tokenResult: $token'); + + return true; + } else { + UserCredential userCredential = await loginGoogle(); + if (token != "") { + return true; + } else { + return false; + } + + // logger.d("user login google: ${userCredential.user?.displayName}"); + } + } catch (e) { + print("Google 登录异常:$e"); + return false; + } + } + + // Google登录 + Future loginGoogle() async { + print('loginGoogle:'); + final GoogleSignInAccount? googleUser = await GoogleSignIn().signIn(); + email = googleUser?.email; + id = googleUser?.id; + displayName = googleUser?.displayName; + print('loginGoogle: $googleUser'); + // Obtain the auth details from the request + final GoogleSignInAuthentication? googleAuth = + await googleUser?.authentication; + accessToken = googleAuth?.accessToken; + token = googleAuth?.idToken; + print('loginGoogle:googleAuth?.idToken= ${token}'); + print('loginGoogle:googleAuth?.accessToken= ${accessToken}'); + // Create a new credential, + final credential = GoogleAuthProvider.credential( + accessToken: googleAuth?.accessToken, + idToken: googleAuth?.idToken, + ); + + // Once signed in, return the UserCredential + return await FirebaseAuth.instance.signInWithCredential(credential); + } + + // 退出Google登录 + Future signOutGoogle() async { + await FirebaseAuth.instance.signOut(); + await GoogleSignIn().signOut(); + } +} diff --git a/lib/modules/login/controllers/weixinLogin.dart b/lib/modules/login/controllers/weixinLogin.dart new file mode 100644 index 000000000..214c5a663 --- /dev/null +++ b/lib/modules/login/controllers/weixinLogin.dart @@ -0,0 +1,889 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; +import 'dart:typed_data'; +import 'package:fluwx/fluwx.dart'; +export 'package:fluwx/fluwx.dart'; +import 'package:http/http.dart' as http; +import 'package:get/get.dart'; + +typedef FlWeChatResponseCallback = void Function(WeChatResponse response); + +/// 支持成功 +typedef FlWeChatPayResponseResultCallback = void Function(); + +/// 支付结果回调 +typedef FlWeChatPayResponseCallback = void Function( + WeChatPaymentResponse response); + +/// LaunchFromWX 回调 +typedef FlWeChatLaunchFromWXRequestCallback = void Function( + WeChatLaunchFromWXRequest response); + +/// OpenBusinessWebView 回调 +typedef FlWeChatOpenBusinessWebViewResponseCallback = void Function( + WeChatOpenBusinessWebviewResponse response); + +/// WeChatOpenBusinessView 回调 +typedef FlWeChatOpenBusinessViewResponseCallback = void Function( + WeChatOpenBusinessViewResponse response); + +/// Share 回调 +typedef FlWeChatShareResponseCallback = void Function( + WeChatShareResponse response); + +/// Auth 回调 +typedef FlWeChatAuthResponseCallback = Function(WeChatAuthResponse response); + +/// Auth token 回调 +typedef FlWeChatAuthResponseTokenCallback = Function( + WeChatAuthResponse response, WXTokenModel token); + +/// Auth userinfo 回调 +typedef FlWeChatAuthResponseUserinfoCallback = Function( + WeChatAuthResponse response, + WXTokenModel token, + WXUserModel userInfo, +); + +/// 显示 toast +typedef FlWXToastCallback = void Function(String msg); + +/// 日志 打印 +typedef FlWXLogCallback = void Function(String msg); + +/// http 请求 +typedef FlWXHTTPCallback = Future Function(String url); + +class FlWX { + factory FlWX() => _singleton ??= FlWX._(); + + FlWX._(); + + static FlWX? _singleton; + + final Fluwx fluwx = Fluwx(); + + String? _appId; + + /// 获取当前设置的 appId + String? get appId => _appId; + + String? _appSecret; + + /// 获取当前设置的 appSecret + String? get appSecret => _appSecret; + + String _appName = ''; + String token = ''; + String refresh_token = ''; + String openid = ''; + // 参数 说明 + // access_token 接口调用凭证 + // expires_in access_token 接口调用凭证超时时间,单位(秒) + // refresh_token 用户刷新 access_token + // openid 授权用户唯一标识 + // scope 用户授权的作用域(snsapi_userinfo) + // unionid 当且仅当该移动应用已获得该用户的 userinfo 授权时,才会出现该字段 + /// callback params + FlWXCallbackParams? _params; + + /// 分享时的缩略图 + Uint8List? _shareThumbnail; + + /// register + bool _isRegister = false; + + Future register({ + required String appId, + required FlWXCallbackParams params, + String? universalLink, + bool doOnIOS = true, + bool doOnAndroid = true, + + /// 用于获取用户信息 + String? appSecret, + + /// app名称(用于分享时使用) + String appName = '', + + /// 分享时的缩略图 + Uint8List? shareThumbnail, + }) { + assert(appId.isNotEmpty); + if (universalLink != null) assert(universalLink.isNotEmpty); + if (appSecret != null) { + assert(appSecret.isNotEmpty); + } + _isRegister = true; + _appId = appId; + _appSecret = appSecret; + _appName = appName; + _shareThumbnail = shareThumbnail; + _params = params; + return fluwx.registerApi( + appId: appId, + doOnAndroid: doOnAndroid, + doOnIOS: doOnIOS, + universalLink: universalLink, + ); + } + + /// 获取微信 用户token + String _getTokenUrl(String code) { + print("_getTokenUrl 数据获取${_appId},${_appSecret}"); + assert( + _appId != null && + _appId!.isNotEmpty && + _appSecret != null && + _appSecret!.isNotEmpty, + ); + return 'https://api.weixin.qq.com/sns/oauth2/access_token?appid=$_appId&secret=$_appSecret&code=$code&grant_type=authorization_code'; + } + + /// 获取微信 用户token + String _getRefreshTokenUrl(String code) { + print("_getRefreshTokenUrl 数据获取${_appId},${_appSecret}"); + assert( + _appId != null && + _appId!.isNotEmpty && + _appSecret != null && + _appSecret!.isNotEmpty, + ); + return 'https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=$_appId&grant_type=refresh_token&&refresh_token=$refresh_token'; + } + + // GET请求示例 + Future> fetchData(String Url) async { + try { + final response = await http.get(Uri.parse(Url)); + + if (response.statusCode == 200) { + final data = jsonDecode(response.body) as Map; + print('GET响应数据: $data'); + + return data; + } else { + print('GET请求失败,状态码: ${response.statusCode}'); + return {}; + } + } catch (e) { + print('GET请求异常: $e'); + return {}; + } + } + + // POST请求示例 + Future postData() async { + try { + final response = await http.post( + Uri.parse('https://jsonplaceholder.typicode.com/posts'), + headers: { + 'Content-Type': 'application/json', + }, // 设置请求头‌:ml-citation{ref="6" data="citationList"} + body: jsonEncode({ + // 编码JSON数据‌:ml-citation{ref="3" data="citationList"} + 'title': 'foo', + 'body': 'bar', + 'userId': 1, + }), + ); + + if (response.statusCode == 201) { + final data = jsonDecode(response.body); + print('POST响应数据: $data'); + return true; + } else { + print('POST请求失败,状态码: ${response.statusCode}'); + return false; + } + } catch (e) { + print('POST请求异常: $e'); + return false; + } + } + + /// 获取微信 用户信息 + String _getUserInfoUrl({required String openId, required String token}) => + 'https://api.weixin.qq.com/sns/userinfo?access_token=$token&openid=$openId'; + //检验授权凭证(access_token)是否有效 + String _getUserTokenEffertive({ + required String openId, + required String token, + }) => + 'https://api.weixin.qq.com/sns/auth?access_token=$token&openid=$openId'; + + /// 是否安装微信 + Future get isInstalled async { + assert(_isRegister, '请先调用 FlWX().register()'); + final installed = await fluwx.isWeChatInstalled; + if (!installed) _params!.onToast?.call('该服务需要先安装微信'); + return installed; + } + + /// isSupportOpenBusinessView + Future get isSupportOpenBusinessView async { + if (!await isInstalled) return false; + return await fluwx.isSupportOpenBusinessView; + } + + /// 添加移除监听 + FluwxCancelable _onListener(FlWeChatResponseCallback onResponse) { + late FluwxCancelable cancelable; + subscriber(response) { + onResponse(response); + Future.delayed(const Duration(milliseconds: 500), () { + cancelable.cancel(); + }); + } + + return cancelable = fluwx.addSubscriber(subscriber); + } + + Future getExtMsg() async { + if (!await isInstalled) return null; + return await fluwx.getExtMsg(); + } + + Future attemptToResumeMsgFromWx({ + Function(WeChatShowMessageFromWXRequest response)? onResponse, + }) async { + if (!await isInstalled) return; + await fluwx.attemptToResumeMsgFromWx(); + if (onResponse != null) { + _onListener((response) { + if (response is WeChatShowMessageFromWXRequest) onResponse(response); + }); + } + } + + /// 调用微信登录 + Future authBy( + AuthType which, { + /// 原始数据回调 + FlWeChatAuthResponseCallback? onResponse, + + /// 获取token回调 + FlWeChatAuthResponseTokenCallback? onToken, + + /// 获取userinfo回调 + FlWeChatAuthResponseUserinfoCallback? onUserinfo, + bool getUserInfo = false, + }) async { + if (!await isInstalled) return false; + final cancelable = _onListener((response) async { + if (response is WeChatAuthResponse) { + onResponse?.call(response); + print( + "用户点击事件${response.isSuccessful},${response.code},${response.lang},${response.state},${response.country}", + ); + if (response.isSuccessful && response.code != null) { + print("数据获取失败${onToken},${onUserinfo}"); + // if (onToken != null || onUserinfo != null) { + try { + var Istoken = await fetchData( + _getTokenUrl(response.code!), + ); // 触发GET请求 + //token有效期是30天,30天后需要重新授权,需要看看API文档 + print("数据获取fetchData${Istoken}"); + + if (Istoken != {}) { + token = Istoken["access_token"].toString(); + refresh_token = Istoken["refresh_token"].toString(); + openid = Istoken["openid"].toString(); + print("数据获取token=${token}"); + print("数据获取openid=${openid}"); + print("数据获取refresh_token=${refresh_token}"); + Istoken = await fetchData(_getRefreshTokenUrl(response.code!)); + print("数据获取_getUserTokenEffertive${Istoken}"); + Istoken = await fetchData( + _getUserTokenEffertive(openId: openid, token: token), + ); // 触发GET请求 + print("数据获取_getUserTokenEffertive${Istoken}"); + Istoken = await fetchData( + _getUserInfoUrl(openId: openid, token: token), + ); // 触发GET请求 + print("数据获取_getUserInfoUrl${Istoken}"); + } + } catch (e) { + _params!.onLog?.call('数据解析失败 $e'); + } + // } + } + } else if (response is WeChatAuthByQRCodeFinishedResponse) {} + }); + final result = await fluwx.authBy(which: which); + print("authBy数据获取${result}"); + if (!result) { + cancelable.cancel(); + _params!.onToast?.call('微信授权失败'); + } + return result; + } + + Future autoDeduct( + AutoDeduct data, { + FlWeChatResponseCallback? onResponse, + }) async { + final cancelable = _onListener((response) { + onResponse?.call(response); + }); + final result = await fluwx.autoDeduct(data: data); + if (!result) { + cancelable.cancel(); + _params!.onToast?.call('微信签约失败'); + } + return result; + } + + /// 调用微信支付 + Future pay( + PayType which, { + FlWeChatPayResponseResultCallback? onSuccess, + FlWeChatPayResponseCallback? onResponse, + }) async { + if (!await isInstalled) return false; + final cancelable = _onListener((response) { + if (response is WeChatPaymentResponse) { + onResponse?.call(response); + _params!.onLog?.call('微信支付 result code: ${response.errCode} '); + if (response.errCode == 0) { + _params!.onToast?.call('支付成功'); + onSuccess?.call(); + } else if (response.errCode == -2) { + _params!.onToast?.call('已取消支付'); + } else { + _params!.onToast?.call('支付失败'); + } + } + }); + final result = await fluwx.pay(which: which); + if (!result) { + cancelable.cancel(); + _params!.onToast?.call('支付失败'); + } + return result; + } + + /// ****************** open ****************** /// + + Future open(OpenType target) async { + if (!await isInstalled) return false; + return fluwx.open(target: target); + } + + Future openWeChatApp({ + FlWeChatLaunchFromWXRequestCallback? onResponse, + }) async { + if (!await isInstalled) return false; + final cancelable = _onListener((response) { + if (response is WeChatLaunchFromWXRequest) onResponse?.call(response); + }); + final result = await fluwx.open(target: WeChatApp()); + if (!result) cancelable.cancel(); + return result; + } + + Future openBrowser( + String url, { + FlWeChatOpenBusinessWebViewResponseCallback? onResponse, + }) async { + if (!await isInstalled) return false; + final cancelable = _onListener((response) { + if (response is WeChatOpenBusinessWebviewResponse) { + onResponse?.call(response); + } + }); + final result = await fluwx.open(target: Browser(url)); + if (!result) cancelable.cancel(); + return result; + } + + Future openRankList() async { + if (!await isInstalled) return false; + return fluwx.open(target: RankList()); + } + + Future openBusinessView({ + required String businessType, + required String query, + FlWeChatOpenBusinessViewResponseCallback? onResponse, + }) async { + if (!await isInstalled) return false; + final cancelable = _onListener((response) { + if (response is WeChatOpenBusinessViewResponse) { + onResponse?.call(response); + } + }); + final result = await fluwx.open( + target: BusinessView(businessType: businessType, query: query), + ); + if (!result) cancelable.cancel(); + return result; + } + + Future openInvoice({ + required String appId, + required String cardType, + String locationId = '', + String cardId = '', + String canMultiSelect = '', + Function(WeChatOpenInvoiceResponse response)? onResponse, + }) async { + if (!await isInstalled) return false; + final cancelable = _onListener((response) { + if (response is WeChatOpenInvoiceResponse) onResponse?.call(response); + }); + final result = await fluwx.open( + target: Invoice( + appId: appId, + cardType: cardType, + locationId: locationId, + cardId: cardId, + canMultiSelect: canMultiSelect, + ), + ); + if (!result) cancelable.cancel(); + return result; + } + + Future openCustomerServiceChat({ + required String corpId, + required String url, + Function(WeChatOpenCustomerServiceChatResponse response)? onResponse, + }) async { + if (!await isInstalled) return false; + final cancelable = _onListener((response) { + if (response is WeChatOpenCustomerServiceChatResponse) { + onResponse?.call(response); + } + }); + final result = await fluwx.open( + target: CustomerServiceChat(corpId: corpId, url: url), + ); + if (!result) cancelable.cancel(); + return result; + } + + Future openMiniProgram( + String username, { + String? path, + Function(WeChatLaunchMiniProgramResponse response)? onResponse, + }) async { + if (!await isInstalled) return false; + final cancelable = _onListener((response) { + if (response is WeChatLaunchMiniProgramResponse) { + onResponse?.call(response); + } + }); + final result = await fluwx.open( + target: MiniProgram(username: username, path: path), + ); + if (!result) { + cancelable.cancel(); + _params?.onToast?.call('小程序打开失败'); + } + return result; + } + + Future openSubscribeMessage({ + required String appId, + required int scene, + required String templateId, + String? reserved, + Function(WeChatSubscribeMsgResponse response)? onResponse, + }) async { + if (!await isInstalled) return false; + final cancelable = _onListener((response) { + if (response is WeChatSubscribeMsgResponse) onResponse?.call(response); + }); + final result = await fluwx.open( + target: SubscribeMessage( + appId: appId, + scene: scene, + templateId: templateId, + reserved: reserved, + ), + ); + if (!result) cancelable.cancel(); + return result; + } + + /// ****************** share ****************** /// + + Future share( + WeChatShareModel what, { + FlWeChatShareResponseCallback? onResponse, + }) async { + if (!await isInstalled) return false; + return _shareResponse(fluwx.share(what), onResponse: onResponse); + } + + Future shareText( + String source, { + String? title, + String? description, + WeChatScene scene = WeChatScene.session, + String? messageExt, + String? messageAction, + String? mediaTagName, + String? msgSignature, + Uint8List? thumbData, + String? thumbDataHash, + FlWeChatShareResponseCallback? onResponse, + }) async { + if (!await isInstalled) return false; + return _shareResponse( + fluwx.share( + WeChatShareTextModel( + source, + title: title ?? _appName, + description: description ?? _appName, + scene: scene, + messageExt: messageExt, + messageAction: messageAction, + mediaTagName: mediaTagName, + msgSignature: msgSignature, + thumbData: thumbData ?? _shareThumbnail, + thumbDataHash: thumbDataHash, + ), + ), + onResponse: onResponse, + ); + } + + Future shareMiniProgram( + String webPage, { + required String webPageUrl, + WXMiniProgramType miniProgramType = WXMiniProgramType.release, + required String userName, + String path = "/", + String? title, + String? description, + bool withShareTicket = false, + String? messageExt, + String? messageAction, + String? mediaTagName, + String? msgSignature, + Uint8List? thumbData, + String? thumbDataHash, + FlWeChatShareResponseCallback? onResponse, + }) async { + if (!await isInstalled) return false; + return _shareResponse( + fluwx.share( + WeChatShareMiniProgramModel( + webPageUrl: webPageUrl, + userName: userName, + title: title ?? _appName, + description: description ?? _appName, + messageExt: messageExt, + messageAction: messageAction, + mediaTagName: mediaTagName, + msgSignature: msgSignature, + thumbData: thumbData ?? _shareThumbnail, + thumbDataHash: thumbDataHash, + ), + ), + onResponse: onResponse, + ); + } + + Future shareImage( + WeChatImageToShare source, { + String? title, + String? description, + WeChatScene scene = WeChatScene.session, + String? messageExt, + String? messageAction, + String? mediaTagName, + String? msgSignature, + Uint8List? thumbData, + String? thumbDataHash, + FlWeChatShareResponseCallback? onResponse, + }) async { + if (!await isInstalled) return false; + return _shareResponse( + fluwx.share( + WeChatShareImageModel( + source, + title: title ?? _appName, + description: description ?? _appName, + scene: scene, + messageExt: messageExt, + messageAction: messageAction, + mediaTagName: mediaTagName, + msgSignature: msgSignature, + thumbData: thumbData ?? _shareThumbnail, + thumbDataHash: thumbDataHash, + ), + ), + onResponse: onResponse, + ); + } + + Future shareMusic({ + String? musicUrl, + String? musicDataUrl, + String? musicLowBandUrl, + String? musicLowBandDataUrl, + String? title, + String? description, + WeChatScene scene = WeChatScene.session, + String? messageExt, + String? messageAction, + String? mediaTagName, + String? msgSignature, + Uint8List? thumbData, + String? thumbDataHash, + FlWeChatShareResponseCallback? onResponse, + }) async { + if (!await isInstalled) return false; + return _shareResponse( + fluwx.share( + WeChatShareMusicModel( + musicUrl: musicUrl, + musicDataUrl: musicDataUrl, + musicLowBandUrl: musicLowBandUrl, + musicLowBandDataUrl: musicLowBandDataUrl, + title: title ?? _appName, + description: description ?? _appName, + scene: scene, + messageExt: messageExt, + messageAction: messageAction, + mediaTagName: mediaTagName, + msgSignature: msgSignature, + thumbData: thumbData ?? _shareThumbnail, + thumbDataHash: thumbDataHash, + ), + ), + onResponse: onResponse, + ); + } + + Future shareVideo( + String videoUrl, { + String? title, + String? description, + WeChatScene scene = WeChatScene.session, + String? messageExt, + String? messageAction, + String? mediaTagName, + String? msgSignature, + Uint8List? thumbData, + String? thumbDataHash, + FlWeChatShareResponseCallback? onResponse, + }) async { + if (!await isInstalled) return false; + return _shareResponse( + fluwx.share( + WeChatShareVideoModel( + title: title ?? _appName, + videoUrl: videoUrl, + description: description ?? _appName, + scene: scene, + messageExt: messageExt, + messageAction: messageAction, + mediaTagName: mediaTagName, + msgSignature: msgSignature, + thumbData: thumbData ?? _shareThumbnail, + thumbDataHash: thumbDataHash, + ), + ), + onResponse: onResponse, + ); + } + + Future shareWebPage( + String webPage, { + String? title, + String? description, + WeChatScene scene = WeChatScene.session, + String? messageExt, + String? messageAction, + String? mediaTagName, + String? msgSignature, + Uint8List? thumbData, + String? thumbDataHash, + FlWeChatShareResponseCallback? onResponse, + }) async { + if (!await isInstalled) return false; + return _shareResponse( + fluwx.share( + WeChatShareWebPageModel( + webPage, + title: title ?? _appName, + description: description ?? _appName, + scene: scene, + messageExt: messageExt, + messageAction: messageAction, + mediaTagName: mediaTagName, + msgSignature: msgSignature, + thumbData: thumbData ?? _shareThumbnail, + thumbDataHash: thumbDataHash, + ), + ), + onResponse: onResponse, + ); + } + + Future shareFile( + File source, { + required String suffix, + String? title, + String? description, + WeChatScene scene = WeChatScene.session, + String? messageExt, + String? messageAction, + String? mediaTagName, + String? msgSignature, + Uint8List? thumbData, + String? thumbDataHash, + FlWeChatShareResponseCallback? onResponse, + }) async { + if (!await isInstalled) return false; + return _shareResponse( + fluwx.share( + WeChatShareFileModel( + WeChatFile.file(source, suffix: suffix), + title: title ?? _appName, + description: description ?? _appName, + scene: scene, + messageExt: messageExt, + messageAction: messageAction, + mediaTagName: mediaTagName, + msgSignature: msgSignature, + thumbData: thumbData ?? _shareThumbnail, + thumbDataHash: thumbDataHash, + ), + ), + onResponse: onResponse, + ); + } + + Future _shareResponse( + Future share, { + FlWeChatShareResponseCallback? onResponse, + }) async { + final cancelable = _onListener((response) { + if (response is WeChatShareResponse) onResponse?.call(response); + }); + final result = await share; + if (!result) cancelable.cancel(); + return result; + } +} + +class FlWXCallbackParams { + FlWXCallbackParams({this.onToast, this.onLog, this.onHttp}); + + /// toast 显示 + final FlWXToastCallback? onToast; + + /// 日志 打印 + final FlWXLogCallback? onLog; + + /// http 请求 返回 json + final FlWXHTTPCallback? onHttp; +} + +class WXUserModel { + final String? openid; + final String? nickname; + final int? sex; + final String? language; + final String? city; + final String? province; + final String? country; + final String? headImgUrl; + final List? privilege; + final String? unionId; + + WXUserModel.fromJson(Map json) + : openid = json['openid'] as String?, + nickname = json['nickname'] as String?, + sex = json['sex'] as int?, + language = json['language'] as String?, + city = json['city'] as String?, + province = json['province'] as String?, + country = json['country'] as String?, + headImgUrl = json['headimgurl'] as String?, + privilege = json['privilege'] as List?, + unionId = json['unionid'] as String?; + + Map toMap() => { + 'openid': openid, + 'unionId': unionId, + 'nickname': nickname, + 'sex': sex, + 'headImgUrl': headImgUrl, + 'privilege': privilege, + 'language': language, + 'city': city, + 'province': province, + 'country': country, + }; +} + +class WXTokenModel { + final String? accessToken; + final int? expiresIn; + final String? refreshToken; + final String? openid; + final String? scope; + final String? unionId; + + WXTokenModel.fromJson(Map json) + : accessToken = json['access_token'] as String?, + expiresIn = json['expires_in'] as int?, + refreshToken = json['refresh_token'] as String?, + openid = json['openid'] as String?, + scope = json['scope'] as String?, + unionId = json['unionid'] as String?; + + Map toMap() => { + 'openid': openid, + 'unionId': unionId, + 'accessToken': accessToken, + 'expiresIn': expiresIn, + 'refreshToken': refreshToken, + 'scope': scope, + }; +} + +extension ExtensionMap on Map { + Payment? toPayment() { + try { + final appId = (this['appId'] ?? this['appid']); + final partnerId = (this['partnerId'] ?? + this['partnerid'] ?? + this['partner']) as String?; + final prepayId = (this['prepayId'] ?? this['prepayid'] ?? this['prepay']); + final packageValue = + (this['packageValue'] ?? this['packagevalue'] ?? this['package']); + final nonceStr = (this['nonceStr'] ?? this['noncestr'] ?? this['nonce']); + final timestamp = (this['timestamp'] ?? this['timeStamp']); + final sign = this['sign']; + if (appId != null && + partnerId != null && + prepayId != null && + packageValue != null && + nonceStr != null && + timestamp != null && + sign != null) { + return Payment( + appId: appId.toString(), + partnerId: partnerId.toString(), + prepayId: prepayId.toString(), + packageValue: packageValue.toString(), + nonceStr: nonceStr.toString(), + timestamp: int.parse(timestamp!.toString()), + sign: sign.toString(), + ); + } + } catch (e) { + FlWX()._params?.onLog?.call(e.toString()); + } + return null; + } +} diff --git a/lib/modules/login/views/login_view.dart b/lib/modules/login/views/login_view.dart index 9fda142f7..0280cbf15 100644 --- a/lib/modules/login/views/login_view.dart +++ b/lib/modules/login/views/login_view.dart @@ -3,6 +3,9 @@ import 'package:get/get.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import '../controllers/login_controller.dart'; +import '../controllers/facebookLogin.dart'; +import '../controllers/googleLogin.dart'; +import '../controllers/weixinLogin.dart'; import '../../../core/themes/app_theme.dart'; class LoginView extends GetView { @@ -11,7 +14,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 +36,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 +48,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 +74,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), @@ -175,7 +178,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( @@ -217,7 +222,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( @@ -253,25 +260,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}s后重新获取' + : '获取验证码', + style: TextStyle( + fontSize: 14.sp, + fontWeight: FontWeight.w500, + ), + ), + )), ], ), ), @@ -283,35 +291,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( + '继续', + style: TextStyle( + fontSize: 16.sp, + fontWeight: FontWeight.w600, + ), + ), + )), ); } @@ -321,7 +330,9 @@ 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( @@ -332,7 +343,9 @@ class LoginView extends GetView { ), ), ), - 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 +354,11 @@ 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), + SizedBox(width: 24.w), + _buildSocialButton( + FontAwesomeIcons.facebook, Colors.blue, isDarkMode), ], ), ], @@ -356,25 +373,71 @@ 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( - icon, - color: color, - size: 20.sp, - ), - onPressed: () {}, - ), + icon: FaIcon( + icon, + color: color, + size: 20.sp, + ), + onPressed: () => SocialLogin(icon)), ); } + Future SocialLogin(IconData icon) async { + switch (icon) { + case FontAwesomeIcons.facebook: + { + print("数据获取Facebook"); + bool authResult = await Facebook().signInWithFacebook(); + print("数据获取Facebook().token${authResult}"); + if (authResult) { + print("数据获取_Facebook.token${Facebook().token}"); + controller.login(); + } + break; + } + case FontAwesomeIcons.google: + { + print("数据获取_Google()"); + bool authResult = await Google().signInWithGoogle(); + print("数据获取_Google().token${authResult}"); + if (authResult) { + controller.login(); + } + + break; + } + case FontAwesomeIcons.weixin: + { + print("数据获取_FlWX()"); + bool authResult = await FlWX().authBy( + NormalAuth( + scope: "snsapi_userinfo", + state: "wechat_sdk_demo_test", + ), + ); + + if (authResult) { + // 确保 FlWX() 返回的是同一个实例,并且 token 已经被设置 + // 这里假设 FlWX().token 在 authBy 成功后被设置 + print("数据获取_FlWX().token${FlWX().token}"); + controller.login(); + } + break; + } + } + } + // 用户协议和隐私政策 Widget _buildTermsAndPrivacy(bool isDarkMode) { return Column( @@ -458,12 +521,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 +535,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 +587,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 +617,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 +659,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/settings/views/settings_view.dart b/lib/modules/settings/views/settings_view.dart index 683076e9c..ee64c42a9 100644 --- a/lib/modules/settings/views/settings_view.dart +++ b/lib/modules/settings/views/settings_view.dart @@ -13,7 +13,7 @@ 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( @@ -28,7 +28,6 @@ class SettingsView extends GetView { ), ), centerTitle: true, - ), body: ListView( padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 16.h), @@ -51,22 +50,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 +73,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( @@ -118,7 +116,8 @@ 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), @@ -137,7 +136,8 @@ class SettingsView extends GetView { '已连接设备: DeepVoice Pro', style: TextStyle( fontSize: 12.sp, - color: isDarkMode ? Colors.white70 : Colors.black54, + color: + isDarkMode ? Colors.white70 : Colors.black54, ), overflow: TextOverflow.ellipsis, ), @@ -159,7 +159,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), @@ -207,7 +207,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, @@ -218,7 +218,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: Column( children: [ // 通知设置 @@ -226,8 +228,11 @@ class SettingsView extends GetView { 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]!, + 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: () { @@ -235,34 +240,44 @@ 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]), + // 主题设置 _buildToggleSetting( title: '暗黑模式', subtitle: '切换应用的明暗主题', 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互动历史', icon: Icons.cloud_upload_outlined, - iconBgColor: isDarkMode ? Colors.cyan[900]!.withOpacity(0.3) : Colors.cyan[100]!, - iconColor: isDarkMode ? Colors.cyan[300]! : Colors.cyan[600]!, + iconBgColor: isDarkMode + ? Colors.cyan[900]!.withOpacity(0.3) + : Colors.cyan[100]!, + iconColor: + isDarkMode ? Colors.cyan[300]! : Colors.cyan[600]!, value: '上次备份: 今天', valueStyle: TextStyle( fontSize: 12.sp, @@ -273,15 +288,22 @@ 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: '隐私设置', subtitle: '管理数据收集和权限', 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: () { @@ -289,20 +311,28 @@ 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: '退出登录', 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( '退出登录', style: TextStyle( @@ -312,7 +342,8 @@ class SettingsView extends GetView { content: Text( '确定要退出当前账号吗?', style: TextStyle( - color: isDarkMode ? Colors.white70 : Colors.black54, + color: + isDarkMode ? Colors.white70 : Colors.black54, ), ), actions: [ @@ -321,7 +352,9 @@ class SettingsView extends GetView { child: Text( '取消', style: TextStyle( - color: isDarkMode ? Colors.white70 : Colors.black54, + color: isDarkMode + ? Colors.white70 + : Colors.black54, ), ), ), @@ -333,7 +366,9 @@ class SettingsView extends GetView { child: Text( '确定', style: TextStyle( - color: isDarkMode ? Colors.red[300] : Colors.red[600], + color: isDarkMode + ? Colors.red[300] + : Colors.red[600], ), ), ), @@ -376,7 +411,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, @@ -387,7 +422,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: [ @@ -399,8 +436,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, @@ -430,7 +469,8 @@ class SettingsView extends GetView { '版本 2.4.1 (Build 87)', style: TextStyle( fontSize: 12.sp, - color: isDarkMode ? Colors.white70 : Colors.black54, + color: + isDarkMode ? Colors.white70 : Colors.black54, ), ), ], @@ -438,9 +478,12 @@ 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( @@ -525,8 +568,7 @@ class SettingsView extends GetView { color: isDarkMode ? Colors.white : Colors.black87, ), ), - if (subtitle != null) - SizedBox(height: 2.h), + if (subtitle != null) SizedBox(height: 2.h), if (subtitle != null) Text( subtitle, @@ -595,8 +637,7 @@ class SettingsView extends GetView { color: isDarkMode ? Colors.white : Colors.black87, ), ), - if (subtitle != null) - SizedBox(height: 2.h), + if (subtitle != null) SizedBox(height: 2.h), if (subtitle != null) Text( subtitle, @@ -613,17 +654,20 @@ class SettingsView extends GetView { children: [ 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, + ), ), SizedBox(width: 8.w), Container( 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( @@ -683,17 +727,18 @@ 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), ), ), - 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), ), ), ], @@ -704,7 +749,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( @@ -718,4 +765,4 @@ class SettingsView extends GetView { ), ); } -} \ No newline at end of file +} diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt index 24b0c8f03..05e1258a9 100644 --- a/linux/CMakeLists.txt +++ b/linux/CMakeLists.txt @@ -7,7 +7,7 @@ project(runner LANGUAGES CXX) set(BINARY_NAME "deep_voice") # The unique GTK application identifier for this application. See: # https://wiki.gnome.org/HowDoI/ChooseApplicationID -set(APPLICATION_ID "com.example.deep_voice") +set(APPLICATION_ID "com.yunqiinnovation.deepsound") # Explicitly opt in to modern CMake behaviors to avoid warnings with recent # versions of CMake. diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index fab21e7ca..f443e8bbe 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -7,6 +7,7 @@ #include "generated_plugin_registrant.h" #include +#include #include #include #include @@ -15,6 +16,9 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) file_selector_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin"); file_selector_plugin_register_with_registrar(file_selector_linux_registrar); + g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin"); + flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar); g_autoptr(FlPluginRegistrar) record_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "RecordLinuxPlugin"); record_linux_plugin_register_with_registrar(record_linux_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 6915aceb9..346ccb75e 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -4,6 +4,7 @@ list(APPEND FLUTTER_PLUGIN_LIST file_selector_linux + flutter_secure_storage_linux record_linux url_launcher_linux window_size diff --git a/pubspec.yaml b/pubspec.yaml index f4e29d2e3..c88d2be43 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -47,7 +47,11 @@ dependencies: font_awesome_flutter: ^10.8.0 lottie: ^3.3.1 app_settings: 5.2.0 - + fluwx: ^5.4.1 + firebase_core: ^3.12.1 + firebase_auth: ^5.5.0 + google_sign_in: ^6.2.2 + flutter_facebook_auth: ^7.1.1 dev_dependencies: flutter_test: sdk: flutter diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index eb676d5f0..74bd2623b 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -7,6 +7,9 @@ #include "generated_plugin_registrant.h" #include +#include +#include +#include #include #include #include @@ -17,6 +20,12 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { FileSelectorWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("FileSelectorWindows")); + FirebaseAuthPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FirebaseAuthPluginCApi")); + FirebaseCorePluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FirebaseCorePluginCApi")); + FlutterSecureStorageWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin")); FlutterTtsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("FlutterTtsPlugin")); PermissionHandlerWindowsPluginRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 47ae460e0..e548eecfe 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -4,6 +4,9 @@ list(APPEND FLUTTER_PLUGIN_LIST file_selector_windows + firebase_auth + firebase_core + flutter_secure_storage_windows flutter_tts permission_handler_windows record_windows