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'; import 'package:loopin/IM/controller/im_user_info_controller.dart'; import 'package:loopin/api/common_api.dart'; import 'package:loopin/components/network_or_asset_image.dart'; import 'package:loopin/service/http.dart'; import 'package:loopin/styles/index.dart'; import 'package:loopin/utils/index.dart'; import 'package:loopin/utils/permissions.dart'; 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 createState() => _UserInfoState(); } class _UserInfoState extends State { final userInfoController = Get.find(); late List> 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 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 { final hasPer = await Permissions.requestPhotoPermission(); if (!hasPer) { Permissions.showPermissionDialog(); return; } 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); if (sizeInMB > 200) { MyDialog.toast('图片大小不能超过200MB', icon: const Icon(Icons.check_circle), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200))); } else { print("视频合法,大小:$sizeInMB MB"); //走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 { final hasPer = await Permissions.requestPhotoPermission(); if (!hasPer) { Permissions.showPermissionDialog(); return; } 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); if (sizeInMB > 20) { MyDialog.toast('图片大小不能超过20MB', icon: const Icon(Icons.check_circle), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200))); } else { print("图片合法,大小:$sizeInMB MB"); //走upload(file)上传图片拿到url地址 final istance = MyDialog.loading('上传中'); final res = await Http.upload(CommonApi.uploadFile, filePath: file.path); userInfoController.faceUrl.value = res['data']['url']; userInfoController.updateFaceUrl(); userInfoController.customInfo.refresh(); istance.close(); } } } } @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), // 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', ), ); }), ), // 白色内容容器 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 { String wxText = val['openId'] == null || val['openId'] == '' ? '未授权' : '已授权'; 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, child: ClipOval( child: NetworkOrAssetImage( imageUrl: avatar, width: double.infinity, height: double.infinity, fit: BoxFit.cover, ), ), ), ), ); }), ), ], ), ), ); } }