Browse Source

微信,谷歌,facebook登陆

weicu
fdp 2 years ago
parent
commit
065b48806b
  1. 36
      GoogleService-Info.plist
  2. 36
      android/app/build.gradle.kts
  3. 47
      android/app/google-services.json
  4. 7
      android/app/sign/DeepSound.properties
  5. 5
      android/app/src/main/AndroidManifest.xml
  6. 4
      android/app/src/main/kotlin/com/example/deep_voice/AzureAsrHelper.kt
  7. 4
      android/app/src/main/kotlin/com/example/deep_voice/AzureTtsHelper.kt
  8. 2
      android/app/src/main/kotlin/com/example/deep_voice/ClassicBluetoothHelper.kt
  9. 4
      android/app/src/main/kotlin/com/example/deep_voice/MainActivity.kt
  10. 10
      android/app/src/main/kotlin/com/example/deep_voice/VoiceInteractionService.kt
  11. 2
      android/app/src/main/kotlin/com/example/deep_voice/VolcanoAIService.kt
  12. 2
      android/app/src/main/kotlin/com/example/deep_voice/core/utils/FileLogger.kt
  13. 6
      android/app/src/main/res/values/strings.xml
  14. 1
      android/build.gradle.kts
  15. 1
      firebase.json
  16. 153
      lib/core/translations/app_translations.dart
  17. 29
      lib/data/services/audio_service.dart
  18. 92
      lib/firebase_options.dart
  19. 39
      lib/main.dart
  20. 39
      lib/modules/login/controllers/facebookLogin.dart
  21. 72
      lib/modules/login/controllers/googleLogin.dart
  22. 889
      lib/modules/login/controllers/weixinLogin.dart
  23. 343
      lib/modules/login/views/login_view.dart
  24. 171
      lib/modules/settings/views/settings_view.dart
  25. 2
      linux/CMakeLists.txt
  26. 4
      linux/flutter/generated_plugin_registrant.cc
  27. 1
      linux/flutter/generated_plugins.cmake
  28. 6
      pubspec.yaml
  29. 9
      windows/flutter/generated_plugin_registrant.cc
  30. 3
      windows/flutter/generated_plugins.cmake

36
GoogleService-Info.plist

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CLIENT_ID</key>
<string>907754492821-oc9e77svgmmlgouo0op7vv00e1hb8278.apps.googleusercontent.com</string>
<key>REVERSED_CLIENT_ID</key>
<string>com.googleusercontent.apps.907754492821-oc9e77svgmmlgouo0op7vv00e1hb8278</string>
<key>ANDROID_CLIENT_ID</key>
<string>907754492821-9jakqvathu61e6b8g95i41ssi7o4dnka.apps.googleusercontent.com</string>
<key>API_KEY</key>
<string>AIzaSyAvyBQcOXuO_q851vevqZIufsPRTMCe808</string>
<key>GCM_SENDER_ID</key>
<string>907754492821</string>
<key>PLIST_VERSION</key>
<string>1</string>
<key>BUNDLE_ID</key>
<string>com.yunqiinnovation.deepsound</string>
<key>PROJECT_ID</key>
<string>deepsound-e0e3d</string>
<key>STORAGE_BUCKET</key>
<string>deepsound-e0e3d.firebasestorage.app</string>
<key>IS_ADS_ENABLED</key>
<false></false>
<key>IS_ANALYTICS_ENABLED</key>
<false></false>
<key>IS_APPINVITE_ENABLED</key>
<true></true>
<key>IS_GCM_ENABLED</key>
<true></true>
<key>IS_SIGNIN_ENABLED</key>
<true></true>
<key>GOOGLE_APP_ID</key>
<string>1:907754492821:ios:246794fc44b23174145c9c</string>
</dict>
</plist>

36
android/app/build.gradle.kts

