Browse Source

等级接入后台服务,小夏的setting_view修改文件展示去除

newdev_shunjiawei
fdp 2 years ago
parent
commit
402121bab7
  1. 25
      lib/data/services/login_service.dart
  2. 25
      lib/main.dart
  3. 104
      lib/modules/login/bindings/user_Info.dart
  4. 12
      lib/modules/login/controllers/facebookLogin.dart
  5. 5
      lib/modules/login/controllers/googleLogin.dart
  6. 253
      lib/modules/login/controllers/login_controller.dart
  7. 126
      lib/modules/login/controllers/weixinLogin.dart
  8. 32
      lib/modules/login/entity/api.dart
  9. 45
      lib/modules/login/entity/dio_manager.dart
  10. 4
      lib/modules/login/entity/nw_api.dart
  11. 8
      lib/modules/login/entity/nw_method.dart
  12. 32
      lib/modules/login/views/login_view.dart
  13. 738
      lib/modules/settings/views/settings_view.dart
  14. 3
      pubspec.yaml

25
lib/data/services/login_service.dart

@ -0,0 +1,25 @@
import '../../modules/login/controllers/weixinLogin.dart';
import 'package:firebase_core/firebase_core.dart';
import '/firebase_options.dart';
class LoginService {
LoginService() {
//微信
FlWX().register(
appSecret: '4cfde33bc22b6cc6b1f8b225dd97d255',
appId: 'wx5bbb7be9324f3e2d',
params: FlWXCallbackParams(onHttp: (url) async {
return '';
}));
if (Firebase.apps.isEmpty) {
Firebase.initializeApp(
name: 'DeepSound',
options: DefaultFirebaseOptions.currentPlatform,
//demoProjectId:'1:907754492821:android:2deffc5aa1037ae2145c9c'
).then((value) {
print('Firebase Initialized:${value.options}');
});
}
}
}

25
lib/main.dart

