4 changed files with 850 additions and 453 deletions
Unified View
Diff Options
-
4lib/main.dart
-
222lib/pages/mine/user_info_controller.dart
-
837lib/pages/mine/user_info_page.dart
-
240pubspec.lock
@ -0,0 +1,222 @@ |
|||||
|
import 'dart:io'; |
||||
|
import 'package:flutter/cupertino.dart'; |
||||
|
import 'package:get/get.dart'; |
||||
|
import 'package:get_storage/get_storage.dart'; |
||||
|
import 'package:flutter/material.dart'; |
||||
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; |
||||
|
import 'package:image_picker/image_picker.dart'; |
||||
|
|
||||
|
class UserInfoController extends GetxController { |
||||
|
// 用户信息表单字段 |
||||
|
final gender = 'male'.obs; // 默认选择男性 |
||||
|
final nickname = ''.obs; |
||||
|
final birthday = ''.obs; |
||||
|
final education = ''.obs; |
||||
|
final invitationCode = ''.obs; |
||||
|
final avatarUrl = ''.obs; |
||||
|
|
||||
|
// 加载状态 |
||||
|
final isSubmitting = false.obs; |
||||
|
|
||||
|
// GetStorage实例,用于存储用户信息 |
||||
|
final storage = GetStorage(); |
||||
|
|
||||
|
// 选择性别 |
||||
|
void selectGender(String selectedGender) { |
||||
|
gender.value = selectedGender; |
||||
|
// 打印选择的性别 |
||||
|
print('选择的性别: $selectedGender'); |
||||
|
// 显示选择结果 |
||||
|
SmartDialog.showToast('已选择${selectedGender == 'male' ? '男' : '女'}性'); |
||||
|
} |
||||
|
|
||||
|
// 选择日期 |
||||
|
void selectBirthday(String selectedDate) { |
||||
|
birthday.value = selectedDate; |
||||
|
// 打印选择的日期 |
||||
|
print('选择的出生日期: $selectedDate'); |
||||
|
// 显示选择结果 |
||||
|
SmartDialog.showToast('出生日期已选择'); |
||||
|
} |
||||
|
|
||||
|
// 选择学历 |
||||
|
void selectEducation(String selectedEducation) { |
||||
|
education.value = selectedEducation; |
||||
|
// 打印选择的学历 |
||||
|
print('选择的学历: $selectedEducation'); |
||||
|
// 显示选择结果 |
||||
|
SmartDialog.showToast('学历已选择'); |
||||
|
|
||||
|
|
||||
|
} |
||||
|
|
||||
|
// 选择头像 - 业务逻辑处理 |
||||
|
Future<void> handleCameraCapture() async { |
||||
|
try { |
||||
|
// 请求相机权限并拍照 |
||||
|
final ImagePicker picker = ImagePicker(); |
||||
|
final XFile? photo = await picker.pickImage(source: ImageSource.camera); |
||||
|
|
||||
|
if (photo != null) { |
||||
|
await processSelectedImage(File(photo.path)); |
||||
|
} |
||||
|
} catch (e) { |
||||
|
print('拍照失败: $e'); |
||||
|
// 更友好的错误提示 |
||||
|
if (e.toString().contains('permission') || e.toString().contains('权限')) { |
||||
|
SmartDialog.showToast('相机权限被拒绝,请在设置中允许访问相机'); |
||||
|
} else if (e.toString().contains('camera') || e.toString().contains('相机')) { |
||||
|
SmartDialog.showToast('设备没有可用的相机'); |
||||
|
} else { |
||||
|
SmartDialog.showToast('拍照失败,请重试'); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
Future<void> handleGallerySelection() async { |
||||
|
try { |
||||
|
// 从相册选择图片 |
||||
|
final ImagePicker picker = ImagePicker(); |
||||
|
final XFile? image = await picker.pickImage(source: ImageSource.gallery); |
||||
|
|
||||
|
if (image != null) { |
||||
|
await processSelectedImage(File(image.path)); |
||||
|
} |
||||
|
} catch (e) { |
||||
|
print('选择图片失败: $e'); |
||||
|
// 更友好的错误提示 |
||||
|
if (e.toString().contains('permission') || e.toString().contains('权限')) { |
||||
|
SmartDialog.showToast('相册权限被拒绝,请在设置中允许访问相册'); |
||||
|
} else { |
||||
|
SmartDialog.showToast('选择图片失败,请重试'); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 处理选中的图片 |
||||
|
Future<void> processSelectedImage(File imageFile) async { |
||||
|
try { |
||||
|
// 显示加载提示 |
||||
|
SmartDialog.showLoading(msg: '正在处理头像...'); |
||||
|
|
||||
|
// 更新本地头像URL(实际项目中应该先上传到服务器) |
||||
|
// 这里直接使用本地文件路径作为临时URL |
||||
|
avatarUrl.value = imageFile.path; |
||||
|
|
||||
|
// 保存到本地存储 |
||||
|
final currentUserInfo = storage.read('userInfo') ?? {}; |
||||
|
if (currentUserInfo is Map<String, dynamic>) { |
||||
|
currentUserInfo['avatarUrl'] = imageFile.path; |
||||
|
await storage.write('userInfo', currentUserInfo); |
||||
|
} |
||||
|
|
||||
|
SmartDialog.dismiss(); |
||||
|
SmartDialog.showToast('头像设置成功'); |
||||
|
} catch (e) { |
||||
|
SmartDialog.dismiss(); |
||||
|
print('处理图片失败: $e'); |
||||
|
SmartDialog.showToast('处理图片失败'); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 选择头像入口方法(保持兼容性) |
||||
|
void selectAvatar() { |
||||
|
// 这个方法现在只是一个入口,实际的UI交互在页面中实现 |
||||
|
// 页面会调用上面的具体处理方法 |
||||
|
} |
||||
|
|
||||
|
// 验证表单 |
||||
|
bool _validateForm() { |
||||
|
// 验证昵称 |
||||
|
if (nickname.value.isEmpty) { |
||||
|
SmartDialog.showToast('请输入昵称'); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// 验证生日 |
||||
|
if (birthday.value.isEmpty) { |
||||
|
SmartDialog.showToast('请选择出生日期'); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// 验证学历 |
||||
|
if (education.value.isEmpty) { |
||||
|
SmartDialog.showToast('请选择学历'); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
// 构建提交参数 |
||||
|
Map<String, dynamic> _buildSubmitParams() { |
||||
|
return { |
||||
|
'gender': gender.value == 'male' ? 1 : 2, // 1:男, 2:女 |
||||
|
'nickname': nickname.value, |
||||
|
'birthday': birthday.value, |
||||
|
'education': education.value, |
||||
|
if (invitationCode.value.isNotEmpty) 'invitationCode': invitationCode.value, |
||||
|
if (avatarUrl.value.isNotEmpty) 'avatarUrl': avatarUrl.value, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
// 提交用户信息 |
||||
|
Future<void> submitUserInfo() async { |
||||
|
// 验证表单 |
||||
|
if (!_validateForm()) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
isSubmitting.value = true; |
||||
|
|
||||
|
try { |
||||
|
// 构建请求参数 |
||||
|
final params = _buildSubmitParams(); |
||||
|
|
||||
|
// 调用UserApi中的完善用户信息接口 |
||||
|
// 注意:这里需要在UserApi中添加updateUserInfo方法 |
||||
|
// 由于目前没有看到完整的API定义,这里先模拟成功 |
||||
|
// 实际项目中应该调用真实的API |
||||
|
|
||||
|
// 模拟网络延迟 |
||||
|
await Future.delayed(const Duration(seconds: 1)); |
||||
|
|
||||
|
// 打印提交的信息 |
||||
|
print('提交用户信息参数: $params'); |
||||
|
|
||||
|
// 模拟成功响应 |
||||
|
// final response = await _userApi.updateUserInfo(params); |
||||
|
|
||||
|
// 处理响应 |
||||
|
// if (response.data.isSuccess) { |
||||
|
// 更新本地存储的用户信息 |
||||
|
final currentUserInfo = storage.read('userInfo') ?? {}; |
||||
|
if (currentUserInfo is Map<String, dynamic>) { |
||||
|
currentUserInfo.addAll({ |
||||
|
'gender': gender.value, |
||||
|
'nickname': nickname.value, |
||||
|
'birthday': birthday.value, |
||||
|
'education': education.value, |
||||
|
}); |
||||
|
await storage.write('userInfo', currentUserInfo); |
||||
|
} |
||||
|
|
||||
|
// 显示成功提示 |
||||
|
SmartDialog.showToast('信息提交成功!'); |
||||
|
// 延迟后跳转 |
||||
|
Future.delayed(const Duration(milliseconds: 1500), () { |
||||
|
// 跳转到主页面(暂时注释,实际使用时需要导入并替换正确的路由) |
||||
|
// Get.offAllNamed('/main'); |
||||
|
}); |
||||
|
// } else { |
||||
|
// Get.snackbar('错误', response.data.message); |
||||
|
// } |
||||
|
} catch (e) { |
||||
|
print('提交用户信息失败: $e'); |
||||
|
// 显示错误提示 |
||||
|
SmartDialog.showToast('提交失败,请重试'); |
||||
|
} finally { |
||||
|
isSubmitting.value = false; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,393 +1,566 @@ |
|||||
|
import 'dart:io'; |
||||
import 'package:flutter/material.dart'; |
import 'package:flutter/material.dart'; |
||||
|
import 'package:flutter/cupertino.dart'; |
||||
import 'package:dating_touchme_app/generated/assets.dart'; |
import 'package:dating_touchme_app/generated/assets.dart'; |
||||
|
import 'package:get/get.dart'; |
||||
|
import 'user_info_controller.dart'; |
||||
|
|
||||
class UserInfoPage extends StatefulWidget { |
|
||||
|
class UserInfoPage extends StatelessWidget { |
||||
const UserInfoPage({Key? key}) : super(key: key); |
const UserInfoPage({Key? key}) : super(key: key); |
||||
|
|
||||
@override |
|
||||
State<UserInfoPage> createState() => _UserInfoPageState(); |
|
||||
} |
|
||||
|
|
||||
class _UserInfoPageState extends State<UserInfoPage> { |
|
||||
String _gender = 'male'; // 默认选择男性 |
|
||||
String _nickname = '强壮的瘦子'; |
|
||||
String _birthday = ''; |
|
||||
String _education = ''; |
|
||||
String _invitationCode = ''; |
|
||||
|
// 显示头像选择选项 |
||||
|
void _showAvatarOptions(UserInfoController controller) { |
||||
|
showCupertinoModalPopup( |
||||
|
context: Get.context!, |
||||
|
builder: (context) => CupertinoActionSheet( |
||||
|
title: const Text('选择头像'), |
||||
|
actions: <Widget>[ |
||||
|
CupertinoActionSheetAction( |
||||
|
child: const Text('拍照'), |
||||
|
onPressed: () async { |
||||
|
Navigator.pop(context); |
||||
|
await controller.handleCameraCapture(); |
||||
|
}, |
||||
|
), |
||||
|
CupertinoActionSheetAction( |
||||
|
child: const Text('从相册选择'), |
||||
|
onPressed: () async { |
||||
|
Navigator.pop(context); |
||||
|
await controller.handleGallerySelection(); |
||||
|
}, |
||||
|
), |
||||
|
], |
||||
|
cancelButton: CupertinoActionSheetAction( |
||||
|
child: const Text('取消'), |
||||
|
isDestructiveAction: true, |
||||
|
onPressed: () { |
||||
|
Navigator.pop(context); |
||||
|
}, |
||||
|
), |
||||
|
), |
||||
|
); |
||||
|
} |
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return Scaffold( |
|
||||
body: Stack( |
|
||||
children: [ |
|
||||
// 背景图 |
|
||||
Positioned.fill( |
|
||||
child: Image.asset( |
|
||||
Assets.imagesBgInformation, |
|
||||
fit: BoxFit.cover, |
|
||||
), |
|
||||
|
// 显示学历选择弹框 |
||||
|
void _showEducationPicker(UserInfoController controller) { |
||||
|
final List<String> educationOptions = ['大专以下', '大专', '本科', '硕士及以上']; |
||||
|
|
||||
|
showModalBottomSheet( |
||||
|
context: Get.context!, |
||||
|
shape: const RoundedRectangleBorder( |
||||
|
borderRadius: BorderRadius.only( |
||||
|
topLeft: Radius.circular(8), |
||||
|
topRight: Radius.circular(8), |
||||
|
), |
||||
|
), |
||||
|
builder: (BuildContext context) { |
||||
|
return Container( |
||||
|
padding: const EdgeInsets.all(16), |
||||
|
decoration: BoxDecoration( |
||||
|
color: Colors.white, // 明确设置背景色 |
||||
|
borderRadius: const BorderRadius.only( |
||||
|
topLeft: Radius.circular(8), |
||||
|
topRight: Radius.circular(8), |
||||
|
), |
||||
|
), |
||||
|
child: Column( |
||||
|
mainAxisSize: MainAxisSize.min, |
||||
|
children: [ |
||||
|
// 标题和关闭按钮 |
||||
|
Stack( |
||||
|
alignment: Alignment.center, |
||||
|
children: [ |
||||
|
// 关闭按钮 - 左对齐 |
||||
|
Align( |
||||
|
alignment: Alignment.centerLeft, |
||||
|
child: IconButton( |
||||
|
icon: const Icon(Icons.close), |
||||
|
onPressed: () { |
||||
|
Navigator.pop(context); |
||||
|
}, |
||||
|
), |
||||
|
), |
||||
|
// 标题 - 居中 |
||||
|
Text( |
||||
|
'学历', |
||||
|
style: const TextStyle( |
||||
|
fontSize: 18, |
||||
|
fontWeight: FontWeight.bold, |
||||
), |
), |
||||
// 内容区域 |
|
||||
SafeArea( |
|
||||
child: SingleChildScrollView( |
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20.0), |
|
||||
child: Column( |
|
||||
children: [ |
|
||||
// 标题 |
|
||||
const SizedBox(height: 20), |
|
||||
Text( |
|
||||
'完善信息', |
|
||||
style: TextStyle( |
|
||||
fontSize: 24, |
|
||||
fontWeight: FontWeight.bold, |
|
||||
color: Color.fromRGBO(51, 51, 51, 1), |
|
||||
|
), |
||||
|
], |
||||
|
), |
||||
|
const SizedBox(height: 16), |
||||
|
|
||||
|
// 学历选项列表 |
||||
|
for (String education in educationOptions) |
||||
|
Container( |
||||
|
margin: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), // 左右10,上下6 |
||||
|
child: ElevatedButton( |
||||
|
style: ElevatedButton.styleFrom( |
||||
|
backgroundColor: controller.education.value == education |
||||
|
? const Color.fromRGBO(123, 104, 238, 1) // 紫色 |
||||
|
: const Color.fromRGBO(247, 247, 247, 1), |
||||
|
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 25), // 增加水平内边距 |
||||
|
minimumSize: const Size(double.infinity, 48), // 宽度充满,最小高度48 |
||||
|
shape: RoundedRectangleBorder( |
||||
|
borderRadius: BorderRadius.circular(32), |
||||
|
), |
||||
|
elevation: 0, |
||||
|
), |
||||
|
onPressed: () { |
||||
|
Navigator.pop(context); |
||||
|
controller.selectEducation(education); |
||||
|
}, |
||||
|
child: Text( |
||||
|
education, |
||||
|
style: TextStyle( |
||||
|
color: controller.education.value == education ? Colors.white : Colors.black, |
||||
|
fontSize: 16, |
||||
|
), |
||||
), |
), |
||||
), |
), |
||||
const SizedBox(height: 30), |
|
||||
|
|
||||
// 头像 |
|
||||
Stack( |
|
||||
|
), |
||||
|
|
||||
|
// 底部间距 |
||||
|
const SizedBox(height: 20), |
||||
|
], |
||||
|
), |
||||
|
); |
||||
|
}, |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
// 显示日期选择器 |
||||
|
void _showDatePicker(UserInfoController controller) { |
||||
|
// 计算最小和最大日期 |
||||
|
final now = DateTime.now(); |
||||
|
final maxDate = DateTime(now.year - 18); // 最小18岁 |
||||
|
final minDate = DateTime(now.year - 80); // 最大80岁 |
||||
|
|
||||
|
// 初始选择日期(默认为25岁) |
||||
|
DateTime initialDate = DateTime(now.year - 25); |
||||
|
if (controller.birthday.value.isNotEmpty) { |
||||
|
try { |
||||
|
final List<String> dateParts = controller.birthday.value.split('-'); |
||||
|
if (dateParts.length == 3) { |
||||
|
initialDate = DateTime( |
||||
|
int.parse(dateParts[0]), |
||||
|
int.parse(dateParts[1]), |
||||
|
int.parse(dateParts[2]), |
||||
|
); |
||||
|
// 确保初始日期在有效范围内 |
||||
|
if (initialDate.isBefore(minDate)) initialDate = minDate; |
||||
|
if (initialDate.isAfter(maxDate)) initialDate = maxDate; |
||||
|
} |
||||
|
} catch (e) { |
||||
|
print('解析日期失败: $e'); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 临时存储选择的日期 |
||||
|
DateTime? selectedDate = initialDate; |
||||
|
|
||||
|
showCupertinoModalPopup( |
||||
|
context: Get.context!, |
||||
|
builder: (BuildContext context) { |
||||
|
return Container( |
||||
|
height: 300, |
||||
|
color: CupertinoColors.white, |
||||
|
child: Column( |
||||
|
children: [ |
||||
|
// 头部按钮区域 |
||||
|
Container( |
||||
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), |
||||
|
child: Row( |
||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween, |
||||
|
children: [ |
||||
|
CupertinoButton( |
||||
|
child: const Text('取消'), |
||||
|
onPressed: () { |
||||
|
Navigator.pop(context); |
||||
|
}, |
||||
|
), |
||||
|
CupertinoButton( |
||||
|
child: const Text('确定'), |
||||
|
onPressed: () { |
||||
|
Navigator.pop(context); |
||||
|
if (selectedDate != null) { |
||||
|
// 格式化为YYYY-MM-DD |
||||
|
final formattedDate = '${selectedDate!.year}-' |
||||
|
'${selectedDate!.month.toString().padLeft(2, '0')}-' |
||||
|
'${selectedDate!.day.toString().padLeft(2, '0')}'; |
||||
|
controller.selectBirthday(formattedDate); |
||||
|
} |
||||
|
}, |
||||
|
), |
||||
|
], |
||||
|
), |
||||
|
), |
||||
|
// 日期选择器 |
||||
|
Expanded( |
||||
|
child: CupertinoDatePicker( |
||||
|
mode: CupertinoDatePickerMode.date, |
||||
|
initialDateTime: initialDate, |
||||
|
minimumDate: minDate, |
||||
|
maximumDate: maxDate, |
||||
|
onDateTimeChanged: (DateTime dateTime) { |
||||
|
selectedDate = dateTime; |
||||
|
}, |
||||
|
use24hFormat: true, |
||||
|
backgroundColor: CupertinoColors.white, |
||||
|
), |
||||
|
), |
||||
|
], |
||||
|
), |
||||
|
); |
||||
|
}, |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
Widget build(BuildContext context) { |
||||
|
return GetX<UserInfoController>( |
||||
|
init: UserInfoController(), |
||||
|
builder: (controller) { |
||||
|
return Scaffold( |
||||
|
body: Stack( |
||||
|
children: [ |
||||
|
// 背景图 |
||||
|
Positioned.fill( |
||||
|
child: Image.asset( |
||||
|
Assets.imagesBgInformation, |
||||
|
fit: BoxFit.cover, |
||||
|
), |
||||
|
), |
||||
|
// 内容区域 |
||||
|
SafeArea( |
||||
|
child: SingleChildScrollView( |
||||
|
padding: const EdgeInsets.symmetric(horizontal: 20.0), |
||||
|
child: Column( |
||||
children: [ |
children: [ |
||||
Container( |
|
||||
width: 85, |
|
||||
height: 85, |
|
||||
decoration: const BoxDecoration( |
|
||||
shape: BoxShape.circle, |
|
||||
color: Colors.grey, |
|
||||
|
// 标题 |
||||
|
const SizedBox(height: 20), |
||||
|
const Text( |
||||
|
'完善信息', |
||||
|
style: TextStyle( |
||||
|
fontSize: 20, |
||||
|
fontWeight: FontWeight.bold, |
||||
|
color: Color.fromRGBO(51, 51, 51, 1), |
||||
), |
), |
||||
child: ClipOval( |
|
||||
// 这里可以替换为实际的头像图片 |
|
||||
child: Image.asset( |
|
||||
Assets.imagesAvatarsExample, |
|
||||
fit: BoxFit.cover, |
|
||||
|
), |
||||
|
const SizedBox(height: 25), |
||||
|
|
||||
|
// 头像 |
||||
|
GestureDetector( |
||||
|
onTap: () => _showAvatarOptions(controller), |
||||
|
child: Stack( |
||||
|
children: [ |
||||
|
Container( |
||||
|
width: 85, |
||||
|
height: 85, |
||||
|
decoration: const BoxDecoration( |
||||
|
shape: BoxShape.circle, |
||||
|
color: Colors.grey, |
||||
|
), |
||||
|
child: ClipOval( |
||||
|
// 根据avatarUrl显示头像,如果为空则显示默认图片 |
||||
|
child: controller.avatarUrl.value.isNotEmpty |
||||
|
? Image.file( |
||||
|
File(controller.avatarUrl.value), |
||||
|
fit: BoxFit.cover, |
||||
|
width: 85, |
||||
|
height: 85, |
||||
|
) |
||||
|
: Image.asset( |
||||
|
Assets.imagesAvatarsExample, |
||||
|
fit: BoxFit.cover, |
||||
|
), |
||||
|
), |
||||
|
), |
||||
|
Positioned( |
||||
|
bottom: 0, |
||||
|
right: 0, |
||||
|
child: Container( |
||||
|
width: 20, |
||||
|
height: 20, |
||||
|
child: Stack( |
||||
|
children: [ |
||||
|
Center( |
||||
|
child: Image.asset( |
||||
|
Assets.imagesBgEditAvatars, |
||||
|
), |
||||
|
), |
||||
|
Center( |
||||
|
child: Image.asset( |
||||
|
Assets.imagesEditAvatarsIcon, |
||||
|
width: 12, |
||||
|
height: 12, |
||||
|
), |
||||
|
), |
||||
|
], |
||||
|
), |
||||
|
), |
||||
|
), |
||||
|
], |
||||
|
), |
||||
|
), |
||||
|
const SizedBox(height: 25), |
||||
|
|
||||
|
// 性别选择 |
||||
|
const Align( |
||||
|
alignment: Alignment.centerLeft, |
||||
|
child: Text( |
||||
|
'性别 (注册后不可修改)', |
||||
|
style: TextStyle( |
||||
|
fontSize: 15, |
||||
|
color: Color.fromRGBO(144, 144, 144, 1), |
||||
|
fontWeight: FontWeight.w500, |
||||
), |
), |
||||
), |
), |
||||
), |
), |
||||
Positioned( |
|
||||
bottom: 0, |
|
||||
right: 0, |
|
||||
child: Container( |
|
||||
width: 30, |
|
||||
height: 30, |
|
||||
decoration: const BoxDecoration( |
|
||||
shape: BoxShape.circle, |
|
||||
color: Colors.blue, |
|
||||
|
const SizedBox(height: 10), |
||||
|
Row( |
||||
|
children: [ |
||||
|
GestureDetector( |
||||
|
onTap: () => controller.selectGender('male'), |
||||
|
child: Container( |
||||
|
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), |
||||
|
decoration: BoxDecoration( |
||||
|
borderRadius: BorderRadius.circular(20), |
||||
|
color: controller.gender.value == 'male' ? Colors.black : Colors.grey[200], |
||||
|
), |
||||
|
child: Row( |
||||
|
children: [ |
||||
|
Image.asset( |
||||
|
Assets.imagesManIcon, |
||||
|
width: 20, |
||||
|
height: 20, |
||||
|
color: controller.gender.value == 'male' ? Colors.white : Colors.black, |
||||
|
), |
||||
|
const SizedBox(width: 5), |
||||
|
], |
||||
|
), |
||||
|
), |
||||
), |
), |
||||
child: Image.asset( |
|
||||
Assets.imagesBgEditAvatars, |
|
||||
width: 16, |
|
||||
height: 16, |
|
||||
color: Colors.white, |
|
||||
|
const SizedBox(width: 20), |
||||
|
GestureDetector( |
||||
|
onTap: () => controller.selectGender('female'), |
||||
|
child: Container( |
||||
|
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), |
||||
|
decoration: BoxDecoration( |
||||
|
borderRadius: BorderRadius.circular(20), |
||||
|
color: controller.gender.value == 'female' ? Colors.pink : Colors.grey[200], |
||||
|
), |
||||
|
child: Row( |
||||
|
children: [ |
||||
|
Image.asset( |
||||
|
Assets.imagesWomenIcon, |
||||
|
width: 20, |
||||
|
height: 20, |
||||
|
color: controller.gender.value == 'female' ? Colors.white : Colors.black, |
||||
|
), |
||||
|
const SizedBox(width: 5), |
||||
|
], |
||||
|
), |
||||
), |
), |
||||
|
), |
||||
|
], |
||||
|
), |
||||
|
const SizedBox(height: 25), |
||||
|
|
||||
|
// 昵称 |
||||
|
const Align( |
||||
|
alignment: Alignment.centerLeft, |
||||
|
child: Text( |
||||
|
'昵称', |
||||
|
style: TextStyle( |
||||
|
fontSize: 15, |
||||
|
color: Color.fromRGBO(144, 144, 144, 1), |
||||
|
fontWeight: FontWeight.w500, |
||||
|
), |
||||
), |
), |
||||
), |
), |
||||
], |
|
||||
), |
|
||||
const SizedBox(height: 30), |
|
||||
|
|
||||
// 性别选择 |
|
||||
const Align( |
|
||||
alignment: Alignment.centerLeft, |
|
||||
child: Text( |
|
||||
'性别 (注册后不可修改)', |
|
||||
style: TextStyle( |
|
||||
fontSize: 16, |
|
||||
color: Color.fromRGBO(144, 144, 144, 1), |
|
||||
fontWeight: FontWeight.w500, |
|
||||
|
const SizedBox(height: 10), |
||||
|
Container( |
||||
|
padding: const EdgeInsets.symmetric(horizontal: 16), |
||||
|
decoration: BoxDecoration( |
||||
|
borderRadius: BorderRadius.circular(32), |
||||
|
color: Color.fromRGBO(247, 247, 247, 1), |
||||
|
), |
||||
|
child: Row( |
||||
|
children: [ |
||||
|
Expanded( |
||||
|
child: TextField( |
||||
|
controller: TextEditingController(text: controller.nickname.value), |
||||
|
onChanged: (value) { |
||||
|
controller.nickname.value = value; |
||||
|
}, |
||||
|
decoration: const InputDecoration( |
||||
|
border: InputBorder.none, |
||||
|
hintText: '请输入昵称', |
||||
|
hintStyle: TextStyle(color: Colors.grey), |
||||
|
), |
||||
|
), |
||||
|
), |
||||
|
], |
||||
|
), |
||||
), |
), |
||||
), |
|
||||
), |
|
||||
const SizedBox(height: 10), |
|
||||
Row( |
|
||||
children: [ |
|
||||
|
const SizedBox(height: 25), |
||||
|
|
||||
|
// 出生日期 |
||||
|
const Align( |
||||
|
alignment: Alignment.centerLeft, |
||||
|
child: Text( |
||||
|
'年龄', |
||||
|
style: TextStyle( |
||||
|
fontSize: 15, |
||||
|
color: Color.fromRGBO(144, 144, 144, 1), |
||||
|
fontWeight: FontWeight.w500, |
||||
|
), |
||||
|
), |
||||
|
), |
||||
|
const SizedBox(height: 10), |
||||
GestureDetector( |
GestureDetector( |
||||
onTap: () { |
onTap: () { |
||||
setState(() { |
|
||||
_gender = 'male'; |
|
||||
}); |
|
||||
|
// 显示Cupertino日期选择器 |
||||
|
_showDatePicker(controller); |
||||
}, |
}, |
||||
child: Container( |
child: Container( |
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), |
|
||||
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), |
||||
decoration: BoxDecoration( |
decoration: BoxDecoration( |
||||
borderRadius: BorderRadius.circular(20), |
|
||||
color: _gender == 'male' ? Colors.black : Colors.grey[200], |
|
||||
|
borderRadius: BorderRadius.circular(32), |
||||
|
color: Color.fromRGBO(247, 247, 247, 1), |
||||
), |
), |
||||
child: Row( |
child: Row( |
||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween, |
||||
children: [ |
children: [ |
||||
Image.asset( |
|
||||
Assets.imagesManIcon, |
|
||||
width: 20, |
|
||||
height: 20, |
|
||||
color: _gender == 'male' ? Colors.white : Colors.black, |
|
||||
|
Text( |
||||
|
controller.birthday.value.isEmpty ? '请选择你的出生日期' : controller.birthday.value, |
||||
|
style: TextStyle( |
||||
|
color: controller.birthday.value.isEmpty ? Colors.grey : Colors.black, |
||||
|
), |
||||
), |
), |
||||
const SizedBox(width: 5), |
|
||||
|
const Icon(Icons.arrow_forward_ios, color: Colors.grey), |
||||
], |
], |
||||
), |
), |
||||
), |
), |
||||
), |
), |
||||
const SizedBox(width: 20), |
|
||||
|
const SizedBox(height: 25), |
||||
|
|
||||
|
// 学历 |
||||
|
const Align( |
||||
|
alignment: Alignment.centerLeft, |
||||
|
child: Text( |
||||
|
'学历', |
||||
|
style: TextStyle( |
||||
|
fontSize: 15, |
||||
|
color: Color.fromRGBO(144, 144, 144, 1), |
||||
|
fontWeight: FontWeight.w500, |
||||
|
), |
||||
|
), |
||||
|
), |
||||
|
const SizedBox(height: 10), |
||||
GestureDetector( |
GestureDetector( |
||||
onTap: () { |
onTap: () { |
||||
setState(() { |
|
||||
_gender = 'female'; |
|
||||
}); |
|
||||
|
// 显示学历选择弹框 |
||||
|
_showEducationPicker(controller); |
||||
}, |
}, |
||||
child: Container( |
child: Container( |
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), |
|
||||
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), |
||||
decoration: BoxDecoration( |
decoration: BoxDecoration( |
||||
borderRadius: BorderRadius.circular(20), |
|
||||
color: _gender == 'female' ? Colors.pink : Colors.grey[200], |
|
||||
|
borderRadius: BorderRadius.circular(32), |
||||
|
color: Color.fromRGBO(247, 247, 247, 1), |
||||
), |
), |
||||
child: Row( |
child: Row( |
||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween, |
||||
children: [ |
children: [ |
||||
Image.asset( |
|
||||
Assets.imagesWomenIcon, |
|
||||
width: 20, |
|
||||
height: 20, |
|
||||
color: _gender == 'female' ? Colors.white : Colors.black, |
|
||||
|
Text( |
||||
|
controller.education.value.isEmpty ? '请选择你的学历' : controller.education.value, |
||||
|
style: TextStyle( |
||||
|
color: controller.education.value.isEmpty ? Colors.grey : Colors.black, |
||||
|
), |
||||
), |
), |
||||
const SizedBox(width: 5), |
|
||||
|
const Icon(Icons.arrow_forward_ios, color: Colors.grey), |
||||
], |
], |
||||
), |
), |
||||
), |
), |
||||
), |
), |
||||
], |
|
||||
), |
|
||||
const SizedBox(height: 30), |
|
||||
|
|
||||
// 昵称 |
|
||||
const Align( |
|
||||
alignment: Alignment.centerLeft, |
|
||||
child: Text( |
|
||||
'昵称', |
|
||||
style: TextStyle( |
|
||||
fontSize: 16, |
|
||||
color: Colors.black, |
|
||||
fontWeight: FontWeight.w500, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
const SizedBox(height: 10), |
|
||||
Container( |
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16), |
|
||||
decoration: BoxDecoration( |
|
||||
borderRadius: BorderRadius.circular(8), |
|
||||
color: Colors.white, |
|
||||
), |
|
||||
child: Row( |
|
||||
children: [ |
|
||||
Expanded( |
|
||||
child: TextField( |
|
||||
controller: TextEditingController(text: _nickname), |
|
||||
onChanged: (value) { |
|
||||
_nickname = value; |
|
||||
}, |
|
||||
decoration: const InputDecoration( |
|
||||
border: InputBorder.none, |
|
||||
), |
|
||||
|
const SizedBox(height: 25), |
||||
|
|
||||
|
// 邀请码 |
||||
|
const Align( |
||||
|
alignment: Alignment.centerLeft, |
||||
|
child: Text( |
||||
|
'邀请码 (非必填)', |
||||
|
style: TextStyle( |
||||
|
fontSize: 15, |
||||
|
color: Color.fromRGBO(144, 144, 144, 1), |
||||
|
fontWeight: FontWeight.w500, |
||||
), |
), |
||||
), |
), |
||||
TextButton( |
|
||||
onPressed: () { |
|
||||
// 随机昵称功能(不需要实现) |
|
||||
|
), |
||||
|
const SizedBox(height: 10), |
||||
|
Container( |
||||
|
padding: const EdgeInsets.symmetric(horizontal: 16), |
||||
|
decoration: BoxDecoration( |
||||
|
borderRadius: BorderRadius.circular(32), |
||||
|
color: Color.fromRGBO(247, 247, 247, 1), |
||||
|
), |
||||
|
child: TextField( |
||||
|
onChanged: (value) { |
||||
|
controller.invitationCode.value = value; |
||||
}, |
}, |
||||
child: const Text( |
|
||||
'随机', |
|
||||
style: TextStyle( |
|
||||
color: Colors.grey, |
|
||||
), |
|
||||
|
decoration: const InputDecoration( |
||||
|
hintText: '请输入邀请码', |
||||
|
hintStyle: TextStyle(color: Colors.grey), |
||||
|
border: InputBorder.none, |
||||
), |
), |
||||
), |
), |
||||
], |
|
||||
), |
|
||||
), |
|
||||
const SizedBox(height: 30), |
|
||||
|
|
||||
// 出生日期 |
|
||||
const Align( |
|
||||
alignment: Alignment.centerLeft, |
|
||||
child: Text( |
|
||||
'年龄', |
|
||||
style: TextStyle( |
|
||||
fontSize: 16, |
|
||||
color: Colors.black, |
|
||||
fontWeight: FontWeight.w500, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
const SizedBox(height: 10), |
|
||||
GestureDetector( |
|
||||
onTap: () { |
|
||||
// 打开日期选择器 |
|
||||
_selectDate(); |
|
||||
}, |
|
||||
child: Container( |
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), |
|
||||
decoration: BoxDecoration( |
|
||||
borderRadius: BorderRadius.circular(8), |
|
||||
color: Colors.white, |
|
||||
), |
|
||||
child: Row( |
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween, |
|
||||
children: [ |
|
||||
Text( |
|
||||
_birthday.isEmpty ? '请选择你的出生日期' : _birthday, |
|
||||
style: TextStyle( |
|
||||
color: _birthday.isEmpty ? Colors.grey : Colors.black, |
|
||||
), |
|
||||
), |
|
||||
const Icon(Icons.arrow_forward_ios, color: Colors.grey), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
const SizedBox(height: 30), |
|
||||
|
|
||||
// 学历 |
|
||||
const Align( |
|
||||
alignment: Alignment.centerLeft, |
|
||||
child: Text( |
|
||||
'学历', |
|
||||
style: TextStyle( |
|
||||
fontSize: 16, |
|
||||
color: Colors.black, |
|
||||
fontWeight: FontWeight.w500, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
const SizedBox(height: 10), |
|
||||
GestureDetector( |
|
||||
onTap: () { |
|
||||
// 打开学历选择器 |
|
||||
_selectEducation(); |
|
||||
}, |
|
||||
child: Container( |
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), |
|
||||
decoration: BoxDecoration( |
|
||||
borderRadius: BorderRadius.circular(8), |
|
||||
color: Colors.white, |
|
||||
), |
), |
||||
child: Row( |
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween, |
|
||||
children: [ |
|
||||
Text( |
|
||||
_education.isEmpty ? '请选择你的学历' : _education, |
|
||||
style: TextStyle( |
|
||||
color: _education.isEmpty ? Colors.grey : Colors.black, |
|
||||
|
const SizedBox(height: 50), |
||||
|
|
||||
|
// 开始交友按钮 |
||||
|
SizedBox( |
||||
|
width: double.infinity, |
||||
|
height: 50, |
||||
|
child: ElevatedButton( |
||||
|
onPressed: controller.isSubmitting.value |
||||
|
? null |
||||
|
: controller.submitUserInfo, |
||||
|
style: ElevatedButton.styleFrom( |
||||
|
backgroundColor: Colors.blue, |
||||
|
shape: RoundedRectangleBorder( |
||||
|
borderRadius: BorderRadius.circular(25), |
||||
), |
), |
||||
), |
), |
||||
const Icon(Icons.arrow_forward_ios, color: Colors.grey), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
const SizedBox(height: 30), |
|
||||
|
|
||||
// 邀请码 |
|
||||
const Align( |
|
||||
alignment: Alignment.centerLeft, |
|
||||
child: Text( |
|
||||
'邀请码 (非必填)', |
|
||||
style: TextStyle( |
|
||||
fontSize: 16, |
|
||||
color: Colors.black, |
|
||||
fontWeight: FontWeight.w500, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
const SizedBox(height: 10), |
|
||||
Container( |
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16), |
|
||||
decoration: BoxDecoration( |
|
||||
borderRadius: BorderRadius.circular(8), |
|
||||
color: Colors.white, |
|
||||
), |
|
||||
child: TextField( |
|
||||
onChanged: (value) { |
|
||||
_invitationCode = value; |
|
||||
}, |
|
||||
decoration: const InputDecoration( |
|
||||
hintText: '请输入邀请码', |
|
||||
hintStyle: TextStyle(color: Colors.grey), |
|
||||
border: InputBorder.none, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
const SizedBox(height: 50), |
|
||||
|
|
||||
// 开始交友按钮 |
|
||||
SizedBox( |
|
||||
width: double.infinity, |
|
||||
height: 50, |
|
||||
child: ElevatedButton( |
|
||||
onPressed: () { |
|
||||
// 提交用户信息 |
|
||||
_submitUserInfo(); |
|
||||
}, |
|
||||
style: ElevatedButton.styleFrom( |
|
||||
backgroundColor: Colors.blue, |
|
||||
shape: RoundedRectangleBorder( |
|
||||
borderRadius: BorderRadius.circular(25), |
|
||||
), |
|
||||
), |
|
||||
child: const Text( |
|
||||
'开始交友', |
|
||||
style: TextStyle( |
|
||||
fontSize: 18, |
|
||||
color: Colors.white, |
|
||||
|
child: controller.isSubmitting.value |
||||
|
? const SizedBox( |
||||
|
width: 20, |
||||
|
height: 20, |
||||
|
child: CircularProgressIndicator( |
||||
|
color: Colors.white, |
||||
|
strokeWidth: 2, |
||||
|
), |
||||
|
) |
||||
|
: const Text( |
||||
|
'开始交友', |
||||
|
style: TextStyle( |
||||
|
fontSize: 18, |
||||
|
color: Colors.white, |
||||
|
), |
||||
|
), |
||||
), |
), |
||||
), |
), |
||||
), |
|
||||
|
const SizedBox(height: 30), |
||||
|
], |
||||
), |
), |
||||
const SizedBox(height: 30), |
|
||||
], |
|
||||
|
), |
||||
), |
), |
||||
), |
|
||||
|
], |
||||
), |
), |
||||
], |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
// 选择日期 |
|
||||
void _selectDate() { |
|
||||
// 这里可以实现日期选择逻辑 |
|
||||
// 为了简单起见,我们先设置一个默认日期 |
|
||||
setState(() { |
|
||||
_birthday = '1990-01-01'; |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
// 选择学历 |
|
||||
void _selectEducation() { |
|
||||
// 这里可以实现学历选择逻辑 |
|
||||
// 为了简单起见,我们先设置一个默认学历 |
|
||||
setState(() { |
|
||||
_education = '本科'; |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
// 提交用户信息 |
|
||||
void _submitUserInfo() { |
|
||||
// 这里可以实现提交逻辑 |
|
||||
print('提交用户信息:'); |
|
||||
print('性别: $_gender'); |
|
||||
print('昵称: $_nickname'); |
|
||||
print('生日: $_birthday'); |
|
||||
print('学历: $_education'); |
|
||||
print('邀请码: $_invitationCode'); |
|
||||
|
|
||||
// 可以添加验证逻辑 |
|
||||
if (_birthday.isEmpty || _education.isEmpty) { |
|
||||
ScaffoldMessenger.of(context).showSnackBar( |
|
||||
const SnackBar(content: Text('请完善所有必填信息')), |
|
||||
); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
// 提交成功后跳转到下一个页面 |
|
||||
ScaffoldMessenger.of(context).showSnackBar( |
|
||||
const SnackBar(content: Text('信息提交成功')), |
|
||||
|
); |
||||
|
}, |
||||
); |
); |
||||
} |
} |
||||
} |
} |
||||
Write
Preview
Loading…
Cancel
Save