forked from eatvio/eatvio_app
plans filters ready
This commit is contained in:
@@ -116,7 +116,7 @@ class AppRouter {
|
|||||||
final data = state.extra as Map<String, dynamic>;
|
final data = state.extra as Map<String, dynamic>;
|
||||||
return AboutPlanScreen(
|
return AboutPlanScreen(
|
||||||
plan: data['plan'] as PlanModel,
|
plan: data['plan'] as PlanModel,
|
||||||
// index: data['index'] as int,
|
index: data['index'] as int,
|
||||||
navigateToComments: data['navigateToComments'] as bool,
|
navigateToComments: data['navigateToComments'] as bool,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,4 +2,5 @@ const String baseUrl = 'https://dev2.eatvio.com';
|
|||||||
|
|
||||||
const List<int> durationValues = [7, 14, 21];
|
const List<int> durationValues = [7, 14, 21];
|
||||||
|
|
||||||
const List<String> typeValues = ['veg', 'light', 'carb', 'meat'];
|
const List<String> dietTypeValues = ['veg', 'light', 'carb', 'meat'];
|
||||||
|
const List<String> planTypeValues = ['fit', 'slim', 'wellness', 'behavior'];
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ Future<void> init() async {
|
|||||||
sl.registerFactory<DietsCubit>(() => DietsCubit(feedRepository: sl(), dietsRepository: sl()));
|
sl.registerFactory<DietsCubit>(() => DietsCubit(feedRepository: sl(), dietsRepository: sl()));
|
||||||
sl.registerFactory<CommentsCubit>(() => CommentsCubit(feedRepository: sl()));
|
sl.registerFactory<CommentsCubit>(() => CommentsCubit(feedRepository: sl()));
|
||||||
sl.registerFactory<ChatSelectCubit>(() => ChatSelectCubit(chatRepository: sl()));
|
sl.registerFactory<ChatSelectCubit>(() => ChatSelectCubit(chatRepository: sl()));
|
||||||
sl.registerFactory<PlansCubit>(() => PlansCubit(feedRepository: sl()));
|
sl.registerFactory<PlansCubit>(() => PlansCubit(feedRepository: sl(), planRepository: sl()));
|
||||||
sl.registerFactory<SettingsCubit>(() => SettingsCubit(profileRepository: sl(), settingsRepository: sl()));
|
sl.registerFactory<SettingsCubit>(() => SettingsCubit(profileRepository: sl(), settingsRepository: sl()));
|
||||||
sl.registerFactory<RegisterConsultantCubit>(() => RegisterConsultantCubit(consultantRepository: sl()));
|
sl.registerFactory<RegisterConsultantCubit>(() => RegisterConsultantCubit(consultantRepository: sl()));
|
||||||
sl.registerFactory<DiaryCubit>(() => DiaryCubit(diaryRepository: sl(), glossaryRepository: sl()));
|
sl.registerFactory<DiaryCubit>(() => DiaryCubit(diaryRepository: sl(), glossaryRepository: sl()));
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ class Meta with _$Meta {
|
|||||||
@JsonKey(name: "per_page") required int perPage,
|
@JsonKey(name: "per_page") required int perPage,
|
||||||
@JsonKey(name: "to") required dynamic to,
|
@JsonKey(name: "to") required dynamic to,
|
||||||
@JsonKey(name: "total") required int total,
|
@JsonKey(name: "total") required int total,
|
||||||
|
@JsonKey(name: "total_min_price") int? totalMinPrice,
|
||||||
|
@JsonKey(name: "total_max_price") int? totalMaxPrice,
|
||||||
}) = _Meta;
|
}) = _Meta;
|
||||||
|
|
||||||
factory Meta.fromJson(Map<String, Object?> json) => _$MetaFromJson(json);
|
factory Meta.fromJson(Map<String, Object?> json) => _$MetaFromJson(json);
|
||||||
|
|||||||
@@ -262,6 +262,10 @@ mixin _$Meta {
|
|||||||
dynamic get to => throw _privateConstructorUsedError;
|
dynamic get to => throw _privateConstructorUsedError;
|
||||||
@JsonKey(name: "total")
|
@JsonKey(name: "total")
|
||||||
int get total => throw _privateConstructorUsedError;
|
int get total => throw _privateConstructorUsedError;
|
||||||
|
@JsonKey(name: "total_min_price")
|
||||||
|
int? get totalMinPrice => throw _privateConstructorUsedError;
|
||||||
|
@JsonKey(name: "total_max_price")
|
||||||
|
int? get totalMaxPrice => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
/// Serializes this Meta to a JSON map.
|
/// Serializes this Meta to a JSON map.
|
||||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||||
@@ -285,7 +289,9 @@ abstract class $MetaCopyWith<$Res> {
|
|||||||
@JsonKey(name: "path") String? path,
|
@JsonKey(name: "path") String? path,
|
||||||
@JsonKey(name: "per_page") int perPage,
|
@JsonKey(name: "per_page") int perPage,
|
||||||
@JsonKey(name: "to") dynamic to,
|
@JsonKey(name: "to") dynamic to,
|
||||||
@JsonKey(name: "total") int total});
|
@JsonKey(name: "total") int total,
|
||||||
|
@JsonKey(name: "total_min_price") int? totalMinPrice,
|
||||||
|
@JsonKey(name: "total_max_price") int? totalMaxPrice});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
@@ -311,6 +317,8 @@ class _$MetaCopyWithImpl<$Res, $Val extends Meta>
|
|||||||
Object? perPage = null,
|
Object? perPage = null,
|
||||||
Object? to = freezed,
|
Object? to = freezed,
|
||||||
Object? total = null,
|
Object? total = null,
|
||||||
|
Object? totalMinPrice = freezed,
|
||||||
|
Object? totalMaxPrice = freezed,
|
||||||
}) {
|
}) {
|
||||||
return _then(_value.copyWith(
|
return _then(_value.copyWith(
|
||||||
currentPage: null == currentPage
|
currentPage: null == currentPage
|
||||||
@@ -345,6 +353,14 @@ class _$MetaCopyWithImpl<$Res, $Val extends Meta>
|
|||||||
? _value.total
|
? _value.total
|
||||||
: total // ignore: cast_nullable_to_non_nullable
|
: total // ignore: cast_nullable_to_non_nullable
|
||||||
as int,
|
as int,
|
||||||
|
totalMinPrice: freezed == totalMinPrice
|
||||||
|
? _value.totalMinPrice
|
||||||
|
: totalMinPrice // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int?,
|
||||||
|
totalMaxPrice: freezed == totalMaxPrice
|
||||||
|
? _value.totalMaxPrice
|
||||||
|
: totalMaxPrice // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int?,
|
||||||
) as $Val);
|
) as $Val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -364,7 +380,9 @@ abstract class _$$MetaImplCopyWith<$Res> implements $MetaCopyWith<$Res> {
|
|||||||
@JsonKey(name: "path") String? path,
|
@JsonKey(name: "path") String? path,
|
||||||
@JsonKey(name: "per_page") int perPage,
|
@JsonKey(name: "per_page") int perPage,
|
||||||
@JsonKey(name: "to") dynamic to,
|
@JsonKey(name: "to") dynamic to,
|
||||||
@JsonKey(name: "total") int total});
|
@JsonKey(name: "total") int total,
|
||||||
|
@JsonKey(name: "total_min_price") int? totalMinPrice,
|
||||||
|
@JsonKey(name: "total_max_price") int? totalMaxPrice});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
@@ -387,6 +405,8 @@ class __$$MetaImplCopyWithImpl<$Res>
|
|||||||
Object? perPage = null,
|
Object? perPage = null,
|
||||||
Object? to = freezed,
|
Object? to = freezed,
|
||||||
Object? total = null,
|
Object? total = null,
|
||||||
|
Object? totalMinPrice = freezed,
|
||||||
|
Object? totalMaxPrice = freezed,
|
||||||
}) {
|
}) {
|
||||||
return _then(_$MetaImpl(
|
return _then(_$MetaImpl(
|
||||||
currentPage: null == currentPage
|
currentPage: null == currentPage
|
||||||
@@ -421,6 +441,14 @@ class __$$MetaImplCopyWithImpl<$Res>
|
|||||||
? _value.total
|
? _value.total
|
||||||
: total // ignore: cast_nullable_to_non_nullable
|
: total // ignore: cast_nullable_to_non_nullable
|
||||||
as int,
|
as int,
|
||||||
|
totalMinPrice: freezed == totalMinPrice
|
||||||
|
? _value.totalMinPrice
|
||||||
|
: totalMinPrice // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int?,
|
||||||
|
totalMaxPrice: freezed == totalMaxPrice
|
||||||
|
? _value.totalMaxPrice
|
||||||
|
: totalMaxPrice // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int?,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -436,7 +464,9 @@ class _$MetaImpl implements _Meta {
|
|||||||
@JsonKey(name: "path") this.path,
|
@JsonKey(name: "path") this.path,
|
||||||
@JsonKey(name: "per_page") required this.perPage,
|
@JsonKey(name: "per_page") required this.perPage,
|
||||||
@JsonKey(name: "to") required this.to,
|
@JsonKey(name: "to") required this.to,
|
||||||
@JsonKey(name: "total") required this.total})
|
@JsonKey(name: "total") required this.total,
|
||||||
|
@JsonKey(name: "total_min_price") this.totalMinPrice,
|
||||||
|
@JsonKey(name: "total_max_price") this.totalMaxPrice})
|
||||||
: _links = links;
|
: _links = links;
|
||||||
|
|
||||||
factory _$MetaImpl.fromJson(Map<String, dynamic> json) =>
|
factory _$MetaImpl.fromJson(Map<String, dynamic> json) =>
|
||||||
@@ -472,10 +502,16 @@ class _$MetaImpl implements _Meta {
|
|||||||
@override
|
@override
|
||||||
@JsonKey(name: "total")
|
@JsonKey(name: "total")
|
||||||
final int total;
|
final int total;
|
||||||
|
@override
|
||||||
|
@JsonKey(name: "total_min_price")
|
||||||
|
final int? totalMinPrice;
|
||||||
|
@override
|
||||||
|
@JsonKey(name: "total_max_price")
|
||||||
|
final int? totalMaxPrice;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'Meta(currentPage: $currentPage, from: $from, lastPage: $lastPage, links: $links, path: $path, perPage: $perPage, to: $to, total: $total)';
|
return 'Meta(currentPage: $currentPage, from: $from, lastPage: $lastPage, links: $links, path: $path, perPage: $perPage, to: $to, total: $total, totalMinPrice: $totalMinPrice, totalMaxPrice: $totalMaxPrice)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -492,7 +528,11 @@ class _$MetaImpl implements _Meta {
|
|||||||
(identical(other.path, path) || other.path == path) &&
|
(identical(other.path, path) || other.path == path) &&
|
||||||
(identical(other.perPage, perPage) || other.perPage == perPage) &&
|
(identical(other.perPage, perPage) || other.perPage == perPage) &&
|
||||||
const DeepCollectionEquality().equals(other.to, to) &&
|
const DeepCollectionEquality().equals(other.to, to) &&
|
||||||
(identical(other.total, total) || other.total == total));
|
(identical(other.total, total) || other.total == total) &&
|
||||||
|
(identical(other.totalMinPrice, totalMinPrice) ||
|
||||||
|
other.totalMinPrice == totalMinPrice) &&
|
||||||
|
(identical(other.totalMaxPrice, totalMaxPrice) ||
|
||||||
|
other.totalMaxPrice == totalMaxPrice));
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@@ -506,7 +546,9 @@ class _$MetaImpl implements _Meta {
|
|||||||
path,
|
path,
|
||||||
perPage,
|
perPage,
|
||||||
const DeepCollectionEquality().hash(to),
|
const DeepCollectionEquality().hash(to),
|
||||||
total);
|
total,
|
||||||
|
totalMinPrice,
|
||||||
|
totalMaxPrice);
|
||||||
|
|
||||||
/// Create a copy of Meta
|
/// Create a copy of Meta
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@@ -533,7 +575,9 @@ abstract class _Meta implements Meta {
|
|||||||
@JsonKey(name: "path") final String? path,
|
@JsonKey(name: "path") final String? path,
|
||||||
@JsonKey(name: "per_page") required final int perPage,
|
@JsonKey(name: "per_page") required final int perPage,
|
||||||
@JsonKey(name: "to") required final dynamic to,
|
@JsonKey(name: "to") required final dynamic to,
|
||||||
@JsonKey(name: "total") required final int total}) = _$MetaImpl;
|
@JsonKey(name: "total") required final int total,
|
||||||
|
@JsonKey(name: "total_min_price") final int? totalMinPrice,
|
||||||
|
@JsonKey(name: "total_max_price") final int? totalMaxPrice}) = _$MetaImpl;
|
||||||
|
|
||||||
factory _Meta.fromJson(Map<String, dynamic> json) = _$MetaImpl.fromJson;
|
factory _Meta.fromJson(Map<String, dynamic> json) = _$MetaImpl.fromJson;
|
||||||
|
|
||||||
@@ -561,6 +605,12 @@ abstract class _Meta implements Meta {
|
|||||||
@override
|
@override
|
||||||
@JsonKey(name: "total")
|
@JsonKey(name: "total")
|
||||||
int get total;
|
int get total;
|
||||||
|
@override
|
||||||
|
@JsonKey(name: "total_min_price")
|
||||||
|
int? get totalMinPrice;
|
||||||
|
@override
|
||||||
|
@JsonKey(name: "total_max_price")
|
||||||
|
int? get totalMaxPrice;
|
||||||
|
|
||||||
/// Create a copy of Meta
|
/// Create a copy of Meta
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ _$MetaImpl _$$MetaImplFromJson(Map<String, dynamic> json) => _$MetaImpl(
|
|||||||
perPage: (json['per_page'] as num).toInt(),
|
perPage: (json['per_page'] as num).toInt(),
|
||||||
to: json['to'],
|
to: json['to'],
|
||||||
total: (json['total'] as num).toInt(),
|
total: (json['total'] as num).toInt(),
|
||||||
|
totalMinPrice: (json['total_min_price'] as num?)?.toInt(),
|
||||||
|
totalMaxPrice: (json['total_max_price'] as num?)?.toInt(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> _$$MetaImplToJson(_$MetaImpl instance) =>
|
Map<String, dynamic> _$$MetaImplToJson(_$MetaImpl instance) =>
|
||||||
@@ -44,6 +46,8 @@ Map<String, dynamic> _$$MetaImplToJson(_$MetaImpl instance) =>
|
|||||||
'per_page': instance.perPage,
|
'per_page': instance.perPage,
|
||||||
'to': instance.to,
|
'to': instance.to,
|
||||||
'total': instance.total,
|
'total': instance.total,
|
||||||
|
'total_min_price': instance.totalMinPrice,
|
||||||
|
'total_max_price': instance.totalMaxPrice,
|
||||||
};
|
};
|
||||||
|
|
||||||
_$LinkImpl _$$LinkImplFromJson(Map<String, dynamic> json) => _$LinkImpl(
|
_$LinkImpl _$$LinkImplFromJson(Map<String, dynamic> json) => _$LinkImpl(
|
||||||
|
|||||||
@@ -13,12 +13,24 @@ extension DateTimeExtension on DateTime {
|
|||||||
}
|
}
|
||||||
return DateFormat(pattern, locale).format(this);
|
return DateFormat(pattern, locale).format(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isYesterday(DateTime date) {
|
||||||
|
final DateTime localDate = date.isUtc ? date.toLocal() : date;
|
||||||
|
final yesterday = DateTime.now().subtract(const Duration(days: 1));
|
||||||
|
return yesterday.day == localDate.day && yesterday.month == localDate.month && yesterday.year == localDate.year;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isToday(DateTime date) {
|
||||||
|
final DateTime localDate = date.isUtc ? date.toLocal() : date;
|
||||||
|
final now = DateTime.now();
|
||||||
|
return now.day == localDate.day && now.month == localDate.month && now.year == localDate.year;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String calculateDifference(DateTime dateTime) {
|
String calculateDifference(DateTime dateTime) {
|
||||||
if (dateTime.difference(DateTime.now()).inDays == 0) {
|
if (dateTime.isToday(dateTime)) {
|
||||||
return 'Сегодня';
|
return 'Сегодня';
|
||||||
} else if (dateTime.difference(DateTime.now()).inDays == -1) {
|
} else if (dateTime.isYesterday(dateTime)) {
|
||||||
return 'Вчера';
|
return 'Вчера';
|
||||||
} else {
|
} else {
|
||||||
return dateTime.format('d MMMM', 'ru');
|
return dateTime.format('d MMMM', 'ru');
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ class CustomTextFormField extends StatelessWidget {
|
|||||||
this.counter = const SizedBox.shrink(),
|
this.counter = const SizedBox.shrink(),
|
||||||
super.key,
|
super.key,
|
||||||
this.hasError = false,
|
this.hasError = false,
|
||||||
|
this.readOnly = false,
|
||||||
});
|
});
|
||||||
final Widget? counter;
|
final Widget? counter;
|
||||||
final Alignment? alignment;
|
final Alignment? alignment;
|
||||||
@@ -76,16 +77,18 @@ class CustomTextFormField extends StatelessWidget {
|
|||||||
final bool expands;
|
final bool expands;
|
||||||
final TextAlignVertical? textAlignVertical;
|
final TextAlignVertical? textAlignVertical;
|
||||||
final bool hasError;
|
final bool hasError;
|
||||||
|
final bool readOnly;
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Align(
|
return Align(
|
||||||
alignment: alignment ?? Alignment.center,
|
alignment: alignment ?? Alignment.center,
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
|
readOnly: readOnly,
|
||||||
textAlignVertical: textAlignVertical,
|
textAlignVertical: textAlignVertical,
|
||||||
expands: expands,
|
expands: expands,
|
||||||
minLines: minLines,
|
minLines: minLines,
|
||||||
maxLength: maxLength,
|
maxLength: maxLength,
|
||||||
onChanged: onChanged,
|
onChanged: onChanged,
|
||||||
controller: controller,
|
controller: controller,
|
||||||
focusNode: focusNode ?? null,
|
focusNode: focusNode ?? null,
|
||||||
autofocus: autofocus,
|
autofocus: autofocus,
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class CreateDietState with _$CreateDietState {
|
|||||||
isLoading: false,
|
isLoading: false,
|
||||||
errorMessage: null,
|
errorMessage: null,
|
||||||
dietData: CreateDietModel(
|
dietData: CreateDietModel(
|
||||||
type: Map.fromIterable(typeValues, value: (element) => false),
|
type: Map.fromIterable(dietTypeValues, value: (element) => false),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ class _DietTypeChoiceWidgetState extends State<DietTypeChoiceWidget> {
|
|||||||
super.didChangeDependencies();
|
super.didChangeDependencies();
|
||||||
final s = S.of(context);
|
final s = S.of(context);
|
||||||
if (choiceContent.isEmpty) {
|
if (choiceContent.isEmpty) {
|
||||||
|
|
||||||
choiceContent.add(
|
choiceContent.add(
|
||||||
(
|
(
|
||||||
'assets/icons/broccoli.svg',
|
'assets/icons/broccoli.svg',
|
||||||
@@ -41,7 +40,7 @@ class _DietTypeChoiceWidgetState extends State<DietTypeChoiceWidget> {
|
|||||||
'assets/icons/bread.svg',
|
'assets/icons/bread.svg',
|
||||||
s.carb_type,
|
s.carb_type,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
choiceContent.add(
|
choiceContent.add(
|
||||||
(
|
(
|
||||||
'assets/icons/meat2.svg',
|
'assets/icons/meat2.svg',
|
||||||
@@ -61,7 +60,7 @@ class _DietTypeChoiceWidgetState extends State<DietTypeChoiceWidget> {
|
|||||||
return Expanded(
|
return Expanded(
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
final key = typeValues[index];
|
final key = dietTypeValues[index];
|
||||||
final value = !(state.dietData.type[key] ?? true);
|
final value = !(state.dietData.type[key] ?? true);
|
||||||
context.read<CreateDietCubit>().setDietType(key: key, value: value);
|
context.read<CreateDietCubit>().setDietType(key: key, value: value);
|
||||||
},
|
},
|
||||||
@@ -69,10 +68,10 @@ class _DietTypeChoiceWidgetState extends State<DietTypeChoiceWidget> {
|
|||||||
margin: EdgeInsets.only(right: index == 3 ? 0 : 4),
|
margin: EdgeInsets.only(right: index == 3 ? 0 : 4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(5),
|
borderRadius: BorderRadius.circular(5),
|
||||||
color: state.dietData.type[typeValues[index]] == true ? appTheme.cardBackground : appTheme.whiteA700,
|
color: state.dietData.type[dietTypeValues[index]] == true ? appTheme.cardBackground : appTheme.whiteA700,
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
width: 1,
|
width: 1,
|
||||||
color: state.dietData.type[typeValues[index]] == true ? appTheme.blueA400 : appTheme.lightGray00,
|
color: state.dietData.type[dietTypeValues[index]] == true ? appTheme.blueA400 : appTheme.lightGray00,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
height: 80,
|
height: 80,
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class AboutDietCubit extends Cubit<AboutDietState> {
|
|||||||
try {
|
try {
|
||||||
final diet = await dietsRepository.getDiet(id);
|
final diet = await dietsRepository.getDiet(id);
|
||||||
if (index != null) {
|
if (index != null) {
|
||||||
dietsRepository.emitCommentsStateToFeed(DietCommentsState(index: index, totalComments: diet.totalComments));
|
dietsRepository.emitCommentsStateToFeed(CommentsState(index: index, totalComments: diet.totalComments));
|
||||||
}
|
}
|
||||||
emit(AboutDietState.loaded(diet));
|
emit(AboutDietState.loaded(diet));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -42,7 +42,7 @@ class AboutDietCubit extends Cubit<AboutDietState> {
|
|||||||
totalLikes: currentDiet.totalLikes - 1,
|
totalLikes: currentDiet.totalLikes - 1,
|
||||||
wasLikedByMe: false,
|
wasLikedByMe: false,
|
||||||
);
|
);
|
||||||
dietsRepository.emitLikesStateToFeed(DietLikesState(wasLikedByMe: updatedDiet.wasLikedByMe, totalLikes: updatedDiet.totalLikes, index: dietIndexInFeed));
|
dietsRepository.emitLikesStateToFeed(LikesState(wasLikedByMe: updatedDiet.wasLikedByMe, totalLikes: updatedDiet.totalLikes, index: dietIndexInFeed));
|
||||||
emit(AboutDietState.loaded(updatedDiet));
|
emit(AboutDietState.loaded(updatedDiet));
|
||||||
} else {
|
} else {
|
||||||
final updatedDiet = currentDiet.copyWith(
|
final updatedDiet = currentDiet.copyWith(
|
||||||
@@ -50,7 +50,7 @@ class AboutDietCubit extends Cubit<AboutDietState> {
|
|||||||
wasLikedByMe: true,
|
wasLikedByMe: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
dietsRepository.emitLikesStateToFeed(DietLikesState(wasLikedByMe: updatedDiet.wasLikedByMe, totalLikes: updatedDiet.totalLikes, index: dietIndexInFeed));
|
dietsRepository.emitLikesStateToFeed(LikesState(wasLikedByMe: updatedDiet.wasLikedByMe, totalLikes: updatedDiet.totalLikes, index: dietIndexInFeed));
|
||||||
emit(AboutDietState.loaded(updatedDiet));
|
emit(AboutDietState.loaded(updatedDiet));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,15 +62,14 @@ class AboutDietCubit extends Cubit<AboutDietState> {
|
|||||||
await dietsRepository.likeDiet(id);
|
await dietsRepository.likeDiet(id);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log(e.toString());
|
log(e.toString());
|
||||||
dietsRepository
|
dietsRepository.emitLikesStateToFeed(LikesState(wasLikedByMe: currentDiet.wasLikedByMe, totalLikes: currentDiet.totalLikes, index: dietIndexInFeed));
|
||||||
.emitLikesStateToFeed(DietLikesState(wasLikedByMe: currentDiet.wasLikedByMe, totalLikes: currentDiet.totalLikes, index: dietIndexInFeed));
|
|
||||||
emit(AboutDietState.loaded(currentDiet));
|
emit(AboutDietState.loaded(currentDiet));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> addToFavorite(int id, int dietIndexInFeed) async {
|
Future<void> addToFavorite(int id, int dietIndex) async {
|
||||||
if (state is! DietStateLoaded) return;
|
if (state is! DietStateLoaded) return;
|
||||||
|
|
||||||
final currentDiet = (state as DietStateLoaded).diet;
|
final currentDiet = (state as DietStateLoaded).diet;
|
||||||
@@ -78,11 +77,11 @@ class AboutDietCubit extends Cubit<AboutDietState> {
|
|||||||
final updatedDiet = currentDiet.copyWith(
|
final updatedDiet = currentDiet.copyWith(
|
||||||
isFavorite: true,
|
isFavorite: true,
|
||||||
);
|
);
|
||||||
dietsRepository.emitFavoriteStateToFeed(DietFavoriteState(isFavorite: updatedDiet.isFavorite, index: dietIndexInFeed));
|
dietsRepository.emitFavoriteStateToFeed(FavoriteState(isFavorite: updatedDiet.isFavorite, index: dietIndex));
|
||||||
emit(AboutDietState.loaded(updatedDiet));
|
emit(AboutDietState.loaded(updatedDiet));
|
||||||
|
|
||||||
EasyDebounce.debounce(
|
EasyDebounce.debounce(
|
||||||
'add_diet_to_favorites',
|
'add_or_remove_diet_to_favorites',
|
||||||
Duration(seconds: 1),
|
Duration(seconds: 1),
|
||||||
() async {
|
() async {
|
||||||
try {
|
try {
|
||||||
@@ -90,14 +89,14 @@ class AboutDietCubit extends Cubit<AboutDietState> {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
log(e.toString());
|
log(e.toString());
|
||||||
showErrorToast('Произошла ошибка. Попробуйте ещё раз через некоторое время');
|
showErrorToast('Произошла ошибка. Попробуйте ещё раз через некоторое время');
|
||||||
dietsRepository.emitFavoriteStateToFeed(DietFavoriteState(isFavorite: currentDiet.isFavorite, index: dietIndexInFeed));
|
dietsRepository.emitFavoriteStateToFeed(FavoriteState(isFavorite: currentDiet.isFavorite, index: dietIndex));
|
||||||
emit(AboutDietState.loaded(currentDiet));
|
emit(AboutDietState.loaded(currentDiet));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> removeFromFavorite(int id, int dietIndexInFeed) async {
|
Future<void> removeFromFavorite(int id, int dietIndex) async {
|
||||||
if (state is! DietStateLoaded) return;
|
if (state is! DietStateLoaded) return;
|
||||||
|
|
||||||
final currentDiet = (state as DietStateLoaded).diet;
|
final currentDiet = (state as DietStateLoaded).diet;
|
||||||
@@ -105,11 +104,11 @@ class AboutDietCubit extends Cubit<AboutDietState> {
|
|||||||
final updatedDiet = currentDiet.copyWith(
|
final updatedDiet = currentDiet.copyWith(
|
||||||
isFavorite: false,
|
isFavorite: false,
|
||||||
);
|
);
|
||||||
dietsRepository.emitFavoriteStateToFeed(DietFavoriteState(isFavorite: updatedDiet.isFavorite, index: dietIndexInFeed));
|
dietsRepository.emitFavoriteStateToFeed(FavoriteState(isFavorite: updatedDiet.isFavorite, index: dietIndex));
|
||||||
emit(AboutDietState.loaded(updatedDiet));
|
emit(AboutDietState.loaded(updatedDiet));
|
||||||
|
|
||||||
EasyDebounce.debounce(
|
EasyDebounce.debounce(
|
||||||
'remove_diet_from_favorites',
|
'add_or_remove_diet_to_favorites',
|
||||||
Duration(seconds: 1),
|
Duration(seconds: 1),
|
||||||
() async {
|
() async {
|
||||||
try {
|
try {
|
||||||
@@ -117,7 +116,7 @@ class AboutDietCubit extends Cubit<AboutDietState> {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
log(e.toString());
|
log(e.toString());
|
||||||
showErrorToast('Произошла ошибка. Попробуйте ещё раз через некоторое время');
|
showErrorToast('Произошла ошибка. Попробуйте ещё раз через некоторое время');
|
||||||
dietsRepository.emitFavoriteStateToFeed(DietFavoriteState(isFavorite: currentDiet.isFavorite, index: dietIndexInFeed));
|
dietsRepository.emitFavoriteStateToFeed(FavoriteState(isFavorite: currentDiet.isFavorite, index: dietIndex));
|
||||||
emit(AboutDietState.loaded(currentDiet));
|
emit(AboutDietState.loaded(currentDiet));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ class _AboutDietScreenState extends State<AboutDietScreen> {
|
|||||||
elevation: 0,
|
elevation: 0,
|
||||||
focusElevation: 0,
|
focusElevation: 0,
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(20),
|
||||||
),
|
),
|
||||||
backgroundColor: appTheme.greenA700,
|
backgroundColor: appTheme.greenA700,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
@@ -159,7 +159,8 @@ class _AboutDietScreenState extends State<AboutDietScreen> {
|
|||||||
width: 52,
|
width: 52,
|
||||||
height: 52,
|
height: 52,
|
||||||
size: 24,
|
size: 24,
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.rectangle,
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
color: appTheme.gray50,
|
color: appTheme.gray50,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -11,8 +11,7 @@ class AuthorCard extends StatelessWidget {
|
|||||||
final Author author;
|
final Author author;
|
||||||
final String title;
|
final String title;
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
// final s = S.of(context);
|
|
||||||
return Container(
|
return Container(
|
||||||
width: MediaQuery.sizeOf(context).width,
|
width: MediaQuery.sizeOf(context).width,
|
||||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(12), color: appTheme.cardBackground),
|
decoration: BoxDecoration(borderRadius: BorderRadius.circular(12), color: appTheme.cardBackground),
|
||||||
|
|||||||
@@ -32,39 +32,42 @@ class _ExpandableTextState extends State<ExpandableText> {
|
|||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Stack(
|
Padding(
|
||||||
children: [
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
Html(
|
child: Stack(
|
||||||
data: widget.text,
|
children: [
|
||||||
style: {
|
Html(
|
||||||
"*": Style(
|
data: widget.text,
|
||||||
color: Colors.black87,
|
style: {
|
||||||
maxLines: isExpanded ? null : widget.maxLines,
|
"*": Style(
|
||||||
textOverflow: TextOverflow.clip,
|
color: Colors.black87,
|
||||||
),
|
maxLines: isExpanded ? null : widget.maxLines,
|
||||||
},
|
textOverflow: TextOverflow.clip,
|
||||||
),
|
),
|
||||||
if (!isExpanded)
|
},
|
||||||
Positioned.fill(
|
),
|
||||||
child: Align(
|
if (!isExpanded)
|
||||||
alignment: Alignment.bottomCenter,
|
Positioned.fill(
|
||||||
child: Container(
|
child: Align(
|
||||||
height: 35,
|
alignment: Alignment.bottomCenter,
|
||||||
decoration: BoxDecoration(
|
child: Container(
|
||||||
gradient: LinearGradient(
|
height: 35,
|
||||||
begin: Alignment.topCenter,
|
decoration: BoxDecoration(
|
||||||
end: Alignment.bottomCenter,
|
gradient: LinearGradient(
|
||||||
colors: [
|
begin: Alignment.topCenter,
|
||||||
Colors.white.withOpacity(0.0),
|
end: Alignment.bottomCenter,
|
||||||
Colors.white.withOpacity(0.6),
|
colors: [
|
||||||
Colors.white,
|
Colors.white.withOpacity(0.0),
|
||||||
],
|
Colors.white.withOpacity(0.6),
|
||||||
|
Colors.white,
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
Align(
|
Align(
|
||||||
alignment: Alignment.bottomCenter,
|
alignment: Alignment.bottomCenter,
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ class DietRepository {
|
|||||||
DietRepository({required this.dietsService});
|
DietRepository({required this.dietsService});
|
||||||
final DietService dietsService;
|
final DietService dietsService;
|
||||||
|
|
||||||
final _dietLikeStateStreamController = StreamController<DietLikesState>.broadcast();
|
final _dietLikeStateStreamController = StreamController<LikesState>.broadcast();
|
||||||
final _dietFavoriteStateStreamController = StreamController<DietFavoriteState>.broadcast();
|
final _dietFavoriteStateStreamController = StreamController<FavoriteState>.broadcast();
|
||||||
final _dietCommentsStateStreamController = StreamController<DietCommentsState>.broadcast();
|
final _dietCommentsStateStreamController = StreamController<CommentsState>.broadcast();
|
||||||
Stream get dietLikeState => _dietLikeStateStreamController.stream;
|
Stream get dietLikeState => _dietLikeStateStreamController.stream;
|
||||||
Stream get dietFavoriteState => _dietFavoriteStateStreamController.stream;
|
Stream get dietFavoriteState => _dietFavoriteStateStreamController.stream;
|
||||||
Stream get dietCommentsState => _dietCommentsStateStreamController.stream;
|
Stream get dietCommentsState => _dietCommentsStateStreamController.stream;
|
||||||
@@ -49,17 +49,17 @@ class DietRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void emitLikesStateToFeed(DietLikesState state) => _dietLikeStateStreamController.add(state);
|
void emitLikesStateToFeed(LikesState state) => _dietLikeStateStreamController.add(state);
|
||||||
void emitFavoriteStateToFeed(DietFavoriteState state) => _dietFavoriteStateStreamController.add(state);
|
void emitFavoriteStateToFeed(FavoriteState state) => _dietFavoriteStateStreamController.add(state);
|
||||||
void emitCommentsStateToFeed(DietCommentsState state) => _dietCommentsStateStreamController.add(state);
|
void emitCommentsStateToFeed(CommentsState state) => _dietCommentsStateStreamController.add(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
class DietLikesState extends Equatable {
|
class LikesState extends Equatable {
|
||||||
final bool wasLikedByMe;
|
final bool wasLikedByMe;
|
||||||
final int totalLikes;
|
final int totalLikes;
|
||||||
final int index;
|
final int index;
|
||||||
|
|
||||||
DietLikesState({
|
LikesState({
|
||||||
required this.wasLikedByMe,
|
required this.wasLikedByMe,
|
||||||
required this.totalLikes,
|
required this.totalLikes,
|
||||||
required this.index,
|
required this.index,
|
||||||
@@ -73,11 +73,11 @@ class DietLikesState extends Equatable {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
class DietFavoriteState extends Equatable {
|
class FavoriteState extends Equatable {
|
||||||
final bool isFavorite;
|
final bool isFavorite;
|
||||||
final int index;
|
final int index;
|
||||||
|
|
||||||
DietFavoriteState({
|
FavoriteState({
|
||||||
required this.isFavorite,
|
required this.isFavorite,
|
||||||
required this.index,
|
required this.index,
|
||||||
});
|
});
|
||||||
@@ -89,11 +89,11 @@ class DietFavoriteState extends Equatable {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
class DietCommentsState extends Equatable {
|
class CommentsState extends Equatable {
|
||||||
final int index;
|
final int index;
|
||||||
final int totalComments;
|
final int totalComments;
|
||||||
|
|
||||||
DietCommentsState({
|
CommentsState({
|
||||||
required this.index,
|
required this.index,
|
||||||
required this.totalComments,
|
required this.totalComments,
|
||||||
});
|
});
|
||||||
@@ -103,4 +103,4 @@ class DietCommentsState extends Equatable {
|
|||||||
index,
|
index,
|
||||||
totalComments,
|
totalComments,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ class FeedBlogPostModel with _$FeedBlogPostModel {
|
|||||||
required String description,
|
required String description,
|
||||||
required Author author,
|
required Author author,
|
||||||
@JsonKey(name: "video_url") required String videoUrl,
|
@JsonKey(name: "video_url") required String videoUrl,
|
||||||
@JsonKey(name: "created_at") required DateTime createdAt,
|
@JsonKey(name: "created_at", fromJson: parseDate) required DateTime createdAt,
|
||||||
@JsonKey(name: "updated_at") required DateTime updatedAt,
|
@JsonKey(name: "updated_at", fromJson: parseDate) required DateTime updatedAt,
|
||||||
required String title,
|
required String title,
|
||||||
@JsonKey(name: "total_likes") required int totalLikes,
|
@JsonKey(name: "total_likes") required int totalLikes,
|
||||||
@JsonKey(name: "total_views") required int totalViews,
|
@JsonKey(name: "total_views") required int totalViews,
|
||||||
@@ -24,3 +24,5 @@ class FeedBlogPostModel with _$FeedBlogPostModel {
|
|||||||
|
|
||||||
factory FeedBlogPostModel.fromJson(Map<String, Object?> json) => _$FeedBlogPostModelFromJson(json);
|
factory FeedBlogPostModel.fromJson(Map<String, Object?> json) => _$FeedBlogPostModelFromJson(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DateTime parseDate(String date) => DateTime.parse(date).toLocal();
|
||||||
@@ -27,9 +27,9 @@ mixin _$FeedBlogPostModel {
|
|||||||
Author get author => throw _privateConstructorUsedError;
|
Author get author => throw _privateConstructorUsedError;
|
||||||
@JsonKey(name: "video_url")
|
@JsonKey(name: "video_url")
|
||||||
String get videoUrl => throw _privateConstructorUsedError;
|
String get videoUrl => throw _privateConstructorUsedError;
|
||||||
@JsonKey(name: "created_at")
|
@JsonKey(name: "created_at", fromJson: parseDate)
|
||||||
DateTime get createdAt => throw _privateConstructorUsedError;
|
DateTime get createdAt => throw _privateConstructorUsedError;
|
||||||
@JsonKey(name: "updated_at")
|
@JsonKey(name: "updated_at", fromJson: parseDate)
|
||||||
DateTime get updatedAt => throw _privateConstructorUsedError;
|
DateTime get updatedAt => throw _privateConstructorUsedError;
|
||||||
String get title => throw _privateConstructorUsedError;
|
String get title => throw _privateConstructorUsedError;
|
||||||
@JsonKey(name: "total_likes")
|
@JsonKey(name: "total_likes")
|
||||||
@@ -64,8 +64,8 @@ abstract class $FeedBlogPostModelCopyWith<$Res> {
|
|||||||
String description,
|
String description,
|
||||||
Author author,
|
Author author,
|
||||||
@JsonKey(name: "video_url") String videoUrl,
|
@JsonKey(name: "video_url") String videoUrl,
|
||||||
@JsonKey(name: "created_at") DateTime createdAt,
|
@JsonKey(name: "created_at", fromJson: parseDate) DateTime createdAt,
|
||||||
@JsonKey(name: "updated_at") DateTime updatedAt,
|
@JsonKey(name: "updated_at", fromJson: parseDate) DateTime updatedAt,
|
||||||
String title,
|
String title,
|
||||||
@JsonKey(name: "total_likes") int totalLikes,
|
@JsonKey(name: "total_likes") int totalLikes,
|
||||||
@JsonKey(name: "total_views") int totalViews,
|
@JsonKey(name: "total_views") int totalViews,
|
||||||
@@ -186,8 +186,8 @@ abstract class _$$FeedBlogPostModelImplCopyWith<$Res>
|
|||||||
String description,
|
String description,
|
||||||
Author author,
|
Author author,
|
||||||
@JsonKey(name: "video_url") String videoUrl,
|
@JsonKey(name: "video_url") String videoUrl,
|
||||||
@JsonKey(name: "created_at") DateTime createdAt,
|
@JsonKey(name: "created_at", fromJson: parseDate) DateTime createdAt,
|
||||||
@JsonKey(name: "updated_at") DateTime updatedAt,
|
@JsonKey(name: "updated_at", fromJson: parseDate) DateTime updatedAt,
|
||||||
String title,
|
String title,
|
||||||
@JsonKey(name: "total_likes") int totalLikes,
|
@JsonKey(name: "total_likes") int totalLikes,
|
||||||
@JsonKey(name: "total_views") int totalViews,
|
@JsonKey(name: "total_views") int totalViews,
|
||||||
@@ -292,8 +292,8 @@ class _$FeedBlogPostModelImpl implements _FeedBlogPostModel {
|
|||||||
required this.description,
|
required this.description,
|
||||||
required this.author,
|
required this.author,
|
||||||
@JsonKey(name: "video_url") required this.videoUrl,
|
@JsonKey(name: "video_url") required this.videoUrl,
|
||||||
@JsonKey(name: "created_at") required this.createdAt,
|
@JsonKey(name: "created_at", fromJson: parseDate) required this.createdAt,
|
||||||
@JsonKey(name: "updated_at") required this.updatedAt,
|
@JsonKey(name: "updated_at", fromJson: parseDate) required this.updatedAt,
|
||||||
required this.title,
|
required this.title,
|
||||||
@JsonKey(name: "total_likes") required this.totalLikes,
|
@JsonKey(name: "total_likes") required this.totalLikes,
|
||||||
@JsonKey(name: "total_views") required this.totalViews,
|
@JsonKey(name: "total_views") required this.totalViews,
|
||||||
@@ -317,10 +317,10 @@ class _$FeedBlogPostModelImpl implements _FeedBlogPostModel {
|
|||||||
@JsonKey(name: "video_url")
|
@JsonKey(name: "video_url")
|
||||||
final String videoUrl;
|
final String videoUrl;
|
||||||
@override
|
@override
|
||||||
@JsonKey(name: "created_at")
|
@JsonKey(name: "created_at", fromJson: parseDate)
|
||||||
final DateTime createdAt;
|
final DateTime createdAt;
|
||||||
@override
|
@override
|
||||||
@JsonKey(name: "updated_at")
|
@JsonKey(name: "updated_at", fromJson: parseDate)
|
||||||
final DateTime updatedAt;
|
final DateTime updatedAt;
|
||||||
@override
|
@override
|
||||||
final String title;
|
final String title;
|
||||||
@@ -413,8 +413,10 @@ abstract class _FeedBlogPostModel implements FeedBlogPostModel {
|
|||||||
required final String description,
|
required final String description,
|
||||||
required final Author author,
|
required final Author author,
|
||||||
@JsonKey(name: "video_url") required final String videoUrl,
|
@JsonKey(name: "video_url") required final String videoUrl,
|
||||||
@JsonKey(name: "created_at") required final DateTime createdAt,
|
@JsonKey(name: "created_at", fromJson: parseDate)
|
||||||
@JsonKey(name: "updated_at") required final DateTime updatedAt,
|
required final DateTime createdAt,
|
||||||
|
@JsonKey(name: "updated_at", fromJson: parseDate)
|
||||||
|
required final DateTime updatedAt,
|
||||||
required final String title,
|
required final String title,
|
||||||
@JsonKey(name: "total_likes") required final int totalLikes,
|
@JsonKey(name: "total_likes") required final int totalLikes,
|
||||||
@JsonKey(name: "total_views") required final int totalViews,
|
@JsonKey(name: "total_views") required final int totalViews,
|
||||||
@@ -439,10 +441,10 @@ abstract class _FeedBlogPostModel implements FeedBlogPostModel {
|
|||||||
@JsonKey(name: "video_url")
|
@JsonKey(name: "video_url")
|
||||||
String get videoUrl;
|
String get videoUrl;
|
||||||
@override
|
@override
|
||||||
@JsonKey(name: "created_at")
|
@JsonKey(name: "created_at", fromJson: parseDate)
|
||||||
DateTime get createdAt;
|
DateTime get createdAt;
|
||||||
@override
|
@override
|
||||||
@JsonKey(name: "updated_at")
|
@JsonKey(name: "updated_at", fromJson: parseDate)
|
||||||
DateTime get updatedAt;
|
DateTime get updatedAt;
|
||||||
@override
|
@override
|
||||||
String get title;
|
String get title;
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ _$FeedBlogPostModelImpl _$$FeedBlogPostModelImplFromJson(
|
|||||||
description: json['description'] as String,
|
description: json['description'] as String,
|
||||||
author: Author.fromJson(json['author'] as Map<String, dynamic>),
|
author: Author.fromJson(json['author'] as Map<String, dynamic>),
|
||||||
videoUrl: json['video_url'] as String,
|
videoUrl: json['video_url'] as String,
|
||||||
createdAt: DateTime.parse(json['created_at'] as String),
|
createdAt: parseDate(json['created_at'] as String),
|
||||||
updatedAt: DateTime.parse(json['updated_at'] as String),
|
updatedAt: parseDate(json['updated_at'] as String),
|
||||||
title: json['title'] as String,
|
title: json['title'] as String,
|
||||||
totalLikes: (json['total_likes'] as num).toInt(),
|
totalLikes: (json['total_likes'] as num).toInt(),
|
||||||
totalViews: (json['total_views'] as num).toInt(),
|
totalViews: (json['total_views'] as num).toInt(),
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ class PlanModel with _$PlanModel {
|
|||||||
@JsonKey(name: "total_views") required int totalViews,
|
@JsonKey(name: "total_views") required int totalViews,
|
||||||
@JsonKey(name: "total_comments") required int totalComments,
|
@JsonKey(name: "total_comments") required int totalComments,
|
||||||
@JsonKey(name: "is_favorite") required bool isFavorite,
|
@JsonKey(name: "is_favorite") required bool isFavorite,
|
||||||
|
@Default(false) bool wasLikedByMe,
|
||||||
}) = _PlanModel;
|
}) = _PlanModel;
|
||||||
|
|
||||||
factory PlanModel.fromJson(Map<String, dynamic> json) => _$PlanModelFromJson(json);
|
factory PlanModel.fromJson(Map<String, dynamic> json) => _$PlanModelFromJson(json);
|
||||||
|
|||||||
@@ -294,6 +294,7 @@ mixin _$PlanModel {
|
|||||||
int get totalComments => throw _privateConstructorUsedError;
|
int get totalComments => throw _privateConstructorUsedError;
|
||||||
@JsonKey(name: "is_favorite")
|
@JsonKey(name: "is_favorite")
|
||||||
bool get isFavorite => throw _privateConstructorUsedError;
|
bool get isFavorite => throw _privateConstructorUsedError;
|
||||||
|
bool get wasLikedByMe => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
/// Serializes this PlanModel to a JSON map.
|
/// Serializes this PlanModel to a JSON map.
|
||||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||||
@@ -327,7 +328,8 @@ abstract class $PlanModelCopyWith<$Res> {
|
|||||||
@JsonKey(name: "total_likes") int totalLikes,
|
@JsonKey(name: "total_likes") int totalLikes,
|
||||||
@JsonKey(name: "total_views") int totalViews,
|
@JsonKey(name: "total_views") int totalViews,
|
||||||
@JsonKey(name: "total_comments") int totalComments,
|
@JsonKey(name: "total_comments") int totalComments,
|
||||||
@JsonKey(name: "is_favorite") bool isFavorite});
|
@JsonKey(name: "is_favorite") bool isFavorite,
|
||||||
|
bool wasLikedByMe});
|
||||||
|
|
||||||
$PlanTypeCopyWith<$Res> get type;
|
$PlanTypeCopyWith<$Res> get type;
|
||||||
$PacketOptionsCopyWith<$Res>? get packet1Options;
|
$PacketOptionsCopyWith<$Res>? get packet1Options;
|
||||||
@@ -368,6 +370,7 @@ class _$PlanModelCopyWithImpl<$Res, $Val extends PlanModel>
|
|||||||
Object? totalViews = null,
|
Object? totalViews = null,
|
||||||
Object? totalComments = null,
|
Object? totalComments = null,
|
||||||
Object? isFavorite = null,
|
Object? isFavorite = null,
|
||||||
|
Object? wasLikedByMe = null,
|
||||||
}) {
|
}) {
|
||||||
return _then(_value.copyWith(
|
return _then(_value.copyWith(
|
||||||
id: null == id
|
id: null == id
|
||||||
@@ -438,6 +441,10 @@ class _$PlanModelCopyWithImpl<$Res, $Val extends PlanModel>
|
|||||||
? _value.isFavorite
|
? _value.isFavorite
|
||||||
: isFavorite // ignore: cast_nullable_to_non_nullable
|
: isFavorite // ignore: cast_nullable_to_non_nullable
|
||||||
as bool,
|
as bool,
|
||||||
|
wasLikedByMe: null == wasLikedByMe
|
||||||
|
? _value.wasLikedByMe
|
||||||
|
: wasLikedByMe // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
) as $Val);
|
) as $Val);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -529,7 +536,8 @@ abstract class _$$PlanModelImplCopyWith<$Res>
|
|||||||
@JsonKey(name: "total_likes") int totalLikes,
|
@JsonKey(name: "total_likes") int totalLikes,
|
||||||
@JsonKey(name: "total_views") int totalViews,
|
@JsonKey(name: "total_views") int totalViews,
|
||||||
@JsonKey(name: "total_comments") int totalComments,
|
@JsonKey(name: "total_comments") int totalComments,
|
||||||
@JsonKey(name: "is_favorite") bool isFavorite});
|
@JsonKey(name: "is_favorite") bool isFavorite,
|
||||||
|
bool wasLikedByMe});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
$PlanTypeCopyWith<$Res> get type;
|
$PlanTypeCopyWith<$Res> get type;
|
||||||
@@ -573,6 +581,7 @@ class __$$PlanModelImplCopyWithImpl<$Res>
|
|||||||
Object? totalViews = null,
|
Object? totalViews = null,
|
||||||
Object? totalComments = null,
|
Object? totalComments = null,
|
||||||
Object? isFavorite = null,
|
Object? isFavorite = null,
|
||||||
|
Object? wasLikedByMe = null,
|
||||||
}) {
|
}) {
|
||||||
return _then(_$PlanModelImpl(
|
return _then(_$PlanModelImpl(
|
||||||
id: null == id
|
id: null == id
|
||||||
@@ -643,6 +652,10 @@ class __$$PlanModelImplCopyWithImpl<$Res>
|
|||||||
? _value.isFavorite
|
? _value.isFavorite
|
||||||
: isFavorite // ignore: cast_nullable_to_non_nullable
|
: isFavorite // ignore: cast_nullable_to_non_nullable
|
||||||
as bool,
|
as bool,
|
||||||
|
wasLikedByMe: null == wasLikedByMe
|
||||||
|
? _value.wasLikedByMe
|
||||||
|
: wasLikedByMe // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -667,7 +680,8 @@ class _$PlanModelImpl implements _PlanModel {
|
|||||||
@JsonKey(name: "total_likes") required this.totalLikes,
|
@JsonKey(name: "total_likes") required this.totalLikes,
|
||||||
@JsonKey(name: "total_views") required this.totalViews,
|
@JsonKey(name: "total_views") required this.totalViews,
|
||||||
@JsonKey(name: "total_comments") required this.totalComments,
|
@JsonKey(name: "total_comments") required this.totalComments,
|
||||||
@JsonKey(name: "is_favorite") required this.isFavorite});
|
@JsonKey(name: "is_favorite") required this.isFavorite,
|
||||||
|
this.wasLikedByMe = false});
|
||||||
|
|
||||||
factory _$PlanModelImpl.fromJson(Map<String, dynamic> json) =>
|
factory _$PlanModelImpl.fromJson(Map<String, dynamic> json) =>
|
||||||
_$$PlanModelImplFromJson(json);
|
_$$PlanModelImplFromJson(json);
|
||||||
@@ -723,10 +737,13 @@ class _$PlanModelImpl implements _PlanModel {
|
|||||||
@override
|
@override
|
||||||
@JsonKey(name: "is_favorite")
|
@JsonKey(name: "is_favorite")
|
||||||
final bool isFavorite;
|
final bool isFavorite;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
final bool wasLikedByMe;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'PlanModel(id: $id, slug: $slug, name: $name, image: $image, duration: $duration, state: $state, type: $type, details: $details, packetsCount: $packetsCount, packet1Options: $packet1Options, packet2Options: $packet2Options, packet3Options: $packet3Options, author: $author, totalLikes: $totalLikes, totalViews: $totalViews, totalComments: $totalComments, isFavorite: $isFavorite)';
|
return 'PlanModel(id: $id, slug: $slug, name: $name, image: $image, duration: $duration, state: $state, type: $type, details: $details, packetsCount: $packetsCount, packet1Options: $packet1Options, packet2Options: $packet2Options, packet3Options: $packet3Options, author: $author, totalLikes: $totalLikes, totalViews: $totalViews, totalComments: $totalComments, isFavorite: $isFavorite, wasLikedByMe: $wasLikedByMe)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -759,7 +776,9 @@ class _$PlanModelImpl implements _PlanModel {
|
|||||||
(identical(other.totalComments, totalComments) ||
|
(identical(other.totalComments, totalComments) ||
|
||||||
other.totalComments == totalComments) &&
|
other.totalComments == totalComments) &&
|
||||||
(identical(other.isFavorite, isFavorite) ||
|
(identical(other.isFavorite, isFavorite) ||
|
||||||
other.isFavorite == isFavorite));
|
other.isFavorite == isFavorite) &&
|
||||||
|
(identical(other.wasLikedByMe, wasLikedByMe) ||
|
||||||
|
other.wasLikedByMe == wasLikedByMe));
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@@ -782,7 +801,8 @@ class _$PlanModelImpl implements _PlanModel {
|
|||||||
totalLikes,
|
totalLikes,
|
||||||
totalViews,
|
totalViews,
|
||||||
totalComments,
|
totalComments,
|
||||||
isFavorite);
|
isFavorite,
|
||||||
|
wasLikedByMe);
|
||||||
|
|
||||||
/// Create a copy of PlanModel
|
/// Create a copy of PlanModel
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@@ -802,24 +822,24 @@ class _$PlanModelImpl implements _PlanModel {
|
|||||||
|
|
||||||
abstract class _PlanModel implements PlanModel {
|
abstract class _PlanModel implements PlanModel {
|
||||||
const factory _PlanModel(
|
const factory _PlanModel(
|
||||||
{@JsonKey(name: "id") required final int id,
|
{@JsonKey(name: "id") required final int id,
|
||||||
@JsonKey(name: "slug") required final String slug,
|
@JsonKey(name: "slug") required final String slug,
|
||||||
@JsonKey(name: "name") required final String name,
|
@JsonKey(name: "name") required final String name,
|
||||||
@JsonKey(name: "image") required final String image,
|
@JsonKey(name: "image") required final String image,
|
||||||
@JsonKey(name: "duration") required final int duration,
|
@JsonKey(name: "duration") required final int duration,
|
||||||
@JsonKey(name: "state") required final String state,
|
@JsonKey(name: "state") required final String state,
|
||||||
@JsonKey(name: "type") required final PlanType type,
|
@JsonKey(name: "type") required final PlanType type,
|
||||||
@JsonKey(name: "details") required final String details,
|
@JsonKey(name: "details") required final String details,
|
||||||
@JsonKey(name: "packets_count") required final int packetsCount,
|
@JsonKey(name: "packets_count") required final int packetsCount,
|
||||||
@JsonKey(name: "packet1_options") final PacketOptions? packet1Options,
|
@JsonKey(name: "packet1_options") final PacketOptions? packet1Options,
|
||||||
@JsonKey(name: "packet2_options") final PacketOptions? packet2Options,
|
@JsonKey(name: "packet2_options") final PacketOptions? packet2Options,
|
||||||
@JsonKey(name: "packet3_options") final PacketOptions? packet3Options,
|
@JsonKey(name: "packet3_options") final PacketOptions? packet3Options,
|
||||||
@JsonKey(name: "author") required final Author author,
|
@JsonKey(name: "author") required final Author author,
|
||||||
@JsonKey(name: "total_likes") required final int totalLikes,
|
@JsonKey(name: "total_likes") required final int totalLikes,
|
||||||
@JsonKey(name: "total_views") required final int totalViews,
|
@JsonKey(name: "total_views") required final int totalViews,
|
||||||
@JsonKey(name: "total_comments") required final int totalComments,
|
@JsonKey(name: "total_comments") required final int totalComments,
|
||||||
@JsonKey(name: "is_favorite") required final bool isFavorite}) =
|
@JsonKey(name: "is_favorite") required final bool isFavorite,
|
||||||
_$PlanModelImpl;
|
final bool wasLikedByMe}) = _$PlanModelImpl;
|
||||||
|
|
||||||
factory _PlanModel.fromJson(Map<String, dynamic> json) =
|
factory _PlanModel.fromJson(Map<String, dynamic> json) =
|
||||||
_$PlanModelImpl.fromJson;
|
_$PlanModelImpl.fromJson;
|
||||||
@@ -875,6 +895,8 @@ abstract class _PlanModel implements PlanModel {
|
|||||||
@override
|
@override
|
||||||
@JsonKey(name: "is_favorite")
|
@JsonKey(name: "is_favorite")
|
||||||
bool get isFavorite;
|
bool get isFavorite;
|
||||||
|
@override
|
||||||
|
bool get wasLikedByMe;
|
||||||
|
|
||||||
/// Create a copy of PlanModel
|
/// Create a copy of PlanModel
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ _$PlanModelImpl _$$PlanModelImplFromJson(Map<String, dynamic> json) =>
|
|||||||
totalViews: (json['total_views'] as num).toInt(),
|
totalViews: (json['total_views'] as num).toInt(),
|
||||||
totalComments: (json['total_comments'] as num).toInt(),
|
totalComments: (json['total_comments'] as num).toInt(),
|
||||||
isFavorite: json['is_favorite'] as bool,
|
isFavorite: json['is_favorite'] as bool,
|
||||||
|
wasLikedByMe: json['wasLikedByMe'] as bool? ?? false,
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> _$$PlanModelImplToJson(_$PlanModelImpl instance) =>
|
Map<String, dynamic> _$$PlanModelImplToJson(_$PlanModelImpl instance) =>
|
||||||
@@ -73,6 +74,7 @@ Map<String, dynamic> _$$PlanModelImplToJson(_$PlanModelImpl instance) =>
|
|||||||
'total_views': instance.totalViews,
|
'total_views': instance.totalViews,
|
||||||
'total_comments': instance.totalComments,
|
'total_comments': instance.totalComments,
|
||||||
'is_favorite': instance.isFavorite,
|
'is_favorite': instance.isFavorite,
|
||||||
|
'wasLikedByMe': instance.wasLikedByMe,
|
||||||
};
|
};
|
||||||
|
|
||||||
_$PacketOptionsImpl _$$PacketOptionsImplFromJson(Map<String, dynamic> json) =>
|
_$PacketOptionsImpl _$$PacketOptionsImplFromJson(Map<String, dynamic> json) =>
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class DietsCubit extends Cubit<DietsState> {
|
|||||||
'name:desc',
|
'name:desc',
|
||||||
];
|
];
|
||||||
static const List<int?> _durationValues = [null, ...durationValues];
|
static const List<int?> _durationValues = [null, ...durationValues];
|
||||||
static const List<String?> _types = [null, ...typeValues];
|
static const List<String?> _types = [null, ...dietTypeValues];
|
||||||
late final StreamSubscription likesStateSubscription;
|
late final StreamSubscription likesStateSubscription;
|
||||||
late final StreamSubscription favoriteStateSubscription;
|
late final StreamSubscription favoriteStateSubscription;
|
||||||
late final StreamSubscription commentsStateSubscription;
|
late final StreamSubscription commentsStateSubscription;
|
||||||
@@ -54,17 +54,35 @@ class DietsCubit extends Cubit<DietsState> {
|
|||||||
|
|
||||||
void changeTypeFilter(int index) {
|
void changeTypeFilter(int index) {
|
||||||
emit(state.copyWith(filterState: state.filterState.copyWith(typeIndex: index), errorMessage: null));
|
emit(state.copyWith(filterState: state.filterState.copyWith(typeIndex: index), errorMessage: null));
|
||||||
loadDiets(query: _query);
|
EasyDebounce.debounce(
|
||||||
|
'loadDiets',
|
||||||
|
Duration(seconds: 1),
|
||||||
|
() => loadDiets(
|
||||||
|
query: _query,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void changeSortingType(int index) {
|
void changeSortingType(int index) {
|
||||||
emit(state.copyWith(filterState: state.filterState.copyWith(sortingIndex: index), errorMessage: null));
|
emit(state.copyWith(filterState: state.filterState.copyWith(sortingIndex: index), errorMessage: null));
|
||||||
loadDiets(query: _query);
|
EasyDebounce.debounce(
|
||||||
|
'loadDiets',
|
||||||
|
Duration(seconds: 1),
|
||||||
|
() => loadDiets(
|
||||||
|
query: _query,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void changeDurationFilter(int index) {
|
void changeDurationFilter(int index) {
|
||||||
emit(state.copyWith(filterState: state.filterState.copyWith(durationIndex: index), errorMessage: null));
|
emit(state.copyWith(filterState: state.filterState.copyWith(durationIndex: index), errorMessage: null));
|
||||||
loadDiets(query: _query);
|
EasyDebounce.debounce(
|
||||||
|
'loadDiets',
|
||||||
|
Duration(seconds: 1),
|
||||||
|
() => loadDiets(
|
||||||
|
query: _query,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
loadDiets({
|
loadDiets({
|
||||||
@@ -82,9 +100,8 @@ class DietsCubit extends Cubit<DietsState> {
|
|||||||
sorting: sorting,
|
sorting: sorting,
|
||||||
);
|
);
|
||||||
lastPage = dietsPagination.meta.lastPage;
|
lastPage = dietsPagination.meta.lastPage;
|
||||||
|
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(dietsList: dietsPagination.data, isLoading: isLoading, errorMessage: null),
|
state.copyWith(dietsList: dietsPagination.data, isLoading: isLoading, errorMessage: null,),
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
emit(
|
emit(
|
||||||
@@ -181,7 +198,7 @@ class DietsCubit extends Cubit<DietsState> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _changeDietLikeState(DietLikesState likesState) {
|
void _changeDietLikeState(LikesState likesState) {
|
||||||
final currentDiets = state.dietsList;
|
final currentDiets = state.dietsList;
|
||||||
final dietsForModification = [...currentDiets];
|
final dietsForModification = [...currentDiets];
|
||||||
final currentDiet = currentDiets[likesState.index];
|
final currentDiet = currentDiets[likesState.index];
|
||||||
@@ -201,7 +218,7 @@ class DietsCubit extends Cubit<DietsState> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _changeDietFavoriteState(DietFavoriteState favoriteState) {
|
void _changeDietFavoriteState(FavoriteState favoriteState) {
|
||||||
final currentDiets = state.dietsList;
|
final currentDiets = state.dietsList;
|
||||||
final dietsForModification = [...currentDiets];
|
final dietsForModification = [...currentDiets];
|
||||||
final currentDiet = currentDiets[favoriteState.index];
|
final currentDiet = currentDiets[favoriteState.index];
|
||||||
@@ -220,7 +237,7 @@ class DietsCubit extends Cubit<DietsState> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _changeDietCommentsState(DietCommentsState commentsState) {
|
void _changeDietCommentsState(CommentsState commentsState) {
|
||||||
final currentDiets = state.dietsList;
|
final currentDiets = state.dietsList;
|
||||||
final dietsForModification = [...currentDiets];
|
final dietsForModification = [...currentDiets];
|
||||||
final currentDiet = currentDiets[commentsState.index];
|
final currentDiet = currentDiets[commentsState.index];
|
||||||
@@ -261,7 +278,7 @@ class DietsCubit extends Cubit<DietsState> {
|
|||||||
Duration(milliseconds: 200),
|
Duration(milliseconds: 200),
|
||||||
() async {
|
() async {
|
||||||
try {
|
try {
|
||||||
await dietsRepository.addToFavorite(id);
|
await dietsRepository.addToFavorite(id);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log(e.toString());
|
log(e.toString());
|
||||||
showErrorToast('Произошла ошибка. Попробуйте ещё раз через некоторое время');
|
showErrorToast('Произошла ошибка. Попробуйте ещё раз через некоторое время');
|
||||||
@@ -301,7 +318,7 @@ class DietsCubit extends Cubit<DietsState> {
|
|||||||
Duration(milliseconds: 200),
|
Duration(milliseconds: 200),
|
||||||
() async {
|
() async {
|
||||||
try {
|
try {
|
||||||
await dietsRepository.removeFromFavorite(id);
|
await dietsRepository.removeFromFavorite(id);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log(e.toString());
|
log(e.toString());
|
||||||
showErrorToast('Произошла ошибка. Попробуйте ещё раз через некоторое время');
|
showErrorToast('Произошла ошибка. Попробуйте ещё раз через некоторое время');
|
||||||
|
|||||||
@@ -1,41 +1,176 @@
|
|||||||
|
import 'dart:async';
|
||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
|
|
||||||
|
import 'package:easy_debounce/easy_debounce.dart';
|
||||||
|
import 'package:eatvio_app/core/const.dart';
|
||||||
|
import 'package:eatvio_app/core/utils/toasts.dart';
|
||||||
|
import 'package:eatvio_app/features/diets/repositories/diet_repository.dart';
|
||||||
import 'package:eatvio_app/features/feed/presentation/bloc/plans_cubit/plans_state.dart';
|
import 'package:eatvio_app/features/feed/presentation/bloc/plans_cubit/plans_state.dart';
|
||||||
import 'package:eatvio_app/features/feed/repositories/feed_repository.dart';
|
import 'package:eatvio_app/features/feed/repositories/feed_repository.dart';
|
||||||
|
import 'package:eatvio_app/features/plans/repositories/plan_repository.dart';
|
||||||
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
class PlansCubit extends Cubit<PlansState> {
|
class PlansCubit extends Cubit<PlansState> {
|
||||||
final FeedRepository feedRepository;
|
final FeedRepository feedRepository;
|
||||||
|
final PlanRepository planRepository;
|
||||||
|
|
||||||
int currentPage = 1;
|
int currentPage = 1;
|
||||||
late int lastPage;
|
late int lastPage;
|
||||||
bool isLoading = false;
|
bool isLoading = false;
|
||||||
String? _query;
|
String? _query;
|
||||||
|
static const List<int?> _durationValues = [null, ...durationValues];
|
||||||
|
static const List<String?> _types = [null, ...planTypeValues];
|
||||||
|
|
||||||
|
late final StreamSubscription likesStateSubscription;
|
||||||
|
late final StreamSubscription favoriteStateSubscription;
|
||||||
|
late final StreamSubscription commentsStateSubscription;
|
||||||
|
|
||||||
PlansCubit({
|
PlansCubit({
|
||||||
required this.feedRepository,
|
required this.feedRepository,
|
||||||
}) : super(PlansState.initial());
|
required this.planRepository,
|
||||||
|
}) : super(PlansState.initial()) {
|
||||||
|
likesStateSubscription = planRepository.planLikeState.listen((likesState) {
|
||||||
|
_changePlanLikeState(likesState);
|
||||||
|
});
|
||||||
|
favoriteStateSubscription = planRepository.planFavoriteState.listen((favoriteState) {
|
||||||
|
_changePlanFavoriteState(favoriteState);
|
||||||
|
});
|
||||||
|
commentsStateSubscription = planRepository.planCommentsState.listen((commentsState) {
|
||||||
|
_changePlanCommentsState(commentsState);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void changeDurationFilter(int index) {
|
||||||
|
emit(state.copyWith(filterState: state.filterState.copyWith(durationIndex: index), errorMessage: null));
|
||||||
|
EasyDebounce.debounce(
|
||||||
|
'loadPlans',
|
||||||
|
Duration(seconds: 1),
|
||||||
|
() => loadPlans(
|
||||||
|
query: _query,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void changeTypeFilter(int index) {
|
||||||
|
emit(state.copyWith(filterState: state.filterState.copyWith(typeIndex: index), errorMessage: null));
|
||||||
|
EasyDebounce.debounce(
|
||||||
|
'loadPlans',
|
||||||
|
Duration(seconds: 1),
|
||||||
|
() => loadPlans(
|
||||||
|
query: _query,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void changePriceFilter(int minPrice, int maxPrice, {bool disableDebounce = false}) {
|
||||||
|
emit(state.copyWith(filterState: state.filterState.copyWith(priceFilter: (minPrice, maxPrice)), errorMessage: null));
|
||||||
|
EasyDebounce.debounce(
|
||||||
|
'loadPlans',
|
||||||
|
Duration(seconds: disableDebounce ? 0 : 1),
|
||||||
|
() => loadPlans(
|
||||||
|
query: _query,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _changePlanLikeState(LikesState likesState) {
|
||||||
|
final currentPlans = state.plans;
|
||||||
|
final plansForModification = [...currentPlans];
|
||||||
|
final currentPlan = currentPlans[likesState.index];
|
||||||
|
|
||||||
|
plansForModification[likesState.index] = currentPlan.copyWith(
|
||||||
|
totalLikes: likesState.totalLikes,
|
||||||
|
wasLikedByMe: likesState.wasLikedByMe,
|
||||||
|
);
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
plans: [
|
||||||
|
...plansForModification,
|
||||||
|
],
|
||||||
|
isLoading: isLoading,
|
||||||
|
errorMessage: null,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _changePlanFavoriteState(FavoriteState favoriteState) {
|
||||||
|
final currentPlans = state.plans;
|
||||||
|
final plansForModification = [...currentPlans];
|
||||||
|
final currentPlan = currentPlans[favoriteState.index];
|
||||||
|
|
||||||
|
plansForModification[favoriteState.index] = currentPlan.copyWith(
|
||||||
|
isFavorite: favoriteState.isFavorite,
|
||||||
|
);
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
plans: [
|
||||||
|
...plansForModification,
|
||||||
|
],
|
||||||
|
isLoading: isLoading,
|
||||||
|
errorMessage: null,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _changePlanCommentsState(CommentsState commentsState) {
|
||||||
|
final currentPlans = state.plans;
|
||||||
|
final plansForModification = [...currentPlans];
|
||||||
|
final currentPlan = currentPlans[commentsState.index];
|
||||||
|
|
||||||
|
plansForModification[commentsState.index] = currentPlan.copyWith(
|
||||||
|
totalComments: commentsState.totalComments,
|
||||||
|
);
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
plans: [
|
||||||
|
...plansForModification,
|
||||||
|
],
|
||||||
|
isLoading: isLoading,
|
||||||
|
errorMessage: null,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
loadPlans({
|
loadPlans({
|
||||||
String? query,
|
String? query,
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
_query = query;
|
_query = query;
|
||||||
|
final type = _types[state.filterState.typeIndex];
|
||||||
|
final duration = _durationValues[state.filterState.durationIndex];
|
||||||
|
final minPrice = state.filterState.priceFilter.$1;
|
||||||
|
final maxPrice = state.filterState.priceFilter.$2;
|
||||||
final plansPagination = await feedRepository.getPlans(
|
final plansPagination = await feedRepository.getPlans(
|
||||||
query: query,
|
query: query,
|
||||||
|
duration: duration,
|
||||||
|
maxPrice: maxPrice,
|
||||||
|
minPrice: minPrice,
|
||||||
|
types: type,
|
||||||
);
|
);
|
||||||
lastPage = plansPagination.meta.lastPage;
|
lastPage = plansPagination.meta.lastPage;
|
||||||
|
final minPlanPrice = plansPagination.meta.totalMinPrice ?? 0;
|
||||||
|
final maxPlanPrice = plansPagination.meta.totalMaxPrice ?? 0;
|
||||||
|
if (state.filterState.priceFilter.$1 == null || state.filterState.priceFilter.$2 == null) {
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
filterState: state.filterState.copyWith(priceFilter: (minPlanPrice, maxPlanPrice)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
plans: plansPagination.data,
|
plans: plansPagination.data,
|
||||||
|
isLoading: isLoading,
|
||||||
errorMessage: null,
|
errorMessage: null,
|
||||||
isLoading: false,
|
minPlanPrice: minPlanPrice,
|
||||||
|
maxPlanPrice: maxPlanPrice,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} catch (e, stackTrace) {
|
} catch (e, stackTrace) {
|
||||||
log(e.toString());
|
log(e.toString());
|
||||||
log(stackTrace.toString());
|
log(stackTrace.toString());
|
||||||
emit(state.copyWith(errorMessage: e.toString()));
|
emit(state.copyWith(isLoading: isLoading, errorMessage: e.toString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,4 +204,84 @@ class PlansCubit extends Cubit<PlansState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> addPlanToFavorite(String slug, int index) async {
|
||||||
|
final currentPlans = state.plans;
|
||||||
|
final plansForModification = [...currentPlans];
|
||||||
|
final currentPlan = currentPlans[index];
|
||||||
|
|
||||||
|
plansForModification[index] = currentPlan.copyWith(
|
||||||
|
isFavorite: true,
|
||||||
|
);
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
plans: [
|
||||||
|
...plansForModification,
|
||||||
|
],
|
||||||
|
isLoading: isLoading,
|
||||||
|
errorMessage: null,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
EasyDebounce.debounce(
|
||||||
|
'add_or_remove_plan_from_favorite',
|
||||||
|
Duration(milliseconds: 200),
|
||||||
|
() async {
|
||||||
|
try {
|
||||||
|
await planRepository.addToFavorite(slug);
|
||||||
|
} catch (e) {
|
||||||
|
log(e.toString());
|
||||||
|
showErrorToast('Произошла ошибка. Попробуйте ещё раз через некоторое время');
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
plans: [
|
||||||
|
...currentPlans,
|
||||||
|
],
|
||||||
|
isLoading: isLoading,
|
||||||
|
errorMessage: null,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> removePlanFromFavorite(String slug, int index) async {
|
||||||
|
final currentPlans = state.plans;
|
||||||
|
final plansForModification = [...currentPlans];
|
||||||
|
final currentPlan = currentPlans[index];
|
||||||
|
|
||||||
|
plansForModification[index] = currentPlan.copyWith(
|
||||||
|
isFavorite: false,
|
||||||
|
);
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
plans: [
|
||||||
|
...plansForModification,
|
||||||
|
],
|
||||||
|
isLoading: isLoading,
|
||||||
|
errorMessage: null,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
EasyDebounce.debounce(
|
||||||
|
'add_or_remove_plan_from_favorite',
|
||||||
|
Duration(milliseconds: 200),
|
||||||
|
() async {
|
||||||
|
try {
|
||||||
|
await planRepository.addToFavorite(slug);
|
||||||
|
} catch (e) {
|
||||||
|
log(e.toString());
|
||||||
|
showErrorToast('Произошла ошибка. Попробуйте ещё раз через некоторое время');
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
plans: [
|
||||||
|
...currentPlans,
|
||||||
|
],
|
||||||
|
isLoading: isLoading,
|
||||||
|
errorMessage: null,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,11 +9,28 @@ abstract class PlansState with _$PlansState {
|
|||||||
required bool isLoading,
|
required bool isLoading,
|
||||||
required List<PlanModel> plans,
|
required List<PlanModel> plans,
|
||||||
required String? errorMessage,
|
required String? errorMessage,
|
||||||
|
required PlansFilterState filterState,
|
||||||
|
@Default(0) int minPlanPrice,
|
||||||
|
@Default(0) int maxPlanPrice,
|
||||||
}) = _PlansState;
|
}) = _PlansState;
|
||||||
|
|
||||||
factory PlansState.initial() => const _PlansState(
|
factory PlansState.initial() => const PlansState(
|
||||||
isLoading: true,
|
isLoading: true,
|
||||||
plans: [],
|
plans: [],
|
||||||
errorMessage: null,
|
errorMessage: null,
|
||||||
);
|
filterState: PlansFilterState(
|
||||||
|
priceFilter: (null, null),
|
||||||
|
durationIndex: 0,
|
||||||
|
typeIndex: 0,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class PlansFilterState with _$PlansFilterState {
|
||||||
|
const factory PlansFilterState({
|
||||||
|
required (int?, int?) priceFilter,
|
||||||
|
required int durationIndex,
|
||||||
|
required int typeIndex,
|
||||||
|
}) = _PlansFilterState;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,9 @@ mixin _$PlansState {
|
|||||||
bool get isLoading => throw _privateConstructorUsedError;
|
bool get isLoading => throw _privateConstructorUsedError;
|
||||||
List<PlanModel> get plans => throw _privateConstructorUsedError;
|
List<PlanModel> get plans => throw _privateConstructorUsedError;
|
||||||
String? get errorMessage => throw _privateConstructorUsedError;
|
String? get errorMessage => throw _privateConstructorUsedError;
|
||||||
|
PlansFilterState get filterState => throw _privateConstructorUsedError;
|
||||||
|
int get minPlanPrice => throw _privateConstructorUsedError;
|
||||||
|
int get maxPlanPrice => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
/// Create a copy of PlansState
|
/// Create a copy of PlansState
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@@ -33,7 +36,15 @@ abstract class $PlansStateCopyWith<$Res> {
|
|||||||
PlansState value, $Res Function(PlansState) then) =
|
PlansState value, $Res Function(PlansState) then) =
|
||||||
_$PlansStateCopyWithImpl<$Res, PlansState>;
|
_$PlansStateCopyWithImpl<$Res, PlansState>;
|
||||||
@useResult
|
@useResult
|
||||||
$Res call({bool isLoading, List<PlanModel> plans, String? errorMessage});
|
$Res call(
|
||||||
|
{bool isLoading,
|
||||||
|
List<PlanModel> plans,
|
||||||
|
String? errorMessage,
|
||||||
|
PlansFilterState filterState,
|
||||||
|
int minPlanPrice,
|
||||||
|
int maxPlanPrice});
|
||||||
|
|
||||||
|
$PlansFilterStateCopyWith<$Res> get filterState;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
@@ -54,6 +65,9 @@ class _$PlansStateCopyWithImpl<$Res, $Val extends PlansState>
|
|||||||
Object? isLoading = null,
|
Object? isLoading = null,
|
||||||
Object? plans = null,
|
Object? plans = null,
|
||||||
Object? errorMessage = freezed,
|
Object? errorMessage = freezed,
|
||||||
|
Object? filterState = null,
|
||||||
|
Object? minPlanPrice = null,
|
||||||
|
Object? maxPlanPrice = null,
|
||||||
}) {
|
}) {
|
||||||
return _then(_value.copyWith(
|
return _then(_value.copyWith(
|
||||||
isLoading: null == isLoading
|
isLoading: null == isLoading
|
||||||
@@ -68,8 +82,30 @@ class _$PlansStateCopyWithImpl<$Res, $Val extends PlansState>
|
|||||||
? _value.errorMessage
|
? _value.errorMessage
|
||||||
: errorMessage // ignore: cast_nullable_to_non_nullable
|
: errorMessage // ignore: cast_nullable_to_non_nullable
|
||||||
as String?,
|
as String?,
|
||||||
|
filterState: null == filterState
|
||||||
|
? _value.filterState
|
||||||
|
: filterState // ignore: cast_nullable_to_non_nullable
|
||||||
|
as PlansFilterState,
|
||||||
|
minPlanPrice: null == minPlanPrice
|
||||||
|
? _value.minPlanPrice
|
||||||
|
: minPlanPrice // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
|
maxPlanPrice: null == maxPlanPrice
|
||||||
|
? _value.maxPlanPrice
|
||||||
|
: maxPlanPrice // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
) as $Val);
|
) as $Val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a copy of PlansState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$PlansFilterStateCopyWith<$Res> get filterState {
|
||||||
|
return $PlansFilterStateCopyWith<$Res>(_value.filterState, (value) {
|
||||||
|
return _then(_value.copyWith(filterState: value) as $Val);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
@@ -80,7 +116,16 @@ abstract class _$$PlansStateImplCopyWith<$Res>
|
|||||||
__$$PlansStateImplCopyWithImpl<$Res>;
|
__$$PlansStateImplCopyWithImpl<$Res>;
|
||||||
@override
|
@override
|
||||||
@useResult
|
@useResult
|
||||||
$Res call({bool isLoading, List<PlanModel> plans, String? errorMessage});
|
$Res call(
|
||||||
|
{bool isLoading,
|
||||||
|
List<PlanModel> plans,
|
||||||
|
String? errorMessage,
|
||||||
|
PlansFilterState filterState,
|
||||||
|
int minPlanPrice,
|
||||||
|
int maxPlanPrice});
|
||||||
|
|
||||||
|
@override
|
||||||
|
$PlansFilterStateCopyWith<$Res> get filterState;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
@@ -99,6 +144,9 @@ class __$$PlansStateImplCopyWithImpl<$Res>
|
|||||||
Object? isLoading = null,
|
Object? isLoading = null,
|
||||||
Object? plans = null,
|
Object? plans = null,
|
||||||
Object? errorMessage = freezed,
|
Object? errorMessage = freezed,
|
||||||
|
Object? filterState = null,
|
||||||
|
Object? minPlanPrice = null,
|
||||||
|
Object? maxPlanPrice = null,
|
||||||
}) {
|
}) {
|
||||||
return _then(_$PlansStateImpl(
|
return _then(_$PlansStateImpl(
|
||||||
isLoading: null == isLoading
|
isLoading: null == isLoading
|
||||||
@@ -113,6 +161,18 @@ class __$$PlansStateImplCopyWithImpl<$Res>
|
|||||||
? _value.errorMessage
|
? _value.errorMessage
|
||||||
: errorMessage // ignore: cast_nullable_to_non_nullable
|
: errorMessage // ignore: cast_nullable_to_non_nullable
|
||||||
as String?,
|
as String?,
|
||||||
|
filterState: null == filterState
|
||||||
|
? _value.filterState
|
||||||
|
: filterState // ignore: cast_nullable_to_non_nullable
|
||||||
|
as PlansFilterState,
|
||||||
|
minPlanPrice: null == minPlanPrice
|
||||||
|
? _value.minPlanPrice
|
||||||
|
: minPlanPrice // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
|
maxPlanPrice: null == maxPlanPrice
|
||||||
|
? _value.maxPlanPrice
|
||||||
|
: maxPlanPrice // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -123,7 +183,10 @@ class _$PlansStateImpl implements _PlansState {
|
|||||||
const _$PlansStateImpl(
|
const _$PlansStateImpl(
|
||||||
{required this.isLoading,
|
{required this.isLoading,
|
||||||
required final List<PlanModel> plans,
|
required final List<PlanModel> plans,
|
||||||
required this.errorMessage})
|
required this.errorMessage,
|
||||||
|
required this.filterState,
|
||||||
|
this.minPlanPrice = 0,
|
||||||
|
this.maxPlanPrice = 0})
|
||||||
: _plans = plans;
|
: _plans = plans;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -138,10 +201,18 @@ class _$PlansStateImpl implements _PlansState {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
final String? errorMessage;
|
final String? errorMessage;
|
||||||
|
@override
|
||||||
|
final PlansFilterState filterState;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
final int minPlanPrice;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
final int maxPlanPrice;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'PlansState(isLoading: $isLoading, plans: $plans, errorMessage: $errorMessage)';
|
return 'PlansState(isLoading: $isLoading, plans: $plans, errorMessage: $errorMessage, filterState: $filterState, minPlanPrice: $minPlanPrice, maxPlanPrice: $maxPlanPrice)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -153,12 +224,24 @@ class _$PlansStateImpl implements _PlansState {
|
|||||||
other.isLoading == isLoading) &&
|
other.isLoading == isLoading) &&
|
||||||
const DeepCollectionEquality().equals(other._plans, _plans) &&
|
const DeepCollectionEquality().equals(other._plans, _plans) &&
|
||||||
(identical(other.errorMessage, errorMessage) ||
|
(identical(other.errorMessage, errorMessage) ||
|
||||||
other.errorMessage == errorMessage));
|
other.errorMessage == errorMessage) &&
|
||||||
|
(identical(other.filterState, filterState) ||
|
||||||
|
other.filterState == filterState) &&
|
||||||
|
(identical(other.minPlanPrice, minPlanPrice) ||
|
||||||
|
other.minPlanPrice == minPlanPrice) &&
|
||||||
|
(identical(other.maxPlanPrice, maxPlanPrice) ||
|
||||||
|
other.maxPlanPrice == maxPlanPrice));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(runtimeType, isLoading,
|
int get hashCode => Object.hash(
|
||||||
const DeepCollectionEquality().hash(_plans), errorMessage);
|
runtimeType,
|
||||||
|
isLoading,
|
||||||
|
const DeepCollectionEquality().hash(_plans),
|
||||||
|
errorMessage,
|
||||||
|
filterState,
|
||||||
|
minPlanPrice,
|
||||||
|
maxPlanPrice);
|
||||||
|
|
||||||
/// Create a copy of PlansState
|
/// Create a copy of PlansState
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@@ -173,7 +256,10 @@ abstract class _PlansState implements PlansState {
|
|||||||
const factory _PlansState(
|
const factory _PlansState(
|
||||||
{required final bool isLoading,
|
{required final bool isLoading,
|
||||||
required final List<PlanModel> plans,
|
required final List<PlanModel> plans,
|
||||||
required final String? errorMessage}) = _$PlansStateImpl;
|
required final String? errorMessage,
|
||||||
|
required final PlansFilterState filterState,
|
||||||
|
final int minPlanPrice,
|
||||||
|
final int maxPlanPrice}) = _$PlansStateImpl;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool get isLoading;
|
bool get isLoading;
|
||||||
@@ -181,6 +267,12 @@ abstract class _PlansState implements PlansState {
|
|||||||
List<PlanModel> get plans;
|
List<PlanModel> get plans;
|
||||||
@override
|
@override
|
||||||
String? get errorMessage;
|
String? get errorMessage;
|
||||||
|
@override
|
||||||
|
PlansFilterState get filterState;
|
||||||
|
@override
|
||||||
|
int get minPlanPrice;
|
||||||
|
@override
|
||||||
|
int get maxPlanPrice;
|
||||||
|
|
||||||
/// Create a copy of PlansState
|
/// Create a copy of PlansState
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@@ -189,3 +281,174 @@ abstract class _PlansState implements PlansState {
|
|||||||
_$$PlansStateImplCopyWith<_$PlansStateImpl> get copyWith =>
|
_$$PlansStateImplCopyWith<_$PlansStateImpl> get copyWith =>
|
||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$PlansFilterState {
|
||||||
|
(int?, int?) get priceFilter => throw _privateConstructorUsedError;
|
||||||
|
int get durationIndex => throw _privateConstructorUsedError;
|
||||||
|
int get typeIndex => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Create a copy of PlansFilterState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
$PlansFilterStateCopyWith<PlansFilterState> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class $PlansFilterStateCopyWith<$Res> {
|
||||||
|
factory $PlansFilterStateCopyWith(
|
||||||
|
PlansFilterState value, $Res Function(PlansFilterState) then) =
|
||||||
|
_$PlansFilterStateCopyWithImpl<$Res, PlansFilterState>;
|
||||||
|
@useResult
|
||||||
|
$Res call({(int?, int?) priceFilter, int durationIndex, int typeIndex});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class _$PlansFilterStateCopyWithImpl<$Res, $Val extends PlansFilterState>
|
||||||
|
implements $PlansFilterStateCopyWith<$Res> {
|
||||||
|
_$PlansFilterStateCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Val _value;
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Res Function($Val) _then;
|
||||||
|
|
||||||
|
/// Create a copy of PlansFilterState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? priceFilter = null,
|
||||||
|
Object? durationIndex = null,
|
||||||
|
Object? typeIndex = null,
|
||||||
|
}) {
|
||||||
|
return _then(_value.copyWith(
|
||||||
|
priceFilter: null == priceFilter
|
||||||
|
? _value.priceFilter
|
||||||
|
: priceFilter // ignore: cast_nullable_to_non_nullable
|
||||||
|
as (int?, int?),
|
||||||
|
durationIndex: null == durationIndex
|
||||||
|
? _value.durationIndex
|
||||||
|
: durationIndex // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
|
typeIndex: null == typeIndex
|
||||||
|
? _value.typeIndex
|
||||||
|
: typeIndex // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
|
) as $Val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$PlansFilterStateImplCopyWith<$Res>
|
||||||
|
implements $PlansFilterStateCopyWith<$Res> {
|
||||||
|
factory _$$PlansFilterStateImplCopyWith(_$PlansFilterStateImpl value,
|
||||||
|
$Res Function(_$PlansFilterStateImpl) then) =
|
||||||
|
__$$PlansFilterStateImplCopyWithImpl<$Res>;
|
||||||
|
@override
|
||||||
|
@useResult
|
||||||
|
$Res call({(int?, int?) priceFilter, int durationIndex, int typeIndex});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$PlansFilterStateImplCopyWithImpl<$Res>
|
||||||
|
extends _$PlansFilterStateCopyWithImpl<$Res, _$PlansFilterStateImpl>
|
||||||
|
implements _$$PlansFilterStateImplCopyWith<$Res> {
|
||||||
|
__$$PlansFilterStateImplCopyWithImpl(_$PlansFilterStateImpl _value,
|
||||||
|
$Res Function(_$PlansFilterStateImpl) _then)
|
||||||
|
: super(_value, _then);
|
||||||
|
|
||||||
|
/// Create a copy of PlansFilterState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? priceFilter = null,
|
||||||
|
Object? durationIndex = null,
|
||||||
|
Object? typeIndex = null,
|
||||||
|
}) {
|
||||||
|
return _then(_$PlansFilterStateImpl(
|
||||||
|
priceFilter: null == priceFilter
|
||||||
|
? _value.priceFilter
|
||||||
|
: priceFilter // ignore: cast_nullable_to_non_nullable
|
||||||
|
as (int?, int?),
|
||||||
|
durationIndex: null == durationIndex
|
||||||
|
? _value.durationIndex
|
||||||
|
: durationIndex // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
|
typeIndex: null == typeIndex
|
||||||
|
? _value.typeIndex
|
||||||
|
: typeIndex // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
|
||||||
|
class _$PlansFilterStateImpl implements _PlansFilterState {
|
||||||
|
const _$PlansFilterStateImpl(
|
||||||
|
{required this.priceFilter,
|
||||||
|
required this.durationIndex,
|
||||||
|
required this.typeIndex});
|
||||||
|
|
||||||
|
@override
|
||||||
|
final (int?, int?) priceFilter;
|
||||||
|
@override
|
||||||
|
final int durationIndex;
|
||||||
|
@override
|
||||||
|
final int typeIndex;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'PlansFilterState(priceFilter: $priceFilter, durationIndex: $durationIndex, typeIndex: $typeIndex)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$PlansFilterStateImpl &&
|
||||||
|
(identical(other.priceFilter, priceFilter) ||
|
||||||
|
other.priceFilter == priceFilter) &&
|
||||||
|
(identical(other.durationIndex, durationIndex) ||
|
||||||
|
other.durationIndex == durationIndex) &&
|
||||||
|
(identical(other.typeIndex, typeIndex) ||
|
||||||
|
other.typeIndex == typeIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode =>
|
||||||
|
Object.hash(runtimeType, priceFilter, durationIndex, typeIndex);
|
||||||
|
|
||||||
|
/// Create a copy of PlansFilterState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$PlansFilterStateImplCopyWith<_$PlansFilterStateImpl> get copyWith =>
|
||||||
|
__$$PlansFilterStateImplCopyWithImpl<_$PlansFilterStateImpl>(
|
||||||
|
this, _$identity);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _PlansFilterState implements PlansFilterState {
|
||||||
|
const factory _PlansFilterState(
|
||||||
|
{required final (int?, int?) priceFilter,
|
||||||
|
required final int durationIndex,
|
||||||
|
required final int typeIndex}) = _$PlansFilterStateImpl;
|
||||||
|
|
||||||
|
@override
|
||||||
|
(int?, int?) get priceFilter;
|
||||||
|
@override
|
||||||
|
int get durationIndex;
|
||||||
|
@override
|
||||||
|
int get typeIndex;
|
||||||
|
|
||||||
|
/// Create a copy of PlansFilterState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
_$$PlansFilterStateImplCopyWith<_$PlansFilterStateImpl> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import 'package:eatvio_app/features/feed/presentation/widgets/diet_filter_bottom
|
|||||||
import 'package:eatvio_app/features/feed/presentation/widgets/diets_widget.dart';
|
import 'package:eatvio_app/features/feed/presentation/widgets/diets_widget.dart';
|
||||||
import 'package:eatvio_app/features/feed/presentation/widgets/filter_badge.dart';
|
import 'package:eatvio_app/features/feed/presentation/widgets/filter_badge.dart';
|
||||||
import 'package:eatvio_app/features/feed/presentation/widgets/plan_widget.dart';
|
import 'package:eatvio_app/features/feed/presentation/widgets/plan_widget.dart';
|
||||||
|
import 'package:eatvio_app/features/feed/presentation/widgets/plans_filter_bottom_sheet.dart';
|
||||||
import 'package:eatvio_app/features/feed/presentation/widgets/recipes_widget.dart';
|
import 'package:eatvio_app/features/feed/presentation/widgets/recipes_widget.dart';
|
||||||
import 'package:eatvio_app/features/feed/presentation/widgets/tags_modal_widget.dart';
|
import 'package:eatvio_app/features/feed/presentation/widgets/tags_modal_widget.dart';
|
||||||
import 'package:eatvio_app/features/feed/presentation/bloc/plans_cubit/plans_cubit.dart';
|
import 'package:eatvio_app/features/feed/presentation/bloc/plans_cubit/plans_cubit.dart';
|
||||||
@@ -94,8 +95,21 @@ class _FeedScreenState extends State<FeedScreen> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider(
|
return MultiBlocProvider(
|
||||||
create: (context) => sl<FeedProfileCubit>()..getProfile(),
|
providers: [
|
||||||
|
BlocProvider<FeedProfileCubit>(
|
||||||
|
create: (context) => sl<FeedProfileCubit>()..getProfile(),
|
||||||
|
),
|
||||||
|
BlocProvider<RecipesCubit>(
|
||||||
|
create: (context) => sl<RecipesCubit>()..loadRecipesList(),
|
||||||
|
),
|
||||||
|
BlocProvider<DietsCubit>(
|
||||||
|
create: (context) => sl<DietsCubit>()..loadDiets(),
|
||||||
|
),
|
||||||
|
BlocProvider<PlansCubit>(
|
||||||
|
create: (context) => sl<PlansCubit>()..loadPlans(),
|
||||||
|
),
|
||||||
|
],
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
floatingActionButton: _selectedChipIndex == 1
|
floatingActionButton: _selectedChipIndex == 1
|
||||||
? FloatingActionButton.small(
|
? FloatingActionButton.small(
|
||||||
@@ -188,185 +202,193 @@ class _FeedScreenState extends State<FeedScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_buildAllBlogPosts() {
|
_buildAllBlogPosts() {
|
||||||
return BlocProvider<FeedCubit>(
|
return RefreshIndicator(
|
||||||
create: (context) => feedCubit,
|
onRefresh: () => feedCubit.loadFeedBlogPostList(),
|
||||||
child: RefreshIndicator(
|
child: BlocBuilder<FeedCubit, FeedState>(
|
||||||
onRefresh: () => feedCubit.loadFeedBlogPostList(),
|
builder: (BuildContext context, FeedState state) {
|
||||||
child: BlocBuilder<FeedCubit, FeedState>(
|
if (state is FeedStateLoaded) {
|
||||||
builder: (BuildContext context, FeedState state) {
|
|
||||||
if (state is FeedStateLoaded) {
|
|
||||||
return ListView.separated(
|
|
||||||
padding: EdgeInsets.symmetric(vertical: 8),
|
|
||||||
shrinkWrap: true,
|
|
||||||
physics: BouncingScrollPhysics(),
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
if (index == state.feedBlogPostList.length) {
|
|
||||||
return const Loader();
|
|
||||||
}
|
|
||||||
return BlogPostWidget(index: index);
|
|
||||||
},
|
|
||||||
separatorBuilder: (context, index) {
|
|
||||||
return SizedBox(height: 8);
|
|
||||||
},
|
|
||||||
itemCount: state.isLoading ? state.feedBlogPostList.length + 1 : state.feedBlogPostList.length,
|
|
||||||
);
|
|
||||||
} else if (state is FeedStateError) {
|
|
||||||
return Scaffold(
|
|
||||||
backgroundColor: Colors.grey.shade200,
|
|
||||||
body: Padding(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
child: Center(
|
|
||||||
child: Text(state.errorMessage),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return Scaffold(
|
|
||||||
backgroundColor: Colors.grey.shade200,
|
|
||||||
body: const Loader(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
_buildAllRecipes() {
|
|
||||||
return BlocProvider<RecipesCubit>(
|
|
||||||
create: (context) => recipesCubit,
|
|
||||||
child: RefreshIndicator(
|
|
||||||
onRefresh: () => recipesCubit.loadRecipesList(),
|
|
||||||
child: BlocBuilder<RecipesCubit, RecipesState>(
|
|
||||||
builder: (BuildContext context, RecipesState state) {
|
|
||||||
if (state is RecipesStateLoaded) {
|
|
||||||
return ListView.separated(
|
|
||||||
padding: EdgeInsets.symmetric(vertical: 8),
|
|
||||||
shrinkWrap: true,
|
|
||||||
physics: BouncingScrollPhysics(),
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
return RecipesWidget(recipes: state.recipesList[index]);
|
|
||||||
},
|
|
||||||
separatorBuilder: (context, index) {
|
|
||||||
return SizedBox(height: 8);
|
|
||||||
},
|
|
||||||
itemCount: state.recipesList.length,
|
|
||||||
);
|
|
||||||
} else if (state is RecipesStateError) {
|
|
||||||
return Scaffold(
|
|
||||||
backgroundColor: Colors.grey.shade200,
|
|
||||||
body: Padding(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
child: Center(
|
|
||||||
child: Text(state.errorMessage),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return Scaffold(
|
|
||||||
backgroundColor: Colors.grey.shade200,
|
|
||||||
body: const Loader(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
_buildDiets() {
|
|
||||||
return BlocProvider<DietsCubit>(
|
|
||||||
create: (context) => dietsCubit,
|
|
||||||
child: RefreshIndicator(
|
|
||||||
onRefresh: () => dietsCubit.loadDiets(),
|
|
||||||
child: BlocBuilder<DietsCubit, DietsState>(
|
|
||||||
builder: (BuildContext context, DietsState state) {
|
|
||||||
if (state.isLoading) {
|
|
||||||
return Scaffold(
|
|
||||||
backgroundColor: Colors.grey.shade200,
|
|
||||||
body: const Loader(),
|
|
||||||
);
|
|
||||||
} else if (state.errorMessage != null) {
|
|
||||||
return Scaffold(
|
|
||||||
backgroundColor: Colors.grey.shade200,
|
|
||||||
body: Padding(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
child: Center(
|
|
||||||
child: Text(state.errorMessage ?? ''),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return Stack(
|
|
||||||
children: [
|
|
||||||
SizedBox.expand(),
|
|
||||||
ListView.separated(
|
|
||||||
padding: EdgeInsets.symmetric(vertical: 8),
|
|
||||||
shrinkWrap: true,
|
|
||||||
physics: BouncingScrollPhysics(),
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
return DietsWidget(diet: state.dietsList[index], index: index);
|
|
||||||
},
|
|
||||||
separatorBuilder: (context, index) {
|
|
||||||
return SizedBox(height: 8);
|
|
||||||
},
|
|
||||||
itemCount: state.dietsList.length,
|
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
right: 0,
|
|
||||||
bottom: 74,
|
|
||||||
child: FilterBadge(
|
|
||||||
filterBottomSheet: DietFilterBottomSheet(
|
|
||||||
dietsCubit: dietsCubit,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
_buildPlans() {
|
|
||||||
return BlocProvider<PlansCubit>(
|
|
||||||
create: (context) => plansCubit,
|
|
||||||
child: RefreshIndicator(
|
|
||||||
onRefresh: () => plansCubit.loadPlans(),
|
|
||||||
child: BlocBuilder<PlansCubit, PlansState>(
|
|
||||||
builder: (BuildContext context, PlansState state) {
|
|
||||||
if (state.isLoading) {
|
|
||||||
return Scaffold(
|
|
||||||
backgroundColor: Colors.grey.shade200,
|
|
||||||
body: const Loader(),
|
|
||||||
);
|
|
||||||
} else if (state.errorMessage != null) {
|
|
||||||
return Scaffold(
|
|
||||||
backgroundColor: Colors.grey.shade200,
|
|
||||||
body: Padding(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
child: Center(
|
|
||||||
child: Text(state.errorMessage ?? ''),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return ListView.separated(
|
return ListView.separated(
|
||||||
padding: EdgeInsets.symmetric(vertical: 8),
|
padding: EdgeInsets.symmetric(vertical: 8),
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
physics: BouncingScrollPhysics(),
|
physics: BouncingScrollPhysics(),
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
return PlanWidget(planModel: state.plans[index]);
|
if (index == state.feedBlogPostList.length) {
|
||||||
|
return const Loader();
|
||||||
|
}
|
||||||
|
return BlogPostWidget(index: index);
|
||||||
},
|
},
|
||||||
separatorBuilder: (context, index) {
|
separatorBuilder: (context, index) {
|
||||||
return SizedBox(height: 8);
|
return SizedBox(height: 8);
|
||||||
},
|
},
|
||||||
itemCount: state.plans.length,
|
itemCount: state.isLoading ? state.feedBlogPostList.length + 1 : state.feedBlogPostList.length,
|
||||||
);
|
);
|
||||||
},
|
} else if (state is FeedStateError) {
|
||||||
),
|
return Scaffold(
|
||||||
|
backgroundColor: Colors.grey.shade200,
|
||||||
|
body: Padding(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Center(
|
||||||
|
child: Text(state.errorMessage),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: Colors.grey.shade200,
|
||||||
|
body: const Loader(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_buildAllRecipes() {
|
||||||
|
return RefreshIndicator(
|
||||||
|
onRefresh: () => recipesCubit.loadRecipesList(),
|
||||||
|
child: BlocBuilder<RecipesCubit, RecipesState>(
|
||||||
|
bloc: recipesCubit,
|
||||||
|
builder: (BuildContext context, RecipesState state) {
|
||||||
|
if (state is RecipesStateLoaded) {
|
||||||
|
return ListView.separated(
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 8),
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: BouncingScrollPhysics(),
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
return RecipesWidget(recipes: state.recipesList[index]);
|
||||||
|
},
|
||||||
|
separatorBuilder: (context, index) {
|
||||||
|
return SizedBox(height: 8);
|
||||||
|
},
|
||||||
|
itemCount: state.recipesList.length,
|
||||||
|
);
|
||||||
|
} else if (state is RecipesStateError) {
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: Colors.grey.shade200,
|
||||||
|
body: Padding(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Center(
|
||||||
|
child: Text(state.errorMessage),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: Colors.grey.shade200,
|
||||||
|
body: const Loader(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_buildDiets() {
|
||||||
|
return RefreshIndicator(
|
||||||
|
onRefresh: () => dietsCubit.loadDiets(),
|
||||||
|
child: BlocBuilder<DietsCubit, DietsState>(
|
||||||
|
bloc: dietsCubit,
|
||||||
|
builder: (BuildContext context, DietsState state) {
|
||||||
|
if (state.isLoading) {
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: Colors.grey.shade200,
|
||||||
|
body: const Loader(),
|
||||||
|
);
|
||||||
|
} else if (state.errorMessage != null) {
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: Colors.grey.shade200,
|
||||||
|
body: Padding(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Center(
|
||||||
|
child: Text(state.errorMessage ?? ''),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Stack(
|
||||||
|
children: [
|
||||||
|
SizedBox.expand(),
|
||||||
|
ListView.separated(
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 8),
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: BouncingScrollPhysics(),
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
return DietsWidget(diet: state.dietsList[index], index: index);
|
||||||
|
},
|
||||||
|
separatorBuilder: (context, index) {
|
||||||
|
return SizedBox(height: 8);
|
||||||
|
},
|
||||||
|
itemCount: state.dietsList.length,
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
right: 0,
|
||||||
|
bottom: 74,
|
||||||
|
child: FilterBadge(
|
||||||
|
filterBottomSheet: DietFilterBottomSheet(
|
||||||
|
dietsCubit: dietsCubit,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_buildPlans() {
|
||||||
|
return RefreshIndicator(
|
||||||
|
onRefresh: () => plansCubit.loadPlans(),
|
||||||
|
child: BlocBuilder<PlansCubit, PlansState>(
|
||||||
|
bloc: plansCubit,
|
||||||
|
builder: (BuildContext context, PlansState state) {
|
||||||
|
if (state.isLoading) {
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: Colors.grey.shade200,
|
||||||
|
body: const Loader(),
|
||||||
|
);
|
||||||
|
} else if (state.errorMessage != null) {
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: Colors.grey.shade200,
|
||||||
|
body: Padding(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Center(
|
||||||
|
child: Text(state.errorMessage ?? ''),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return Stack(
|
||||||
|
children: [
|
||||||
|
SizedBox.expand(),
|
||||||
|
ListView.separated(
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 8),
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: BouncingScrollPhysics(),
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
return PlanWidget(
|
||||||
|
plan: state.plans[index],
|
||||||
|
index: index,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
separatorBuilder: (context, index) {
|
||||||
|
return SizedBox(height: 8);
|
||||||
|
},
|
||||||
|
itemCount: state.plans.length,
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
right: 0,
|
||||||
|
bottom: 74,
|
||||||
|
child: FilterBadge(
|
||||||
|
filterBottomSheet: PlansFilterBottomSheet(
|
||||||
|
plansCubit: plansCubit,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -500,6 +522,9 @@ class _FeedScreenState extends State<FeedScreen> {
|
|||||||
case 2:
|
case 2:
|
||||||
dietsCubit.loadDiets(query: query);
|
dietsCubit.loadDiets(query: query);
|
||||||
break;
|
break;
|
||||||
|
case 3:
|
||||||
|
plansCubit.loadPlans(query: query);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -90,7 +90,8 @@ class _BlogPostWidgetState extends State<BlogPostWidget> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
if (!(state.feedBlogPostList[widget.index].author.isEntitySubscribedByMe ?? false) && !state.feedBlogPostList[widget.index].author.authorIsMe)
|
if (!(state.feedBlogPostList[widget.index].author.isEntitySubscribedByMe ?? false) &&
|
||||||
|
!state.feedBlogPostList[widget.index].author.authorIsMe)
|
||||||
SubscribeButtonWidget(
|
SubscribeButtonWidget(
|
||||||
slug: state.feedBlogPostList[widget.index].author.authorSlug,
|
slug: state.feedBlogPostList[widget.index].author.authorSlug,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -31,22 +31,9 @@ class DietFilterBottomSheet extends StatelessWidget {
|
|||||||
style: TextStyles.textMSemibold.copyWith(color: appTheme.black),
|
style: TextStyles.textMSemibold.copyWith(color: appTheme.black),
|
||||||
),
|
),
|
||||||
SizedBox(height: 12),
|
SizedBox(height: 12),
|
||||||
SortingDropDownFilter(cubit: dietsCubit,),
|
SortingDropDownFilter(
|
||||||
SizedBox(
|
cubit: dietsCubit,
|
||||||
height: 16,
|
|
||||||
),
|
),
|
||||||
CustomDivider(),
|
|
||||||
SizedBox(
|
|
||||||
height: 16,
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
s.diet_time,
|
|
||||||
style: TextStyles.textMSemibold.copyWith(color: appTheme.black),
|
|
||||||
),
|
|
||||||
SizedBox(
|
|
||||||
height: 12,
|
|
||||||
),
|
|
||||||
TypeChoiceFilterWidget(cubit: dietsCubit,),
|
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 16,
|
height: 16,
|
||||||
),
|
),
|
||||||
@@ -61,7 +48,26 @@ class DietFilterBottomSheet extends StatelessWidget {
|
|||||||
SizedBox(
|
SizedBox(
|
||||||
height: 12,
|
height: 12,
|
||||||
),
|
),
|
||||||
DurationChoiceFilterWidget(cubit: dietsCubit,),
|
TypeChoiceFilterWidget(
|
||||||
|
dietsCubit: dietsCubit,
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 16,
|
||||||
|
),
|
||||||
|
CustomDivider(),
|
||||||
|
SizedBox(
|
||||||
|
height: 16,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
s.time,
|
||||||
|
style: TextStyles.textMSemibold.copyWith(color: appTheme.black),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 12,
|
||||||
|
),
|
||||||
|
DurationChoiceFilterWidget(
|
||||||
|
dietCubit: dietsCubit,
|
||||||
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 24,
|
height: 24,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -2,21 +2,23 @@ import 'package:eatvio_app/core/const.dart';
|
|||||||
import 'package:eatvio_app/core/utils/localization_utils.dart';
|
import 'package:eatvio_app/core/utils/localization_utils.dart';
|
||||||
import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart';
|
import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart';
|
||||||
import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_state.dart';
|
import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_state.dart';
|
||||||
|
import 'package:eatvio_app/features/feed/presentation/bloc/plans_cubit/plans_cubit.dart';
|
||||||
|
import 'package:eatvio_app/features/feed/presentation/bloc/plans_cubit/plans_state.dart';
|
||||||
import 'package:eatvio_app/features/feed/presentation/widgets/choice_chip_widget.dart';
|
import 'package:eatvio_app/features/feed/presentation/widgets/choice_chip_widget.dart';
|
||||||
import 'package:eatvio_app/generated/l10n.dart';
|
import 'package:eatvio_app/generated/l10n.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
class DurationChoiceFilterWidget extends StatefulWidget {
|
class DurationChoiceFilterWidget extends StatefulWidget {
|
||||||
const DurationChoiceFilterWidget({super.key, required this.cubit});
|
const DurationChoiceFilterWidget({super.key, this.dietCubit, this.plansCubit});
|
||||||
final DietsCubit cubit;
|
final DietsCubit? dietCubit;
|
||||||
|
final PlansCubit? plansCubit;
|
||||||
@override
|
@override
|
||||||
State<DurationChoiceFilterWidget> createState() => _DurationChoiceFilterWidgetState();
|
State<DurationChoiceFilterWidget> createState() => _DurationChoiceFilterWidgetState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _DurationChoiceFilterWidgetState extends State<DurationChoiceFilterWidget> {
|
class _DurationChoiceFilterWidgetState extends State<DurationChoiceFilterWidget> {
|
||||||
final List<String> chipsLabelList = [];
|
final List<String> chipsLabelList = [];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didChangeDependencies() {
|
void didChangeDependencies() {
|
||||||
final s = S.of(context);
|
final s = S.of(context);
|
||||||
@@ -31,55 +33,108 @@ class _DurationChoiceFilterWidgetState extends State<DurationChoiceFilterWidget>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocBuilder<DietsCubit, DietsState>(
|
if (widget.dietCubit != null) {
|
||||||
bloc: widget.cubit,
|
return BlocBuilder<DietsCubit, DietsState>(
|
||||||
builder: (context, state) {
|
bloc: widget.dietCubit!,
|
||||||
return Wrap(
|
builder: (context, state) {
|
||||||
children: [
|
return Wrap(
|
||||||
ChoiceChipWidget(
|
children: [
|
||||||
chipsLabelList: chipsLabelList,
|
ChoiceChipWidget(
|
||||||
index: 0,
|
chipsLabelList: chipsLabelList,
|
||||||
selectedChipIndex: state.filterState.durationIndex,
|
index: 0,
|
||||||
onSelected: (selectedValue) {
|
selectedChipIndex: state.filterState.durationIndex,
|
||||||
widget.cubit.changeDurationFilter(0);
|
onSelected: (selectedValue) {
|
||||||
},
|
widget.dietCubit?.changeDurationFilter(0);
|
||||||
),
|
},
|
||||||
SizedBox(
|
),
|
||||||
width: 8,
|
SizedBox(
|
||||||
),
|
width: 8,
|
||||||
ChoiceChipWidget(
|
),
|
||||||
chipsLabelList: chipsLabelList,
|
ChoiceChipWidget(
|
||||||
index: 1,
|
chipsLabelList: chipsLabelList,
|
||||||
selectedChipIndex: state.filterState.durationIndex,
|
index: 1,
|
||||||
onSelected: (selectedValue) {
|
selectedChipIndex: state.filterState.durationIndex,
|
||||||
widget.cubit.changeDurationFilter(1);
|
onSelected: (selectedValue) {
|
||||||
},
|
widget.dietCubit?.changeDurationFilter(1);
|
||||||
),
|
},
|
||||||
SizedBox(
|
),
|
||||||
width: 8,
|
SizedBox(
|
||||||
),
|
width: 8,
|
||||||
ChoiceChipWidget(
|
),
|
||||||
chipsLabelList: chipsLabelList,
|
ChoiceChipWidget(
|
||||||
index: 2,
|
chipsLabelList: chipsLabelList,
|
||||||
selectedChipIndex: state.filterState.durationIndex,
|
index: 2,
|
||||||
onSelected: (selectedValue) {
|
selectedChipIndex: state.filterState.durationIndex,
|
||||||
widget.cubit.changeDurationFilter(2);
|
onSelected: (selectedValue) {
|
||||||
},
|
widget.dietCubit?.changeDurationFilter(2);
|
||||||
),
|
},
|
||||||
SizedBox(
|
),
|
||||||
width: 8,
|
SizedBox(
|
||||||
),
|
width: 8,
|
||||||
ChoiceChipWidget(
|
),
|
||||||
chipsLabelList: chipsLabelList,
|
ChoiceChipWidget(
|
||||||
index: 3,
|
chipsLabelList: chipsLabelList,
|
||||||
selectedChipIndex: state.filterState.durationIndex,
|
index: 3,
|
||||||
onSelected: (selectedValue) {
|
selectedChipIndex: state.filterState.durationIndex,
|
||||||
widget.cubit.changeDurationFilter(3);
|
onSelected: (selectedValue) {
|
||||||
},
|
widget.dietCubit?.changeDurationFilter(3);
|
||||||
),
|
},
|
||||||
],
|
),
|
||||||
);
|
],
|
||||||
},
|
);
|
||||||
);
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return BlocBuilder<PlansCubit, PlansState>(
|
||||||
|
bloc: widget.plansCubit!,
|
||||||
|
builder: (context, state) {
|
||||||
|
return Wrap(
|
||||||
|
children: [
|
||||||
|
ChoiceChipWidget(
|
||||||
|
chipsLabelList: chipsLabelList,
|
||||||
|
index: 0,
|
||||||
|
selectedChipIndex: state.filterState.durationIndex,
|
||||||
|
onSelected: (selectedValue) {
|
||||||
|
widget.plansCubit?.changeDurationFilter(0);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 8,
|
||||||
|
),
|
||||||
|
ChoiceChipWidget(
|
||||||
|
chipsLabelList: chipsLabelList,
|
||||||
|
index: 1,
|
||||||
|
selectedChipIndex: state.filterState.durationIndex,
|
||||||
|
onSelected: (selectedValue) {
|
||||||
|
widget.plansCubit?.changeDurationFilter(1);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 8,
|
||||||
|
),
|
||||||
|
ChoiceChipWidget(
|
||||||
|
chipsLabelList: chipsLabelList,
|
||||||
|
index: 2,
|
||||||
|
selectedChipIndex: state.filterState.durationIndex,
|
||||||
|
onSelected: (selectedValue) {
|
||||||
|
widget.plansCubit?.changeDurationFilter(2);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 8,
|
||||||
|
),
|
||||||
|
ChoiceChipWidget(
|
||||||
|
chipsLabelList: chipsLabelList,
|
||||||
|
index: 3,
|
||||||
|
selectedChipIndex: state.filterState.durationIndex,
|
||||||
|
onSelected: (selectedValue) {
|
||||||
|
widget.plansCubit?.changeDurationFilter(3);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,11 +10,13 @@ class FilterBadge extends StatelessWidget {
|
|||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => filterBottomSheet,
|
isScrollControlled: true,
|
||||||
useRootNavigator: true,
|
builder: (context) => filterBottomSheet,
|
||||||
barrierColor: Colors.transparent,
|
useRootNavigator: true,
|
||||||
backgroundColor: appTheme.whiteA700,);
|
barrierColor: Colors.transparent,
|
||||||
|
backgroundColor: appTheme.whiteA700,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: EdgeInsets.only(left: 20, top: 12, bottom: 12, right: 24),
|
padding: EdgeInsets.only(left: 20, top: 12, bottom: 12, right: 24),
|
||||||
|
|||||||
@@ -1,174 +1,172 @@
|
|||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:eatvio_app/core/const.dart';
|
import 'package:eatvio_app/core/const.dart';
|
||||||
|
import 'package:eatvio_app/core/widgets/bookmark_button.dart';
|
||||||
|
import 'package:eatvio_app/core/widgets/diet_info_row.dart';
|
||||||
|
import 'package:eatvio_app/core/widgets/feed_icon_button.dart';
|
||||||
import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart';
|
import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart';
|
||||||
|
import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart';
|
||||||
|
import 'package:eatvio_app/core/theme/custom_text_style.dart';
|
||||||
|
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||||
|
import 'package:eatvio_app/features/feed/presentation/bloc/plans_cubit/plans_cubit.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter_html/flutter_html.dart';
|
import 'package:flutter_html/flutter_html.dart';
|
||||||
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:share_plus/share_plus.dart';
|
||||||
|
|
||||||
class PlanWidget extends StatelessWidget {
|
class PlanWidget extends StatefulWidget {
|
||||||
final PlanModel planModel;
|
final PlanModel plan;
|
||||||
|
final int index;
|
||||||
const PlanWidget({
|
const PlanWidget({
|
||||||
super.key,
|
super.key,
|
||||||
required this.planModel,
|
required this.plan,
|
||||||
|
required this.index,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<PlanWidget> createState() => _PlanWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PlanWidgetState extends State<PlanWidget> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return InkWell(
|
return DecoratedBox(
|
||||||
borderRadius: BorderRadius.circular(12),
|
decoration: BoxDecoration(
|
||||||
onTap: () => GoRouter.of(context).pushNamed(
|
borderRadius: BorderRadius.circular(12),
|
||||||
'aboutPlanScreen',
|
color: Colors.white,
|
||||||
extra: {
|
|
||||||
'plan': planModel,
|
|
||||||
'navigateToComments': false,
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
child: Ink(
|
child: Column(
|
||||||
decoration: BoxDecoration(
|
mainAxisSize: MainAxisSize.min,
|
||||||
borderRadius: BorderRadius.circular(12),
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
color: Colors.white,
|
children: [
|
||||||
),
|
Stack(
|
||||||
child: Column(
|
children: [
|
||||||
mainAxisSize: MainAxisSize.min,
|
InkWell(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
onTap: () =>
|
||||||
children: [
|
GoRouter.of(context).pushNamed('aboutPlanScreen', extra: {'plan': widget.plan, 'index': widget.index, 'navigateToComments': false}),
|
||||||
Stack(
|
child: ClipRRect(
|
||||||
children: [
|
|
||||||
ClipRRect(
|
|
||||||
borderRadius: BorderRadius.vertical(top: Radius.circular(12)),
|
borderRadius: BorderRadius.vertical(top: Radius.circular(12)),
|
||||||
child: CachedNetworkImage(
|
child: CachedNetworkImage(
|
||||||
imageUrl: baseUrl + '/storage/' + planModel.image,
|
imageUrl: baseUrl + '/storage/' + widget.plan.image,
|
||||||
fit: BoxFit.fitWidth,
|
fit: BoxFit.fitWidth,
|
||||||
width: MediaQuery.of(context).size.width,
|
width: MediaQuery.of(context).size.width,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Positioned(
|
),
|
||||||
left: 8,
|
Positioned(
|
||||||
bottom: 8,
|
right: 14,
|
||||||
child: Wrap(
|
child: BookmarkButton(
|
||||||
runSpacing: 8,
|
onTap: () async {
|
||||||
children: [
|
if (widget.plan.isFavorite) {
|
||||||
_iconButton(Icons.timelapse_rounded, '${planModel.duration} дней'),
|
await context.read<PlansCubit>().removePlanFromFavorite(widget.plan.slug, widget.index);
|
||||||
SizedBox(width: 8),
|
} else {
|
||||||
],
|
await context.read<PlansCubit>().addPlanToFavorite(widget.plan.slug, widget.index);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
color: appTheme.whiteA700,
|
||||||
|
isFavorite: widget.plan.isFavorite,
|
||||||
|
borderRadius: BorderRadius.vertical(bottom: Radius.circular(12)),
|
||||||
|
width: 40,
|
||||||
|
height: 44,
|
||||||
|
size: 19,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
bottom: 16,
|
||||||
|
child: SizedBox(
|
||||||
|
width: MediaQuery.sizeOf(context).width,
|
||||||
|
child: InfoRow(
|
||||||
|
plan: widget.plan,
|
||||||
|
itemsBackgroundColor: appTheme.whiteA700,
|
||||||
|
disableDescriptionOfTypes: true,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Positioned(
|
),
|
||||||
left: 8,
|
],
|
||||||
top: 8,
|
),
|
||||||
child: Container(
|
SizedBox(height: 12),
|
||||||
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
Column(
|
||||||
decoration: BoxDecoration(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
color: Colors.white.withOpacity(0.8),
|
children: [
|
||||||
borderRadius: BorderRadius.circular(16),
|
Padding(
|
||||||
),
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
child: Row(
|
child: Text(
|
||||||
children: [
|
widget.plan.name,
|
||||||
Icon(
|
style: TextStyles.textMSemibold.copyWith(color: appTheme.black),
|
||||||
Icons.favorite_rounded,
|
|
||||||
size: 16,
|
|
||||||
color: Colors.black,
|
|
||||||
),
|
|
||||||
SizedBox(width: 4),
|
|
||||||
Text(
|
|
||||||
'0',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
color: Colors.black,
|
|
||||||
fontWeight: FontWeight.w400,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Positioned(
|
),
|
||||||
right: 8,
|
if (widget.plan.details.isNotEmpty) ...[
|
||||||
top: 8,
|
SizedBox(
|
||||||
child: Container(
|
height: 12,
|
||||||
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
),
|
||||||
decoration: BoxDecoration(
|
Padding(
|
||||||
color: Colors.white.withOpacity(0.8),
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
borderRadius: BorderRadius.circular(16),
|
child: Html(
|
||||||
),
|
data: widget.plan.details.length > 200 ? widget.plan.details.substring(0, 200) + '...' : widget.plan.details,
|
||||||
child: Row(
|
style: {
|
||||||
children: [
|
"*": Style(
|
||||||
Icon(
|
margin: Margins.zero,
|
||||||
Icons.visibility_rounded,
|
color: appTheme.black,
|
||||||
size: 16,
|
fontSize: FontSize(TextStyles.textSMedium.fontSize ?? 14),
|
||||||
color: Colors.black,
|
fontWeight: TextStyles.textSMedium.fontWeight ?? FontWeight.w400,
|
||||||
),
|
),
|
||||||
SizedBox(width: 4),
|
},
|
||||||
Text(
|
|
||||||
'0',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
color: Colors.black,
|
|
||||||
fontWeight: FontWeight.w400,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
],
|
||||||
SizedBox(height: 12),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
|
||||||
child: Text(
|
|
||||||
planModel.name,
|
|
||||||
style: TextStyle(
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: Colors.black87,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 4),
|
|
||||||
child: Html(
|
|
||||||
data: planModel.details,
|
|
||||||
style: {
|
|
||||||
"*": Style(
|
|
||||||
color: Colors.black87,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(height: 12),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
_iconButton(IconData icon, [String? text]) {
|
|
||||||
return Container(
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 4),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white.withOpacity(0.8),
|
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
),
|
|
||||||
height: 32,
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
icon,
|
|
||||||
size: 16,
|
|
||||||
color: Colors.black,
|
|
||||||
),
|
),
|
||||||
if (text != null) SizedBox(width: 8),
|
Padding(
|
||||||
if (text != null)
|
padding: const EdgeInsets.only(left: 16, top: 12),
|
||||||
Text(
|
child: Row(
|
||||||
text,
|
children: [
|
||||||
style: TextStyle(
|
FeedIconButton(
|
||||||
fontSize: 12,
|
icon: widget.plan.wasLikedByMe ? Icons.favorite_rounded : Icons.favorite_border_rounded,
|
||||||
color: Colors.black,
|
text: widget.plan.totalLikes.toString(),
|
||||||
fontWeight: FontWeight.w500,
|
iconColor: widget.plan.wasLikedByMe ? appTheme.blueA400 : null,
|
||||||
),
|
backgroundColor: widget.plan.wasLikedByMe ? appTheme.transparentBlue : null,
|
||||||
|
onTap: () async {
|
||||||
|
await context.read<DietsCubit>().likeDiet(widget.plan.id, widget.index);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 12,
|
||||||
|
),
|
||||||
|
FeedIconButton(
|
||||||
|
icon: Icons.chat_bubble_outline,
|
||||||
|
text: widget.plan.totalComments.toString(),
|
||||||
|
onTap: () =>
|
||||||
|
GoRouter.of(context).pushNamed('aboutPlanScreen', extra: {'plan': widget.plan, 'index': widget.index, 'navigateToComments': true}),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 12,
|
||||||
|
),
|
||||||
|
FeedIconButton(
|
||||||
|
svgIcon: SvgPicture.asset('assets/icons/repost.svg'),
|
||||||
|
onTap: () async {
|
||||||
|
final result = await Share.shareUri(
|
||||||
|
Uri.parse(
|
||||||
|
baseUrl + '/ru/diet/' + widget.plan.id.toString(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
switch (result.status) {
|
||||||
|
case ShareResultStatus.success:
|
||||||
|
case ShareResultStatus.dismissed:
|
||||||
|
case ShareResultStatus.unavailable:
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Spacer(),
|
||||||
|
FeedIconButton(
|
||||||
|
svgIcon: SvgPicture.asset('assets/icons/eye2.svg'),
|
||||||
|
text: widget.plan.totalViews.toString(),
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 12),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
import 'package:eatvio_app/core/widgets/bottom_sheet_handler_bar.dart';
|
||||||
|
import 'package:eatvio_app/core/widgets/custom_divider.dart';
|
||||||
|
import 'package:eatvio_app/features/feed/presentation/bloc/plans_cubit/plans_cubit.dart';
|
||||||
|
import 'package:eatvio_app/features/feed/presentation/widgets/duration_choice_filter_widget.dart';
|
||||||
|
import 'package:eatvio_app/features/feed/presentation/widgets/price_choice_filter_widget.dart';
|
||||||
|
import 'package:eatvio_app/features/feed/presentation/widgets/type_choice_filter_widget.dart';
|
||||||
|
import 'package:eatvio_app/generated/l10n.dart';
|
||||||
|
import 'package:eatvio_app/core/theme/custom_text_style.dart';
|
||||||
|
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class PlansFilterBottomSheet extends StatelessWidget {
|
||||||
|
const PlansFilterBottomSheet({super.key, required this.plansCubit});
|
||||||
|
final PlansCubit plansCubit;
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final s = S.of(context);
|
||||||
|
return SizedBox(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
SizedBox(height: 12),
|
||||||
|
Center(child: BottomSheetHandlerBar()),
|
||||||
|
SizedBox(height: 12),
|
||||||
|
Text(
|
||||||
|
s.price,
|
||||||
|
style: TextStyles.textMSemibold.copyWith(color: appTheme.black),
|
||||||
|
),
|
||||||
|
SizedBox(height: 12),
|
||||||
|
PriceChoiceFilterWidget(plansCubit: plansCubit),
|
||||||
|
SizedBox(height: 16),
|
||||||
|
CustomDivider(),
|
||||||
|
SizedBox(
|
||||||
|
height: 16,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
s.time,
|
||||||
|
style: TextStyles.textMSemibold.copyWith(color: appTheme.black),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 12,
|
||||||
|
),
|
||||||
|
DurationChoiceFilterWidget(
|
||||||
|
plansCubit: plansCubit,
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 16,
|
||||||
|
),
|
||||||
|
CustomDivider(),
|
||||||
|
SizedBox(
|
||||||
|
height: 16,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
s.plan_type,
|
||||||
|
style: TextStyles.textMSemibold.copyWith(color: appTheme.black),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 12,
|
||||||
|
),
|
||||||
|
TypeChoiceFilterWidget(
|
||||||
|
plansCubit: plansCubit,
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 24,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,183 @@
|
|||||||
|
import 'package:easy_debounce/easy_debounce.dart';
|
||||||
|
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||||
|
import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
|
||||||
|
import 'package:eatvio_app/features/feed/presentation/bloc/plans_cubit/plans_cubit.dart';
|
||||||
|
import 'package:eatvio_app/features/feed/presentation/bloc/plans_cubit/plans_state.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
|
class PriceChoiceFilterWidget extends StatefulWidget {
|
||||||
|
PriceChoiceFilterWidget({super.key, required this.plansCubit});
|
||||||
|
final PlansCubit plansCubit;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<PriceChoiceFilterWidget> createState() => _PriceChoiceFilterWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PriceChoiceFilterWidgetState extends State<PriceChoiceFilterWidget> {
|
||||||
|
final minPriceController = TextEditingController()..text = '₽';
|
||||||
|
final maxPriceController = TextEditingController()..text = '₽';
|
||||||
|
int? prevMinPrice;
|
||||||
|
int? prevMaxPrice;
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
minPriceController.addListener(() {
|
||||||
|
if (!minPriceController.text.endsWith('₽')) {
|
||||||
|
final textWithoutCurrency = minPriceController.text.replaceAll('₽', '');
|
||||||
|
minPriceController.value = TextEditingValue(
|
||||||
|
text: "$textWithoutCurrency₽",
|
||||||
|
selection: TextSelection.collapsed(offset: textWithoutCurrency.length),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
maxPriceController.addListener(() {
|
||||||
|
if (!maxPriceController.text.endsWith('₽')) {
|
||||||
|
final textWithoutCurrency = maxPriceController.text.replaceAll('₽', '');
|
||||||
|
maxPriceController.value = TextEditingValue(
|
||||||
|
text: "$textWithoutCurrency₽",
|
||||||
|
selection: TextSelection.collapsed(offset: textWithoutCurrency.length),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocBuilder<PlansCubit, PlansState>(
|
||||||
|
bloc: widget.plansCubit,
|
||||||
|
builder: (context, state) {
|
||||||
|
prevMinPrice ??= state.minPlanPrice;
|
||||||
|
prevMaxPrice ??= state.maxPlanPrice;
|
||||||
|
|
||||||
|
minPriceController.text = state.filterState.priceFilter.$1?.toStringAsFixed(0) ?? '₽';
|
||||||
|
maxPriceController.text = state.filterState.priceFilter.$2?.toStringAsFixed(0) ?? '₽';
|
||||||
|
return Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: CustomTextFormField(
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
controller: minPriceController,
|
||||||
|
onChanged: (value) {
|
||||||
|
final price = int.tryParse(value.replaceAll('₽', ''));
|
||||||
|
if (price == null) {
|
||||||
|
EasyDebounce.cancel('changeMinPriceFilter');
|
||||||
|
minPriceController.value = TextEditingValue(
|
||||||
|
text: '₽',
|
||||||
|
selection: TextSelection.collapsed(offset: 0),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (price < state.minPlanPrice) {
|
||||||
|
minPriceController.value = TextEditingValue(
|
||||||
|
text: '${prevMinPrice ?? ''}₽',
|
||||||
|
selection: TextSelection.collapsed(offset: prevMinPrice.toString().length),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (price > (state.filterState.priceFilter.$2 ?? state.maxPlanPrice)) {
|
||||||
|
minPriceController.value = TextEditingValue(
|
||||||
|
text: '${prevMinPrice ?? ''}₽',
|
||||||
|
selection: TextSelection.collapsed(offset: prevMinPrice.toString().length),
|
||||||
|
);
|
||||||
|
EasyDebounce.cancel('changeMinPriceFilter');
|
||||||
|
widget.plansCubit.changePriceFilter(
|
||||||
|
prevMinPrice ?? state.minPlanPrice,
|
||||||
|
state.filterState.priceFilter.$2 ?? state.maxPlanPrice,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
prevMinPrice = price;
|
||||||
|
EasyDebounce.debounce(
|
||||||
|
'changeMinPriceFilter',
|
||||||
|
Duration(seconds: 2),
|
||||||
|
() => widget.plansCubit.changePriceFilter(
|
||||||
|
price,
|
||||||
|
state.filterState.priceFilter.$2 ?? state.maxPlanPrice,
|
||||||
|
disableDebounce: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(width: 8),
|
||||||
|
Expanded(
|
||||||
|
child: CustomTextFormField(
|
||||||
|
onChanged: (value) {
|
||||||
|
final price = int.tryParse(value.replaceAll('₽', ''));
|
||||||
|
if (price == null) {
|
||||||
|
EasyDebounce.cancel('changeMaxPriceFilter');
|
||||||
|
maxPriceController.value = TextEditingValue(
|
||||||
|
text: '₽',
|
||||||
|
selection: TextSelection.collapsed(offset: 0),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (price > state.maxPlanPrice) {
|
||||||
|
maxPriceController.value = TextEditingValue(
|
||||||
|
text: '${prevMaxPrice ?? ''}₽',
|
||||||
|
selection: TextSelection.collapsed(offset: prevMaxPrice.toString().length),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (price < (state.filterState.priceFilter.$1 ?? state.minPlanPrice)) {
|
||||||
|
maxPriceController.value = TextEditingValue(
|
||||||
|
text: '${prevMaxPrice ?? ''}₽',
|
||||||
|
selection: TextSelection.collapsed(offset: prevMaxPrice.toString().length),
|
||||||
|
);
|
||||||
|
EasyDebounce.cancel('changeMaxPriceFilter');
|
||||||
|
widget.plansCubit.changePriceFilter(
|
||||||
|
state.filterState.priceFilter.$1 ?? state.minPlanPrice,
|
||||||
|
prevMaxPrice ?? state.maxPlanPrice,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
prevMaxPrice = price;
|
||||||
|
EasyDebounce.debounce(
|
||||||
|
'changeMaxPriceFilter',
|
||||||
|
Duration(seconds: 2),
|
||||||
|
() => widget.plansCubit.changePriceFilter(
|
||||||
|
state.filterState.priceFilter.$1 ?? state.minPlanPrice,
|
||||||
|
price,
|
||||||
|
disableDebounce: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
controller: maxPriceController,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(height: 12),
|
||||||
|
Theme(
|
||||||
|
data: Theme.of(context).copyWith(
|
||||||
|
sliderTheme: SliderThemeData(
|
||||||
|
overlayShape: SliderComponentShape.noOverlay,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: RangeSlider(
|
||||||
|
activeColor: appTheme.blueA400,
|
||||||
|
inactiveColor: appTheme.blueA400,
|
||||||
|
min: state.minPlanPrice.toDouble(),
|
||||||
|
max: state.maxPlanPrice.toDouble(),
|
||||||
|
onChanged: (values) {
|
||||||
|
if (values.start <= values.end) {
|
||||||
|
widget.plansCubit.changePriceFilter(values.start.toInt(), values.end.toInt());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
values: RangeValues(
|
||||||
|
state.filterState.priceFilter.$1?.toDouble() ?? state.minPlanPrice.toDouble(),
|
||||||
|
state.filterState.priceFilter.$2?.toDouble() ?? state.maxPlanPrice.toDouble(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,20 @@
|
|||||||
import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart';
|
import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart';
|
||||||
import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_state.dart';
|
import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_state.dart';
|
||||||
|
import 'package:eatvio_app/features/feed/presentation/bloc/plans_cubit/plans_cubit.dart';
|
||||||
|
import 'package:eatvio_app/features/feed/presentation/bloc/plans_cubit/plans_state.dart';
|
||||||
import 'package:eatvio_app/features/feed/presentation/widgets/choice_chip_widget.dart';
|
import 'package:eatvio_app/features/feed/presentation/widgets/choice_chip_widget.dart';
|
||||||
import 'package:eatvio_app/generated/l10n.dart';
|
import 'package:eatvio_app/generated/l10n.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
class TypeChoiceFilterWidget extends StatefulWidget {
|
class TypeChoiceFilterWidget extends StatefulWidget {
|
||||||
const TypeChoiceFilterWidget({super.key, required this.cubit});
|
const TypeChoiceFilterWidget({
|
||||||
final DietsCubit cubit;
|
super.key,
|
||||||
|
this.dietsCubit,
|
||||||
|
this.plansCubit,
|
||||||
|
});
|
||||||
|
final DietsCubit? dietsCubit;
|
||||||
|
final PlansCubit? plansCubit;
|
||||||
@override
|
@override
|
||||||
State<TypeChoiceFilterWidget> createState() => _TypeChoiceFilterWidgetState();
|
State<TypeChoiceFilterWidget> createState() => _TypeChoiceFilterWidgetState();
|
||||||
}
|
}
|
||||||
@@ -18,77 +25,151 @@ class _TypeChoiceFilterWidgetState extends State<TypeChoiceFilterWidget> {
|
|||||||
void didChangeDependencies() {
|
void didChangeDependencies() {
|
||||||
final s = S.of(context);
|
final s = S.of(context);
|
||||||
super.didChangeDependencies();
|
super.didChangeDependencies();
|
||||||
if (chipsLabelList.isEmpty) {
|
if (widget.dietsCubit != null) {
|
||||||
chipsLabelList.add(s.all);
|
if (chipsLabelList.isEmpty) {
|
||||||
chipsLabelList.add(s.vegan_type);
|
chipsLabelList.add(s.all);
|
||||||
chipsLabelList.add(s.light_type);
|
chipsLabelList.add(s.vegan_type);
|
||||||
chipsLabelList.add(s.carb_type);
|
chipsLabelList.add(s.light_type);
|
||||||
chipsLabelList.add(s.meat_type);
|
chipsLabelList.add(s.carb_type);
|
||||||
|
chipsLabelList.add(s.meat_type);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (chipsLabelList.isEmpty) {
|
||||||
|
chipsLabelList.add(s.all);
|
||||||
|
chipsLabelList.add(s.fit_type);
|
||||||
|
chipsLabelList.add(s.slim_type);
|
||||||
|
chipsLabelList.add(s.wellness_type);
|
||||||
|
chipsLabelList.add(s.eating_behavior_type);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocBuilder<DietsCubit, DietsState>(
|
if (widget.plansCubit != null) {
|
||||||
bloc: widget.cubit,
|
return BlocBuilder<PlansCubit, PlansState>(
|
||||||
builder: (context, state) {
|
bloc: widget.plansCubit,
|
||||||
return Wrap(
|
builder: (context, state) {
|
||||||
children: [
|
return Wrap(
|
||||||
ChoiceChipWidget(
|
children: [
|
||||||
chipsLabelList: chipsLabelList,
|
ChoiceChipWidget(
|
||||||
index: 0,
|
chipsLabelList: chipsLabelList,
|
||||||
selectedChipIndex: state.filterState.typeIndex,
|
index: 0,
|
||||||
onSelected: (selectedValue) {
|
selectedChipIndex: state.filterState.typeIndex,
|
||||||
widget.cubit.changeTypeFilter(0);
|
onSelected: (selectedValue) {
|
||||||
},
|
widget.plansCubit?.changeTypeFilter(0);
|
||||||
),
|
},
|
||||||
SizedBox(
|
),
|
||||||
width: 8,
|
SizedBox(
|
||||||
),
|
width: 8,
|
||||||
ChoiceChipWidget(
|
),
|
||||||
chipsLabelList: chipsLabelList,
|
ChoiceChipWidget(
|
||||||
index: 1,
|
chipsLabelList: chipsLabelList,
|
||||||
selectedChipIndex: state.filterState.typeIndex,
|
index: 1,
|
||||||
onSelected: (selectedValue) {
|
selectedChipIndex: state.filterState.typeIndex,
|
||||||
widget.cubit.changeTypeFilter(1);
|
onSelected: (selectedValue) {
|
||||||
},
|
widget.plansCubit?.changeTypeFilter(1);
|
||||||
),
|
},
|
||||||
SizedBox(
|
),
|
||||||
width: 8,
|
SizedBox(
|
||||||
),
|
width: 8,
|
||||||
ChoiceChipWidget(
|
),
|
||||||
chipsLabelList: chipsLabelList,
|
ChoiceChipWidget(
|
||||||
index: 2,
|
chipsLabelList: chipsLabelList,
|
||||||
selectedChipIndex: state.filterState.typeIndex,
|
index: 2,
|
||||||
onSelected: (selectedValue) {
|
selectedChipIndex: state.filterState.typeIndex,
|
||||||
widget.cubit.changeTypeFilter(2);
|
onSelected: (selectedValue) {
|
||||||
},
|
widget.plansCubit?.changeTypeFilter(2);
|
||||||
),
|
},
|
||||||
SizedBox(
|
),
|
||||||
width: 8,
|
SizedBox(
|
||||||
),
|
width: 8,
|
||||||
ChoiceChipWidget(
|
),
|
||||||
chipsLabelList: chipsLabelList,
|
ChoiceChipWidget(
|
||||||
index: 3,
|
chipsLabelList: chipsLabelList,
|
||||||
selectedChipIndex: state.filterState.typeIndex,
|
index: 3,
|
||||||
onSelected: (selectedValue) {
|
selectedChipIndex: state.filterState.typeIndex,
|
||||||
widget.cubit.changeTypeFilter(3);
|
onSelected: (selectedValue) {
|
||||||
},
|
widget.plansCubit?.changeTypeFilter(3);
|
||||||
),
|
},
|
||||||
SizedBox(
|
),
|
||||||
width: 8,
|
SizedBox(
|
||||||
),
|
width: 8,
|
||||||
ChoiceChipWidget(
|
),
|
||||||
chipsLabelList: chipsLabelList,
|
ChoiceChipWidget(
|
||||||
index: 4,
|
chipsLabelList: chipsLabelList,
|
||||||
selectedChipIndex: state.filterState.typeIndex,
|
index: 4,
|
||||||
onSelected: (selectedValue) {
|
selectedChipIndex: state.filterState.typeIndex,
|
||||||
widget.cubit.changeTypeFilter(4);
|
onSelected: (selectedValue) {
|
||||||
},
|
widget.plansCubit?.changeTypeFilter(4);
|
||||||
),
|
},
|
||||||
],
|
),
|
||||||
);
|
],
|
||||||
},
|
);
|
||||||
);
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return BlocBuilder<DietsCubit, DietsState>(
|
||||||
|
bloc: widget.dietsCubit,
|
||||||
|
builder: (context, state) {
|
||||||
|
return Wrap(
|
||||||
|
children: [
|
||||||
|
ChoiceChipWidget(
|
||||||
|
chipsLabelList: chipsLabelList,
|
||||||
|
index: 0,
|
||||||
|
selectedChipIndex: state.filterState.typeIndex,
|
||||||
|
onSelected: (selectedValue) {
|
||||||
|
widget.dietsCubit?.changeTypeFilter(0);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 8,
|
||||||
|
),
|
||||||
|
ChoiceChipWidget(
|
||||||
|
chipsLabelList: chipsLabelList,
|
||||||
|
index: 1,
|
||||||
|
selectedChipIndex: state.filterState.typeIndex,
|
||||||
|
onSelected: (selectedValue) {
|
||||||
|
widget.dietsCubit?.changeTypeFilter(1);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 8,
|
||||||
|
),
|
||||||
|
ChoiceChipWidget(
|
||||||
|
chipsLabelList: chipsLabelList,
|
||||||
|
index: 2,
|
||||||
|
selectedChipIndex: state.filterState.typeIndex,
|
||||||
|
onSelected: (selectedValue) {
|
||||||
|
widget.dietsCubit?.changeTypeFilter(2);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 8,
|
||||||
|
),
|
||||||
|
ChoiceChipWidget(
|
||||||
|
chipsLabelList: chipsLabelList,
|
||||||
|
index: 3,
|
||||||
|
selectedChipIndex: state.filterState.typeIndex,
|
||||||
|
onSelected: (selectedValue) {
|
||||||
|
widget.dietsCubit?.changeTypeFilter(3);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 8,
|
||||||
|
),
|
||||||
|
ChoiceChipWidget(
|
||||||
|
chipsLabelList: chipsLabelList,
|
||||||
|
index: 4,
|
||||||
|
selectedChipIndex: state.filterState.typeIndex,
|
||||||
|
onSelected: (selectedValue) {
|
||||||
|
widget.dietsCubit?.changeTypeFilter(4);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,11 +68,17 @@ class FeedRepository {
|
|||||||
String? query,
|
String? query,
|
||||||
String? types,
|
String? types,
|
||||||
int? duration,
|
int? duration,
|
||||||
String? sorting,
|
int? minPrice,
|
||||||
|
int? maxPrice,
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
return (await feedService.getPlans(
|
return (await feedService.getPlans(
|
||||||
page: page,
|
page: page,
|
||||||
|
query: query,
|
||||||
|
types: types,
|
||||||
|
duration: duration,
|
||||||
|
minPrice: minPrice,
|
||||||
|
maxPrice: maxPrice,
|
||||||
));
|
));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
rethrow;
|
rethrow;
|
||||||
|
|||||||
@@ -37,6 +37,11 @@ abstract class FeedService {
|
|||||||
@GET('/api/plans/index')
|
@GET('/api/plans/index')
|
||||||
Future<PlanPaginationModel> getPlans({
|
Future<PlanPaginationModel> getPlans({
|
||||||
@Query('page') required int page,
|
@Query('page') required int page,
|
||||||
|
@Query('filters[name][\$startsWith]') String? query,
|
||||||
|
@Query('filters[type][\$contains]') String? types,
|
||||||
|
@Query('filters[duration][\$eq]') int? duration,
|
||||||
|
@Query('filters[min_price_of_options][\$between][0]') int? minPrice,
|
||||||
|
@Query('filters[min_price_of_options][\$between][1]') int? maxPrice,
|
||||||
});
|
});
|
||||||
|
|
||||||
@GET('/api/blogPosts/{id}/comments/show')
|
@GET('/api/blogPosts/{id}/comments/show')
|
||||||
|
|||||||
@@ -150,9 +150,24 @@ class _FeedService implements FeedService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<PlanPaginationModel> getPlans({required int page}) async {
|
Future<PlanPaginationModel> getPlans({
|
||||||
|
required int page,
|
||||||
|
String? query,
|
||||||
|
String? types,
|
||||||
|
int? duration,
|
||||||
|
int? minPrice,
|
||||||
|
int? maxPrice,
|
||||||
|
}) async {
|
||||||
final _extra = <String, dynamic>{};
|
final _extra = <String, dynamic>{};
|
||||||
final queryParameters = <String, dynamic>{r'page': page};
|
final queryParameters = <String, dynamic>{
|
||||||
|
r'page': page,
|
||||||
|
r'filters[name][$startsWith]': query,
|
||||||
|
r'filters[type][$contains]': types,
|
||||||
|
r'filters[duration][$eq]': duration,
|
||||||
|
r'filters[min_price_of_options][$between][0]': minPrice,
|
||||||
|
r'filters[min_price_of_options][$between][1]': maxPrice,
|
||||||
|
};
|
||||||
|
queryParameters.removeWhere((k, v) => v == null);
|
||||||
final _headers = <String, dynamic>{};
|
final _headers = <String, dynamic>{};
|
||||||
const Map<String, dynamic>? _data = null;
|
const Map<String, dynamic>? _data = null;
|
||||||
final _options = _setStreamType<PlanPaginationModel>(Options(
|
final _options = _setStreamType<PlanPaginationModel>(Options(
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
|
|
||||||
|
import 'package:easy_debounce/easy_debounce.dart';
|
||||||
|
import 'package:eatvio_app/core/utils/toasts.dart';
|
||||||
|
import 'package:eatvio_app/features/diets/repositories/diet_repository.dart';
|
||||||
import 'package:eatvio_app/features/plans/presentation/cubit/about_plan_state.dart';
|
import 'package:eatvio_app/features/plans/presentation/cubit/about_plan_state.dart';
|
||||||
import 'package:eatvio_app/features/plans/repositories/plan_repository.dart';
|
import 'package:eatvio_app/features/plans/repositories/plan_repository.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
@@ -22,11 +25,13 @@ class AboutPlanCubit extends Cubit<AboutPlanState> {
|
|||||||
try {
|
try {
|
||||||
emit(state.copyWith(isLoading: true));
|
emit(state.copyWith(isLoading: true));
|
||||||
final plan = await planRepository.getPlan(slug);
|
final plan = await planRepository.getPlan(slug);
|
||||||
emit(state.copyWith(
|
emit(
|
||||||
planModel: plan,
|
state.copyWith(
|
||||||
error: null,
|
planModel: plan,
|
||||||
isLoading: false,
|
error: null,
|
||||||
),);
|
isLoading: false,
|
||||||
|
),
|
||||||
|
);
|
||||||
} catch (e, stackTrace) {
|
} catch (e, stackTrace) {
|
||||||
log(e.toString(), stackTrace: stackTrace);
|
log(e.toString(), stackTrace: stackTrace);
|
||||||
emit(state.copyWith(error: e.toString(), isLoading: false));
|
emit(state.copyWith(error: e.toString(), isLoading: false));
|
||||||
@@ -37,36 +42,73 @@ class AboutPlanCubit extends Cubit<AboutPlanState> {
|
|||||||
emit(state.copyWith(chosenOptionIndex: index));
|
emit(state.copyWith(chosenOptionIndex: index));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> likePlan(String slug) async {
|
Future<void> likePlan({required int index, required String slug}) async {
|
||||||
try {
|
final currentPlan = state.planModel;
|
||||||
await planRepository.likePlan(slug);
|
if (currentPlan == null) return;
|
||||||
final plan = await planRepository.getPlan(slug);
|
if (currentPlan.wasLikedByMe) {
|
||||||
emit(state.copyWith(planModel: plan, error: null));
|
emit(state.copyWith(planModel: currentPlan.copyWith(wasLikedByMe: false, totalLikes: currentPlan.totalLikes - 1), error: null));
|
||||||
} catch (e, stackTrace) {
|
planRepository.emitLikesStateToFeed(LikesState(wasLikedByMe: false, totalLikes: currentPlan.totalLikes - 1, index: index));
|
||||||
log(e.toString(), stackTrace: stackTrace);
|
} else {
|
||||||
emit(state.copyWith(error: e.toString()));
|
emit(state.copyWith(planModel: currentPlan.copyWith(wasLikedByMe: true, totalLikes: currentPlan.totalLikes + 1), error: null));
|
||||||
|
planRepository.emitLikesStateToFeed(LikesState(wasLikedByMe: true, totalLikes: currentPlan.totalLikes + 1, index: index));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EasyDebounce.debounce(
|
||||||
|
'like_plan_$slug',
|
||||||
|
Duration(seconds: 1),
|
||||||
|
() async {
|
||||||
|
try {
|
||||||
|
await planRepository.likePlan(slug);
|
||||||
|
} catch (e) {
|
||||||
|
log(e.toString());
|
||||||
|
planRepository.emitLikesStateToFeed(LikesState(wasLikedByMe: currentPlan.wasLikedByMe, totalLikes: currentPlan.totalLikes, index: index));
|
||||||
|
emit(state.copyWith(planModel: currentPlan, error: null));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> addToFavorite(String slug) async {
|
Future<void> addToFavorite({required String slug, required int index}) async {
|
||||||
try {
|
final currentPlan = state.planModel;
|
||||||
await planRepository.addToFavorite(slug);
|
if (currentPlan == null) return;
|
||||||
final plan = await planRepository.getPlan(slug);
|
|
||||||
emit(state.copyWith(planModel: plan, error: null));
|
emit(state.copyWith(planModel: currentPlan.copyWith(isFavorite: true)));
|
||||||
} catch (e, stackTrace) {
|
planRepository.emitFavoriteStateToFeed(FavoriteState(isFavorite: true, index: index));
|
||||||
log(e.toString(), stackTrace: stackTrace);
|
EasyDebounce.debounce(
|
||||||
emit(state.copyWith(error: e.toString()));
|
'add_or_remove_plan_to_favorites',
|
||||||
}
|
Duration(seconds: 1),
|
||||||
|
() async {
|
||||||
|
try {
|
||||||
|
await planRepository.addToFavorite(slug);
|
||||||
|
} catch (e) {
|
||||||
|
log(e.toString());
|
||||||
|
showErrorToast('Произошла ошибка. Попробуйте ещё раз через некоторое время');
|
||||||
|
planRepository.emitFavoriteStateToFeed(FavoriteState(isFavorite: currentPlan.isFavorite, index: index));
|
||||||
|
emit(state.copyWith(planModel: currentPlan, error: null));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> removeFromFavorite(String slug) async {
|
Future<void> removeFromFavorite({required String slug, required int index}) async {
|
||||||
try {
|
final currentPlan = state.planModel;
|
||||||
await planRepository.removeFromFavorite(slug);
|
if (currentPlan == null) return;
|
||||||
final plan = await planRepository.getPlan(slug);
|
|
||||||
emit(state.copyWith(planModel: plan, error: null));
|
emit(state.copyWith(planModel: currentPlan.copyWith(isFavorite: false)));
|
||||||
} catch (e, stackTrace) {
|
planRepository.emitFavoriteStateToFeed(FavoriteState(isFavorite: false, index: index));
|
||||||
log(e.toString(), stackTrace: stackTrace);
|
EasyDebounce.debounce(
|
||||||
emit(state.copyWith(error: e.toString()));
|
'add_or_remove_plan_to_favorites',
|
||||||
}
|
Duration(seconds: 1),
|
||||||
|
() async {
|
||||||
|
try {
|
||||||
|
await planRepository.removeFromFavorite(slug);
|
||||||
|
} catch (e) {
|
||||||
|
log(e.toString());
|
||||||
|
showErrorToast('Произошла ошибка. Попробуйте ещё раз через некоторое время');
|
||||||
|
planRepository.emitFavoriteStateToFeed(FavoriteState(isFavorite: currentPlan.isFavorite, index: currentPlan.id));
|
||||||
|
emit(state.copyWith(planModel: currentPlan, error: null));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,9 +31,10 @@ import 'package:flutter_svg/svg.dart';
|
|||||||
import '../widgets/option_widget.dart';
|
import '../widgets/option_widget.dart';
|
||||||
|
|
||||||
class AboutPlanScreen extends StatefulWidget {
|
class AboutPlanScreen extends StatefulWidget {
|
||||||
AboutPlanScreen({super.key, required this.plan, required this.navigateToComments});
|
AboutPlanScreen({super.key, required this.plan, required this.navigateToComments, required this.index});
|
||||||
final PlanModel plan;
|
final PlanModel plan;
|
||||||
final bool navigateToComments;
|
final bool navigateToComments;
|
||||||
|
final int index;
|
||||||
@override
|
@override
|
||||||
State<AboutPlanScreen> createState() => _AboutPlanScreenState();
|
State<AboutPlanScreen> createState() => _AboutPlanScreenState();
|
||||||
}
|
}
|
||||||
@@ -90,14 +91,14 @@ class _AboutPlanScreenState extends State<AboutPlanScreen> {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
builder: (BuildContext context, CommentsState commentsState) {
|
builder: (BuildContext context, CommentsState commentsState) {
|
||||||
Future.delayed(Duration(milliseconds: 500), () {
|
|
||||||
if (widget.navigateToComments && !_jumped) {
|
|
||||||
_scrollToBottom();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return BlocBuilder<AboutPlanCubit, AboutPlanState>(
|
return BlocBuilder<AboutPlanCubit, AboutPlanState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
if (state.isLoading) return Scaffold(body: Loader());
|
if (state.isLoading) return Scaffold(body: Loader());
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
if (widget.navigateToComments && !_jumped) {
|
||||||
|
_scrollToBottom();
|
||||||
|
}
|
||||||
|
});
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: appTheme.gray100,
|
backgroundColor: appTheme.gray100,
|
||||||
resizeToAvoidBottomInset: true,
|
resizeToAvoidBottomInset: true,
|
||||||
@@ -152,9 +153,9 @@ class _AboutPlanScreenState extends State<AboutPlanScreen> {
|
|||||||
final planModel = state.planModel;
|
final planModel = state.planModel;
|
||||||
if (planModel == null) return;
|
if (planModel == null) return;
|
||||||
if (planModel.isFavorite) {
|
if (planModel.isFavorite) {
|
||||||
context.read<AboutPlanCubit>().removeFromFavorite(planModel.slug);
|
context.read<AboutPlanCubit>().removeFromFavorite(slug: planModel.slug, index: widget.index);
|
||||||
} else {
|
} else {
|
||||||
context.read<AboutPlanCubit>().addToFavorite(planModel.slug);
|
context.read<AboutPlanCubit>().addToFavorite(slug: planModel.slug, index: widget.index);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
width: 52,
|
width: 52,
|
||||||
@@ -352,7 +353,7 @@ class _AboutPlanScreenState extends State<AboutPlanScreen> {
|
|||||||
SizedBox(width: 12),
|
SizedBox(width: 12),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
context.read<AboutPlanCubit>().likePlan(widget.plan.slug);
|
context.read<AboutPlanCubit>().likePlan(slug: widget.plan.slug, index: widget.index);
|
||||||
},
|
},
|
||||||
child: InfoBouble(
|
child: InfoBouble(
|
||||||
children: [
|
children: [
|
||||||
|
|||||||
@@ -1,13 +1,21 @@
|
|||||||
import 'dart:developer';
|
import 'dart:async';
|
||||||
|
import 'package:eatvio_app/features/diets/repositories/diet_repository.dart';
|
||||||
import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart';
|
import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart';
|
||||||
import 'package:eatvio_app/features/plans/services/plan_service.dart';
|
import 'package:eatvio_app/features/plans/services/plan_service.dart';
|
||||||
|
|
||||||
|
|
||||||
class PlanRepository {
|
class PlanRepository {
|
||||||
final PlanService planService;
|
final PlanService planService;
|
||||||
|
|
||||||
PlanRepository({required this.planService});
|
PlanRepository({required this.planService});
|
||||||
|
|
||||||
|
final _planLikeStateStreamController = StreamController<LikesState>.broadcast();
|
||||||
|
final _planFavoriteStateStreamController = StreamController<FavoriteState>.broadcast();
|
||||||
|
final _planCommentsStateStreamController = StreamController<CommentsState>.broadcast();
|
||||||
|
Stream get planLikeState => _planLikeStateStreamController.stream;
|
||||||
|
Stream get planFavoriteState => _planFavoriteStateStreamController.stream;
|
||||||
|
Stream get planCommentsState => _planCommentsStateStreamController.stream;
|
||||||
|
|
||||||
Future<PlanModel> getPlan(String slug) async {
|
Future<PlanModel> getPlan(String slug) async {
|
||||||
try {
|
try {
|
||||||
return planService.getPlan(slug);
|
return planService.getPlan(slug);
|
||||||
@@ -35,9 +43,14 @@ class PlanRepository {
|
|||||||
Future<void> removeFromFavorite(String slug) async {
|
Future<void> removeFromFavorite(String slug) async {
|
||||||
try {
|
try {
|
||||||
await planService.removeFromFavorite(slug: slug);
|
await planService.removeFromFavorite(slug: slug);
|
||||||
} catch (e, stackTrace) {
|
} catch (e) {
|
||||||
log(e.toString(), stackTrace: stackTrace);
|
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void emitLikesStateToFeed(LikesState state) => _planLikeStateStreamController.add(state);
|
||||||
|
void emitFavoriteStateToFeed(FavoriteState state) => _planFavoriteStateStreamController.add(state);
|
||||||
|
void emitCommentsStateToFeed(CommentsState state) => _planCommentsStateStreamController.add(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'dart:developer';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:eatvio_app/core/image_compressing/image_compress_utility.dart';
|
import 'package:eatvio_app/core/image_compressing/image_compress_utility.dart';
|
||||||
import 'package:eatvio_app/features/chat/repositories/chat_repository.dart';
|
import 'package:eatvio_app/features/chat/repositories/chat_repository.dart';
|
||||||
@@ -45,7 +46,8 @@ class ProfileCubit extends Cubit<ProfileState> {
|
|||||||
circumferencePoints: await profileRepository.getCircumferences(period: period),
|
circumferencePoints: await profileRepository.getCircumferences(period: period),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e, stackTrace) {
|
||||||
|
log(e.toString(), stackTrace: stackTrace);
|
||||||
emit(ProfileState.error(errorMessage: e.toString()));
|
emit(ProfileState.error(errorMessage: e.toString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -273,7 +273,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
|
|||||||
height: 378,
|
height: 378,
|
||||||
child: PageView.builder(
|
child: PageView.builder(
|
||||||
itemBuilder: (BuildContext context, int index) {
|
itemBuilder: (BuildContext context, int index) {
|
||||||
return PlanWidget(planModel: state.plansList[index]);
|
return PlanWidget(plan: state.plansList[index], index: index,);
|
||||||
},
|
},
|
||||||
onPageChanged: (bullet) {
|
onPageChanged: (bullet) {
|
||||||
setState(() {
|
setState(() {
|
||||||
|
|||||||
@@ -92,7 +92,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"diet_author": MessageLookupByLibrary.simpleMessage("Автор диеты"),
|
"diet_author": MessageLookupByLibrary.simpleMessage("Автор диеты"),
|
||||||
"diet_plan": MessageLookupByLibrary.simpleMessage("План диеты"),
|
"diet_plan": MessageLookupByLibrary.simpleMessage("План диеты"),
|
||||||
"diet_state": MessageLookupByLibrary.simpleMessage("Статус диеты"),
|
"diet_state": MessageLookupByLibrary.simpleMessage("Статус диеты"),
|
||||||
"diet_time": MessageLookupByLibrary.simpleMessage("Время прохождения"),
|
|
||||||
"diet_type": MessageLookupByLibrary.simpleMessage("Тип диеты"),
|
"diet_type": MessageLookupByLibrary.simpleMessage("Тип диеты"),
|
||||||
"do_you_have_hypertension_tendency":
|
"do_you_have_hypertension_tendency":
|
||||||
MessageLookupByLibrary.simpleMessage("Есть ли у вас гипертония?"),
|
MessageLookupByLibrary.simpleMessage("Есть ли у вас гипертония?"),
|
||||||
@@ -233,6 +232,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"plan_duration": MessageLookupByLibrary.simpleMessage("Срок плана"),
|
"plan_duration": MessageLookupByLibrary.simpleMessage("Срок плана"),
|
||||||
"plan_label_desc":
|
"plan_label_desc":
|
||||||
MessageLookupByLibrary.simpleMessage("План для поддержания формы"),
|
MessageLookupByLibrary.simpleMessage("План для поддержания формы"),
|
||||||
|
"plan_type": MessageLookupByLibrary.simpleMessage("Тип плана"),
|
||||||
"plans_complete":
|
"plans_complete":
|
||||||
MessageLookupByLibrary.simpleMessage("Пройденные планы"),
|
MessageLookupByLibrary.simpleMessage("Пройденные планы"),
|
||||||
"popular": MessageLookupByLibrary.simpleMessage("Популярные"),
|
"popular": MessageLookupByLibrary.simpleMessage("Популярные"),
|
||||||
@@ -243,6 +243,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"pressure_is_not_a_number": MessageLookupByLibrary.simpleMessage(
|
"pressure_is_not_a_number": MessageLookupByLibrary.simpleMessage(
|
||||||
"Все показатели давления должны быть числами"),
|
"Все показатели давления должны быть числами"),
|
||||||
"preview": MessageLookupByLibrary.simpleMessage("Превью"),
|
"preview": MessageLookupByLibrary.simpleMessage("Превью"),
|
||||||
|
"price": MessageLookupByLibrary.simpleMessage("Цена"),
|
||||||
"price_explanation": MessageLookupByLibrary.simpleMessage(
|
"price_explanation": MessageLookupByLibrary.simpleMessage(
|
||||||
"Разница в ценах, это комиссия Итвио"),
|
"Разница в ценах, это комиссия Итвио"),
|
||||||
"profile": MessageLookupByLibrary.simpleMessage("Профиль"),
|
"profile": MessageLookupByLibrary.simpleMessage("Профиль"),
|
||||||
@@ -262,6 +263,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"tell_your_schedule": MessageLookupByLibrary.simpleMessage(
|
"tell_your_schedule": MessageLookupByLibrary.simpleMessage(
|
||||||
"Расскажите в свободной форме о том как просыпаетесь, когда едите, когда ложитесь спать:"),
|
"Расскажите в свободной форме о том как просыпаетесь, когда едите, когда ложитесь спать:"),
|
||||||
"three_options": MessageLookupByLibrary.simpleMessage("3 опции"),
|
"three_options": MessageLookupByLibrary.simpleMessage("3 опции"),
|
||||||
|
"time": MessageLookupByLibrary.simpleMessage("Время прохождения"),
|
||||||
"to_draft": MessageLookupByLibrary.simpleMessage("В черновик"),
|
"to_draft": MessageLookupByLibrary.simpleMessage("В черновик"),
|
||||||
"unfavorite_foods":
|
"unfavorite_foods":
|
||||||
MessageLookupByLibrary.simpleMessage("Нелюбимые продукты"),
|
MessageLookupByLibrary.simpleMessage("Нелюбимые продукты"),
|
||||||
|
|||||||
@@ -1381,10 +1381,10 @@ class S {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// `Время прохождения`
|
/// `Время прохождения`
|
||||||
String get diet_time {
|
String get time {
|
||||||
return Intl.message(
|
return Intl.message(
|
||||||
'Время прохождения',
|
'Время прохождения',
|
||||||
name: 'diet_time',
|
name: 'time',
|
||||||
desc: '',
|
desc: '',
|
||||||
args: [],
|
args: [],
|
||||||
);
|
);
|
||||||
@@ -1410,6 +1410,16 @@ class S {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `Тип плана`
|
||||||
|
String get plan_type {
|
||||||
|
return Intl.message(
|
||||||
|
'Тип плана',
|
||||||
|
name: 'plan_type',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// `Популярные`
|
/// `Популярные`
|
||||||
String get popular {
|
String get popular {
|
||||||
return Intl.message(
|
return Intl.message(
|
||||||
@@ -2039,6 +2049,16 @@ class S {
|
|||||||
args: [],
|
args: [],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `Цена`
|
||||||
|
String get price {
|
||||||
|
return Intl.message(
|
||||||
|
'Цена',
|
||||||
|
name: 'price',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AppLocalizationDelegate extends LocalizationsDelegate<S> {
|
class AppLocalizationDelegate extends LocalizationsDelegate<S> {
|
||||||
|
|||||||
@@ -132,9 +132,10 @@
|
|||||||
"choose_start_diet_date": "Выберите дату старты диеты. Она отобразиться у Вас в дневнике",
|
"choose_start_diet_date": "Выберите дату старты диеты. Она отобразиться у Вас в дневнике",
|
||||||
"continue_": "Продолжить",
|
"continue_": "Продолжить",
|
||||||
"order": "Порядок",
|
"order": "Порядок",
|
||||||
"diet_time": "Время прохождения",
|
"time": "Время прохождения",
|
||||||
"all": "Все",
|
"all": "Все",
|
||||||
"diet_type": "Тип диеты",
|
"diet_type": "Тип диеты",
|
||||||
|
"plan_type": "Тип плана",
|
||||||
"popular": "Популярные",
|
"popular": "Популярные",
|
||||||
"most_new": "Самые новые",
|
"most_new": "Самые новые",
|
||||||
"most_old": "Самые старые",
|
"most_old": "Самые старые",
|
||||||
@@ -197,5 +198,8 @@
|
|||||||
"wellness_type": "Улучшение самочувствия",
|
"wellness_type": "Улучшение самочувствия",
|
||||||
"wellness_type_desc": "Если вы любите мучные продукты, но хотите придерживаться диеты, вам подходит диета с добавленим мучных блюд на основе цельнозерновых вариантов. В них больше сложных углеводов и клетчатки, что способствует длительному чувству сытости и поддерживает здоровье пищеварительной системы.",
|
"wellness_type_desc": "Если вы любите мучные продукты, но хотите придерживаться диеты, вам подходит диета с добавленим мучных блюд на основе цельнозерновых вариантов. В них больше сложных углеводов и клетчатки, что способствует длительному чувству сытости и поддерживает здоровье пищеварительной системы.",
|
||||||
"eating_behavior_type": "Формирование пищевого поведения",
|
"eating_behavior_type": "Формирование пищевого поведения",
|
||||||
"eating_behavior_type_desc": "Формирование здорового пищевого поведения — это процесс, который включает в себя изменение привычек и отношения к еде. Он направлен на то, чтобы помочь людям научиться правильно питаться и поддерживать здоровый образ жизни.Основные принципы формирования здорового пищевого поведения: \n● Разнообразие продуктов; \n● Сбалансированный рацион; \n● Питьевой режим; \n● Ограничение вредных продуктов; \n● Регулярные приёмы пищи; \n● Умеренность;Осознанное питание; \n● Физическая активность;"
|
"eating_behavior_type_desc": "Формирование здорового пищевого поведения — это процесс, который включает в себя изменение привычек и отношения к еде. Он направлен на то, чтобы помочь людям научиться правильно питаться и поддерживать здоровый образ жизни.Основные принципы формирования здорового пищевого поведения: \n● Разнообразие продуктов; \n● Сбалансированный рацион; \n● Питьевой режим; \n● Ограничение вредных продуктов; \n● Регулярные приёмы пищи; \n● Умеренность;Осознанное питание; \n● Физическая активность;",
|
||||||
|
"price": "Цена"
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user