You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

660 lines
27 KiB

import 'package:cached_network_image/cached_network_image.dart';
import 'package:dating_touchme_app/extension/ex_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:dating_touchme_app/generated/assets.dart';
import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:dating_touchme_app/controller/mine/user_info_controller.dart';
import 'package:tdesign_flutter/tdesign_flutter.dart';
class UserInfoPage extends StatefulWidget {
const UserInfoPage({super.key});
@override
_UserInfoState createState() => _UserInfoState();
}
class _UserInfoState extends State<UserInfoPage> {
// 显示头像选择选项
void _showAvatarPopup(UserInfoController controller){
Navigator.of(Get.context!).push(
TDSlidePopupRoute(
slideTransitionFrom: SlideTransitionFrom.bottom,
builder: (context) {
return Container(
// color: TDTheme.of(context).bgColorContainer,
height: 490,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(12.0),
topRight: Radius.circular(12.0),
),
),
child: Column(
children: [
const SizedBox(height: 4),
CachedNetworkImage(imageUrl: 'https://dating-agency-prod.oss-cn-shenzhen.aliyuncs.com/1A437A945667.jpg', width: 375, height: 314),
const TDDivider(),
TDCell(
arrow: false,
titleWidget: Center(
child: Text('拍照', style: TextStyle(fontSize: 16.w, color: const Color.fromRGBO(51, 51, 51, 1))),
),
onClick: (cell) async{
Navigator.pop(context);
await controller.handleCameraCapture();
},
),
const TDDivider(),
TDCell(
arrow: false,
titleWidget: Center(
child: Text('从相册选择'),
),
onClick: (cell) async{
Navigator.pop(context);
await controller.handleGallerySelection();
},
),
Expanded(
child: Container(
color: Color(0xFFF3F3F3),
),
),
TDCell(
arrow: false,
titleWidget: Center(
child: Text('取消'),
),
onClick: (cell){
Navigator.pop(context);
},
),
],
),
);
}),
);
}
// 显示学历选择弹框
Future _showEducationPicker(UserInfoController controller) async {
final List<String> educationOptions = ['大专以下', '大专', '本科', '硕士及以上'];
await 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,
),
),
],
),
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: 20),
],
),
);
},
);
}
// 显示日期选择器
Future _showDatePicker(UserInfoController controller) async {
// 计算最小和最大日期
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;
await 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,
),
),
],
),
);
},
);
}
final FocusNode _blankFocusNode = FocusNode();
final TextEditingController _controller = TextEditingController();
final FocusNode _blankFocusNode2 = FocusNode();
final TextEditingController _controller2 = TextEditingController();
@override
Widget build(BuildContext context) {
return GetX<UserInfoController>(
init: UserInfoController(),
builder: (controller) {
return Scaffold(
extendBodyBehindAppBar: true,
appBar: AppBar(
title: const Text(
'完善信息',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Color.fromRGBO(51, 51, 51, 1),
),
),
backgroundColor: Colors.transparent,
shadowColor: Colors.transparent,
elevation: 0,
centerTitle: true,
automaticallyImplyLeading: false,
),
body: Stack(
children: [
// 背景图 - 覆盖全页面包括AppBar
Image.asset(
Assets.imagesBgInformation,
fit: BoxFit.cover,
width: double.infinity,
height: double.infinity,
),
// 内容区域,顶部留出AppBar高度空间
SafeArea(
child: SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
child: Column(
children: [
const SizedBox(height: 25),
// 头像
GestureDetector(
onTap: () => _showAvatarPopup(controller),
child: Stack(
children: [
Container(
width: 85,
height: 85,
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Colors.grey,
),
child: ClipOval(
child: (controller.avatarUrl.value.startsWith('http')
? Image.network(
controller.avatarUrl.value,
fit: BoxFit.cover,
)
: Image.asset(
Assets.imagesUserAvatar,
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,
),
),
),
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),
],
),
),
),
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: 20),
// 昵称
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),
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(
focusNode: _blankFocusNode,
controller: _controller,
onChanged: (value) {
controller.nickname.value = value;
},
onTapOutside: (e){
_blankFocusNode.unfocus();
},
onTap: (){
// 确保光标在文本末尾
WidgetsBinding.instance.addPostFrameCallback((_) {
_controller.selection = TextSelection.fromPosition(
TextPosition(offset: _controller.text.length),
);
});
},
decoration: const InputDecoration(
border: InputBorder.none,
hintText: '请输入昵称',
hintStyle: TextStyle(color: Colors.grey),
),
),
),
],
),
),
const SizedBox(height: 20),
// 出生日期
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(
onTap: () async {
// FocusScope.of(context).requestFocus(_blankFocusNode);
// 显示Cupertino日期选择器
// _blankFocusNode.unfocus();
await _showDatePicker(controller);
},
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(32),
color: Color.fromRGBO(247, 247, 247, 1),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
controller.birthday.value.isEmpty ? '请选择你的出生日期' : controller.birthday.value,
style: TextStyle(
color: controller.birthday.value.isEmpty ? Colors.grey : Colors.black,
),
),
Image.asset(
Assets.imagesArrowForwardRight,
width: 5,
height: 10,
),
],
),
),
),
const SizedBox(height: 20),
// 学历
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(
onTap: () async {
// FocusScope.of(context).requestFocus(_blankFocusNode);
// _blankFocusNode.unfocus();
// 显示学历选择弹框
await _showEducationPicker(controller);
},
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(32),
color: Color.fromRGBO(247, 247, 247, 1),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
controller.education.value.isEmpty ? '请选择你的学历' : controller.education.value,
style: TextStyle(
color: controller.education.value.isEmpty ? Colors.grey : Colors.black,
),
),
Image.asset(
Assets.imagesArrowForwardRight,
width: 5,
height: 10,
),
],
),
),
),
const SizedBox(height: 20),
// 邀请码
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),
Container(
padding: const EdgeInsets.symmetric(horizontal: 16),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(32),
color: Color.fromRGBO(247, 247, 247, 1),
),
child: TextField(
focusNode: _blankFocusNode2,
controller: _controller2,
onChanged: (value) {
controller.invitationCode.value = value;
},
onTapOutside: (e){
_blankFocusNode2.unfocus();
},
onTap: (){
// 确保光标在文本末尾
WidgetsBinding.instance.addPostFrameCallback((_) {
_controller2.selection = TextSelection.fromPosition(
TextPosition(offset: _controller2.text.length),
);
});
},
decoration: const InputDecoration(
hintText: '请输入邀请码',
hintStyle: TextStyle(color: Colors.grey),
border: InputBorder.none,
),
),
),
const SizedBox(height: 24),
// 开始交友按钮
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),
),
),
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: 20),
],
),
),
),
],
),
);
},
);
}
@override
void dispose() {
// TODO: implement dispose
super.dispose();
_blankFocusNode.dispose();
_controller.dispose();
_blankFocusNode2.dispose();
_controller2.dispose();
}
}