import 'dart:convert'; import 'package:eatvio_app/core/const.dart'; import 'package:eatvio_app/core/token_storage.dart'; import 'package:eatvio_app/generated/l10n.dart'; import 'package:eatvio_app/old/profile/my_profile_screen/bloc/my_profile_bloc.dart'; import 'package:eatvio_app/theme/custom_button_style.dart'; import 'package:eatvio_app/widgets/custom_hyper_switch.dart'; import 'package:eatvio_app/widgets/custom_hypo_switch.dart'; import 'package:eatvio_app/widgets/next_step_button.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; import 'package:http/http.dart' as http; import 'package:intl/intl.dart'; import 'package:rive/rive.dart' as rive; class ExpandableList extends StatefulWidget { final String title; ExpandableList(this.title); @override _ExpandableListState createState() => _ExpandableListState(); } class _ExpandableListState extends State { bool isExpanded = false; @override Widget build(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ GestureDetector( onTap: toggleExpand(), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( widget.title, style: TextStyle( color: Colors.black, fontSize: 14, fontWeight: FontWeight.bold, ), ), Icon(isExpanded ? Icons.arrow_drop_up : Icons.arrow_drop_down), ], ), ), if (isExpanded) Padding( padding: const EdgeInsets.only(left: 16.0), child: BlocBuilder( builder: (context, state) { if (state is MyProfileInitialState) { // Это начальное состояние, данные ещё не загружены. return SizedBox( height: 32, width: 32, child: rive.RiveAnimation.asset( 'assets/rive_animations/loading.riv', ), ); } else if (state is MyProfileLoadedState) { // Данные загружены, можно строить форму. return _buildForm(context, state); } else if (state is MyProfileErrorState) { // Произошла ошибка при загрузке данных. return Center(child: Text(state.errorMessage)); } else { // Другие состояния, если они есть. return Container(); // Пустой контейнер для необработанных состояний. } }, ), ), Divider(), ], ); } toggleExpand() { return () { setState(() { isExpanded = !isExpanded; }); }; } _buildForm(BuildContext context, MyProfileLoadedState state) { final s = S.of(context); List cities = [ 'Москва', 'Санкт-Петербург', 'Новосибирск', 'Екатеринбург', 'Нижний Новгород', 'Казань', 'Челябинск', 'Омск', 'Самара', 'Ростов-на-Дону', 'Уфа', 'Красноярск', 'Воронеж', 'Пермь', 'Волгоград', 'Краснодар', 'Саратов', 'Тюмень', 'Тольятти', 'Ижевск', 'Барнаул', 'Ульяновск', 'Иркутск', 'Хабаровск', 'Ярославль', 'Владивосток', 'Махачкала', 'Томск', 'Оренбург', 'Кемерово', ]; List countries = [ 'Россия', ]; List activities = [ 'сидячий', 'низкий (1-3 тренировки в неделю по 20 мин)', 'умеренный (3-4 тренировки в неделю по 30-60 мин)', 'высокий (5-7 тренировок по 30-60 мин)', 'экстремальный (несколько интенсивных тренировок в день)', ]; final digitsOnly = FilteringTextInputFormatter.digitsOnly; return Column( children: [ TextField( controller: state.firstNameFormController, decoration: InputDecoration(labelText: s.first_name), ), TextField( controller: state.lastNameFormController, decoration: InputDecoration(labelText: s.last_name), ), TextField( decoration: InputDecoration(labelText: s.date_of_birth), controller: state.dateFormController, onTap: inputTime(context, state), ), DropdownButtonFormField( value: state.selectedCountry, hint: Text(state.selectedCountry ?? "Не выбрано"), onChanged: setCountry(state), items: countries.map>((String value) { return DropdownMenuItem( value: value, child: Text(value), ); }).toList(), decoration: InputDecoration(labelText: s.country), ), DropdownButtonFormField( hint: Text(state.selectedCity ?? "Не выбрано"), value: state.selectedCity, onChanged: setCity(state), items: cities.map>((String value) { return DropdownMenuItem( value: value, child: Text(value), ); }).toList(), decoration: InputDecoration(labelText: s.city_of_living), ), TextField( controller: state.weightFormController, decoration: InputDecoration(labelText: s.weight_measurements), keyboardType: TextInputType.number, inputFormatters: [digitsOnly], ), TextField( controller: state.heightFormController, decoration: InputDecoration(labelText: s.height_measurements), keyboardType: TextInputType.number, inputFormatters: [digitsOnly], ), TextField( controller: state.chestCircumferenceFormController, decoration: InputDecoration(labelText: s.chest_circumference), keyboardType: TextInputType.number, inputFormatters: [digitsOnly], ), TextField( controller: state.waistCircumferenceFormController, decoration: InputDecoration(labelText: s.waist_circumference), keyboardType: TextInputType.number, inputFormatters: [digitsOnly], ), TextField( controller: state.hipCircumferenceFormController, decoration: InputDecoration(labelText: s.hip_circumference), keyboardType: TextInputType.number, inputFormatters: [digitsOnly], ), TextField( controller: state.upPressureFormController, decoration: InputDecoration(labelText: s.up_pressure), keyboardType: TextInputType.number, inputFormatters: [digitsOnly], ), TextField( controller: state.downPressureFormController, decoration: InputDecoration(labelText: s.down_pressure), keyboardType: TextInputType.number, inputFormatters: [digitsOnly], ), TextField( controller: state.allergiesFormController, decoration: InputDecoration(labelText: s.allergies_presence), maxLines: null, ), TextField( controller: state.pillsFormController, decoration: InputDecoration(labelText: s.do_you_take_pills), maxLines: null, ), TextField( controller: state.sportsFormController, decoration: InputDecoration(labelText: s.your_favorite_sports), maxLines: null, ), TextField( controller: state.favoriteFormController, decoration: InputDecoration(labelText: s.favorite_foods), maxLines: null, ), TextField( controller: state.unfavoriteFormController, decoration: InputDecoration(labelText: s.unfavorite_foods), maxLines: null, ), TextField( controller: state.additionalFormController, decoration: InputDecoration(labelText: s.additional_information), maxLines: null, ), DropdownButtonFormField( hint: Text(state.selectedActivity ?? "Не выбрано"), value: state.selectedActivity, onChanged: setActivity(state), items: activities.map>((String value) { return DropdownMenuItem( value: value, child: SizedBox( // Используем SizedBox для ограничения ширины элемента списка width: MediaQuery.of(context).size.width * 0.8, // Устанавливаем желаемую ширину элемента списка child: Text(value), ), ); }).toList(), isExpanded: true, // Расширяем список на всю доступную ширину decoration: InputDecoration(labelText: 'Активность'), ), Row( children: [ HypoSwitch(s.do_you_have_hypotension_tendency, state), ], ), Row( children: [ HyperSwitch(s.do_you_have_hypertension_tendency, state), ], ), NextStepButton( onPressed: () => sendProfileData(state), buttonStyle: CustomButtonStyles.outlinePrimaryTL15, text: s.save.toUpperCase(), margin: EdgeInsets.only(top: 20, bottom: 20), ), ], ); } sendData(MyProfileLoadedState state) { return () { sendProfileData(state); }; } setCity(MyProfileLoadedState state) { return (newValue) { // Обработчик изменения выбранного города setSelectedCity(state, newValue); }; } setCountry(MyProfileLoadedState state) { return (newValue) { // Обработчик изменения выбранной страны setSelectedCountry(state, newValue); }; } setActivity(MyProfileLoadedState state) { return (newValue) { // Обработчик изменения выбранной страны setSelectedActivity(state, newValue); }; } inputTime(BuildContext context, MyProfileLoadedState state) { return () { showDatePicker( context: context, initialDate: DateTime.now(), firstDate: DateTime(1900), lastDate: DateTime.now(), ).then((selectedDate) { if (selectedDate != null) { setState(() { state.dateFormController!.text = DateFormat('dd-MM-yyyy').format(selectedDate); }); } }); }; } void setSelectedCity(MyProfileLoadedState? state, String? newValue) { setState(() { state!.selectedCity = newValue; }); } void setSelectedCountry(MyProfileLoadedState? state, String? newValue) { setState(() { state!.selectedCountry = newValue; }); } void setSelectedActivity(MyProfileLoadedState? state, String? newValue) { setState(() { state!.selectedActivity = newValue; }); } Future sendProfileData(MyProfileLoadedState? state) async { try { final profileData = _createProfileData(state); final token = await _getTokenFromBox(); final apiUrl = _buildApiUrl(); final response = await _sendHttpRequest(profileData, apiUrl, token); if (response.statusCode == 200) { _navigateToProfilePage(); } else { final errorMessage = _getErrorMessage(response); _showErrorDialog(errorMessage); throw Exception('Ошибка при отправке данных профиля'); } } catch (error) { throw Exception('Ошибка при отправке данных профиля: $error'); } } Map _createProfileData(MyProfileLoadedState? state) { return { 'first_name': state?.firstNameFormController?.text, 'last_name': state?.lastNameFormController?.text, 'chest_circumference': state?.chestCircumferenceFormController?.text, 'waist_circumference': state?.waistCircumferenceFormController?.text, 'hip_circumference': state?.hipCircumferenceFormController?.text, 'birthdate': state?.dateFormController?.text, 'height': state?.heightFormController?.text, 'weight': state?.weightFormController?.text, 'blood_pressure_up': state?.upPressureFormController?.text, 'blood_pressure_down': state?.downPressureFormController?.text, 'about_alergy': state?.allergiesFormController?.text ?? 'Нет аллергий', 'about_taking_pils': state?.pillsFormController?.text, 'about_sport_hobbies': state?.sportsFormController?.text, 'favorite_foods': state?.favoriteFormController?.text, 'unfavorite_foods': state?.unfavoriteFormController?.text, 'additional_info': state?.additionalFormController?.text, 'country': state?.selectedCountry ?? 'Россия', 'city': state?.selectedCity ?? 'Москва', 'has_hypo': state?.hypoFormController, 'has_hyper': state?.hyperFormController, 'activity_coefficient': determineActivityFactor(state?.selectedActivity ?? 'сидячий'), }; } Future _getTokenFromBox() async { return await TokenStorage.getTokenFromStorage(); } String _buildApiUrl() { return baseUrl + '/api/profile/update'; } Future _sendHttpRequest( Map profileData, String apiUrl, String? token, ) { return http.post( Uri.parse(apiUrl), body: json.encode(profileData), encoding: utf8, headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', 'Authorization': 'Bearer $token', }, ); } void _navigateToProfilePage() { GoRouter.of(context).go('/my-profile-screen'); } String _getErrorMessage(http.Response response) { final res = jsonDecode(response.body); return res['message']; } void _showErrorDialog(String errorMessage) { showDialog( context: context, builder: (context) => AlertDialog( title: Text('Ошибка'), content: Text(errorMessage), actions: [ TextButton( onPressed: onTapOk(context), child: Text('OK'), ), ], ), ); } onTapOk(BuildContext context) { return () { Navigator.pop(context); }; } double determineActivityFactor(String activityLevel) { switch (activityLevel) { case "сидячий": return 1.2; case "низкий (1-3 тренировки в неделю по 20 мин)": return 1.375; case "умеренный (3-4 тренировки в неделю по 30-60 мин)": return 1.55; case "высокий (5-7 тренировок по 30-60 мин)": return 1.7; case "экстремальный (несколько интенсивных тренировок в день)": return 1.9; default: return 1.2; // Возвращаемое значение по умолчанию, если уровень активности не распознан } } }