@ -12,9 +12,7 @@ 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';
import '../../data/services/login_service.dart';
void main() async {
// 确保Flutter绑定初始化
@ -25,26 +23,7 @@ void main() async {
// 初始化环境变量
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}');
});
}
LoginService();
// 设置窗口大小(仅在 macOS 下生效)
if (Platform.isMacOS) {

104
lib/modules/login/bindings/user_Info.dart

@ -0,0 +1,104 @@
import 'dart:convert';
import 'dart:ffi';
class User {
// 单例实例
static User? _instance;
// 单例访问器(增加空安全保护)
static User get instance {
if (_instance == null) {
throw Exception("User must be initialized first");
}
return _instance!;
}
// 最终属性(不可变)
final String name;
final String uid;
final String avatar;
final String deviceid;
final String language;
final String token;
final String mail;
final String phone;
final String phonemac;
final String password;
// final Int64 lastbettime;
// final Int64 createtime;
// final String wxopenid;
// final String googleopenid;
// final String facebookopenid;
// 私有构造函数
User._({
required this.name,
required this.uid,
required this.avatar,
required this.deviceid,
required this.language,
required this.token,
required this.mail,
required this.phone,
required this.phonemac,
required this.password,
// required this.lastbettime,
// required this.createtime,
// required this.wxopenid,
// required this.googleopenid,
// required this.facebookopenid,
});
factory User.fromJson(Map<String, dynamic> json) {
// 修复1:安全获取嵌套的 user 字段
final userData = json['data']['user'] as Map<String, dynamic>?; // 允许空值
final token = json['data']['token'] as String? ?? '';
// 更新单例实例
return _instance = User._(
token: token as String? ?? '',
name: (userData?['name'] as String?) ?? '默认名称', // 提供默认值
uid: (userData?['uid'] as String?) ?? '',
avatar: (userData?['avatar'] as String?) ?? '',
deviceid: (userData?['deviceid'] as String?) ?? '',
language: (userData?['language'] as String?) ?? 'en',
mail: (userData?['mail'] as String?) ?? '',
phone: (userData?['phone'] as String?) ?? '',
phonemac: (userData?['phonemac'] as String?) ?? '',
password: (userData?['password'] as String?) ?? '',
// lastbettime: _parseTimestamp(userData['lastbettime']),
// createtime: _parseTimestamp(userData['createtime']),
// wxopenid: (userData?['wxopenid'] as String?) ?? '',
// googleopenid: (userData?['googleopenid'] as String?) ?? '',
// facebookopenid: (userData?['facebookopenid'] as String?) ?? '',
);
}
// 完整的JSON序列化
Map<String, dynamic> toJson() => {
'user': {
'name': name,
'uid': uid,
'avatar': avatar,
'deviceid': deviceid,
'language': language,
'token': token,
'mail': mail,
'phone': phone,
'phonemac': phonemac,
'password': password,
// 'lastbettime': lastbettime,
// 'createtime': createtime,
// 'wxopenid': wxopenid,
// 'googleopenid': googleopenid,
// 'facebookopenid': facebookopenid,
}
};
// 获取用户名的快捷方法
static String getname() => instance.name;
static String getdeviceInfo() => instance.deviceid;
static String getuserId() => instance.uid;
static String getstanding() => "";
static String getnavatar() => instance.avatar;
}

12
lib/modules/login/controllers/facebookLogin.dart

@ -4,6 +4,12 @@ import 'package:get/get.dart';
class Facebook {
String token = '';
String name = '';
String email = '';
String pictureurl = '';
String id = '';
// FaceBook登录
// 修改后的 signInWithFacebook
Future<bool> signInWithFacebook() async {
@ -19,7 +25,13 @@ class Facebook {
final credential = FacebookAuthProvider.credential(token);
print("Facebook Token: $credential");
final userData = await FacebookAuth.instance.getUserData();
name = userData["name"].toString();
email = userData["email"].toString();
pictureurl = userData["picture"]["data"]["url"].toString();
id = userData["id"].toString();
print("user login facebook ${userData..values}");
print("user login facebook ${email}${pictureurl}${name}${id}");
return true;
} else {

5
lib/modules/login/controllers/googleLogin.dart

@ -13,12 +13,13 @@ class Google {
Future<bool> signInWithGoogle() async {
try {
// 确保用户已经登录,并获取当前用户的身份验证令牌
final currentUser = FirebaseAuth.instance.currentUser;
print('Future响应数据: $currentUser');
if (currentUser != null) {
// 这里处理您需要使用这个JWT令牌的逻辑,例如将它存储到本地存储中作为凭据。
token != await currentUser.getIdToken();
token = await currentUser.getIdToken();
print('Future响应数据tokenResult: $token');
@ -47,6 +48,8 @@ class Google {
id = googleUser?.id;
displayName = googleUser?.displayName;
print('loginGoogle: $googleUser');
print("user login loginGoogle ${email}${id}${displayName}");
// Obtain the auth details from the request
final GoogleSignInAuthentication? googleAuth =
await googleUser?.authentication;

253
lib/modules/login/controllers/login_controller.dart

@ -1,15 +1,24 @@
import 'package:get/get.dart';
import 'package:flutter/material.dart';
import '../../../routes/app_routes.dart';
import 'dart:convert';
import 'dart:ffi';
import '../../settings/controllers/settings_controller.dart';
import 'facebookLogin.dart';
import 'googleLogin.dart';
import 'weixinLogin.dart';
import '../entity/api.dart';
import '../bindings/user_Info.dart';
import 'package:shared_preferences/shared_preferences.dart';
enum LoginViewState {
welcome, // 欢迎页面
welcome, // 欢迎页面
verification // 验证码页面
}
class LoginController extends GetxController {
static LoginController get to => Get.find();
// 状态管理
final _viewState = LoginViewState.welcome.obs;
final _isLoading = false.obs;
@ -17,7 +26,7 @@ class LoginController extends GetxController {
final _contactValue = ''.obs;
final _displayContact = ''.obs;
final _verificationCode = ['', '', '', ''].obs;
int loginType = -1;
// Getters
LoginViewState get viewState => _viewState.value;
bool get isLoading => _isLoading.value;
@ -25,29 +34,29 @@ class LoginController extends GetxController {
String get contactValue => _contactValue.value;
String get displayContact => _displayContact.value;
List<String> get verificationCode => _verificationCode;
// 文本控制器
final contactController = TextEditingController();
final verificationCodeController = TextEditingController();
@override
void onInit() {
super.onInit();
_viewState.value = LoginViewState.welcome;
}
@override
void onClose() {
contactController.dispose();
verificationCodeController.dispose();
super.onClose();
}
// 获取验证码
void getVerificationCode() {
if (contactController.text.isEmpty) {
Get.snackbar(
'提示',
'提示',
'请输入手机号码或邮箱',
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.red.withOpacity(0.1),
@ -55,19 +64,19 @@ class LoginController extends GetxController {
);
return;
}
_contactValue.value = contactController.text;
_formatDisplayContact();
_startCountdown();
_sendVerificationCode();
}
// 使用验证码直接登录
void loginWithVerificationCode() {
//测试期间暂时关闭验证码登录
// if (contactController.text.isEmpty) {
// Get.snackbar(
// '提示',
// '提示',
// '请输入手机号码或邮箱',
// snackPosition: SnackPosition.TOP,
// backgroundColor: Colors.red.withOpacity(0.1),
@ -75,10 +84,10 @@ class LoginController extends GetxController {
// );
// return;
// }
// if (verificationCodeController.text.isEmpty) {
// Get.snackbar(
// '提示',
// '提示',
// '请输入验证码',
// snackPosition: SnackPosition.TOP,
// backgroundColor: Colors.red.withOpacity(0.1),
@ -86,10 +95,10 @@ class LoginController extends GetxController {
// );
// return;
// }
// if (verificationCodeController.text.length < 6) {
// Get.snackbar(
// '提示',
// '提示',
// '请输入完整的验证码',
// snackPosition: SnackPosition.TOP,
// backgroundColor: Colors.red.withOpacity(0.1),
@ -97,9 +106,9 @@ class LoginController extends GetxController {
// );
// return;
// }
_isLoading.value = true;
// 模拟验证过程
Future.delayed(const Duration(seconds: 2), () {
_isLoading.value = false;
@ -107,12 +116,12 @@ class LoginController extends GetxController {
Get.offAllNamed(Routes.pairing);
});
}
// 切换到验证码页面
void showVerificationView() {
if (contactController.text.isEmpty) {
Get.snackbar(
'提示',
'提示',
'请输入手机号码或邮箱',
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.red.withOpacity(0.1),
@ -120,33 +129,38 @@ class LoginController extends GetxController {
);
return;
}
_contactValue.value = contactController.text;
_formatDisplayContact();
_viewState.value = LoginViewState.verification;
_startCountdown();
_sendVerificationCode();
}
// 返回欢迎页面
void showWelcomeView() {
_viewState.value = LoginViewState.welcome;
_resetVerificationCode();
}
// 格式化显示的联系方式(隐藏部分字符)
void _formatDisplayContact() {
String contact = _contactValue.value;
if (contact.contains('@')) { // 邮箱
if (contact.contains('@')) {
// 邮箱
final parts = contact.split('@');
if (parts[0].length > 3) {
_displayContact.value = parts[0].substring(0, 3) + '****@' + parts[1];
} else {
_displayContact.value = contact;
}
} else if (RegExp(r'^\d+$').hasMatch(contact)) { // 手机号
} else if (RegExp(r'^\d+$').hasMatch(contact)) {
// 手机号
if (contact.length > 7) {
_displayContact.value = '+86 ' + contact.substring(0, 3) + '****' + contact.substring(contact.length - 4);
_displayContact.value = '+86 ' +
contact.substring(0, 3) +
'****' +
contact.substring(contact.length - 4);
} else {
_displayContact.value = contact;
}
@ -154,7 +168,7 @@ class LoginController extends GetxController {
_displayContact.value = contact;
}
}
// 开始倒计时
void _startCountdown() {
_countdownSeconds.value = 60;
@ -164,20 +178,20 @@ class LoginController extends GetxController {
return _countdownSeconds.value > 0;
});
}
// 发送验证码
void _sendVerificationCode() {
// 这里应该是实际发送验证码的逻辑
// 为了演示,我们只是模拟发送
}
// 重新发送验证码
void resendVerificationCode() {
if (_countdownSeconds.value > 0) return;
_startCountdown();
_sendVerificationCode();
}
// 更新验证码输入
void updateVerificationCode(int index, String value) {
if (index >= 0 && index < 4) {
@ -186,23 +200,23 @@ class LoginController extends GetxController {
_verificationCode.value = newList;
}
}
// 重置验证码
void _resetVerificationCode() {
_verificationCode.value = ['', '', '', ''];
}
// 检查验证码是否完整
bool isVerificationCodeComplete() {
return !_verificationCode.contains('');
}
// 登录
Future<void> login() async {
// 暂时关闭验证码验证
// if (!isVerificationCodeComplete()) {
// Get.snackbar(
// '提示',
// '提示',
// '请输入完整的验证码',
// snackPosition: SnackPosition.TOP,
// backgroundColor: Colors.red.withOpacity(0.1),
@ -210,18 +224,102 @@ class LoginController extends GetxController {
// );
// return;
// }
_isLoading.value = true;
try {
// 模拟验证过程
await Future.delayed(const Duration(seconds: 2));
// 验证成功,跳转到配对页面
Get.offAllNamed(Routes.pairing);
switch (loginType) {
case 1:
{
print("数据获取Facebook");
final facebook = Facebook();
bool authResult = await facebook.signInWithFacebook();
print("数据获取Facebook().token${authResult}");
if (authResult) {
print("数据获取_Facebook.token${facebook.token}");
// SettingsController().userName = Facebook().name;
// SettingsController().avatar = Facebook().pictureurl;
// SettingsController().userId = Facebook().id;
//SettingsController().avatar = Google().email;
// 验证成功,跳转到配对页面
requestLogin({
"email": facebook.email,
"name": facebook.name,
"avatar": facebook.pictureurl,
"stype": 4,
"ttoken": facebook.token,
});
}
break;
}
case 2:
{
print("数据获取_Google()");
final google = Google();
bool authResult = await google.signInWithGoogle();
print("数据获取_Google().token${authResult}");
if (authResult) {
// 验证成功,跳转到配对页面
// SettingsController().userName = Google().displayName!;
// //SettingsController().avatar = Google().email;
// SettingsController().userId = Google().id!;
requestLogin({
"name": google.displayName,
"email": google.email,
"stype": 3,
"ttoken": google.token,
});
}
break;
}
case 3:
{
print("数据获取_FlWX()");
bool authResult = await FlWX().authBy(
NormalAuth(
scope: "snsapi_userinfo",
state: "wechat_sdk_demo_test",
),
getUserInfo: true, // 按需获取用户信息
);
if (authResult) {
// 确保 FlWX() 返回的是同一个实例,并且 token 已经被设置
// 这里假设 FlWX().token 在 authBy 成功后被设置
print("数据获取_FlWX().token${FlWX().token}");
// SettingsController().userName = FlWX().nickname;
// SettingsController().avatar = FlWX().headimgurl;
// SettingsController().userId = FlWX().openid;
requestLogin({
"name": FlWX().nickname,
"avatar": FlWX().headimgurl,
"stype": 2,
"ttoken": FlWX().code,
});
// 验证成功,跳转到配对页面
}
break;
}
default:
{
Get.snackbar(
'错误',
'登录失败,请重试',
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.red.withOpacity(0.1),
colorText: Colors.red,
);
break;
}
}
} catch (e) {
Get.snackbar(
'错误',
'错误',
'登录失败,请重试',
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.red.withOpacity(0.1),
@ -231,16 +329,87 @@ class LoginController extends GetxController {
_isLoading.value = false;
}
}
// 使用登陆请求服务器
Future<void> requestLogin(Map params) async {
// switch (loginType) {
// case 0: //邮箱
// {
// String verificationCode = verificationCodeController.text;
// String contactControll22er = contactController.text;
// }
// case 1: //手机号
// {
// String verificationCode = verificationCodeController.text;
// String contactContr22oller = contactController.text;
// }
// case 2: //微信
// {}
// case 3: //谷歌
// {}
// case 4: //facebook
// {}
// }
var response = await Api.login({
"mail": params?["mail"].toString() ?? "",
"phone": params?["phone"].toString() ?? "",
"stype": params["stype"],
"vcode": params?["vcode"].toString() ?? "",
"ttoken": params?["ttoken"].toString() ?? "",
"phonemodel": params?["phonemodel"].toString() ?? "",
"phonemac": params?["phonemac"].toString() ?? "",
"name": params?["name"].toString() ?? "",
"avatar": params?["avatar"].toString() ?? "",
});
if (response != null) {
print("请求后台数据login回复数据=${response}");
final instance = await SharedPreferences.getInstance();
await instance.setString('loginInfo', jsonEncode(response));
// 2. 将响应数据转换为User对象
try {
print('响应数据: ${response.toString()}'); // 调试输出原始数据
if (response['code'].toString() == '0' &&
response['msg'].toString() == "Success") {
// 新增:检查 user 字段是否存在且类型正确
if (response.containsKey('data') && response['data'] is Map) {
final user = User.fromJson(response);
print('用户初始化成功: ${user.name}');
// 存储到 SharedPreferences (仅存储用户数据)
final prefs = await SharedPreferences.getInstance();
await prefs.setString(
'user', jsonEncode(user.toJson())); // 使用 user.toJson()
Get.offAllNamed(Routes.pairing);
} else {
throw FormatException('API 响应缺少 user 字段');
}
} else {
throw Exception('接口返回错误: ${response['msg']}');
}
} catch (e, stack) {
// 新增:打印堆栈跟踪
print('用户数据处理失败: $e');
print('堆栈跟踪: $stack');
Get.snackbar('错误', '用户数据解析失败,请检查网络');
}
// Get.offAll(() => IndexWidget());
}
}
// 使用密码登录
void usePasswordLogin() {
// 这里可以实现密码登录的逻辑
Get.snackbar(
'提示',
'提示',
'密码登录功能尚未实现',
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.blue.withOpacity(0.1),
colorText: Colors.blue,
);
}
}
}

126
lib/modules/login/controllers/weixinLogin.dart

@ -5,7 +5,6 @@ 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);
@ -76,8 +75,14 @@ class FlWX {
String _appName = '';
String token = '';
String code = '';
String refresh_token = '';
String openid = '';
String nickname = '';
String sex = '';
String headimgurl = '';
String city = '';
// 参数 说明
// access_token 接口调用凭证
// expires_in access_token 接口调用凭证超时时间,单位(秒)
@ -257,68 +262,97 @@ class FlWX {
}
}
/// 调用微信登录
// graph TD
// A[开始] --> B{微信是否安装?}
// B -->|否| C[返回 false]
// B -->|是| D[注册监听器]
// D --> E[调起微信授权]
// E -->|失败| F[触发 completer(false)]
// E -->|成功| G[等待微信响应]
// G -->|用户取消/接口错误| H[触发 completer(false)]
// G -->|成功获取 code| I[获取 Token]
// I -->|Token 失败| H
// I -->|Token 成功| J[刷新 Token]
// J -->|刷新失败| H
// J -->|刷新成功| K{需用户信息?}
// K -->|是| L[获取用户信息]
// L -->|信息失败| H
// K -->|否| M[触发 completer(true)]
// L -->|信息成功| M
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 (!await isInstalled) {
_params?.onToast?.call('微信未安装');
return false;
}
final completer = Completer<bool>(); // 创建 Completer 控制 Future 完成时机
FluwxCancelable? subscription; // 用于取消监听
subscription = _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');
}
code = response.code!;
completer.complete(true); // 所有操作成功时完成 Future
// try {
// code = response.code!;
// // 获取 token
// final tokenData = await fetchData(_getTokenUrl(response.code!));
// if (tokenData.isEmpty) {
// _params!.onLog?.call('Token 数据获取失败');
// completer.complete(false); // 失败时完成 Future
// return;
// }
// // 更新 token
// token = tokenData["access_token"].toString();
// openid = tokenData["openid"].toString();
// // Istoken = await fetchData(
// // _getUserTokenEffertive(openId: openid, token: token),
// // ); // 触发GET请求
// // print("数据获取_getUserTokenEffertive${Istoken}");
// // 可选:刷新 token 或获取用户信息
// if (getUserInfo) {
// final userInfo = await fetchData(
// _getUserInfoUrl(openId: openid, token: token));
// List<int> bytes = latin1.encode(userInfo["nickname"].toString());
// print("ISO-8859-1 字节数组: $bytes");
// nickname = String.fromCharCodes(bytes);
// // nickname = userInfo["nickname"].toString();
// sex = userInfo["sex"].toString();
// headimgurl = userInfo["headimgurl"].toString();
// city = userInfo["city"].toString();
// print("_getUserInfoUrl${nickname}");
// // onUserinfo?.call(userInfo);
// }
// completer.complete(true); // 所有操作成功时完成 Future
// } catch (e) {
// _params!.onLog?.call('授权流程异常: $e');
// completer.complete(false);
// }
} else {
completer.complete(false); // 用户取消授权或授权失败
}
} else if (response is WeChatAuthByQRCodeFinishedResponse) {}
}
});
// 调起微信授权
final result = await fluwx.authBy(which: which);
print("authBy数据获取${result}");
if (!result) {
cancelable.cancel();
subscription.cancel(); // 调起失败时取消监听
_params!.onToast?.call('微信授权失败');
completer.complete(false);
}
return result;
return completer.future; // 返回由回调控制的 Future
}
Future<bool> autoDeduct(

32
lib/modules/login/entity/api.dart

@ -0,0 +1,32 @@
import 'dio_manager.dart';
import 'nw_method.dart';
class Api {
// 登录
static login([params]) {
print("请求后台数据params=${params}");
return DioManager().request(
NWMethod.post,
'/api/home/user_sgin',
params: params,
);
}
// 获取验证码
static getCode([params]) {
return DioManager().request(
NWMethod.post,
'/api/home/user_verification',
params: params,
);
}
// 获取阿里token
static getAliToken([params]) {
return DioManager().request(
NWMethod.post,
'/api/home/auth_alitoken',
params: params,
);
}
}

45
lib/modules/login/entity/dio_manager.dart

@ -0,0 +1,45 @@
import 'package:dio/dio.dart';
//import 'auth_interceptor.dart';
import 'nw_api.dart';
import 'nw_method.dart';
class DioManager {
static final DioManager _shared = DioManager._internal();
factory DioManager() => _shared;
late Dio dio;
DioManager._internal() {
BaseOptions options = BaseOptions(
baseUrl: NWApi.baseApi,
contentType: Headers.jsonContentType,
responseType: ResponseType.json,
connectTimeout: const Duration(seconds: 30),
receiveTimeout: const Duration(seconds: 30),
);
dio = Dio(options);
//dio.interceptors.add(AuthInterceptor());
}
// 请求,返回参数为 T
// method:请求方法,NWMethod.POST等
// path:请求地址
// params:请求参数
Future request<T>(
NWMethod method,
String path, {
Map? params,
Map<String, dynamic>? queryParameters,
}) async {
Response response = await dio.request(
path,
data: params,
queryParameters: queryParameters,
options: Options(method: nwMethodValues[method]),
);
return response.data;
}
}

4
lib/modules/login/entity/nw_api.dart

@ -0,0 +1,4 @@
class NWApi {
// static const baseApi = "https://www.deapsound.com";
static const baseApi = "https://www.deapsound.com";
}

8
lib/modules/login/entity/nw_method.dart

@ -0,0 +1,8 @@
enum NWMethod { get, post, delete, put }
const nwMethodValues = {
NWMethod.get: "get",
NWMethod.post: "post",
NWMethod.delete: "delete",
NWMethod.put: "put"
};

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

@ -397,45 +397,21 @@ class LoginView extends GetView<LoginController> {
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();
}
controller.loginType = 1;
break;
}
case FontAwesomeIcons.google:
{
print("数据获取_Google()");
bool authResult = await Google().signInWithGoogle();
print("数据获取_Google().token${authResult}");
if (authResult) {
controller.login();
}
controller.loginType = 2;
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();
}
controller.loginType = 3;
break;
}
}
controller.login();
}
// 用户协议和隐私政策

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

@ -1,17 +1,22 @@
import 'package:app_settings/app_settings.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'dart:ui';
import '../../../core/theme/app_colors.dart';
import '../controllers/settings_controller.dart';
import '../../../core/widgets/common_bottom_nav.dart';
import '../../../routes/app_routes.dart';
import 'dart:io';
import '../../login/bindings/user_Info.dart';
class SettingsView extends GetView<SettingsController> {
const SettingsView({Key? key}) : super(key: key);
// 正确获取用户名方式
String get userName => User.getname();
String get deviceInfo => User.getdeviceInfo();
String get userId => User.getuserId();
String get standing => User.getstanding();
String get avatar => User.getnavatar();
@override
Widget build(BuildContext context) {
// 直接使用 Theme.of(context) 获取当前主题
@ -23,7 +28,7 @@ class SettingsView extends GetView<SettingsController> {
backgroundColor: isDarkMode ? Colors.black : Colors.white,
elevation: 0.5,
title: Text(
'settings'.tr, // 设置
'设置',
style: TextStyle(
fontSize: 17.sp,
fontWeight: FontWeight.w600,
@ -73,7 +78,7 @@ class SettingsView extends GetView<SettingsController> {
),
],
),
child: ClipRRect(//裁剪圆角矩形
child: ClipRRect(
borderRadius: BorderRadius.circular(16.r),
child: BackdropFilter(
filter: isDarkMode
@ -81,9 +86,7 @@ class SettingsView extends GetView<SettingsController> {
: ImageFilter.blur(sigmaX: 0, sigmaY: 0),
child: Row(
children: [
Obx(()=>GestureDetector(
//onTap: ()=>controller.updateUserAvatar(),
child:Container(//用户头像部分
Container(
width: 60.w,
height: 60.w,
decoration: BoxDecoration(
@ -96,33 +99,26 @@ class SettingsView extends GetView<SettingsController> {
),
],
),
child: ClipRRect(// 裁剪圆形头像
child: ClipRRect(
borderRadius: BorderRadius.circular(30.r),
child: controller.userInfo.value.avatar.isEmpty
? Image.network(
'https://images.unsplash.com/photo-1534528741775-53994a69daeb?ixlib=rb-1.2.1&auto=format&fit=crop&w=200&q=80',
fit: BoxFit.cover,// 图片填充方式
)
:Image.file(
File(controller.userInfo.value.avatar),
fit: BoxFit.cover,// 图片填充方式
)
child: Image.network(
avatar,
fit: BoxFit.cover,
),
),
),
)),
SizedBox(width: 16.w),
Expanded(// 占据剩余空间
child: Obx(()=>Column(
crossAxisAlignment: CrossAxisAlignment.start,// 左对齐
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(//用户名
controller.userInfo.value.name,
Text(
userName,
style: TextStyle(
fontSize: 18.sp,
fontWeight: FontWeight.w600,
color: isDarkMode ? Colors.white : Colors.black87,
),
overflow: TextOverflow.ellipsis,
),
SizedBox(height: 4.h),
Row(
@ -135,7 +131,7 @@ class SettingsView extends GetView<SettingsController> {
borderRadius: BorderRadius.circular(10.r),
),
child: Text(
controller.userInfo.value.membershipLevel,
standing,
style: TextStyle(
fontSize: 10.sp,
color: Colors.white,
@ -145,7 +141,7 @@ class SettingsView extends GetView<SettingsController> {
SizedBox(width: 8.w),
Flexible(
child: Text(
'${'joinDevice'.tr} DeepSound Pro', // 已连接设备:
deviceInfo,
style: TextStyle(
fontSize: 12.sp,
color:
@ -158,7 +154,7 @@ class SettingsView extends GetView<SettingsController> {
),
SizedBox(height: 4.h),
Text(
controller.userInfo.value.id,
userId,
style: TextStyle(
fontSize: 12.sp,
color: isDarkMode ? Colors.white70 : Colors.black54,
@ -166,8 +162,8 @@ class SettingsView extends GetView<SettingsController> {
),
],
),
),),
Container(// 编辑按钮
),
Container(
width: 32.w,
height: 32.w,
decoration: BoxDecoration(
@ -183,15 +179,11 @@ class SettingsView extends GetView<SettingsController> {
),
],
),
child: InkWell(
onTap: () => Get.toNamed(Routes.editProfile),
child: Icon(
Icons.edit,
size: 16.w,
color: isDarkMode ? Colors.white70 : Colors.grey[600],
),
)
child: Icon(
Icons.edit,
size: 16.w,
color: isDarkMode ? Colors.white70 : Colors.grey[600],
),
),
],
),
@ -208,7 +200,7 @@ class SettingsView extends GetView<SettingsController> {
Padding(
padding: EdgeInsets.only(left: 4.w, bottom: 8.h),
child: Text(
'systemSetting'.tr, // 系统设置
'系统设置',
style: TextStyle(
fontSize: 12.sp,
fontWeight: FontWeight.w500,
@ -239,25 +231,22 @@ class SettingsView extends GetView<SettingsController> {
: ImageFilter.blur(sigmaX: 0, sigmaY: 0),
child: Column(
children: [
// 语言设置
Obx(
() => _buildSimpleNavigationSetting(
title: 'languageSetting'.tr, // 语言设置
subtitle: controller.language.value.name.tr,
icon: Icons.language,
iconBgColor: isDarkMode
? Colors.indigo[900]!.withOpacity(0.3)
: Colors.indigo[100]!,
iconColor: isDarkMode
? Colors.indigo[300]!
: Colors.indigo[600]!,
titleColor: isDarkMode ? Colors.white : null,
subtitleColor: isDarkMode ? Colors.white70 : null,
onTap: () {
_showLanguageSelectionDialog(isDarkMode);
},
isDarkMode: isDarkMode,
),
// 通知设置
_buildSimpleNavigationSetting(
title: '通知设置',
subtitle: '管理应用推送和提醒',
icon: Icons.notifications_none,
iconBgColor: isDarkMode
? Colors.indigo[900]!.withOpacity(0.3)
: Colors.indigo[100]!,
iconColor:
isDarkMode ? Colors.indigo[300]! : Colors.indigo[600]!,
titleColor: isDarkMode ? Colors.white : null,
subtitleColor: isDarkMode ? Colors.white70 : null,
onTap: () {
// 打开通知设置页面
},
isDarkMode: isDarkMode,
),
Divider(
height: 1,
@ -267,8 +256,8 @@ class SettingsView extends GetView<SettingsController> {
// 主题设置
_buildToggleSetting(
title: 'darkMode'.tr, // 暗黑模式
subtitle: 'darkModeInfo'.tr, // 切换应用的明暗主题
title: '暗黑模式',
subtitle: '切换应用的明暗主题',
icon: isDarkMode ? Icons.dark_mode : Icons.light_mode,
iconBgColor: isDarkMode
? Colors.indigo[900]!.withOpacity(0.3)
@ -289,15 +278,15 @@ class SettingsView extends GetView<SettingsController> {
// 备份与恢复
_buildNavigationSetting(
title: 'backups'.tr, // 备份与恢复
subtitle: 'backupsInfo'.tr, // 备份设置和AI互动历史
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]!,
value: '${'lastBackup'.tr} 今天', // 上次备份:
value: '上次备份: 今天',
valueStyle: TextStyle(
fontSize: 12.sp,
color: isDarkMode ? Colors.white70 : Colors.grey[500],
@ -307,15 +296,6 @@ class SettingsView extends GetView<SettingsController> {
},
isDarkMode: isDarkMode,
),
Divider(
height: 1,
color: isDarkMode
? Colors.white.withOpacity(0.1)
: Colors.grey[200]),
// 语音服务选择
_buildSpeechServiceSetting(isDarkMode),
Divider(
height: 1,
color: isDarkMode
@ -324,8 +304,8 @@ class SettingsView extends GetView<SettingsController> {
// 隐私设置
_buildSimpleNavigationSetting(
title: 'privacySettings'.tr, // 隐私设置
subtitle: 'privacySettingsInfo'.tr, // 管理数据收集和权限
title: '隐私设置',
subtitle: '管理数据收集和权限',
icon: Icons.shield_outlined,
iconBgColor: isDarkMode
? Colors.purple[900]!.withOpacity(0.3)
@ -335,7 +315,7 @@ class SettingsView extends GetView<SettingsController> {
titleColor: isDarkMode ? Colors.white : null,
subtitleColor: isDarkMode ? Colors.white70 : null,
onTap: () {
AppSettings.openAppSettings();
// 打开隐私设置页面
},
isDarkMode: isDarkMode,
),
@ -347,7 +327,7 @@ class SettingsView extends GetView<SettingsController> {
// 退出登录
_buildSimpleNavigationSetting(
title: 'logout'.tr, // 退出登录
title: '退出登录',
icon: Icons.logout,
iconBgColor: isDarkMode
? Colors.red[900]!.withOpacity(0.3)
@ -362,13 +342,13 @@ class SettingsView extends GetView<SettingsController> {
backgroundColor:
isDarkMode ? Colors.grey[900] : Colors.white,
title: Text(
'logout'.tr, // 退出登录
'退出登录',
style: TextStyle(
color: isDarkMode ? Colors.white : Colors.black87,
),
),
content: Text(
'logoutTips'.tr, // 确定要退出当前账号吗?
'确定要退出当前账号吗?',
style: TextStyle(
color:
isDarkMode ? Colors.white70 : Colors.black54,
@ -378,7 +358,7 @@ class SettingsView extends GetView<SettingsController> {
TextButton(
onPressed: () => Get.back(),
child: Text(
'cancel'.tr, // 取消
'取消',
style: TextStyle(
color: isDarkMode
? Colors.white70
@ -392,7 +372,7 @@ class SettingsView extends GetView<SettingsController> {
controller.signOut();
},
child: Text(
'confirm'.tr, // 确定
'确定',
style: TextStyle(
color: isDarkMode
? Colors.red[300]
@ -423,7 +403,7 @@ class SettingsView extends GetView<SettingsController> {
Padding(
padding: EdgeInsets.only(left: 4.w, bottom: 8.h),
child: Text(
'aboutApplication'.tr, // 关于应用
'关于应用',
style: TextStyle(
fontSize: 12.sp,
fontWeight: FontWeight.w500,
@ -485,7 +465,7 @@ class SettingsView extends GetView<SettingsController> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'DeepSound',
'DeepVoice',
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w500,
@ -494,7 +474,7 @@ class SettingsView extends GetView<SettingsController> {
),
SizedBox(height: 2.h),
Text(
'${'versions'.tr} 2.4.1 (Build 87)', // 版本
'版本 2.4.1 (Build 87)',
style: TextStyle(
fontSize: 12.sp,
color:
@ -515,7 +495,7 @@ class SettingsView extends GetView<SettingsController> {
borderRadius: BorderRadius.circular(16.r),
),
child: Text(
'checkUpdates'.tr, // 检查更新
'检查更新',
style: TextStyle(
fontSize: 12.sp,
color: isDarkMode ? Colors.blue[300] : Colors.blue[600],
@ -532,7 +512,7 @@ class SettingsView extends GetView<SettingsController> {
child: Column(
children: [
Text(
'© 2025 DeepSound Technologies',
'© 2023 DeepVoice Technologies',
style: TextStyle(
fontSize: 12.sp,
color: isDarkMode ? Colors.grey[400] : Colors.grey[500],
@ -540,7 +520,7 @@ class SettingsView extends GetView<SettingsController> {
),
SizedBox(height: 4.h),
Text(
'homeSlogan'.tr, // 连接世界,超越声音
'连接世界,超越声音',
style: TextStyle(
fontSize: 12.sp,
color: isDarkMode ? Colors.grey[400] : Colors.grey[500],
@ -569,51 +549,45 @@ class SettingsView extends GetView<SettingsController> {
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Row(
children: [
Container(
width: 32.w,
height: 32.w,
decoration: BoxDecoration(
color: iconBgColor,
borderRadius: BorderRadius.circular(16.r),
),
child: Icon(
icon,
size: 16.w,
color: iconColor,
),
Row(
children: [
Container(
width: 32.w,
height: 32.w,
decoration: BoxDecoration(
color: iconBgColor,
borderRadius: BorderRadius.circular(16.r),
),
SizedBox(width: 12.w),
Flexible(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w500,
color: isDarkMode ? Colors.white : Colors.black87,
),
overflow: TextOverflow.ellipsis,
),
if (subtitle != null) SizedBox(height: 2.h),
if (subtitle != null)
Text(
subtitle,
style: TextStyle(
fontSize: 12.sp,
color: isDarkMode ? Colors.white70 : Colors.black54,
),
overflow: TextOverflow.ellipsis,
),
],
),
child: Icon(
icon,
size: 16.w,
color: iconColor,
),
],
),
),
SizedBox(width: 12.w),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w500,
color: isDarkMode ? Colors.white : Colors.black87,
),
),
if (subtitle != null) SizedBox(height: 2.h),
if (subtitle != null)
Text(
subtitle,
style: TextStyle(
fontSize: 12.sp,
color: isDarkMode ? Colors.white70 : Colors.black54,
),
),
],
),
],
),
Switch(
value: value,
@ -644,66 +618,55 @@ class SettingsView extends GetView<SettingsController> {
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Row(
children: [
Container(
width: 32.w,
height: 32.w,
decoration: BoxDecoration(
color: iconBgColor,
borderRadius: BorderRadius.circular(16.r),
),
child: Icon(
icon,
size: 16.w,
color: iconColor,
),
Row(
children: [
Container(
width: 32.w,
height: 32.w,
decoration: BoxDecoration(
color: iconBgColor,
borderRadius: BorderRadius.circular(16.r),
),
SizedBox(width: 12.w),
Flexible(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w500,
color: isDarkMode ? Colors.white : Colors.black87,
),
overflow: TextOverflow.ellipsis,
),
if (subtitle != null) SizedBox(height: 2.h),
if (subtitle != null)
Text(
subtitle,
style: TextStyle(
fontSize: 12.sp,
color:
isDarkMode ? Colors.white70 : Colors.black54,
),
overflow: TextOverflow.ellipsis,
),
],
),
child: Icon(
icon,
size: 16.w,
color: iconColor,
),
],
),
),
SizedBox(width: 12.w),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w500,
color: isDarkMode ? Colors.white : Colors.black87,
),
),
if (subtitle != null) SizedBox(height: 2.h),
if (subtitle != null)
Text(
subtitle,
style: TextStyle(
fontSize: 12.sp,
color: isDarkMode ? Colors.white70 : Colors.black54,
),
),
],
),
],
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
Flexible(
child: Text(
value,
style: valueStyle ??
TextStyle(
fontSize: 14.sp,
color: isDarkMode ? Colors.white : Colors.black87,
),
overflow: TextOverflow.ellipsis,
),
Text(
value,
style: valueStyle ??
TextStyle(
fontSize: 14.sp,
color: isDarkMode ? Colors.white : Colors.black87,
),
),
SizedBox(width: 8.w),
Container(
@ -748,391 +711,64 @@ class SettingsView extends GetView<SettingsController> {
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Row(
children: [
Container(
width: 32.w,
height: 32.w,
decoration: BoxDecoration(
color: iconBgColor,
borderRadius: BorderRadius.circular(16.r),
),
child: Icon(
icon,
size: 16.w,
color: iconColor,
),
),
SizedBox(width: 12.w),
Flexible(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w500,
color: titleColor ??
(isDarkMode ? Colors.white : Colors.black87),
),
overflow: TextOverflow.ellipsis,
),
if (subtitle != null) SizedBox(height: 2.h),
if (subtitle != null)
Text(
subtitle,
style: TextStyle(
fontSize: 12.sp,
color: subtitleColor ??
(isDarkMode
? Colors.white70
: Colors.black54),
),
overflow: TextOverflow.ellipsis,
),
],
),
),
],
),
),
Container(
width: 32.w,
height: 32.w,
decoration: BoxDecoration(
color: isDarkMode
? Colors.white.withOpacity(0.05)
: Colors.grey[100],
borderRadius: BorderRadius.circular(16.r),
),
child: Icon(
Icons.chevron_right,
size: 16.w,
color: isDarkMode ? Colors.white70 : Colors.grey[500],
),
),
],
),
),
);
}
// 语音服务设置项
Widget _buildSpeechServiceSetting(bool isDarkMode) {
return InkWell(
onTap: () {
_showSpeechServiceSelectionDialog(isDarkMode);
},
child: Padding(
padding: EdgeInsets.all(16.w),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Row(
children: [
Container(
width: 32.w,
height: 32.w,
decoration: BoxDecoration(
color: isDarkMode
? Colors.teal[900]!.withOpacity(0.3)
: Colors.teal[100]!,
borderRadius: BorderRadius.circular(16.r),
),
child: Icon(
Icons.record_voice_over,
size: 16.w,
color: isDarkMode ? Colors.teal[300]! : Colors.teal[600]!,
),
),
SizedBox(width: 12.w),
Flexible(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'voiceService'.tr, // 语音服务
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w500,
color: isDarkMode ? Colors.white : Colors.black87,
),
),
SizedBox(height: 2.h),
Text(
'voiceServiceInfo'.tr, // 选择语音识别和合成服务
style: TextStyle(
fontSize: 12.sp,
color: isDarkMode ? Colors.white70 : Colors.black54,
),
overflow: TextOverflow.ellipsis,
),
],
),
),
],
),
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
Obx(() {
final serviceType = controller.speechServiceType.value;
final serviceName = controller.availableSpeechServices
.firstWhere(
(service) => service['type'] == serviceType)['name'];
return Flexible(
child: Text(
'$serviceName'.tr,
style: TextStyle(
fontSize: 12.sp,
color: isDarkMode ? Colors.white70 : Colors.grey[600],
),
overflow: TextOverflow.ellipsis,
),
);
}),
SizedBox(width: 8.w),
Container(
width: 32.w,
height: 32.w,
decoration: BoxDecoration(
color: isDarkMode
? Colors.white.withOpacity(0.05)
: Colors.grey[100],
color: iconBgColor,
borderRadius: BorderRadius.circular(16.r),
),
child: Icon(
Icons.chevron_right,
icon,
size: 16.w,
color: isDarkMode ? Colors.white70 : Colors.grey[500],
color: iconColor,
),
),
],
),
],
),
),
);
}
// 显示语音服务选择对话框
void _showSpeechServiceSelectionDialog(bool isDarkMode) {
Get.dialog(
Dialog(
backgroundColor: isDarkMode ? Colors.grey[900] : Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16.r),
),
child: Padding(
padding: EdgeInsets.all(16.w),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'selectVoiceService'.tr, // 选择语音服务
style: TextStyle(
fontSize: 18.sp,
fontWeight: FontWeight.w600,
color: isDarkMode ? Colors.white : Colors.black87,
),
),
SizedBox(height: 16.h),
...controller.availableSpeechServices.map((service) {
return Obx(() {
final isSelected =
controller.speechServiceType.value == service['type'];
return InkWell(
onTap: () {
controller.setSpeechServiceType(service['type']);
Get.back();
},
child: Container(
margin: EdgeInsets.only(bottom: 12.h),
padding: EdgeInsets.all(12.w),
decoration: BoxDecoration(
color: isSelected
? (isDarkMode
? Colors.teal[900]!.withOpacity(0.3)
: Colors.teal[50]!)
: (isDarkMode
? Colors.white.withOpacity(0.05)
: Colors.grey[100]!),
borderRadius: BorderRadius.circular(12.r),
border: Border.all(
color: isSelected
? (isDarkMode
? Colors.teal[300]!
: Colors.teal[600]!)
: Colors.transparent,
width: 1,
),
),
child: Row(
children: [
Container(
width: 24.w,
height: 24.w,
decoration: BoxDecoration(
color: isSelected
? (isDarkMode
? Colors.teal[300]
: Colors.teal[600])
: (isDarkMode
? Colors.white.withOpacity(0.1)
: Colors.grey[300]),
shape: BoxShape.circle,
),
child: isSelected
? Icon(
Icons.check,
size: 16.w,
color: Colors.white,
)
: null,
),
SizedBox(width: 12.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${service['name']}'.tr,
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w500,
color: isDarkMode
? Colors.white
: Colors.black87,
),
overflow: TextOverflow.ellipsis,
),
SizedBox(height: 4.h),
Text(
'${service['description']}'.tr,
style: TextStyle(
fontSize: 12.sp,
color: isDarkMode
? Colors.white70
: Colors.black54,
),
overflow: TextOverflow.ellipsis,
maxLines: 2,
),
],
),
),
],
),
),
);
});
}).toList(),
SizedBox(height: 16.h),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () => Get.back(),
child: Text(
'cancel'.tr, // 取消
SizedBox(width: 12.w),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
color: isDarkMode ? Colors.white70 : Colors.black54,
fontSize: 16.sp,
fontWeight: FontWeight.w500,
color: titleColor ??
(isDarkMode ? Colors.white : Colors.black87),
),
),
),
],
),
],
),
),
),
);
}
// 显示语言选择对话框
void _showLanguageSelectionDialog(bool isDarkMode) {
Get.dialog(
Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24.r),
),
insetPadding: EdgeInsets.symmetric(horizontal: 20.w),
backgroundColor: isDarkMode ? AppColors.cardBackground : Colors.white,
child: Container(
width: 0.9.sw,
padding: EdgeInsets.only(top: 32.h, bottom: 16.h),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.symmetric(horizontal: 24.w),
child: Text(
'selectLanguage'.tr, // 选择语言
style: TextStyle(
fontSize: 20.sp,
fontWeight: FontWeight.bold,
color: isDarkMode ? Colors.white : Colors.black,
),
),
),
SizedBox(height: 16.h),
Container(
constraints: BoxConstraints(maxHeight: 0.5.sh),
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: controller.languageList.map((language) {
return ListTile(
title: Text(
language.name.tr,
style: TextStyle(
color: isDarkMode ? Colors.white : Colors.black,
),
if (subtitle != null) SizedBox(height: 2.h),
if (subtitle != null)
Text(
subtitle,
style: TextStyle(
fontSize: 12.sp,
color: subtitleColor ??
(isDarkMode ? Colors.white70 : Colors.black54),
),
trailing:
controller.language.value.value == language.value
? Icon(
Icons.check_circle,
color: isDarkMode
? AppColors.primary
: Colors.blue,
)
: const SizedBox.shrink(),
onTap: () {
controller.setLanguage(language);
},
);
}).toList(),
),
),
),
SizedBox(height: 16.h),
Align(
alignment: Alignment.centerRight,
child: Padding(
padding: EdgeInsets.only(right: 24.w),
child: TextButton(
onPressed: () => Get.back(),
child: Text(
'cancel'.tr, // 取消
style: TextStyle(
color: isDarkMode ? AppColors.primary : Colors.blue,
fontSize: 16.sp,
),
),
),
],
),
],
),
Container(
width: 32.w,
height: 32.w,
decoration: BoxDecoration(
color: isDarkMode
? Colors.white.withOpacity(0.05)
: Colors.grey[100],
borderRadius: BorderRadius.circular(16.r),
),
],
),
child: Icon(
Icons.chevron_right,
size: 16.w,
color: isDarkMode ? Colors.white70 : Colors.grey[500],
),
),
],
),
),
);

3
pubspec.yaml

@ -58,7 +58,8 @@ dependencies:
common_utils: ^2.1.0
flutter_image_compress: ^2.4.0
# image_cropper: ^9.0.0
dio: ^5.8.0+1
shared_preferences: ^2.5.1
dev_dependencies:
flutter_test:
sdk: flutter

Loading…
Cancel
Save