import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:yumi/app_localizations.dart'; import 'package:yumi/ui_kit/components/sc_debounce_widget.dart'; import 'package:yumi/shared/tools/sc_loading_manager.dart'; import 'package:yumi/shared/data_sources/sources/local/user_manager.dart'; import 'package:yumi/shared/data_sources/sources/repositories/sc_user_repository_impl.dart'; import 'package:yumi/services/auth/user_profile_manager.dart'; import 'package:yumi/services/general/sc_app_general_manager.dart'; import 'package:provider/provider.dart'; import 'package:yumi/ui_kit/components/appbar/socialchat_appbar.dart'; import 'package:yumi/ui_kit/components/sc_compontent.dart'; import 'package:yumi/ui_kit/components/text/sc_text.dart'; import 'package:yumi/ui_kit/components/sc_tts.dart'; import 'package:yumi/app/routes/sc_fluro_navigator.dart'; import 'package:yumi/shared/tools/sc_lk_dialog_util.dart'; import 'package:yumi/services/audio/rtc_manager.dart'; import 'package:yumi/ui_kit/theme/socialchat_theme.dart'; import 'package:yumi/modules/country/country_route.dart'; import '../../../shared/tools/sc_pick_utils.dart'; import '../../../shared/business_logic/models/res/country_res.dart'; import '../../../shared/business_logic/models/res/login_res.dart'; import '../../../shared/business_logic/models/res/sc_user_identity_res.dart'; import '../../../shared/business_logic/usecases/sc_accurate_length_limiting_textInput_formatter.dart'; class EditUserInfoPage2 extends StatefulWidget { const EditUserInfoPage2({super.key}); @override State createState() => _EditUserInfoPage2State(); } class _EditUserInfoPage2State extends State with SingleTickerProviderStateMixin { static const Object _noChange = Object(); String? userCover; String? bornMonth; String? autograph; String? hobby; String? countryId; String? countryCode; String? countryName; String? bornDay; String? bornYear; String nickName = ""; DateTime? birthdayDate; num? sex; num? age; bool _isSubmitting = false; @override void initState() { super.initState(); final currentProfile = AccountStorage().getCurrentUser()?.userProfile; userCover = currentProfile?.userAvatar ?? ""; nickName = currentProfile?.userNickname ?? ""; autograph = currentProfile?.autograph ?? ""; hobby = currentProfile?.hobby ?? ""; countryId = currentProfile?.countryId ?? ""; countryCode = currentProfile?.countryCode ?? ""; countryName = currentProfile?.countryName ?? ""; birthdayDate = _birthdayFromProfile(currentProfile) ?? _birthdayFromAge(currentProfile?.age); bornMonth = _formatTwoDigits( currentProfile?.bornMonth ?? birthdayDate?.month, ); bornDay = _formatTwoDigits(currentProfile?.bornDay ?? birthdayDate?.day); bornYear = _formatYear(currentProfile?.bornYear ?? birthdayDate?.year); age = currentProfile?.age ?? _ageFromBirthday(birthdayDate); sex = currentProfile?.userSex; WidgetsBinding.instance.addPostFrameCallback((_) { if (!mounted) { return; } Provider.of( context, listen: false, ).getUserIdentity(); Provider.of( context, listen: false, ).fetchCountryList(); }); } String? _preferNonEmpty(String? primary, String? fallback) { if ((primary ?? "").trim().isNotEmpty) { return primary; } if ((fallback ?? "").trim().isNotEmpty) { return fallback; } return primary ?? fallback; } DateTime? _birthdayFromProfile([SocialChatUserProfile? profile]) { final targetProfile = profile ?? AccountStorage().getCurrentUser()?.userProfile; final year = targetProfile?.bornYear?.toInt() ?? 0; final month = targetProfile?.bornMonth?.toInt() ?? 0; final day = targetProfile?.bornDay?.toInt() ?? 0; if (year <= 0 || month <= 0 || day <= 0) { return null; } try { return DateTime(year, month, day); } catch (_) { return null; } } String _formatTwoDigits(num? value) { final intValue = value?.toInt() ?? 0; if (intValue <= 0) { return ""; } return intValue < 10 ? "0$intValue" : "$intValue"; } String _formatYear(num? value) { final intValue = value?.toInt() ?? 0; if (intValue <= 0) { return ""; } return "$intValue"; } num? _ageFromBirthday(DateTime? date) { if (date == null) { return null; } return (DateTime.now().year - date.year).abs(); } DateTime? _birthdayFromAge(num? age) { final ageValue = age?.toInt() ?? 0; if (ageValue <= 0) { return null; } final now = DateTime.now(); return DateTime(now.year - ageValue, now.month, now.day); } String _birthdayDisplayText() { if ((bornYear ?? "").isEmpty || (bornMonth ?? "").isEmpty || (bornDay ?? "").isEmpty) { return ""; } return "$bornYear-$bornMonth-$bornDay"; } bool _isSameDate(DateTime first, DateTime second) { return first.year == second.year && first.month == second.month && first.day == second.day; } bool _hasChanges(List values) { return values.any((value) => !identical(value, _noChange)); } bool _hasIdentity(SCUserIdentityRes? identity) { if (identity == null) { return true; } return (identity.anchor ?? false) || (identity.bd ?? false) || (identity.agent ?? false) || (identity.bdLeader ?? false) || (identity.freightAgent ?? false) || (identity.superFreightAgent ?? false) || (identity.admin ?? false) || (identity.superAdmin ?? false) || (identity.manager ?? false) || (identity.yumiManager ?? false); } bool _canChangeCountry(SCUserIdentityRes? identity) { return !_hasIdentity(identity); } String _countryDisplayText() { final displayName = (countryName ?? "").trim(); if (displayName.isNotEmpty) { return displayName; } return (countryCode ?? "").trim(); } void _syncLocalProfileState(SocialChatUserProfile profile) { userCover = profile.userAvatar; nickName = profile.userNickname ?? nickName; autograph = profile.autograph ?? autograph; hobby = profile.hobby ?? hobby; countryId = profile.countryId ?? countryId; countryCode = profile.countryCode ?? countryCode; countryName = profile.countryName ?? countryName; sex = profile.userSex ?? sex; birthdayDate = _birthdayFromProfile(profile) ?? _birthdayFromAge(profile.age) ?? birthdayDate; bornYear = _formatYear(profile.bornYear ?? birthdayDate?.year); bornMonth = _formatTwoDigits(profile.bornMonth ?? birthdayDate?.month); bornDay = _formatTwoDigits(profile.bornDay ?? birthdayDate?.day); age = profile.age ?? _ageFromBirthday(birthdayDate) ?? age; } @override Widget build(BuildContext context) { return Stack( children: [ Image.asset( "sc_images/person/sc_icon_edit_userinfo_bg.png", width: ScreenUtil().screenWidth, height: ScreenUtil().screenHeight, fit: BoxFit.fill, ), Scaffold( backgroundColor: Colors.transparent, resizeToAvoidBottomInset: true, appBar: SocialChatStandardAppBar( title: SCAppLocalizations.of(context)!.editProfile, actions: [], ), body: Consumer( builder: (context, ref, child) { return Column( spacing: 10.w, children: [ SCDebounceWidget( child: Row( children: [ SizedBox(width: 15.w), text( SCAppLocalizations.of(context)!.profilePhoto, textColor: Colors.white, fontSize: 15.sp, fontWeight: FontWeight.w600, ), Spacer(), Stack( alignment: AlignmentDirectional.bottomEnd, children: [ netImage( url: _preferNonEmpty( userCover, UserManager() .getCurrentUser() ?.userProfile ?.userAvatar, ) ?? "", width: 45.w, height: 45.w, shape: BoxShape.circle, ), ], ), Icon( Icons.keyboard_arrow_right, size: 20.w, color: Colors.white70, ), SizedBox(width: 8.w), ], ), onTap: () { SCPickUtils.pickImage(context, ( bool success, String url, ) { if (success) { submit(userAvatarValue: url); } }, allowGif: true); }, ), SizedBox(height: 3.w), _buildItem( "${SCAppLocalizations.of(context)!.userName}:", nickName, () { _showInputBioHobby(nickName, 3); }, ), _buildItem( "${SCAppLocalizations.of(context)!.gender}:", sex == 1 ? SCAppLocalizations.of(context)!.man : SCAppLocalizations.of(context)!.woman, null, ), _buildItem( "${SCAppLocalizations.of(context)!.birthday}:", _birthdayDisplayText(), () { _selectDate(); }, ), _buildItem( "${SCAppLocalizations.of(context)!.bio}:", autograph ?? "", () { _showInputBioHobby(autograph ?? "", 1); }, ), _buildItem( "${SCAppLocalizations.of(context)!.hobby}:", hobby ?? "", () { _showInputBioHobby(hobby ?? "", 2); }, ), if (_canChangeCountry(ref.userIdentity)) _buildItem( "${SCAppLocalizations.of(context)!.countryRegion}:", _countryDisplayText(), _openCountryPicker, ), ], ); }, ), ), ], ); } Future _selectDate() async { final initialDate = birthdayDate ?? _getBefor18(); DateTime selectedDate = initialDate; SmartDialog.show( tag: "showSelectDate", alignment: Alignment.bottomCenter, animationType: SmartAnimationType.fade, builder: (_) { return SafeArea( top: false, child: Container( height: 240.w, padding: EdgeInsets.only(top: 6), color: CupertinoColors.systemBackground, child: Column( children: [ SizedBox(height: 10.w), Row( children: [ SizedBox(width: 10.w), GestureDetector( child: Container( alignment: Alignment.topCenter, child: text( SCAppLocalizations.of(context)!.cancel, textColor: Colors.black87, fontSize: 15.sp, fontWeight: FontWeight.w600, ), ), onTap: () { SmartDialog.dismiss(tag: "showSelectDate"); }, ), Expanded( child: Container( alignment: Alignment.topCenter, child: text( SCAppLocalizations.of(context)!.birthday, textColor: Colors.black87, fontSize: 15.sp, fontWeight: FontWeight.w600, ), ), ), GestureDetector( child: Container( alignment: Alignment.topCenter, child: text( SCAppLocalizations.of(context)!.confirm, textColor: Color(0xff7726FF), fontSize: 15.sp, fontWeight: FontWeight.w600, ), ), onTap: () { final hasChanged = !_isSameDate(initialDate, selectedDate); birthdayDate = selectedDate; bornMonth = _formatTwoDigits(selectedDate.month); bornDay = _formatTwoDigits(selectedDate.day); bornYear = "${selectedDate.year}"; age = _ageFromBirthday(selectedDate); setState(() {}); SmartDialog.dismiss(tag: "showSelectDate"); if (hasChanged) { submit( ageValue: age, bornYearValue: selectedDate.year, bornMonthValue: selectedDate.month, bornDayValue: selectedDate.day, ); } }, ), SizedBox(width: 10.w), ], ), Expanded( child: CupertinoDatePicker( mode: CupertinoDatePickerMode.date, initialDateTime: initialDate, maximumDate: _getBefor18(), onDateTimeChanged: (date) { selectedDate = date; }, ), ), ], ), ), ); }, ); } DateTime _getBefor18() { DateTime currentDate = DateTime.now(); DateTime eighteenYearsAgo = DateTime( currentDate.year - 18, currentDate.month, currentDate.day, currentDate.hour, currentDate.minute, currentDate.second, currentDate.millisecond, currentDate.microsecond, ); return eighteenYearsAgo; } void submit({ Object? userAvatarValue = _noChange, Object? userNicknameValue = _noChange, Object? userSexValue = _noChange, Object? ageValue = _noChange, Object? bornYearValue = _noChange, Object? bornMonthValue = _noChange, Object? bornDayValue = _noChange, Object? hobbyValue = _noChange, Object? autographValue = _noChange, Object? countryValue = _noChange, }) async { if (!_hasChanges([ userAvatarValue, userNicknameValue, userSexValue, ageValue, bornYearValue, bornMonthValue, bornDayValue, hobbyValue, autographValue, countryValue, ])) { return; } if (!identical(userNicknameValue, _noChange) && ((userNicknameValue as String?) ?? "").isEmpty) { SCTts.show(SCAppLocalizations.of(context)!.pleaseEnterNickname); return; } if (_isSubmitting) { return; } _isSubmitting = true; SCLoadingManager.show(); try { final selectedCountry = identical(countryValue, _noChange) ? null : countryValue as Country?; final updatedProfile = await SCAccountRepository().updateUserInfo( userAvatar: identical(userAvatarValue, _noChange) ? null : userAvatarValue as String?, userSex: identical(userSexValue, _noChange) ? null : userSexValue as num?, userNickname: identical(userNicknameValue, _noChange) ? null : userNicknameValue as String?, age: identical(ageValue, _noChange) ? null : ageValue as num?, bornDay: identical(bornDayValue, _noChange) ? null : bornDayValue as num?, bornMonth: identical(bornMonthValue, _noChange) ? null : bornMonthValue as num?, bornYear: identical(bornYearValue, _noChange) ? null : bornYearValue as num?, hobby: identical(hobbyValue, _noChange) ? null : hobbyValue as String?, autograph: identical(autographValue, _noChange) ? null : autographValue as String?, countryId: selectedCountry?.id, ); final mergedProfile = updatedProfile.copyWith( userNickname: _preferNonEmpty( updatedProfile.userNickname, identical(userNicknameValue, _noChange) ? nickName : userNicknameValue as String?, ), autograph: _preferNonEmpty( updatedProfile.autograph, identical(autographValue, _noChange) ? autograph : autographValue as String?, ), hobby: _preferNonEmpty( updatedProfile.hobby, identical(hobbyValue, _noChange) ? hobby : hobbyValue as String?, ), userSex: updatedProfile.userSex ?? (identical(userSexValue, _noChange) ? sex : userSexValue as num?), age: updatedProfile.age ?? (identical(ageValue, _noChange) ? age : ageValue as num?), bornDay: updatedProfile.bornDay ?? (identical(bornDayValue, _noChange) ? birthdayDate?.day : bornDayValue as num?), bornMonth: updatedProfile.bornMonth ?? (identical(bornMonthValue, _noChange) ? birthdayDate?.month : bornMonthValue as num?), bornYear: updatedProfile.bornYear ?? (identical(bornYearValue, _noChange) ? birthdayDate?.year : bornYearValue as num?), countryId: _preferNonEmpty( updatedProfile.countryId, selectedCountry?.id, ), countryCode: _preferNonEmpty( updatedProfile.countryCode, selectedCountry?.alphaTwo, ), countryName: _preferNonEmpty( updatedProfile.countryName, _preferNonEmpty( selectedCountry?.countryName, selectedCountry?.aliasName, ), ), ); _syncLocalProfileState(mergedProfile); if (!mounted) { return; } Provider.of( context, listen: false, ).syncCurrentUserProfile(mergedProfile); Provider.of(context, listen: false).needUpDataUserInfo = true; setState(() {}); SCTts.show(SCAppLocalizations.of(context)!.operationSuccessful); if (selectedCountry != null) { AccountStorage().logout(context); return; } } catch (_) { // The repository/network layer already shows request errors, so keep the // current edit page state and only release the submit/loading guards here. } finally { _isSubmitting = false; SCLoadingManager.hide(); } } Future _openCountryPicker() async { final userIdentity = Provider.of( context, listen: false, ).userIdentity; if (!_canChangeCountry(userIdentity)) { return; } final generalManager = Provider.of( context, listen: false, ); generalManager.clearCountrySelection(); await SCNavigatorUtils.push(context, CountryRoute.country, replace: false); if (!mounted) { return; } final selectedCountry = generalManager.selectCountryInfo; if (selectedCountry == null) { return; } final selectedCountryId = (selectedCountry.id ?? "").trim(); if (selectedCountryId.isEmpty || selectedCountryId == (countryId ?? "").trim()) { return; } submit(countryValue: selectedCountry); } // ignore: unused_element void _showSexDialog() { showCenterDialog( context, Container( height: 150.w, width: ScreenUtil().screenWidth * 0.7, decoration: BoxDecoration( color: Color(0xff09372E), borderRadius: BorderRadius.all(Radius.circular(12)), ), child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ SizedBox(height: 15.w), text( SCAppLocalizations.of(context)!.pleaseSelectYourGender, fontSize: 15.sp, textColor: Colors.white, ), SizedBox(height: 5.w), Divider(color: Colors.black12, thickness: 0.5, height: 14.w), SizedBox(height: 8.w), GestureDetector( onTap: () { SCNavigatorUtils.goBack(context); if (sex == 1) { return; } sex = 1; setState(() {}); submit(userSexValue: sex); }, behavior: HitTestBehavior.opaque, child: Row( mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.max, children: [ xb(1, color: Colors.white, height: 18.w), SizedBox(width: 5.w), text( SCAppLocalizations.of(context)!.man, textColor: Colors.white, fontSize: 15.sp, ), ], ), ), SizedBox(height: 8.w), Divider(color: Colors.black12, thickness: 0.5, height: 14.w), SizedBox(height: 8.w), GestureDetector( onTap: () { SCNavigatorUtils.goBack(context); if (sex == 0) { return; } sex = 0; setState(() {}); submit(userSexValue: sex); }, behavior: HitTestBehavior.opaque, child: Row( mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.max, children: [ xb(0, color: Colors.white, height: 18.w), SizedBox(width: 5.w), text( SCAppLocalizations.of(context)!.woman, textColor: Colors.white, fontSize: 15.sp, ), ], ), ), ], ), ), ); } _buildItem(String title, String value, Function()? func) { return SCDebounceWidget( child: Column( children: [ Row( children: [ SizedBox(width: 15.w), text( title, textColor: Colors.white, fontSize: 15.sp, fontWeight: FontWeight.w600, ), Expanded( child: Container( alignment: AlignmentDirectional.centerEnd, child: text(value, textColor: Colors.white, fontSize: 15.sp), ), ), if (func != null) Icon( Icons.keyboard_arrow_right, size: 20.w, color: Colors.white70, ), SizedBox(width: 8.w), ], ), SizedBox(height: 8.w), Container( margin: EdgeInsets.only(left: 15.w, right: 15.w), color: Colors.white12, height: 0.5.w, width: ScreenUtil().screenWidth, ), ], ), onTap: () { func?.call(); }, ); } void _showInputBioHobby(String content, int type) { SmartDialog.dismiss(tag: "showInputBioHobby"); TextEditingController inputController = TextEditingController(); inputController.text = content; SmartDialog.show( tag: "showInputBioHobby", alignment: Alignment.center, debounce: true, animationType: SmartAnimationType.fade, builder: (_) { return Container( height: 250.w, margin: EdgeInsets.symmetric(horizontal: 20.w), decoration: BoxDecoration( color: Color(0xff09372E), borderRadius: BorderRadius.circular(10.w), ), child: Column( children: [ SizedBox(height: 8.w), Row( children: [ SizedBox(width: 10.w), GestureDetector( child: Icon(Icons.close, color: Colors.white, size: 20.w), onTap: () { SmartDialog.dismiss(tag: "showInputBioHobby"); }, ), Spacer(), text( SCAppLocalizations.of(context)!.pleaseEnterContent, textColor: Colors.white, fontSize: 15.sp, ), Spacer(), SizedBox(width: 20.w), ], ), SizedBox(height: 15.w), Container( margin: EdgeInsets.symmetric(horizontal: 25.w), padding: EdgeInsets.only(top: 5.w, left: 12.w, right: 12.w), alignment: AlignmentDirectional.centerStart, height: 110.w, width: ScreenUtil().screenWidth, decoration: BoxDecoration( color: Colors.black26, borderRadius: BorderRadius.all(Radius.circular(8.w)), border: Border.all(color: Color(0xffE6E6E6), width: 0.5.w), ), child: TextField( controller: inputController, maxLines: 5, inputFormatters: [ SCAccurateLengthLimitingTextInputFormatter(50), ], decoration: InputDecoration( isDense: true, hintText: "", hintStyle: TextStyle(color: Colors.white, fontSize: 14.sp), contentPadding: EdgeInsets.only(top: 0.w), counterText: '', filled: false, focusColor: Colors.transparent, hoverColor: Colors.transparent, border: InputBorder.none, enabledBorder: InputBorder.none, focusedBorder: InputBorder.none, disabledBorder: InputBorder.none, errorBorder: InputBorder.none, focusedErrorBorder: InputBorder.none, // enabledBorder: UnderlineInputBorder( // borderSide: BorderSide(width: 0.5,color: Colors.white,style: BorderStyle.solid),), // focusedBorder: UnderlineInputBorder( // borderSide: BorderSide(width: 0.5,color: Colors.white,style: BorderStyle.solid),), // prefixIcon: Padding(padding: EdgeInsets.all(8.w), child: Image.asset("images/login/sc_icon_phone.png",width: 20.w, height: 20.w,fit: BoxFit.fill,),), fillColor: Colors.white54, ), style: TextStyle( fontSize: 15.w, color: Colors.white, textBaseline: TextBaseline.alphabetic, ), ), ), SizedBox(height: 15.w), SCDebounceWidget( child: Container( alignment: Alignment.center, margin: EdgeInsets.symmetric(horizontal: 60.w), height: 42.w, decoration: BoxDecoration( borderRadius: BorderRadius.circular(20.w), color: SocialChatTheme.primaryLight, ), child: text( SCAppLocalizations.of(context)!.confirm, textColor: Colors.white, fontSize: 16.sp, fontWeight: FontWeight.w600, ), ), onTap: () { SmartDialog.dismiss(tag: "showInputBioHobby"); if (type == 1) { if (inputController.text == autograph) { return; } autograph = inputController.text; setState(() {}); submit(autographValue: autograph); } else if (type == 2) { if (inputController.text == hobby) { return; } hobby = inputController.text; setState(() {}); submit(hobbyValue: hobby); } else if (type == 3) { if (inputController.text == nickName) { return; } nickName = inputController.text; setState(() {}); submit(userNicknameValue: nickName); } }, ), ], ), ); }, ); } }