2025-09-17 15:32:18 +08:00
|
|
|
|
import 'dart:io';
|
|
|
|
|
|
2025-08-21 10:50:38 +08:00
|
|
|
|
import 'package:bottom_picker/bottom_picker.dart';
|
|
|
|
|
import 'package:city_pickers/city_pickers.dart';
|
|
|
|
|
import 'package:flutter/cupertino.dart';
|
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
|
import 'package:get/get.dart';
|
2025-09-17 15:32:18 +08:00
|
|
|
|
import 'package:image_cropper/image_cropper.dart';
|
2025-08-21 10:50:38 +08:00
|
|
|
|
import 'package:loopin/IM/controller/im_user_info_controller.dart';
|
|
|
|
|
import 'package:loopin/api/common_api.dart';
|
2025-09-03 11:25:31 +08:00
|
|
|
|
import 'package:loopin/components/network_or_asset_image.dart';
|
2025-08-21 10:50:38 +08:00
|
|
|
|
import 'package:loopin/service/http.dart';
|
|
|
|
|
import 'package:loopin/styles/index.dart';
|
2025-09-17 15:32:18 +08:00
|
|
|
|
import 'package:loopin/utils/image_utils.dart';
|
2025-08-21 10:50:38 +08:00
|
|
|
|
import 'package:loopin/utils/index.dart';
|
2025-09-13 17:01:01 +08:00
|
|
|
|
import 'package:loopin/utils/permissions.dart';
|
2025-08-21 10:50:38 +08:00
|
|
|
|
import 'package:loopin/utils/wxsdk.dart';
|
|
|
|
|
import 'package:shirne_dialog/shirne_dialog.dart';
|
|
|
|
|
import 'package:wechat_assets_picker/wechat_assets_picker.dart';
|
|
|
|
|
|
|
|
|
|
class UserInfo extends StatefulWidget {
|
|
|
|
|
const UserInfo({super.key});
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
State<UserInfo> createState() => _UserInfoState();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class _UserInfoState extends State<UserInfo> {
|
|
|
|
|
final userInfoController = Get.find<ImUserInfoController>();
|
|
|
|
|
late List<Map<String, dynamic>> items;
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
void initState() {
|
|
|
|
|
super.initState();
|
|
|
|
|
items = [
|
|
|
|
|
{
|
|
|
|
|
'title': '昵称',
|
|
|
|
|
'value': userInfoController.nickname,
|
|
|
|
|
'onTap': () => Get.toNamed('/nickName'),
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
'title': '简介',
|
|
|
|
|
'value': userInfoController.signature,
|
|
|
|
|
'onTap': () => Get.toNamed('/des'),
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
'title': '性别',
|
|
|
|
|
'value': userInfoController.gender,
|
|
|
|
|
'onTap': () => setGender(context),
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
'title': '生日',
|
|
|
|
|
'value': userInfoController.birthday,
|
|
|
|
|
'onTap': () => selectDate(context),
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
'title': '区域',
|
|
|
|
|
'value': userInfoController.customInfo,
|
|
|
|
|
'onTap': () => pickCity(),
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
'title': '绑定微信',
|
|
|
|
|
'value': userInfoController.customInfo,
|
|
|
|
|
'onTap': () => wechatLogin(),
|
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 微信授权
|
|
|
|
|
Future<void> wechatLogin() async {
|
|
|
|
|
await Wxsdk.login();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 选性别
|
|
|
|
|
void setGender(BuildContext context) {
|
|
|
|
|
showModalBottomSheet(
|
|
|
|
|
context: context,
|
|
|
|
|
isScrollControlled: true,
|
|
|
|
|
backgroundColor: Colors.transparent,
|
|
|
|
|
builder: (BuildContext context) {
|
|
|
|
|
return SafeArea(
|
|
|
|
|
child: Container(
|
|
|
|
|
height: 300,
|
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
|
color: Colors.white,
|
|
|
|
|
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
|
|
|
|
|
),
|
|
|
|
|
child: Column(
|
|
|
|
|
children: [
|
|
|
|
|
Expanded(
|
|
|
|
|
child: Column(
|
|
|
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
|
|
|
children: [
|
|
|
|
|
ListTile(
|
|
|
|
|
title: Center(child: Text('男')),
|
|
|
|
|
onTap: () {
|
|
|
|
|
userInfoController.gender.value = 1;
|
|
|
|
|
userInfoController.updateGender();
|
|
|
|
|
Get.back();
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
Divider(),
|
|
|
|
|
ListTile(
|
|
|
|
|
title: Center(child: Text('女')),
|
|
|
|
|
onTap: () {
|
|
|
|
|
userInfoController.gender.value = 2;
|
|
|
|
|
userInfoController.updateGender();
|
|
|
|
|
Get.back();
|
|
|
|
|
}),
|
|
|
|
|
Divider(),
|
|
|
|
|
ListTile(
|
|
|
|
|
title: Center(child: Text('保密')),
|
|
|
|
|
onTap: () {
|
|
|
|
|
userInfoController.gender.value = 0;
|
|
|
|
|
userInfoController.updateGender();
|
|
|
|
|
Get.back();
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
Container(
|
|
|
|
|
height: 10,
|
|
|
|
|
color: Colors.grey[200],
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
ListTile(
|
|
|
|
|
title: Center(child: Text('取消')),
|
|
|
|
|
onTap: () => Get.back(),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 选生日
|
|
|
|
|
void selectDate(BuildContext context) {
|
|
|
|
|
DateTime selectedDate = DateTime.now(); // 初始值设为当前时间
|
|
|
|
|
BottomPicker.date(
|
|
|
|
|
pickerTitle: Row(
|
|
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
|
|
|
children: [
|
|
|
|
|
TextButton(
|
|
|
|
|
onPressed: () {
|
|
|
|
|
Get.back();
|
|
|
|
|
},
|
|
|
|
|
child: Text('取消'),
|
|
|
|
|
),
|
|
|
|
|
TextButton(
|
|
|
|
|
onPressed: () {
|
|
|
|
|
// 获取选择结果逻辑
|
|
|
|
|
final dateStr = "${selectedDate.year}-${selectedDate.month.toString().padLeft(2, '0')}-${selectedDate.day.toString().padLeft(2, '0')}";
|
|
|
|
|
print(dateStr);
|
|
|
|
|
DateTime birthdayDate = DateTime(selectedDate.year, selectedDate.month, selectedDate.day);
|
|
|
|
|
userInfoController.birthday.value = birthdayDate.millisecondsSinceEpoch ~/ 1000; //秒级时间戳
|
|
|
|
|
userInfoController.updateBirthday();
|
|
|
|
|
Get.back();
|
|
|
|
|
},
|
|
|
|
|
child: Text('确认'),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
displaySubmitButton: false,
|
|
|
|
|
displayCloseIcon: false,
|
|
|
|
|
dismissable: true, // 允许点击空白关闭
|
|
|
|
|
initialDateTime: DateTime.now(),
|
|
|
|
|
minDateTime: DateTime(1900),
|
|
|
|
|
maxDateTime: DateTime(
|
|
|
|
|
DateTime.now().year,
|
|
|
|
|
DateTime.now().month,
|
|
|
|
|
DateTime.now().day,
|
|
|
|
|
23,
|
|
|
|
|
59,
|
|
|
|
|
59,
|
|
|
|
|
),
|
|
|
|
|
dateOrder: DatePickerDateOrder.ymd,
|
|
|
|
|
pickerTextStyle: const TextStyle(fontSize: 16, color: Colors.black87),
|
|
|
|
|
height: 300,
|
|
|
|
|
onChange: (date) {
|
|
|
|
|
selectedDate = date as DateTime;
|
|
|
|
|
},
|
|
|
|
|
).show(context);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
///选所在地
|
|
|
|
|
void pickCity() async {
|
|
|
|
|
final result = await CityPickers.showCityPicker(
|
|
|
|
|
context: context,
|
|
|
|
|
showType: ShowType.pca, // 显示省市区
|
|
|
|
|
height: 300.0,
|
|
|
|
|
borderRadius: 16.0,
|
|
|
|
|
barrierDismissible: true,
|
|
|
|
|
theme: Theme.of(context).copyWith(
|
|
|
|
|
scaffoldBackgroundColor: Colors.white,
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
if (result != null) {
|
|
|
|
|
final areaName = '${result.provinceName}-${result.cityName}-${result.areaName}';
|
|
|
|
|
print(result.toString());
|
|
|
|
|
print('${result.provinceName}-${result.cityName}-${result.areaName}-${result.areaId}');
|
|
|
|
|
//修改
|
|
|
|
|
userInfoController.customInfo['area'] = areaName;
|
|
|
|
|
userInfoController.customInfo['areaCode'] = '${result.areaId}';
|
|
|
|
|
userInfoController.updateArea();
|
|
|
|
|
userInfoController.customInfo.refresh();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
///选背景
|
|
|
|
|
void pickCover(BuildContext context) async {
|
2025-09-13 17:01:01 +08:00
|
|
|
|
final hasPer = await Permissions.requestPhotoPermission();
|
|
|
|
|
if (!hasPer) {
|
2025-09-17 15:32:18 +08:00
|
|
|
|
Permissions.showPermissionDialog('相册');
|
2025-09-13 17:01:01 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
2025-08-21 10:50:38 +08:00
|
|
|
|
final pickedAssets = await AssetPicker.pickAssets(
|
|
|
|
|
context,
|
|
|
|
|
pickerConfig: AssetPickerConfig(
|
|
|
|
|
textDelegate: const AssetPickerTextDelegate(),
|
|
|
|
|
pathNameBuilder: (AssetPathEntity album) {
|
|
|
|
|
return Utils.translateAlbumName(album);
|
|
|
|
|
},
|
|
|
|
|
maxAssets: 1,
|
|
|
|
|
requestType: RequestType.image,
|
|
|
|
|
filterOptions: FilterOptionGroup(
|
|
|
|
|
imageOption: const FilterOption(),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (pickedAssets != null && pickedAssets.isNotEmpty) {
|
|
|
|
|
final asset = pickedAssets.first;
|
|
|
|
|
final file = await asset.file; // 获取实际文件
|
|
|
|
|
if (file != null) {
|
|
|
|
|
final fileSizeInBytes = await file.length();
|
|
|
|
|
final sizeInMB = fileSizeInBytes / (1024 * 1024);
|
2025-09-13 17:01:01 +08:00
|
|
|
|
if (sizeInMB > 200) {
|
|
|
|
|
MyDialog.toast('图片大小不能超过200MB', icon: const Icon(Icons.check_circle), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
|
2025-08-21 10:50:38 +08:00
|
|
|
|
} else {
|
2025-09-13 17:01:01 +08:00
|
|
|
|
print("视频合法,大小:$sizeInMB MB");
|
2025-08-21 10:50:38 +08:00
|
|
|
|
//走upload(file)上传图片拿到url地址
|
|
|
|
|
final istance = MyDialog.loading('上传中');
|
|
|
|
|
final res = await Http.upload(CommonApi.uploadFile, filePath: file.path);
|
|
|
|
|
userInfoController.customInfo['coverBg'] = res['data']['url'];
|
|
|
|
|
userInfoController.updateCover();
|
|
|
|
|
userInfoController.customInfo.refresh();
|
|
|
|
|
istance.close();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
///选头像
|
|
|
|
|
void pickFaceUrl(BuildContext context) async {
|
2025-09-13 17:01:01 +08:00
|
|
|
|
final hasPer = await Permissions.requestPhotoPermission();
|
|
|
|
|
if (!hasPer) {
|
2025-09-17 15:32:18 +08:00
|
|
|
|
Permissions.showPermissionDialog('相册');
|
2025-09-13 17:01:01 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
2025-08-21 10:50:38 +08:00
|
|
|
|
final pickedAssets = await AssetPicker.pickAssets(
|
|
|
|
|
context,
|
|
|
|
|
pickerConfig: AssetPickerConfig(
|
|
|
|
|
textDelegate: const AssetPickerTextDelegate(),
|
|
|
|
|
pathNameBuilder: (AssetPathEntity album) {
|
|
|
|
|
return Utils.translateAlbumName(album);
|
|
|
|
|
},
|
|
|
|
|
maxAssets: 1,
|
|
|
|
|
requestType: RequestType.image,
|
|
|
|
|
filterOptions: FilterOptionGroup(
|
|
|
|
|
imageOption: const FilterOption(),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (pickedAssets != null && pickedAssets.isNotEmpty) {
|
|
|
|
|
final asset = pickedAssets.first;
|
|
|
|
|
final file = await asset.file; // 获取实际文件
|
|
|
|
|
if (file != null) {
|
|
|
|
|
final fileSizeInBytes = await file.length();
|
|
|
|
|
final sizeInMB = fileSizeInBytes / (1024 * 1024);
|
2025-09-13 17:01:01 +08:00
|
|
|
|
if (sizeInMB > 20) {
|
|
|
|
|
MyDialog.toast('图片大小不能超过20MB', icon: const Icon(Icons.check_circle), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
|
2025-08-21 10:50:38 +08:00
|
|
|
|
} else {
|
|
|
|
|
print("图片合法,大小:$sizeInMB MB");
|
|
|
|
|
//走upload(file)上传图片拿到url地址
|
2025-09-17 15:32:18 +08:00
|
|
|
|
|
|
|
|
|
final croppedFile = await ImageCropper().cropImage(
|
|
|
|
|
sourcePath: file.path,
|
|
|
|
|
maxWidth: 1024,
|
|
|
|
|
maxHeight: null,
|
|
|
|
|
compressFormat: ImageCompressFormat.png,
|
|
|
|
|
compressQuality: 100, // png 时无效
|
|
|
|
|
uiSettings: [
|
|
|
|
|
AndroidUiSettings(
|
|
|
|
|
toolbarTitle: '裁剪',
|
|
|
|
|
toolbarColor: Colors.black,
|
|
|
|
|
toolbarWidgetColor: Colors.white,
|
|
|
|
|
hideBottomControls: false,
|
|
|
|
|
lockAspectRatio: false,
|
|
|
|
|
cropStyle: CropStyle.circle,
|
|
|
|
|
),
|
|
|
|
|
IOSUiSettings(
|
|
|
|
|
title: '裁剪',
|
|
|
|
|
doneButtonTitle: '确认',
|
|
|
|
|
cropStyle: CropStyle.circle,
|
|
|
|
|
aspectRatioPickerButtonHidden: true, // 隐藏比例选择按钮
|
|
|
|
|
resetAspectRatioEnabled: false, // 点击“重置”按钮时将裁剪框恢复到原始图片比例
|
|
|
|
|
cancelButtonTitle: '返回',
|
|
|
|
|
aspectRatioLockEnabled: false, // 锁定裁剪框比例
|
|
|
|
|
rotateButtonsHidden: false,
|
|
|
|
|
resetButtonHidden: false,
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
//---
|
|
|
|
|
if (croppedFile != null) {
|
|
|
|
|
final istance = MyDialog.loading('上传中');
|
|
|
|
|
// 处理
|
|
|
|
|
final uploadImg = await ImageUtils.toCircleImageFile(imageFile: File(croppedFile.path));
|
|
|
|
|
final res = await Http.upload(CommonApi.uploadFile, filePath: uploadImg);
|
|
|
|
|
userInfoController.faceUrl.value = res['data']['url'];
|
|
|
|
|
userInfoController.updateFaceUrl();
|
|
|
|
|
userInfoController.customInfo.refresh();
|
|
|
|
|
istance.close();
|
|
|
|
|
}
|
2025-08-21 10:50:38 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
|
return Scaffold(
|
|
|
|
|
extendBodyBehindAppBar: true,
|
|
|
|
|
appBar: AppBar(
|
|
|
|
|
backgroundColor: Colors.transparent,
|
|
|
|
|
elevation: 0,
|
|
|
|
|
actions: [
|
|
|
|
|
Padding(
|
|
|
|
|
padding: const EdgeInsets.only(right: 12.0),
|
|
|
|
|
child: GestureDetector(
|
|
|
|
|
onTap: () => pickCover(context),
|
|
|
|
|
child: Container(
|
|
|
|
|
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 6.0),
|
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
|
color: Colors.black.withAlpha(100),
|
|
|
|
|
borderRadius: BorderRadius.circular(20.0),
|
|
|
|
|
),
|
|
|
|
|
child: Row(
|
|
|
|
|
children: const [
|
|
|
|
|
Icon(Icons.camera_alt, color: Colors.white, size: 16),
|
|
|
|
|
SizedBox(width: 4),
|
|
|
|
|
Text('更换封面', style: TextStyle(color: Colors.white, fontSize: 14)),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
body: SizedBox.expand(
|
|
|
|
|
child: Stack(
|
|
|
|
|
children: [
|
|
|
|
|
// 封面图
|
|
|
|
|
SizedBox(
|
|
|
|
|
height: 240,
|
|
|
|
|
width: double.infinity,
|
|
|
|
|
child: Obx(() {
|
|
|
|
|
final imageUrl = userInfoController.customInfo['coverBg'];
|
|
|
|
|
return GestureDetector(
|
|
|
|
|
onTap: () => pickCover(context),
|
2025-09-03 11:25:31 +08:00
|
|
|
|
// child: Image(
|
|
|
|
|
// image: (imageUrl != null && imageUrl.isNotEmpty) ? NetworkImage(imageUrl) : const AssetImage('assets/images/pic2.jpg') as ImageProvider,
|
|
|
|
|
// fit: BoxFit.cover,
|
|
|
|
|
// ),
|
|
|
|
|
child: NetworkOrAssetImage(
|
|
|
|
|
imageUrl: imageUrl,
|
|
|
|
|
placeholderAsset: 'assets/images/bk.jpg',
|
2025-08-21 10:50:38 +08:00
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}),
|
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
// 白色内容容器
|
|
|
|
|
Positioned(
|
|
|
|
|
top: 220,
|
|
|
|
|
left: 0,
|
|
|
|
|
right: 0,
|
|
|
|
|
bottom: 0,
|
|
|
|
|
child: ClipRRect(
|
|
|
|
|
borderRadius: const BorderRadius.only(
|
|
|
|
|
topLeft: Radius.circular(30),
|
|
|
|
|
topRight: Radius.circular(30),
|
|
|
|
|
),
|
|
|
|
|
child: Container(
|
|
|
|
|
decoration: BoxDecoration(
|
|
|
|
|
color: Colors.white,
|
|
|
|
|
borderRadius: BorderRadius.only(
|
|
|
|
|
topLeft: Radius.circular(30),
|
|
|
|
|
topRight: Radius.circular(30),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
child: SingleChildScrollView(
|
|
|
|
|
padding: const EdgeInsets.only(top: 60, bottom: 40),
|
|
|
|
|
child: Column(
|
|
|
|
|
children: [
|
|
|
|
|
const SizedBox(height: 0),
|
|
|
|
|
const SizedBox(height: 20),
|
|
|
|
|
Card(
|
|
|
|
|
elevation: 2,
|
|
|
|
|
shape: RoundedRectangleBorder(
|
|
|
|
|
borderRadius: BorderRadius.circular(12),
|
|
|
|
|
),
|
|
|
|
|
color: Colors.white,
|
|
|
|
|
child: Column(
|
|
|
|
|
children: items.map((item) {
|
|
|
|
|
return Column(
|
|
|
|
|
children: [
|
|
|
|
|
ListTile(
|
|
|
|
|
title: Row(
|
|
|
|
|
children: [
|
|
|
|
|
Text(
|
|
|
|
|
item['title'],
|
|
|
|
|
style: const TextStyle(fontWeight: FontWeight.bold),
|
|
|
|
|
),
|
|
|
|
|
const SizedBox(width: 50),
|
|
|
|
|
Expanded(child: Obx(() {
|
|
|
|
|
final val = item['value'];
|
|
|
|
|
if (val is RxString) {
|
|
|
|
|
return Text(
|
|
|
|
|
val.value,
|
|
|
|
|
style: const TextStyle(color: Colors.black),
|
|
|
|
|
overflow: TextOverflow.ellipsis,
|
|
|
|
|
maxLines: 1,
|
|
|
|
|
);
|
|
|
|
|
} else if (val is RxInt) {
|
|
|
|
|
String displayText;
|
|
|
|
|
if (item['title'] == '性别') {
|
|
|
|
|
displayText = val.value == 0
|
|
|
|
|
? '保密'
|
|
|
|
|
: val.value == 1
|
|
|
|
|
? '男'
|
|
|
|
|
: val.value == 2
|
|
|
|
|
? '女'
|
|
|
|
|
: '';
|
|
|
|
|
} else {
|
|
|
|
|
// 生日
|
|
|
|
|
// displayText = val.value == 0 ? '' : val.value.toString();
|
|
|
|
|
if (val.value == 0) {
|
|
|
|
|
displayText = '';
|
|
|
|
|
} else {
|
|
|
|
|
final date = DateTime.fromMillisecondsSinceEpoch(val.value * 1000);
|
|
|
|
|
displayText = "${date.year}-${date.month.toString().padLeft(2, '0')}-${date.day.toString().padLeft(2, '0')}";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return Text(
|
|
|
|
|
displayText,
|
|
|
|
|
style: const TextStyle(color: Colors.black),
|
|
|
|
|
overflow: TextOverflow.ellipsis,
|
|
|
|
|
maxLines: 1,
|
|
|
|
|
);
|
|
|
|
|
} else if (val is String) {
|
|
|
|
|
return Text(
|
|
|
|
|
val,
|
|
|
|
|
style: const TextStyle(color: Colors.black),
|
|
|
|
|
overflow: TextOverflow.ellipsis,
|
|
|
|
|
maxLines: 1,
|
|
|
|
|
);
|
|
|
|
|
} else if (val is Map) {
|
|
|
|
|
if (item['title'] == '区域') {
|
|
|
|
|
return Text(
|
|
|
|
|
val['area'] ?? '',
|
|
|
|
|
style: const TextStyle(color: Colors.black),
|
|
|
|
|
overflow: TextOverflow.ellipsis,
|
|
|
|
|
maxLines: 1,
|
|
|
|
|
);
|
|
|
|
|
} else {
|
2025-09-03 11:25:31 +08:00
|
|
|
|
String wxText = val['openId'] == null || val['openId'] == '' ? '未授权' : '已授权';
|
2025-08-21 10:50:38 +08:00
|
|
|
|
return Row(
|
|
|
|
|
children: [
|
|
|
|
|
Spacer(),
|
|
|
|
|
Text(
|
|
|
|
|
wxText,
|
|
|
|
|
style: const TextStyle(color: Colors.black),
|
|
|
|
|
overflow: TextOverflow.ellipsis,
|
|
|
|
|
maxLines: 1,
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
return const SizedBox.shrink();
|
|
|
|
|
}
|
|
|
|
|
}))
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
trailing: const Icon(
|
|
|
|
|
Icons.chevron_right,
|
|
|
|
|
color: FStyle.c999,
|
|
|
|
|
),
|
|
|
|
|
onTap: item['onTap'],
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
}).toList(),
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
// 头像层级最高,放在Stack最后面,覆盖白色容器和封面图
|
|
|
|
|
Positioned(
|
|
|
|
|
top: 220 - 60, // 封面图高度减去头像半径,使头像中线对齐封面底线
|
|
|
|
|
left: 0,
|
|
|
|
|
right: 0,
|
|
|
|
|
child: Obx(() {
|
|
|
|
|
final avatar = userInfoController.faceUrl.value;
|
|
|
|
|
return Center(
|
|
|
|
|
child: GestureDetector(
|
|
|
|
|
onTap: () => pickFaceUrl(context),
|
|
|
|
|
child: CircleAvatar(
|
|
|
|
|
radius: 60,
|
|
|
|
|
backgroundColor: Colors.white,
|
2025-09-03 11:25:31 +08:00
|
|
|
|
child: ClipOval(
|
|
|
|
|
child: NetworkOrAssetImage(
|
|
|
|
|
imageUrl: avatar,
|
|
|
|
|
width: double.infinity,
|
|
|
|
|
height: double.infinity,
|
|
|
|
|
fit: BoxFit.cover,
|
|
|
|
|
),
|
2025-08-21 10:50:38 +08:00
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|