@ -1,3 +1,5 @@
import java.util.Properties
import java.io.FileInputStream
repositories { repositories {
google() google()
mavenCentral() mavenCentral()
@ -11,10 +13,14 @@ plugins {
id("kotlin-android") id("kotlin-android")
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
id("dev.flutter.flutter-gradle-plugin") 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 { android {
namespace = "com.example.deep_voice" namespace = "com.yunqiinnovation.deepsound"
compileSdk = 35 compileSdk = 35
ndkVersion = "28.0.13004108" ndkVersion = "28.0.13004108"
@ -31,7 +37,7 @@ android {
defaultConfig { defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). // 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. // You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config. // For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = 24 // Updated to meet azure_speech_recognition_null_safety requirements minSdk = 24 // Updated to meet azure_speech_recognition_null_safety requirements
@ -39,12 +45,34 @@ android {
versionCode = flutter.versionCode versionCode = flutter.versionCode
versionName = flutter.versionName 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 { buildTypes {
release { release {
// TODO: Add your own signing config for the release build. // TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works. // 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")
} }
} }
} }

47
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"
}

7
android/app/sign/DeepSound.properties

@ -0,0 +1,7 @@
storePassword=13071376716
keyPassword=13071376716
keyAlias=DeepSound
storeFile=sign/DeepSound.keystore

5
android/app/src/main/AndroidManifest.xml

@ -29,7 +29,7 @@
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<application <application
android:label="deep_voice" android:label="DeepSound"
android:name="${applicationName}" android:name="${applicationName}"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:requestLegacyExternalStorage="true" android:requestLegacyExternalStorage="true"
@ -38,7 +38,8 @@
android:enableOnBackInvokedCallback="true" android:enableOnBackInvokedCallback="true"
tools:replace="android:label"> tools:replace="android:label">
<meta-data android:name="com.facebook.sdk.ApplicationId" android:value="@string/facebook_app_id"/>
<meta-data android:name="com.facebook.sdk.ClientToken" android:value="@string/facebook_client_token"/>
<!-- 添加语音交互服务 --> <!-- 添加语音交互服务 -->
<service <service

4
android/app/src/main/kotlin/com/example/deep_voice/AzureAsrHelper.kt

@ -1,7 +1,7 @@
package com.example.deep_voice package com.yunqiinnovation.deepsound
import android.content.Context import android.content.Context
import com.example.deep_voice.core.utils.FileLogger import com.yunqiinnovation.deepsound.core.utils.FileLogger
import com.microsoft.cognitiveservices.speech.* import com.microsoft.cognitiveservices.speech.*
import com.microsoft.cognitiveservices.speech.audio.* import com.microsoft.cognitiveservices.speech.audio.*
import com.microsoft.cognitiveservices.speech.util.EventHandler import com.microsoft.cognitiveservices.speech.util.EventHandler

4
android/app/src/main/kotlin/com/example/deep_voice/AzureTtsHelper.kt

@ -1,11 +1,11 @@
package com.example.deep_voice package com.yunqiinnovation.deepsound
import android.content.Context import android.content.Context
import android.media.AudioAttributes import android.media.AudioAttributes
import android.media.AudioDeviceInfo import android.media.AudioDeviceInfo
import android.media.AudioManager import android.media.AudioManager
import android.os.Build import android.os.Build
import com.example.deep_voice.core.utils.FileLogger import com.yunqiinnovation.deepsound.core.utils.FileLogger
import com.microsoft.cognitiveservices.speech.* import com.microsoft.cognitiveservices.speech.*
import com.microsoft.cognitiveservices.speech.audio.* import com.microsoft.cognitiveservices.speech.audio.*
import java.util.concurrent.Future import java.util.concurrent.Future

2
android/app/src/main/kotlin/com/example/deep_voice/ClassicBluetoothHelper.kt

@ -1,4 +1,4 @@
package com.example.deep_voice package com.yunqiinnovation.deepsound
import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothProfile import android.bluetooth.BluetoothProfile

4
android/app/src/main/kotlin/com/example/deep_voice/MainActivity.kt

@ -1,4 +1,4 @@
package com.example.deep_voice package com.yunqiinnovation.deepsound
import android.content.Intent import android.content.Intent
import android.os.Build import android.os.Build
@ -19,7 +19,7 @@ import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothDevice
import androidx.security.crypto.EncryptedSharedPreferences import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKey import androidx.security.crypto.MasterKey
import com.example.deep_voice.core.utils.FileLogger import com.yunqiinnovation.deepsound.core.utils.FileLogger
class MainActivity: FlutterActivity() { class MainActivity: FlutterActivity() {

10
android/app/src/main/kotlin/com/example/deep_voice/VoiceInteractionService.kt

@ -1,4 +1,4 @@
package com.example.deep_voice package com.yunqiinnovation.deepsound
import android.app.Notification import android.app.Notification
import android.app.NotificationChannel import android.app.NotificationChannel
@ -25,7 +25,7 @@ import org.json.JSONArray
import android.media.MediaPlayer import android.media.MediaPlayer
import android.media.AudioAttributes import android.media.AudioAttributes
import android.net.Uri import android.net.Uri
import com.example.deep_voice.core.utils.FileLogger import com.yunqiinnovation.deepsound.core.utils.FileLogger
/** /**
* 后台语音交互 Service: * 后台语音交互 Service:
@ -50,9 +50,9 @@ class VoiceInteractionService : Service() {
fun isServiceRunning(): Boolean = isRunning.get() fun isServiceRunning(): Boolean = isRunning.get()
// 广播 Action // 广播 Action
const val ACTION_RECOGNITION_STARTED = "com.example.deep_voice.ACTION_RECOGNITION_STARTED" const val ACTION_RECOGNITION_STARTED = "com.yunqiinnovation.deepsound.ACTION_RECOGNITION_STARTED"
const val ACTION_PAUSE_VOICE_INTERACTION = "com.example.deep_voice.ACTION_PAUSE_VOICE_INTERACTION" const val ACTION_PAUSE_VOICE_INTERACTION = "com.yunqiinnovation.deepsound.ACTION_PAUSE_VOICE_INTERACTION"
const val ACTION_CHAT_HISTORY_UPDATED = "com.example.deep_voice.ACTION_CHAT_HISTORY_UPDATED" const val ACTION_CHAT_HISTORY_UPDATED = "com.yunqiinnovation.deepsound.ACTION_CHAT_HISTORY_UPDATED"
} }
// 服务状态 // 服务状态

2
android/app/src/main/kotlin/com/example/deep_voice/VolcanoAIService.kt

@ -1,4 +1,4 @@
package com.example.deep_voice package com.yunqiinnovation.deepsound
import android.util.Log import android.util.Log
import okhttp3.* import okhttp3.*

2
android/app/src/main/kotlin/com/example/deep_voice/core/utils/FileLogger.kt

@ -1,4 +1,4 @@
package com.example.deep_voice.core.utils package com.yunqiinnovation.deepsound.core.utils
import android.content.Context import android.content.Context
import android.util.Log import android.util.Log

6
android/app/src/main/res/values/strings.xml

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="facebook_app_id">3829022037361855</string>
<string name="fb_login_protocol_scheme">fb3829022037361855</string>
<string name="facebook_client_token">193d0a2b1757c41cfa557ea8e8f21c33</string>
</resources>

1
android/build.gradle.kts

@ -3,6 +3,7 @@ allprojects {
google() google()
mavenCentral() mavenCentral()
} }
} }
val newBuildDir: Directory = rootProject.layout.buildDirectory.dir("../../build").get() val newBuildDir: Directory = rootProject.layout.buildDirectory.dir("../../build").get()

1
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"}}}}}}

153
lib/core/translations/app_translations.dart

@ -3,79 +3,80 @@ import 'package:get/get.dart';
class AppTranslations extends Translations { class AppTranslations extends Translations {
@override @override
Map<String, Map<String, String>> get keys => { Map<String, Map<String, String>> get keys => {
'zh_CN': { 'zh_CN': {
// 通用 // 通用
'app_name': 'i深音', 'app_name': 'i深音',
'confirm': '确认', 'confirm': '确认',
'cancel': '取消', 'cancel': '取消',
'retry': '重试', 'retry': '重试',
'error': '错误', 'error': '错误',
'success': '成功', 'success': '成功',
// 主页 // 主页
'add_device': '添加设备', 'add_device': '添加设备',
'searching': '搜索中', 'searching': '搜索中',
'searching_hint': '请将设备放在手机附近', 'searching_hint': '请将设备放在手机附近',
'connected': '已连接', 'connected': '已连接',
'device_ready': '设备已就绪', 'device_ready': '设备已就绪',
'connection_failed': '连接失败', 'connection_failed': '连接失败',
'connection_failed_hint': '未找到设备。请确保蓝牙已开启且设备在5米范围内。尝试重启设备。', 'connection_failed_hint': '未找到设备。请确保蓝牙已开启且设备在5米范围内。尝试重启设备。',
'disconnect': '断开连接', 'disconnect': '断开连接',
// 聊天页面 // 聊天页面
'clear_chat': '清空对话', 'clear_chat': '清空对话',
'thinking': '正在思考...', 'thinking': '正在思考...',
'type_message': '发消息...', 'type_message': '发消息...',
'voice_enabled': '语音已开启', 'voice_enabled': '语音已开启',
'voice_disabled': '语音已关闭', 'voice_disabled': '语音已关闭',
// 设置页面 // 设置页面
'settings': '设置', 'settings': '设置',
'language_settings': '语言设置', 'language_settings': '语言设置',
'about': '关于', 'about': '关于',
'check_update': '检查更新', 'check_update': '检查更新',
'clear_cache': '清除缓存', 'clear_cache': '清除缓存',
'home': '首页', 'home': '首页',
'explore': '发现', 'explore': '发现',
'profile': '我的', 'profile': '我的',
'device': '设备', 'device': '设备',
}, },
'en_US': { 'en_US': {
// General // General
'app_name': 'iDeep Voice', 'app_name': 'iDeep Voice',
'confirm': 'Confirm', 'confirm': 'Confirm',
'cancel': 'Cancel', 'cancel': 'Cancel',
'retry': 'Retry', 'retry': 'Retry',
'error': 'Error', 'error': 'Error',
'success': 'Success', 'success': 'Success',
// Home // Home
'add_device': '+ Add Device', 'add_device': '+ Add Device',
'searching': 'Searching', 'searching': 'Searching',
'searching_hint': 'Please place the device near your phone', 'searching_hint': 'Please place the device near your phone',
'connected': 'Connected!', 'connected': 'Connected!',
'device_ready': 'Your device is ready', 'device_ready': 'Your device is ready',
'connection_failed': 'Connection Failed', '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.', 'connection_failed_hint':
'disconnect': 'Disconnect', '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', // Chat
'thinking': 'Thinking...', 'clear_chat': 'Clear Chat',
'type_message': 'Type a message...', 'thinking': 'Thinking...',
'voice_enabled': 'Voice Enabled', 'type_message': 'Type a message...',
'voice_disabled': 'Voice Disabled', 'voice_enabled': 'Voice Enabled',
'voice_disabled': 'Voice Disabled',
// Settings
'settings': 'Settings', // Settings
'language_settings': 'Language Settings', 'settings': 'Settings',
'about': 'About', 'language_settings': 'Language Settings',
'check_update': 'Check for Updates', 'about': 'About',
'clear_cache': 'Clear Cache', 'check_update': 'Check for Updates',
'home': 'Home', 'clear_cache': 'Clear Cache',
'explore': 'Explore', 'home': 'Home',
'profile': 'Profile', 'explore': 'Explore',
'device': 'Device', 'profile': 'Profile',
}, 'device': 'Device',
}; },
} };
}

29
lib/data/services/audio_service.dart

@ -13,13 +13,13 @@ class AudioServiceManager extends GetxService {
// 添加一个标志,表示是否已经尝试过初始化 // 添加一个标志,表示是否已经尝试过初始化
bool _hasAttemptedInit = false; bool _hasAttemptedInit = false;
// 添加一个计时器,用于定期检查服务状态 // 添加一个计时器,用于定期检查服务状态
Timer? _serviceCheckTimer; Timer? _serviceCheckTimer;
Future<AudioServiceManager> init() async { Future<AudioServiceManager> init() async {
print('初始化 AudioServiceManager'); print('初始化 AudioServiceManager');
if (_isInitialized) { if (_isInitialized) {
print('AudioServiceManager 已经初始化'); print('AudioServiceManager 已经初始化');
return this; return this;
@ -38,12 +38,13 @@ class AudioServiceManager extends GetxService {
// 使用锁确保初始化的原子性 // 使用锁确保初始化的原子性
await _lock.synchronized(() async { await _lock.synchronized(() async {
if (_isInitialized) return; if (_isInitialized) return;
// 使用 AudioService.init 初始化音频服务 // 使用 AudioService.init 初始化音频服务
_audioHandler = await AudioService.init<MyAudioHandler>( _audioHandler = await AudioService.init<MyAudioHandler>(
builder: () => MyAudioHandler(), builder: () => MyAudioHandler(),
config: AudioServiceConfig( config: AudioServiceConfig(
androidNotificationChannelId: 'com.example.deep_voice.channel.audio', androidNotificationChannelId:
'com.yunqiinnovation.deepsound.channel.audio',
androidNotificationChannelName: 'Deep Voice Audio Service', androidNotificationChannelName: 'Deep Voice Audio Service',
// 使用应用图标作为通知图标,确保这个图标存在 // 使用应用图标作为通知图标,确保这个图标存在
androidNotificationIcon: 'mipmap/ic_launcher', androidNotificationIcon: 'mipmap/ic_launcher',
@ -58,13 +59,13 @@ class AudioServiceManager extends GetxService {
preloadArtwork: false, preloadArtwork: false,
), ),
); );
// 注册自定义音频处理器 // 注册自定义音频处理器
await Get.put(_audioHandler!, permanent: true); await Get.put(_audioHandler!, permanent: true);
_isInitialized = true; _isInitialized = true;
print('AudioServiceManager 初始化成功'); print('AudioServiceManager 初始化成功');
// 启动定期检查服务状态的计时器 // 启动定期检查服务状态的计时器
_startServiceCheckTimer(); _startServiceCheckTimer();
}); });
@ -73,18 +74,18 @@ class AudioServiceManager extends GetxService {
} catch (e) { } catch (e) {
print('AudioServiceManager初始化失败: $e'); print('AudioServiceManager初始化失败: $e');
_isInitialized = false; // 确保失败时重置状态 _isInitialized = false; // 确保失败时重置状态
// 延迟5秒后尝试重新初始化 // 延迟5秒后尝试重新初始化
Future.delayed(Duration(seconds: 5), () { Future.delayed(Duration(seconds: 5), () {
print('尝试重新初始化 AudioServiceManager'); print('尝试重新初始化 AudioServiceManager');
_hasAttemptedInit = false; _hasAttemptedInit = false;
init(); init();
}); });
return this; // 即使失败也返回实例,避免空指针异常 return this; // 即使失败也返回实例,避免空指针异常
} }
} }
// 启动定期检查服务状态的计时器 // 启动定期检查服务状态的计时器
void _startServiceCheckTimer() { void _startServiceCheckTimer() {
_serviceCheckTimer?.cancel(); _serviceCheckTimer?.cancel();
@ -92,7 +93,7 @@ class AudioServiceManager extends GetxService {
_checkAndRestartServiceIfNeeded(); _checkAndRestartServiceIfNeeded();
}); });
} }
// 检查服务状态,如果需要则重启服务 // 检查服务状态,如果需要则重启服务
Future<void> _checkAndRestartServiceIfNeeded() async { Future<void> _checkAndRestartServiceIfNeeded() async {
if (!_isInitialized || _audioHandler == null) { if (!_isInitialized || _audioHandler == null) {
@ -115,7 +116,7 @@ class AudioServiceManager extends GetxService {
await _audioHandler!.customAction('media_button'); await _audioHandler!.customAction('media_button');
} catch (e) { } catch (e) {
print('发送媒体按钮事件失败: $e'); print('发送媒体按钮事件失败: $e');
// 如果发送失败,尝试重新初始化服务 // 如果发送失败,尝试重新初始化服务
_isInitialized = false; _isInitialized = false;
_hasAttemptedInit = false; _hasAttemptedInit = false;
@ -129,7 +130,7 @@ class AudioServiceManager extends GetxService {
await init(); await init();
} }
} }
// 手动触发媒体按钮事件(用于测试) // 手动触发媒体按钮事件(用于测试)
Future<void> triggerMediaButtonEvent() async { Future<void> triggerMediaButtonEvent() async {
print('手动触发媒体按钮事件'); print('手动触发媒体按钮事件');
@ -153,4 +154,4 @@ class AudioServiceManager extends GetxService {
super.onClose(); super.onClose();
} }
} }

92
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',
);
}

39
lib/main.dart

@ -11,26 +11,50 @@ import 'package:window_size/window_size.dart';
import 'core/translations/app_translations.dart'; import 'core/translations/app_translations.dart';
import 'core/themes/app_theme.dart'; import 'core/themes/app_theme.dart';
import 'core/themes/theme_service.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 { void main() async {
// 确保Flutter绑定初始化 // 确保Flutter绑定初始化
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
// 初始化存储 - 在创建应用之前初始化 // 初始化存储 - 在创建应用之前初始化
await GetStorage.init(); await GetStorage.init();
// 初始化环境变量 // 初始化环境变量
await dotenv.load(); 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 下生效) // 设置窗口大小(仅在 macOS 下生效)
if (Platform.isMacOS) { if (Platform.isMacOS) {
const double width = 375.0; const double width = 375.0;
const double height = 812.0; const double height = 812.0;
setWindowTitle('Deep Voice'); setWindowTitle('Deep Voice');
setWindowMinSize(const Size(width, height)); setWindowMinSize(const Size(width, height));
setWindowMaxSize(const Size(width, height)); setWindowMaxSize(const Size(width, height));
// 居中显示窗口 // 居中显示窗口
final screen = await getCurrentScreen(); final screen = await getCurrentScreen();
if (screen != null) { if (screen != null) {
@ -43,7 +67,7 @@ void main() async {
setWindowFrame(windowFrame); setWindowFrame(windowFrame);
} }
} }
// 运行应用 // 运行应用
runApp(const MainApp()); runApp(const MainApp());
} }
@ -55,7 +79,7 @@ class MainApp extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
// 获取保存的主题设置 // 获取保存的主题设置
final themeMode = ThemeService().getThemeMode(); final themeMode = ThemeService().getThemeMode();
return ScreenUtilInit( return ScreenUtilInit(
designSize: const Size(375, 812), designSize: const Size(375, 812),
minTextAdapt: true, minTextAdapt: true,
@ -73,7 +97,6 @@ class MainApp extends StatelessWidget {
translations: AppTranslations(), translations: AppTranslations(),
locale: const Locale('zh', 'CN'), locale: const Locale('zh', 'CN'),
fallbackLocale: const Locale('en', 'US'), fallbackLocale: const Locale('en', 'US'),
); );
}, },
); );

39
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<bool> 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<void> signOutFacebook() async {
await FacebookAuth.instance.logOut();
}
}

72
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<bool> 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<UserCredential> 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<void> signOutGoogle() async {
await FirebaseAuth.instance.signOut();
await GoogleSignIn().signOut();
}
}

889
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<String?> 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<bool> 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<Map<String, dynamic>> fetchData(String Url) async {
try {
final response = await http.get(Uri.parse(Url));
if (response.statusCode == 200) {
final data = jsonDecode(response.body) as Map<String, dynamic>;
print('GET响应数据: $data');
return data;
} else {
print('GET请求失败,状态码: ${response.statusCode}');
return {};
}
} catch (e) {
print('GET请求异常: $e');
return {};
}
}
// POST请求示例
Future<bool> 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<bool> get isInstalled async {
assert(_isRegister, '请先调用 FlWX().register()');
final installed = await fluwx.isWeChatInstalled;
if (!installed) _params!.onToast?.call('该服务需要先安装微信');
return installed;
}
/// isSupportOpenBusinessView
Future<bool> 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<String?> getExtMsg() async {
if (!await isInstalled) return null;
return await fluwx.getExtMsg();
}
Future<void> 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<bool> 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<bool> 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<bool> 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<bool> open(OpenType target) async {
if (!await isInstalled) return false;
return fluwx.open(target: target);
}
Future<bool> 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<bool> 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<bool> openRankList() async {
if (!await isInstalled) return false;
return fluwx.open(target: RankList());
}
Future<bool> 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<bool> 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<bool> 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<bool> 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<bool> 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<bool> share(
WeChatShareModel what, {
FlWeChatShareResponseCallback? onResponse,
}) async {
if (!await isInstalled) return false;
return _shareResponse(fluwx.share(what), onResponse: onResponse);
}
Future<bool> 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<bool> 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<bool> 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<bool> 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<bool> 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<bool> 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<bool> 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<bool> _shareResponse(
Future<bool> 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<dynamic>? privilege;
final String? unionId;
WXUserModel.fromJson(Map<String, dynamic> 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<dynamic>?,
unionId = json['unionid'] as String?;
Map<String, dynamic> 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<String, dynamic> 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<String, dynamic> 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;
}
}

343
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:flutter_screenutil/flutter_screenutil.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import '../controllers/login_controller.dart'; import '../controllers/login_controller.dart';
import '../controllers/facebookLogin.dart';
import '../controllers/googleLogin.dart';
import '../controllers/weixinLogin.dart';
import '../../../core/themes/app_theme.dart'; import '../../../core/themes/app_theme.dart';
class LoginView extends GetView<LoginController> { class LoginView extends GetView<LoginController> {
@ -11,7 +14,7 @@ class LoginView extends GetView<LoginController> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isDarkMode = Theme.of(context).brightness == Brightness.dark; final isDarkMode = Theme.of(context).brightness == Brightness.dark;
return Scaffold( return Scaffold(
backgroundColor: isDarkMode ? Colors.black : Colors.grey[50], backgroundColor: isDarkMode ? Colors.black : Colors.grey[50],
body: SafeArea( body: SafeArea(
@ -33,11 +36,11 @@ class LoginView extends GetView<LoginController> {
// 应用标志和标题 // 应用标志和标题
SizedBox(height: 32.h), SizedBox(height: 32.h),
_buildAppLogo(isDarkMode), _buildAppLogo(isDarkMode),
// 欢迎文本 // 欢迎文本
SizedBox(height: 32.h), SizedBox(height: 32.h),
_buildWelcomeText(isDarkMode), _buildWelcomeText(isDarkMode),
// 输入框和继续按钮 // 输入框和继续按钮
SizedBox(height: 24.h), SizedBox(height: 24.h),
_buildInputField(isDarkMode), _buildInputField(isDarkMode),
@ -45,11 +48,11 @@ class LoginView extends GetView<LoginController> {
_buildVerificationInputField(isDarkMode), _buildVerificationInputField(isDarkMode),
SizedBox(height: 16.h), SizedBox(height: 16.h),
_buildContinueButton(isDarkMode), _buildContinueButton(isDarkMode),
// 社交媒体登录 // 社交媒体登录
SizedBox(height: 32.h), SizedBox(height: 32.h),
_buildSocialLoginSection(isDarkMode), _buildSocialLoginSection(isDarkMode),
// 用户协议 // 用户协议
SizedBox(height: 40.h), SizedBox(height: 40.h),
_buildTermsAndPrivacy(isDarkMode), _buildTermsAndPrivacy(isDarkMode),
@ -71,23 +74,23 @@ class LoginView extends GetView<LoginController> {
children: [ children: [
// 返回按钮 // 返回按钮
_buildBackButton(isDarkMode), _buildBackButton(isDarkMode),
// 验证码标题 // 验证码标题
SizedBox(height: 24.h), SizedBox(height: 24.h),
_buildVerificationTitle(isDarkMode), _buildVerificationTitle(isDarkMode),
// 验证码输入框 // 验证码输入框
SizedBox(height: 32.h), SizedBox(height: 32.h),
_buildVerificationInputs(context, isDarkMode), _buildVerificationInputs(context, isDarkMode),
// 重新发送验证码 // 重新发送验证码
SizedBox(height: 16.h), SizedBox(height: 16.h),
_buildResendButton(isDarkMode), _buildResendButton(isDarkMode),
// 登录按钮 // 登录按钮
SizedBox(height: 32.h), SizedBox(height: 32.h),
_buildLoginButton(isDarkMode), _buildLoginButton(isDarkMode),
// 使用密码登录 // 使用密码登录
SizedBox(height: 16.h), SizedBox(height: 16.h),
_buildUsePasswordButton(isDarkMode), _buildUsePasswordButton(isDarkMode),
@ -175,7 +178,9 @@ class LoginView extends GetView<LoginController> {
Widget _buildInputField(bool isDarkMode) { Widget _buildInputField(bool isDarkMode) {
return Container( return Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDarkMode ? Colors.grey[900]!.withOpacity(0.4) : Colors.grey.withOpacity(0.1), color: isDarkMode
? Colors.grey[900]!.withOpacity(0.4)
: Colors.grey.withOpacity(0.1),
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(16.r),
), ),
child: Padding( child: Padding(
@ -217,7 +222,9 @@ class LoginView extends GetView<LoginController> {
Widget _buildVerificationInputField(bool isDarkMode) { Widget _buildVerificationInputField(bool isDarkMode) {
return Container( return Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDarkMode ? Colors.grey[900]!.withOpacity(0.4) : Colors.grey.withOpacity(0.1), color: isDarkMode
? Colors.grey[900]!.withOpacity(0.4)
: Colors.grey.withOpacity(0.1),
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(16.r),
), ),
child: Padding( child: Padding(
@ -253,25 +260,26 @@ class LoginView extends GetView<LoginController> {
), ),
), ),
Obx(() => TextButton( Obx(() => TextButton(
onPressed: controller.countdownSeconds > 0 onPressed: controller.countdownSeconds > 0
? null ? null
: controller.getVerificationCode, : controller.getVerificationCode,
style: TextButton.styleFrom( style: TextButton.styleFrom(
padding: EdgeInsets.symmetric(horizontal: 16.w), padding: EdgeInsets.symmetric(horizontal: 16.w),
foregroundColor: const Color(0xFF3B82F6), foregroundColor: const Color(0xFF3B82F6),
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
disabledForegroundColor: isDarkMode ? Colors.grey[600] : Colors.grey[400], disabledForegroundColor:
), isDarkMode ? Colors.grey[600] : Colors.grey[400],
child: Text( ),
controller.countdownSeconds > 0 child: Text(
? '${controller.countdownSeconds}s后重新获取' controller.countdownSeconds > 0
: '获取验证码', ? '${controller.countdownSeconds}s后重新获取'
style: TextStyle( : '获取验证码',
fontSize: 14.sp, style: TextStyle(
fontWeight: FontWeight.w500, fontSize: 14.sp,
), fontWeight: FontWeight.w500,
), ),
)), ),
)),
], ],
), ),
), ),
@ -283,35 +291,36 @@ class LoginView extends GetView<LoginController> {
return SizedBox( return SizedBox(
width: double.infinity, width: double.infinity,
child: Obx(() => ElevatedButton( child: Obx(() => ElevatedButton(
onPressed: controller.isLoading ? null : () => controller.login(), onPressed: controller.isLoading ? null : () => controller.login(),
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF0066CC), backgroundColor: const Color(0xFF0066CC),
foregroundColor: Colors.white, foregroundColor: Colors.white,
padding: EdgeInsets.symmetric(vertical: 14.h), padding: EdgeInsets.symmetric(vertical: 14.h),
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(16.r),
),
elevation: 0,
disabledBackgroundColor: const Color(0xFF0066CC).withOpacity(0.7),
disabledForegroundColor: Colors.white,
),
child: controller.isLoading
? SizedBox(
height: 20.h,
width: 20.h,
child: CircularProgressIndicator(
strokeWidth: 2.w,
valueColor: const AlwaysStoppedAnimation<Color>(Colors.white),
),
)
: Text(
'继续',
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w600,
),
), ),
)), elevation: 0,
disabledBackgroundColor: const Color(0xFF0066CC).withOpacity(0.7),
disabledForegroundColor: Colors.white,
),
child: controller.isLoading
? SizedBox(
height: 20.h,
width: 20.h,
child: CircularProgressIndicator(
strokeWidth: 2.w,
valueColor:
const AlwaysStoppedAnimation<Color>(Colors.white),
),
)
: Text(
'继续',
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w600,
),
),
)),
); );
} }
@ -321,7 +330,9 @@ class LoginView extends GetView<LoginController> {
children: [ children: [
Row( Row(
children: [ children: [
Expanded(child: Divider(color: isDarkMode ? Colors.grey[800] : Colors.grey[300])), Expanded(
child: Divider(
color: isDarkMode ? Colors.grey[800] : Colors.grey[300])),
Padding( Padding(
padding: EdgeInsets.symmetric(horizontal: 16.w), padding: EdgeInsets.symmetric(horizontal: 16.w),
child: Text( child: Text(
@ -332,7 +343,9 @@ class LoginView extends GetView<LoginController> {
), ),
), ),
), ),
Expanded(child: Divider(color: isDarkMode ? Colors.grey[800] : Colors.grey[300])), Expanded(
child: Divider(
color: isDarkMode ? Colors.grey[800] : Colors.grey[300])),
], ],
), ),
SizedBox(height: 24.h), SizedBox(height: 24.h),
@ -341,7 +354,11 @@ class LoginView extends GetView<LoginController> {
children: [ children: [
_buildSocialButton(FontAwesomeIcons.google, Colors.red, isDarkMode), _buildSocialButton(FontAwesomeIcons.google, Colors.red, isDarkMode),
SizedBox(width: 24.w), SizedBox(width: 24.w),
_buildSocialButton(FontAwesomeIcons.weixin, Colors.green, isDarkMode), _buildSocialButton(
FontAwesomeIcons.weixin, Colors.green, isDarkMode),
SizedBox(width: 24.w),
_buildSocialButton(
FontAwesomeIcons.facebook, Colors.blue, isDarkMode),
], ],
), ),
], ],
@ -356,25 +373,71 @@ class LoginView extends GetView<LoginController> {
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDarkMode ? Colors.grey[800] : Colors.white, color: isDarkMode ? Colors.grey[800] : Colors.white,
borderRadius: BorderRadius.circular(24.r), borderRadius: BorderRadius.circular(24.r),
boxShadow: isDarkMode ? [] : [ boxShadow: isDarkMode
BoxShadow( ? []
color: Colors.grey.withOpacity(0.2), : [
blurRadius: 4, BoxShadow(
offset: const Offset(0, 2), color: Colors.grey.withOpacity(0.2),
), blurRadius: 4,
], offset: const Offset(0, 2),
),
],
), ),
child: IconButton( child: IconButton(
icon: FaIcon( icon: FaIcon(
icon, icon,
color: color, color: color,
size: 20.sp, size: 20.sp,
), ),
onPressed: () {}, onPressed: () => SocialLogin(icon)),
),
); );
} }
Future<void> 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) { Widget _buildTermsAndPrivacy(bool isDarkMode) {
return Column( return Column(
@ -458,12 +521,12 @@ class LoginView extends GetView<LoginController> {
), ),
SizedBox(height: 8.h), SizedBox(height: 8.h),
Obx(() => Text( Obx(() => Text(
'验证码已发送至 ${controller.displayContact}', '验证码已发送至 ${controller.displayContact}',
style: TextStyle( style: TextStyle(
fontSize: 14.sp, fontSize: 14.sp,
color: isDarkMode ? Colors.grey[400] : Colors.grey[600], color: isDarkMode ? Colors.grey[400] : Colors.grey[600],
), ),
)), )),
], ],
); );
} }
@ -472,17 +535,21 @@ class LoginView extends GetView<LoginController> {
Widget _buildVerificationInputs(BuildContext context, bool isDarkMode) { Widget _buildVerificationInputs(BuildContext context, bool isDarkMode) {
return Row( return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: List.generate(4, (index) => _buildVerificationInput(index, context, isDarkMode)), children: List.generate(
4, (index) => _buildVerificationInput(index, context, isDarkMode)),
); );
} }
// 单个验证码输入框 // 单个验证码输入框
Widget _buildVerificationInput(int index, BuildContext context, bool isDarkMode) { Widget _buildVerificationInput(
int index, BuildContext context, bool isDarkMode) {
return Container( return Container(
width: 60.w, width: 60.w,
height: 60.w, height: 60.w,
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDarkMode ? Colors.grey[900]!.withOpacity(0.3) : Colors.grey.withOpacity(0.1), color: isDarkMode
? Colors.grey[900]!.withOpacity(0.3)
: Colors.grey.withOpacity(0.1),
borderRadius: BorderRadius.circular(12.r), borderRadius: BorderRadius.circular(12.r),
border: Border.all( border: Border.all(
color: isDarkMode ? Colors.grey[700]! : Colors.grey[300]!, color: isDarkMode ? Colors.grey[700]! : Colors.grey[300]!,
@ -520,28 +587,28 @@ class LoginView extends GetView<LoginController> {
Widget _buildResendButton(bool isDarkMode) { Widget _buildResendButton(bool isDarkMode) {
return Center( return Center(
child: Obx(() => TextButton.icon( child: Obx(() => TextButton.icon(
onPressed: controller.countdownSeconds > 0 onPressed: controller.countdownSeconds > 0
? null ? null
: controller.resendVerificationCode, : controller.resendVerificationCode,
icon: Icon( icon: Icon(
Icons.refresh, Icons.refresh,
size: 14.sp, size: 14.sp,
color: controller.countdownSeconds > 0 color: controller.countdownSeconds > 0
? (isDarkMode ? Colors.grey[600] : Colors.grey[500]) ? (isDarkMode ? Colors.grey[600] : Colors.grey[500])
: const Color(0xFF3B82F6), : const Color(0xFF3B82F6),
), ),
label: Text( label: Text(
controller.countdownSeconds > 0 controller.countdownSeconds > 0
? '重新发送验证码 (${controller.countdownSeconds}s)' ? '重新发送验证码 (${controller.countdownSeconds}s)'
: '重新发送验证码', : '重新发送验证码',
style: TextStyle( style: TextStyle(
fontSize: 14.sp, fontSize: 14.sp,
color: controller.countdownSeconds > 0 color: controller.countdownSeconds > 0
? (isDarkMode ? Colors.grey[600] : Colors.grey[500]) ? (isDarkMode ? Colors.grey[600] : Colors.grey[500])
: const Color(0xFF3B82F6), : const Color(0xFF3B82F6),
), ),
), ),
)), )),
); );
} }
@ -550,35 +617,36 @@ class LoginView extends GetView<LoginController> {
return SizedBox( return SizedBox(
width: double.infinity, width: double.infinity,
child: Obx(() => ElevatedButton( child: Obx(() => ElevatedButton(
onPressed: controller.isLoading ? null : controller.login, onPressed: controller.isLoading ? null : controller.login,
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF3B82F6), backgroundColor: const Color(0xFF3B82F6),
foregroundColor: Colors.white, foregroundColor: Colors.white,
padding: EdgeInsets.symmetric(vertical: 14.h), padding: EdgeInsets.symmetric(vertical: 14.h),
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(16.r),
),
elevation: 0,
disabledBackgroundColor: const Color(0xFF3B82F6).withOpacity(0.7),
disabledForegroundColor: Colors.white,
),
child: controller.isLoading
? SizedBox(
height: 20.h,
width: 20.h,
child: CircularProgressIndicator(
strokeWidth: 2.w,
valueColor: const AlwaysStoppedAnimation<Color>(Colors.white),
),
)
: Text(
'登录',
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w600,
),
), ),
)), elevation: 0,
disabledBackgroundColor: const Color(0xFF3B82F6).withOpacity(0.7),
disabledForegroundColor: Colors.white,
),
child: controller.isLoading
? SizedBox(
height: 20.h,
width: 20.h,
child: CircularProgressIndicator(
strokeWidth: 2.w,
valueColor:
const AlwaysStoppedAnimation<Color>(Colors.white),
),
)
: Text(
'登录',
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w600,
),
),
)),
); );
} }
@ -591,10 +659,11 @@ class LoginView extends GetView<LoginController> {
'使用密码登录', '使用密码登录',
style: TextStyle( style: TextStyle(
fontSize: 14.sp, fontSize: 14.sp,
color: isDarkMode ? const Color(0xFF3B82F6) : const Color(0xFF3B82F6), color:
isDarkMode ? const Color(0xFF3B82F6) : const Color(0xFF3B82F6),
), ),
), ),
), ),
); );
} }
} }

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

@ -13,7 +13,7 @@ class SettingsView extends GetView<SettingsController> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
// 直接使用 Theme.of(context) 获取当前主题 // 直接使用 Theme.of(context) 获取当前主题
final isDarkMode = Theme.of(context).brightness == Brightness.dark; final isDarkMode = Theme.of(context).brightness == Brightness.dark;
return Scaffold( return Scaffold(
backgroundColor: isDarkMode ? Colors.black : Colors.grey[50], backgroundColor: isDarkMode ? Colors.black : Colors.grey[50],
appBar: AppBar( appBar: AppBar(
@ -28,7 +28,6 @@ class SettingsView extends GetView<SettingsController> {
), ),
), ),
centerTitle: true, centerTitle: true,
), ),
body: ListView( body: ListView(
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 16.h), padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 16.h),
@ -51,22 +50,19 @@ class SettingsView extends GetView<SettingsController> {
padding: EdgeInsets.all(16.w), padding: EdgeInsets.all(16.w),
decoration: BoxDecoration( decoration: BoxDecoration(
gradient: LinearGradient( gradient: LinearGradient(
colors: isDarkMode colors: isDarkMode
? [ ? [
const Color(0xFF1A1A1A).withOpacity(0.7), const Color(0xFF1A1A1A).withOpacity(0.7),
const Color(0xFF2A2A2A).withOpacity(0.7) const Color(0xFF2A2A2A).withOpacity(0.7)
] ]
: [ : [const Color(0xFFE3F2FD), const Color(0xFFF3E5F5)],
const Color(0xFFE3F2FD),
const Color(0xFFF3E5F5)
],
begin: Alignment.topLeft, begin: Alignment.topLeft,
end: Alignment.bottomRight, end: Alignment.bottomRight,
), ),
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(16.r),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: isDarkMode color: isDarkMode
? Colors.black.withOpacity(0.2) ? Colors.black.withOpacity(0.2)
: Colors.black.withOpacity(0.05), : Colors.black.withOpacity(0.05),
blurRadius: 10, blurRadius: 10,
@ -77,7 +73,9 @@ class SettingsView extends GetView<SettingsController> {
child: ClipRRect( child: ClipRRect(
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(16.r),
child: BackdropFilter( child: BackdropFilter(
filter: isDarkMode ? ImageFilter.blur(sigmaX: 10, sigmaY: 10) : ImageFilter.blur(sigmaX: 0, sigmaY: 0), filter: isDarkMode
? ImageFilter.blur(sigmaX: 10, sigmaY: 10)
: ImageFilter.blur(sigmaX: 0, sigmaY: 0),
child: Row( child: Row(
children: [ children: [
Container( Container(
@ -118,7 +116,8 @@ class SettingsView extends GetView<SettingsController> {
Row( Row(
children: [ children: [
Container( Container(
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 2.h), padding: EdgeInsets.symmetric(
horizontal: 8.w, vertical: 2.h),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.blue[600], color: Colors.blue[600],
borderRadius: BorderRadius.circular(10.r), borderRadius: BorderRadius.circular(10.r),
@ -137,7 +136,8 @@ class SettingsView extends GetView<SettingsController> {
'已连接设备: DeepVoice Pro', '已连接设备: DeepVoice Pro',
style: TextStyle( style: TextStyle(
fontSize: 12.sp, fontSize: 12.sp,
color: isDarkMode ? Colors.white70 : Colors.black54, color:
isDarkMode ? Colors.white70 : Colors.black54,
), ),
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
@ -159,7 +159,7 @@ class SettingsView extends GetView<SettingsController> {
width: 32.w, width: 32.w,
height: 32.w, height: 32.w,
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDarkMode color: isDarkMode
? Colors.white.withOpacity(0.1) ? Colors.white.withOpacity(0.1)
: Colors.white.withOpacity(0.7), : Colors.white.withOpacity(0.7),
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(16.r),
@ -207,7 +207,7 @@ class SettingsView extends GetView<SettingsController> {
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(16.r),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: isDarkMode color: isDarkMode
? Colors.black.withOpacity(0.2) ? Colors.black.withOpacity(0.2)
: Colors.black.withOpacity(0.05), : Colors.black.withOpacity(0.05),
blurRadius: 10, blurRadius: 10,
@ -218,7 +218,9 @@ class SettingsView extends GetView<SettingsController> {
child: ClipRRect( child: ClipRRect(
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(16.r),
child: BackdropFilter( child: BackdropFilter(
filter: isDarkMode ? ImageFilter.blur(sigmaX: 5, sigmaY: 5) : ImageFilter.blur(sigmaX: 0, sigmaY: 0), filter: isDarkMode
? ImageFilter.blur(sigmaX: 5, sigmaY: 5)
: ImageFilter.blur(sigmaX: 0, sigmaY: 0),
child: Column( child: Column(
children: [ children: [
// 通知设置 // 通知设置
@ -226,8 +228,11 @@ class SettingsView extends GetView<SettingsController> {
title: '通知设置', title: '通知设置',
subtitle: '管理应用推送和提醒', subtitle: '管理应用推送和提醒',
icon: Icons.notifications_none, icon: Icons.notifications_none,
iconBgColor: isDarkMode ? Colors.indigo[900]!.withOpacity(0.3) : Colors.indigo[100]!, iconBgColor: isDarkMode
iconColor: isDarkMode ? Colors.indigo[300]! : Colors.indigo[600]!, ? Colors.indigo[900]!.withOpacity(0.3)
: Colors.indigo[100]!,
iconColor:
isDarkMode ? Colors.indigo[300]! : Colors.indigo[600]!,
titleColor: isDarkMode ? Colors.white : null, titleColor: isDarkMode ? Colors.white : null,
subtitleColor: isDarkMode ? Colors.white70 : null, subtitleColor: isDarkMode ? Colors.white70 : null,
onTap: () { onTap: () {
@ -235,34 +240,44 @@ class SettingsView extends GetView<SettingsController> {
}, },
isDarkMode: isDarkMode, isDarkMode: isDarkMode,
), ),
Divider(height: 1, color: isDarkMode ? Colors.white.withOpacity(0.1) : Colors.grey[200]), Divider(
height: 1,
color: isDarkMode
? Colors.white.withOpacity(0.1)
: Colors.grey[200]),
// 主题设置 // 主题设置
_buildToggleSetting( _buildToggleSetting(
title: '暗黑模式', title: '暗黑模式',
subtitle: '切换应用的明暗主题', subtitle: '切换应用的明暗主题',
icon: isDarkMode ? Icons.dark_mode : Icons.light_mode, icon: isDarkMode ? Icons.dark_mode : Icons.light_mode,
iconBgColor: isDarkMode iconBgColor: isDarkMode
? Colors.indigo[900]!.withOpacity(0.3) ? Colors.indigo[900]!.withOpacity(0.3)
: Colors.amber[100]!, : Colors.amber[100]!,
iconColor: isDarkMode iconColor:
? Colors.indigo[300]! isDarkMode ? Colors.indigo[300]! : Colors.amber[600]!,
: Colors.amber[600]!,
value: isDarkMode, value: isDarkMode,
onChanged: (value) { onChanged: (value) {
controller.toggleTheme(); controller.toggleTheme();
}, },
isDarkMode: isDarkMode, isDarkMode: isDarkMode,
), ),
Divider(height: 1, color: isDarkMode ? Colors.white.withOpacity(0.1) : Colors.grey[200]), Divider(
height: 1,
color: isDarkMode
? Colors.white.withOpacity(0.1)
: Colors.grey[200]),
// 备份与恢复 // 备份与恢复
_buildNavigationSetting( _buildNavigationSetting(
title: '备份与恢复', title: '备份与恢复',
subtitle: '备份设置和AI互动历史', subtitle: '备份设置和AI互动历史',
icon: Icons.cloud_upload_outlined, icon: Icons.cloud_upload_outlined,
iconBgColor: isDarkMode ? Colors.cyan[900]!.withOpacity(0.3) : Colors.cyan[100]!, iconBgColor: isDarkMode
iconColor: isDarkMode ? Colors.cyan[300]! : Colors.cyan[600]!, ? Colors.cyan[900]!.withOpacity(0.3)
: Colors.cyan[100]!,
iconColor:
isDarkMode ? Colors.cyan[300]! : Colors.cyan[600]!,
value: '上次备份: 今天', value: '上次备份: 今天',
valueStyle: TextStyle( valueStyle: TextStyle(
fontSize: 12.sp, fontSize: 12.sp,
@ -273,15 +288,22 @@ class SettingsView extends GetView<SettingsController> {
}, },
isDarkMode: isDarkMode, isDarkMode: isDarkMode,
), ),
Divider(height: 1, color: isDarkMode ? Colors.white.withOpacity(0.1) : Colors.grey[200]), Divider(
height: 1,
color: isDarkMode
? Colors.white.withOpacity(0.1)
: Colors.grey[200]),
// 隐私设置 // 隐私设置
_buildSimpleNavigationSetting( _buildSimpleNavigationSetting(
title: '隐私设置', title: '隐私设置',
subtitle: '管理数据收集和权限', subtitle: '管理数据收集和权限',
icon: Icons.shield_outlined, icon: Icons.shield_outlined,
iconBgColor: isDarkMode ? Colors.purple[900]!.withOpacity(0.3) : Colors.purple[100]!, iconBgColor: isDarkMode
iconColor: isDarkMode ? Colors.purple[300]! : Colors.purple[600]!, ? Colors.purple[900]!.withOpacity(0.3)
: Colors.purple[100]!,
iconColor:
isDarkMode ? Colors.purple[300]! : Colors.purple[600]!,
titleColor: isDarkMode ? Colors.white : null, titleColor: isDarkMode ? Colors.white : null,
subtitleColor: isDarkMode ? Colors.white70 : null, subtitleColor: isDarkMode ? Colors.white70 : null,
onTap: () { onTap: () {
@ -289,20 +311,28 @@ class SettingsView extends GetView<SettingsController> {
}, },
isDarkMode: isDarkMode, isDarkMode: isDarkMode,
), ),
Divider(height: 1, color: isDarkMode ? Colors.white.withOpacity(0.1) : Colors.grey[200]), Divider(
height: 1,
color: isDarkMode
? Colors.white.withOpacity(0.1)
: Colors.grey[200]),
// 退出登录 // 退出登录
_buildSimpleNavigationSetting( _buildSimpleNavigationSetting(
title: '退出登录', title: '退出登录',
icon: Icons.logout, icon: Icons.logout,
iconBgColor: isDarkMode ? Colors.red[900]!.withOpacity(0.3) : Colors.red[50]!, iconBgColor: isDarkMode
? Colors.red[900]!.withOpacity(0.3)
: Colors.red[50]!,
iconColor: isDarkMode ? Colors.red[300]! : Colors.red[600]!, iconColor: isDarkMode ? Colors.red[300]! : Colors.red[600]!,
titleColor: isDarkMode ? Colors.red[300]! : Colors.red[600]!, titleColor:
isDarkMode ? Colors.red[300]! : Colors.red[600]!,
onTap: () { onTap: () {
// 退出登录确认对话框 // 退出登录确认对话框
Get.dialog( Get.dialog(
AlertDialog( AlertDialog(
backgroundColor: isDarkMode ? Colors.grey[900] : Colors.white, backgroundColor:
isDarkMode ? Colors.grey[900] : Colors.white,
title: Text( title: Text(
'退出登录', '退出登录',
style: TextStyle( style: TextStyle(
@ -312,7 +342,8 @@ class SettingsView extends GetView<SettingsController> {
content: Text( content: Text(
'确定要退出当前账号吗?', '确定要退出当前账号吗?',
style: TextStyle( style: TextStyle(
color: isDarkMode ? Colors.white70 : Colors.black54, color:
isDarkMode ? Colors.white70 : Colors.black54,
), ),
), ),
actions: [ actions: [
@ -321,7 +352,9 @@ class SettingsView extends GetView<SettingsController> {
child: Text( child: Text(
'取消', '取消',
style: TextStyle( style: TextStyle(
color: isDarkMode ? Colors.white70 : Colors.black54, color: isDarkMode
? Colors.white70
: Colors.black54,
), ),
), ),
), ),
@ -333,7 +366,9 @@ class SettingsView extends GetView<SettingsController> {
child: Text( child: Text(
'确定', '确定',
style: TextStyle( 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<SettingsController> {
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(16.r),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: isDarkMode color: isDarkMode
? Colors.black.withOpacity(0.2) ? Colors.black.withOpacity(0.2)
: Colors.black.withOpacity(0.05), : Colors.black.withOpacity(0.05),
blurRadius: 10, blurRadius: 10,
@ -387,7 +422,9 @@ class SettingsView extends GetView<SettingsController> {
child: ClipRRect( child: ClipRRect(
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(16.r),
child: BackdropFilter( child: BackdropFilter(
filter: isDarkMode ? ImageFilter.blur(sigmaX: 5, sigmaY: 5) : ImageFilter.blur(sigmaX: 0, sigmaY: 0), filter: isDarkMode
? ImageFilter.blur(sigmaX: 5, sigmaY: 5)
: ImageFilter.blur(sigmaX: 0, sigmaY: 0),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
@ -399,8 +436,10 @@ class SettingsView extends GetView<SettingsController> {
decoration: BoxDecoration( decoration: BoxDecoration(
gradient: LinearGradient( gradient: LinearGradient(
colors: [ colors: [
const Color(0xFF2196F3).withOpacity(isDarkMode ? 0.7 : 1.0), const Color(0xFF2196F3)
const Color(0xFF9C27B0).withOpacity(isDarkMode ? 0.7 : 1.0) .withOpacity(isDarkMode ? 0.7 : 1.0),
const Color(0xFF9C27B0)
.withOpacity(isDarkMode ? 0.7 : 1.0)
], ],
begin: Alignment.topLeft, begin: Alignment.topLeft,
end: Alignment.bottomRight, end: Alignment.bottomRight,
@ -430,7 +469,8 @@ class SettingsView extends GetView<SettingsController> {
'版本 2.4.1 (Build 87)', '版本 2.4.1 (Build 87)',
style: TextStyle( style: TextStyle(
fontSize: 12.sp, fontSize: 12.sp,
color: isDarkMode ? Colors.white70 : Colors.black54, color:
isDarkMode ? Colors.white70 : Colors.black54,
), ),
), ),
], ],
@ -438,9 +478,12 @@ class SettingsView extends GetView<SettingsController> {
], ],
), ),
Container( Container(
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 6.h), padding:
EdgeInsets.symmetric(horizontal: 12.w, vertical: 6.h),
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDarkMode ? Colors.blue[900]!.withOpacity(0.3) : Colors.blue[100], color: isDarkMode
? Colors.blue[900]!.withOpacity(0.3)
: Colors.blue[100],
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(16.r),
), ),
child: Text( child: Text(
@ -525,8 +568,7 @@ class SettingsView extends GetView<SettingsController> {
color: isDarkMode ? Colors.white : Colors.black87, color: isDarkMode ? Colors.white : Colors.black87,
), ),
), ),
if (subtitle != null) if (subtitle != null) SizedBox(height: 2.h),
SizedBox(height: 2.h),
if (subtitle != null) if (subtitle != null)
Text( Text(
subtitle, subtitle,
@ -595,8 +637,7 @@ class SettingsView extends GetView<SettingsController> {
color: isDarkMode ? Colors.white : Colors.black87, color: isDarkMode ? Colors.white : Colors.black87,
), ),
), ),
if (subtitle != null) if (subtitle != null) SizedBox(height: 2.h),
SizedBox(height: 2.h),
if (subtitle != null) if (subtitle != null)
Text( Text(
subtitle, subtitle,
@ -613,17 +654,20 @@ class SettingsView extends GetView<SettingsController> {
children: [ children: [
Text( Text(
value, value,
style: valueStyle ?? TextStyle( style: valueStyle ??
fontSize: 14.sp, TextStyle(
color: isDarkMode ? Colors.white : Colors.black87, fontSize: 14.sp,
), color: isDarkMode ? Colors.white : Colors.black87,
),
), ),
SizedBox(width: 8.w), SizedBox(width: 8.w),
Container( Container(
width: 32.w, width: 32.w,
height: 32.w, height: 32.w,
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDarkMode ? Colors.white.withOpacity(0.05) : Colors.grey[100], color: isDarkMode
? Colors.white.withOpacity(0.05)
: Colors.grey[100],
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(16.r),
), ),
child: Icon( child: Icon(
@ -683,17 +727,18 @@ class SettingsView extends GetView<SettingsController> {
style: TextStyle( style: TextStyle(
fontSize: 16.sp, fontSize: 16.sp,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: titleColor ?? (isDarkMode ? Colors.white : Colors.black87), color: titleColor ??
(isDarkMode ? Colors.white : Colors.black87),
), ),
), ),
if (subtitle != null) if (subtitle != null) SizedBox(height: 2.h),
SizedBox(height: 2.h),
if (subtitle != null) if (subtitle != null)
Text( Text(
subtitle, subtitle,
style: TextStyle( style: TextStyle(
fontSize: 12.sp, fontSize: 12.sp,
color: subtitleColor ?? (isDarkMode ? Colors.white70 : Colors.black54), color: subtitleColor ??
(isDarkMode ? Colors.white70 : Colors.black54),
), ),
), ),
], ],
@ -704,7 +749,9 @@ class SettingsView extends GetView<SettingsController> {
width: 32.w, width: 32.w,
height: 32.w, height: 32.w,
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDarkMode ? Colors.white.withOpacity(0.05) : Colors.grey[100], color: isDarkMode
? Colors.white.withOpacity(0.05)
: Colors.grey[100],
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(16.r),
), ),
child: Icon( child: Icon(
@ -718,4 +765,4 @@ class SettingsView extends GetView<SettingsController> {
), ),
); );
} }
} }

2
linux/CMakeLists.txt

@ -7,7 +7,7 @@ project(runner LANGUAGES CXX)
set(BINARY_NAME "deep_voice") set(BINARY_NAME "deep_voice")
# The unique GTK application identifier for this application. See: # The unique GTK application identifier for this application. See:
# https://wiki.gnome.org/HowDoI/ChooseApplicationID # 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 # Explicitly opt in to modern CMake behaviors to avoid warnings with recent
# versions of CMake. # versions of CMake.

4
linux/flutter/generated_plugin_registrant.cc

@ -7,6 +7,7 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <file_selector_linux/file_selector_plugin.h> #include <file_selector_linux/file_selector_plugin.h>
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
#include <record_linux/record_linux_plugin.h> #include <record_linux/record_linux_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h> #include <url_launcher_linux/url_launcher_plugin.h>
#include <window_size/window_size_plugin.h> #include <window_size/window_size_plugin.h>
@ -15,6 +16,9 @@ void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar = g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin"); fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
file_selector_plugin_register_with_registrar(file_selector_linux_registrar); 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 = g_autoptr(FlPluginRegistrar) record_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "RecordLinuxPlugin"); fl_plugin_registry_get_registrar_for_plugin(registry, "RecordLinuxPlugin");
record_linux_plugin_register_with_registrar(record_linux_registrar); record_linux_plugin_register_with_registrar(record_linux_registrar);

1
linux/flutter/generated_plugins.cmake

@ -4,6 +4,7 @@
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
file_selector_linux file_selector_linux
flutter_secure_storage_linux
record_linux record_linux
url_launcher_linux url_launcher_linux
window_size window_size

6
pubspec.yaml

@ -47,7 +47,11 @@ dependencies:
font_awesome_flutter: ^10.8.0 font_awesome_flutter: ^10.8.0
lottie: ^3.3.1 lottie: ^3.3.1
app_settings: 5.2.0 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: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter

9
windows/flutter/generated_plugin_registrant.cc

@ -7,6 +7,9 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <file_selector_windows/file_selector_windows.h> #include <file_selector_windows/file_selector_windows.h>
#include <firebase_auth/firebase_auth_plugin_c_api.h>
#include <firebase_core/firebase_core_plugin_c_api.h>
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
#include <flutter_tts/flutter_tts_plugin.h> #include <flutter_tts/flutter_tts_plugin.h>
#include <permission_handler_windows/permission_handler_windows_plugin.h> #include <permission_handler_windows/permission_handler_windows_plugin.h>
#include <record_windows/record_windows_plugin_c_api.h> #include <record_windows/record_windows_plugin_c_api.h>
@ -17,6 +20,12 @@
void RegisterPlugins(flutter::PluginRegistry* registry) { void RegisterPlugins(flutter::PluginRegistry* registry) {
FileSelectorWindowsRegisterWithRegistrar( FileSelectorWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FileSelectorWindows")); registry->GetRegistrarForPlugin("FileSelectorWindows"));
FirebaseAuthPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FirebaseAuthPluginCApi"));
FirebaseCorePluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FirebaseCorePluginCApi"));
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
FlutterTtsPluginRegisterWithRegistrar( FlutterTtsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterTtsPlugin")); registry->GetRegistrarForPlugin("FlutterTtsPlugin"));
PermissionHandlerWindowsPluginRegisterWithRegistrar( PermissionHandlerWindowsPluginRegisterWithRegistrar(

3
windows/flutter/generated_plugins.cmake

@ -4,6 +4,9 @@
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
file_selector_windows file_selector_windows
firebase_auth
firebase_core
flutter_secure_storage_windows
flutter_tts flutter_tts
permission_handler_windows permission_handler_windows
record_windows record_windows

Loading…
Cancel
Save