fixed likes and favourite sync and adeded filter leayoutfor diets

This commit is contained in:
Username1548
2024-11-15 19:07:15 +07:00
parent 64baf15cbd
commit 00da652860
24 changed files with 831 additions and 100 deletions

View File

@@ -0,0 +1,14 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'diet_likes_model.freezed.dart';
part 'diet_likes_model.g.dart';
@freezed
class DietLikesModel with _$DietLikesModel {
const factory DietLikesModel({
@JsonKey(name: 'total_likers') required int totalLikes,
@JsonKey(name: 'is_liked') required bool isLiked,
}) = _DietLikesModel;
factory DietLikesModel.fromJson(Map<String, Object?> json) => _$DietLikesModelFromJson(json);
}

View File

@@ -0,0 +1,197 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'diet_likes_model.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
DietLikesModel _$DietLikesModelFromJson(Map<String, dynamic> json) {
return _DietLikesModel.fromJson(json);
}
/// @nodoc
mixin _$DietLikesModel {
@JsonKey(name: 'total_likers')
int get totalLikes => throw _privateConstructorUsedError;
@JsonKey(name: 'is_liked')
bool get isLiked => throw _privateConstructorUsedError;
/// Serializes this DietLikesModel to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of DietLikesModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$DietLikesModelCopyWith<DietLikesModel> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $DietLikesModelCopyWith<$Res> {
factory $DietLikesModelCopyWith(
DietLikesModel value, $Res Function(DietLikesModel) then) =
_$DietLikesModelCopyWithImpl<$Res, DietLikesModel>;
@useResult
$Res call(
{@JsonKey(name: 'total_likers') int totalLikes,
@JsonKey(name: 'is_liked') bool isLiked});
}
/// @nodoc
class _$DietLikesModelCopyWithImpl<$Res, $Val extends DietLikesModel>
implements $DietLikesModelCopyWith<$Res> {
_$DietLikesModelCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of DietLikesModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? totalLikes = null,
Object? isLiked = null,
}) {
return _then(_value.copyWith(
totalLikes: null == totalLikes
? _value.totalLikes
: totalLikes // ignore: cast_nullable_to_non_nullable
as int,
isLiked: null == isLiked
? _value.isLiked
: isLiked // ignore: cast_nullable_to_non_nullable
as bool,
) as $Val);
}
}
/// @nodoc
abstract class _$$DietLikesModelImplCopyWith<$Res>
implements $DietLikesModelCopyWith<$Res> {
factory _$$DietLikesModelImplCopyWith(_$DietLikesModelImpl value,
$Res Function(_$DietLikesModelImpl) then) =
__$$DietLikesModelImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{@JsonKey(name: 'total_likers') int totalLikes,
@JsonKey(name: 'is_liked') bool isLiked});
}
/// @nodoc
class __$$DietLikesModelImplCopyWithImpl<$Res>
extends _$DietLikesModelCopyWithImpl<$Res, _$DietLikesModelImpl>
implements _$$DietLikesModelImplCopyWith<$Res> {
__$$DietLikesModelImplCopyWithImpl(
_$DietLikesModelImpl _value, $Res Function(_$DietLikesModelImpl) _then)
: super(_value, _then);
/// Create a copy of DietLikesModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? totalLikes = null,
Object? isLiked = null,
}) {
return _then(_$DietLikesModelImpl(
totalLikes: null == totalLikes
? _value.totalLikes
: totalLikes // ignore: cast_nullable_to_non_nullable
as int,
isLiked: null == isLiked
? _value.isLiked
: isLiked // ignore: cast_nullable_to_non_nullable
as bool,
));
}
}
/// @nodoc
@JsonSerializable()
class _$DietLikesModelImpl implements _DietLikesModel {
const _$DietLikesModelImpl(
{@JsonKey(name: 'total_likers') required this.totalLikes,
@JsonKey(name: 'is_liked') required this.isLiked});
factory _$DietLikesModelImpl.fromJson(Map<String, dynamic> json) =>
_$$DietLikesModelImplFromJson(json);
@override
@JsonKey(name: 'total_likers')
final int totalLikes;
@override
@JsonKey(name: 'is_liked')
final bool isLiked;
@override
String toString() {
return 'DietLikesModel(totalLikes: $totalLikes, isLiked: $isLiked)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$DietLikesModelImpl &&
(identical(other.totalLikes, totalLikes) ||
other.totalLikes == totalLikes) &&
(identical(other.isLiked, isLiked) || other.isLiked == isLiked));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType, totalLikes, isLiked);
/// Create a copy of DietLikesModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$DietLikesModelImplCopyWith<_$DietLikesModelImpl> get copyWith =>
__$$DietLikesModelImplCopyWithImpl<_$DietLikesModelImpl>(
this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$DietLikesModelImplToJson(
this,
);
}
}
abstract class _DietLikesModel implements DietLikesModel {
const factory _DietLikesModel(
{@JsonKey(name: 'total_likers') required final int totalLikes,
@JsonKey(name: 'is_liked') required final bool isLiked}) =
_$DietLikesModelImpl;
factory _DietLikesModel.fromJson(Map<String, dynamic> json) =
_$DietLikesModelImpl.fromJson;
@override
@JsonKey(name: 'total_likers')
int get totalLikes;
@override
@JsonKey(name: 'is_liked')
bool get isLiked;
/// Create a copy of DietLikesModel
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$DietLikesModelImplCopyWith<_$DietLikesModelImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@@ -0,0 +1,20 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'diet_likes_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$DietLikesModelImpl _$$DietLikesModelImplFromJson(Map<String, dynamic> json) =>
_$DietLikesModelImpl(
totalLikes: (json['total_likers'] as num).toInt(),
isLiked: json['is_liked'] as bool,
);
Map<String, dynamic> _$$DietLikesModelImplToJson(
_$DietLikesModelImpl instance) =>
<String, dynamic>{
'total_likers': instance.totalLikes,
'is_liked': instance.isLiked,
};

View File

@@ -1,5 +1,6 @@
import 'dart:async';
import 'package:eatvio_app/core/data/diet/models/diet_likes_model.dart';
import 'package:eatvio_app/core/data/diet/services/diet_service.dart';
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
import 'package:equatable/equatable.dart';
@@ -15,9 +16,9 @@ class DietRepository {
Stream get dietFavoriteState => _dietFavoriteStateStreamController.stream;
Stream get dietCommentsState => _dietCommentsStateStreamController.stream;
Future<void> likeDiet(int id) async {
Future<DietLikesModel> likeDiet(int id) async {
try {
await dietsService.likeDiet(id: id);
return await dietsService.likeDiet(id: id);
} catch (e) {
rethrow;
}
@@ -32,17 +33,17 @@ class DietRepository {
}
}
Future<void> addToFavorite(int id) async {
Future<Diet> addToFavorite(int id) async {
try {
await dietsService.addToFavorite(id: id);
return await dietsService.addToFavorite(id: id);
} catch (e) {
rethrow;
}
}
Future<void> removeFromFavorite(int id) async {
Future<Diet> removeFromFavorite(int id) async {
try {
await dietsService.removeFromFavorite(id: id);
return await dietsService.removeFromFavorite(id: id);
} catch (e) {
rethrow;
}

View File

@@ -1,4 +1,5 @@
import 'package:dio/dio.dart';
import 'package:eatvio_app/core/data/diet/models/diet_likes_model.dart';
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
import 'package:retrofit/retrofit.dart';
@@ -9,17 +10,17 @@ abstract class DietService {
factory DietService(Dio dio, {String baseUrl}) = _DietService;
@POST('/api/diets/{id}/like')
Future<void> likeDiet({
Future<DietLikesModel> likeDiet({
@Path() required int id,
});
@POST('/api/diets/{id}/add_to_favorite')
Future<void> addToFavorite({
Future<Diet> addToFavorite({
@Path() required int id,
});
@POST('/api/diets/{id}/remove_from_favorite')
Future<void> removeFromFavorite({
Future<Diet> removeFromFavorite({
@Path() required int id,
});

View File

@@ -22,12 +22,12 @@ class _DietService implements DietService {
final ParseErrorLogger? errorLogger;
@override
Future<void> likeDiet({required int id}) async {
Future<DietLikesModel> likeDiet({required int id}) async {
final _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
final _headers = <String, dynamic>{};
const Map<String, dynamic>? _data = null;
final _options = _setStreamType<void>(Options(
final _options = _setStreamType<DietLikesModel>(Options(
method: 'POST',
headers: _headers,
extra: _extra,
@@ -43,16 +43,24 @@ class _DietService implements DietService {
_dio.options.baseUrl,
baseUrl,
)));
await _dio.fetch<void>(_options);
final _result = await _dio.fetch<Map<String, dynamic>>(_options);
late DietLikesModel _value;
try {
_value = DietLikesModel.fromJson(_result.data!);
} on Object catch (e, s) {
errorLogger?.logError(e, s, _options);
rethrow;
}
return _value;
}
@override
Future<void> addToFavorite({required int id}) async {
Future<Diet> addToFavorite({required int id}) async {
final _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
final _headers = <String, dynamic>{};
const Map<String, dynamic>? _data = null;
final _options = _setStreamType<void>(Options(
final _options = _setStreamType<Diet>(Options(
method: 'POST',
headers: _headers,
extra: _extra,
@@ -68,16 +76,24 @@ class _DietService implements DietService {
_dio.options.baseUrl,
baseUrl,
)));
await _dio.fetch<void>(_options);
final _result = await _dio.fetch<Map<String, dynamic>>(_options);
late Diet _value;
try {
_value = Diet.fromJson(_result.data!);
} on Object catch (e, s) {
errorLogger?.logError(e, s, _options);
rethrow;
}
return _value;
}
@override
Future<void> removeFromFavorite({required int id}) async {
Future<Diet> removeFromFavorite({required int id}) async {
final _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
final _headers = <String, dynamic>{};
const Map<String, dynamic>? _data = null;
final _options = _setStreamType<void>(Options(
final _options = _setStreamType<Diet>(Options(
method: 'POST',
headers: _headers,
extra: _extra,
@@ -93,7 +109,15 @@ class _DietService implements DietService {
_dio.options.baseUrl,
baseUrl,
)));
await _dio.fetch<void>(_options);
final _result = await _dio.fetch<Map<String, dynamic>>(_options);
late Diet _value;
try {
_value = Diet.fromJson(_result.data!);
} on Object catch (e, s) {
errorLogger?.logError(e, s, _options);
rethrow;
}
return _value;
}
@override

View File

@@ -0,0 +1,15 @@
import 'package:eatvio_app/theme/theme_helper.dart';
import 'package:flutter/material.dart';
class BottomSheetHandlerBar extends StatelessWidget {
const BottomSheetHandlerBar({super.key});
@override
Widget build(BuildContext context) {
return Container(
height: 4,
width: 40,
decoration: BoxDecoration(color: appTheme.lightGray, borderRadius: BorderRadius.circular(20)),
);
}
}

View File

@@ -59,8 +59,13 @@ class AboutDietCubit extends Cubit<AboutDietState> {
Duration(seconds: 1),
() async {
try {
await dietsRepository.likeDiet(id);
await getDiet(id);
final dietLikeStateData = await dietsRepository.likeDiet(id);
final actualDiet = currentDiet.copyWith(
wasLikedByMe: dietLikeStateData.isLiked,
totalLikes: dietLikeStateData.totalLikes,
);
emit(AboutDietState.loaded(actualDiet));
dietsRepository.emitLikesStateToFeed(DietLikesState(wasLikedByMe: actualDiet.wasLikedByMe, totalLikes: actualDiet.totalLikes, index: dietIndexInFeed));
} catch (e) {
log(e.toString());
dietsRepository
@@ -87,10 +92,9 @@ class AboutDietCubit extends Cubit<AboutDietState> {
Duration(seconds: 1),
() async {
try {
await dietsRepository.addToFavorite(id);
Future.delayed(const Duration(seconds: 1), () async {
await getDiet(id);
});
final diet = await dietsRepository.addToFavorite(id);
emit(AboutDietState.loaded(diet));
dietsRepository.emitFavoriteStateToFeed(DietFavoriteState(isFavorite: diet.isFavorite, index: dietIndexInFeed));
} catch (e) {
log(e.toString());
dietsRepository.emitFavoriteStateToFeed(DietFavoriteState(isFavorite: currentDiet.isFavorite, index: dietIndexInFeed));
@@ -116,10 +120,9 @@ class AboutDietCubit extends Cubit<AboutDietState> {
Duration(seconds: 1),
() async {
try {
await dietsRepository.removeFromFavorite(id);
Future.delayed(const Duration(seconds: 1), () async {
await getDiet(id);
});
final diet = await dietsRepository.removeFromFavorite(id);
emit(AboutDietState.loaded(diet));
dietsRepository.emitFavoriteStateToFeed(DietFavoriteState(isFavorite: diet.isFavorite, index: dietIndexInFeed));
} catch (e) {
log(e.toString());
dietsRepository.emitFavoriteStateToFeed(DietFavoriteState(isFavorite: currentDiet.isFavorite, index: dietIndexInFeed));

View File

@@ -1,3 +1,4 @@
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/diets/presentation/widgets/calendar_widget.dart';
import 'package:eatvio_app/generated/l10n.dart';
@@ -16,11 +17,7 @@ class StartDietBottomSheet extends StatelessWidget {
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: 4),
Container(
height: 4,
width: 40,
decoration: BoxDecoration(color: appTheme.lightGray, borderRadius: BorderRadius.circular(20)),
),
BottomSheetHandlerBar(),
SizedBox(height: 12),
Text(
s.startDiet,

View File

@@ -132,7 +132,20 @@ class DietsCubit extends Cubit<DietsState> {
Duration(milliseconds: 200),
() async {
try {
await dietsRepository.likeDiet(id);
final dietLikeStateData = await dietsRepository.likeDiet(id);
final actualDiet = currentDiet.copyWith(
wasLikedByMe: dietLikeStateData.isLiked,
totalLikes: dietLikeStateData.totalLikes,
);
dietsForModification[index] = actualDiet;
emit(
DietsState.loaded(
dietsList: [
...dietsForModification,
],
isLoading: isLoading,
),
);
} catch (e) {
log(e.toString());
emit(
@@ -231,7 +244,16 @@ class DietsCubit extends Cubit<DietsState> {
Duration(milliseconds: 200),
() async {
try {
await dietsRepository.addToFavorite(id);
final diet = await dietsRepository.addToFavorite(id);
dietsForModification[index] = diet;
emit(
DietsState.loaded(
dietsList: [
...dietsForModification,
],
isLoading: isLoading,
),
);
} catch (e) {
log(e.toString());
emit(
@@ -269,7 +291,16 @@ class DietsCubit extends Cubit<DietsState> {
Duration(milliseconds: 200),
() async {
try {
await dietsRepository.removeFromFavorite(id);
final diet = await dietsRepository.removeFromFavorite(id);
dietsForModification[index] = diet;
emit(
DietsState.loaded(
dietsList: [
...dietsForModification,
],
isLoading: isLoading,
),
);
} catch (e) {
log(e.toString());
emit(

View File

@@ -6,8 +6,10 @@ import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_state
import 'package:eatvio_app/features/feed/presentation/bloc/recipes_cubit/recipes_cubit.dart';
import 'package:eatvio_app/features/feed/presentation/bloc/recipes_cubit/recipes_state.dart';
import 'package:eatvio_app/features/feed/presentation/widgets/blog_post_widget.dart';
import 'package:eatvio_app/features/feed/presentation/widgets/choice_chip_widget.dart';
import 'package:eatvio_app/features/feed/presentation/widgets/diet_filter_bottom_sheet.dart';
import 'package:eatvio_app/features/feed/presentation/widgets/diets_widget.dart';
import 'package:eatvio_app/features/feed/presentation/widgets/filter_widget.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/recipes_widget.dart';
import 'package:eatvio_app/features/feed/presentation/widgets/tags_modal_widget.dart';
@@ -300,7 +302,9 @@ class _FeedScreenState extends State<FeedScreen> {
Positioned(
right: 0,
bottom: 74,
child: FilterWidget(),
child: FilterBadge(
filterBottomSheet: DietFilterBottomSheet(),
),
),
],
);
@@ -394,15 +398,70 @@ class _FeedScreenState extends State<FeedScreen> {
scrollDirection: Axis.horizontal,
child: Row(
children: [
_choiceChipWidget(0),
ChoiceChipWidget(
index: 0,
selectedChipIndex: _selectedChipIndex,
chipsLabelList: chipsLabelList,
onSelected: (bool selected) {
setState(() {
_selectedChipIndex = 0;
searchController.clear();
FocusScope.of(context).requestFocus(new FocusNode());
});
},
),
SizedBox(width: 8),
_choiceChipWidget(1),
ChoiceChipWidget(
index: 1,
selectedChipIndex: _selectedChipIndex,
chipsLabelList: chipsLabelList,
onSelected: (bool selected) {
setState(() {
_selectedChipIndex = 1;
searchController.clear();
FocusScope.of(context).requestFocus(new FocusNode());
});
},
),
SizedBox(width: 8),
_choiceChipWidget(2),
ChoiceChipWidget(
index: 2,
selectedChipIndex: _selectedChipIndex,
chipsLabelList: chipsLabelList,
onSelected: (bool selected) {
setState(() {
_selectedChipIndex = 2;
searchController.clear();
FocusScope.of(context).requestFocus(new FocusNode());
});
},
),
SizedBox(width: 8),
_choiceChipWidget(3),
ChoiceChipWidget(
index: 3,
selectedChipIndex: _selectedChipIndex,
chipsLabelList: chipsLabelList,
onSelected: (bool selected) {
setState(() {
_selectedChipIndex = 3;
searchController.clear();
FocusScope.of(context).requestFocus(new FocusNode());
});
},
),
SizedBox(width: 8),
_choiceChipWidget(4),
ChoiceChipWidget(
index: 4,
selectedChipIndex: _selectedChipIndex,
chipsLabelList: chipsLabelList,
onSelected: (bool selected) {
setState(() {
_selectedChipIndex = 4;
searchController.clear();
FocusScope.of(context).requestFocus(new FocusNode());
});
},
),
],
),
),

View File

@@ -0,0 +1,32 @@
import 'package:eatvio_app/theme/theme_helper.dart';
import 'package:flutter/material.dart';
class ChoiceChipWidget extends StatelessWidget {
const ChoiceChipWidget({super.key, required this.chipsLabelList, required this.index, required this.selectedChipIndex, required this.onSelected});
final List<String> chipsLabelList;
final int index;
final int selectedChipIndex;
final void Function(bool) onSelected;
@override
Widget build(BuildContext context) {
return ChoiceChip(
selectedColor: appTheme.blueA400,
pressElevation: 0,
showCheckmark: false,
side: BorderSide.none,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
label: Text(chipsLabelList[index]),
labelStyle: TextStyle(
color: index == selectedChipIndex ? Colors.white : appTheme.gray600,
fontWeight: FontWeight.w500,
fontSize: 12,
),
selected: selectedChipIndex == index,
backgroundColor: appTheme.gray100,
onSelected: onSelected,
);
}
}

View File

@@ -0,0 +1,97 @@
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/widgets/type_choice_filter_widget.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:eatvio_app/theme/custom_text_style.dart';
import 'package:eatvio_app/theme/theme_helper.dart';
import 'package:flutter/material.dart';
class DietFilterBottomSheet extends StatelessWidget {
const DietFilterBottomSheet({super.key});
@override
Widget build(BuildContext context) {
final s = S.of(context);
return SizedBox(
width: MediaQuery.sizeOf(context).width,
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: 16),
Text(
s.order,
style: TextStyles.textMSemibold.copyWith(color: appTheme.black),
),
SizedBox(height: 12),
DropdownButtonFormField(
borderRadius: BorderRadius.circular(20),
dropdownColor: appTheme.qwe,
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: const BorderRadius.all(Radius.circular(20)),
borderSide: BorderSide.none,
),
fillColor: appTheme.qwe,
filled: true,
),
value: 'qwe',
items: [
DropdownMenuItem(
child: Text('qwe'),
value: 'qwe',
),
DropdownMenuItem(
child: Text('qwe2'),
value: 'qwe2',
),
DropdownMenuItem(
child: Text('qwe3'),
value: 'qwe3',
),
],
onChanged: (value) {},
),
SizedBox(
height: 16,
),
CustomDivider(),
SizedBox(
height: 16,
),
Text(
s.diet_time,
style: TextStyles.textMSemibold.copyWith(color: appTheme.black),
),
SizedBox(
height: 12,
),
TypeChoiceFilterWidget(),
SizedBox(
height: 16,
),
CustomDivider(),
SizedBox(
height: 16,
),
Text(
s.diet_type,
style: TextStyles.textMSemibold.copyWith(color: appTheme.black),
),
SizedBox(
height: 12,
),
TypeChoiceFilterWidget(),
SizedBox(
height: 24,
),
],
),
),
);
}
}

View File

@@ -143,7 +143,6 @@ class _DietsWidgetState extends State<DietsWidget> {
),
FeedIconButton(
svgIcon: SvgPicture.asset('assets/icons/repost.svg'),
text: '0',
onTap: () async {
final result = await Share.shareUri(
Uri.parse(

View File

@@ -0,0 +1,84 @@
import 'package:eatvio_app/core/utils/localization_utils.dart';
import 'package:eatvio_app/features/feed/presentation/widgets/choice_chip_widget.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/widgets.dart';
class DurationChoiceFilterWidget extends StatefulWidget {
const DurationChoiceFilterWidget({super.key});
@override
State<DurationChoiceFilterWidget> createState() => _DurationChoiceFilterWidgetState();
}
class _DurationChoiceFilterWidgetState extends State<DurationChoiceFilterWidget> {
final List<String> chipsLabelList = [];
int selectedChipIndex = 0;
@override
void didChangeDependencies() {
final s = S.of(context);
super.didChangeDependencies();
if (chipsLabelList.isEmpty) {
chipsLabelList.add(s.all);
chipsLabelList.add('7 ${getDayForm(7, s)}');
chipsLabelList.add('14 ${getDayForm(14, s)}');
chipsLabelList.add('21 ${getDayForm(21, s)}');
}
}
@override
Widget build(BuildContext context) {
return Wrap(
children: [
ChoiceChipWidget(
chipsLabelList: chipsLabelList,
index: 0,
selectedChipIndex: selectedChipIndex,
onSelected: (selectedValue) {
setState(() {
selectedChipIndex = 0;
});
},
),
SizedBox(
width: 8,
),
ChoiceChipWidget(
chipsLabelList: chipsLabelList,
index: 1,
selectedChipIndex: selectedChipIndex,
onSelected: (selectedValue) {
setState(() {
selectedChipIndex = 1;
});
},
),
SizedBox(
width: 8,
),
ChoiceChipWidget(
chipsLabelList: chipsLabelList,
index: 2,
selectedChipIndex: selectedChipIndex,
onSelected: (selectedValue) {
setState(() {
selectedChipIndex = 2;
});
},
),
SizedBox(
width: 8,
),
ChoiceChipWidget(
chipsLabelList: chipsLabelList,
index: 3,
selectedChipIndex: selectedChipIndex,
onSelected: (selectedValue) {
setState(() {
selectedChipIndex = 3;
});
},
),
],
);
}
}

View File

@@ -0,0 +1,34 @@
import 'package:eatvio_app/theme/theme_helper.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
class FilterBadge extends StatelessWidget {
const FilterBadge({super.key, required this.filterBottomSheet});
final Widget filterBottomSheet;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
showModalBottomSheet(
context: context,
builder: (context) => filterBottomSheet,
useRootNavigator: true,
barrierColor: Colors.transparent,
backgroundColor: appTheme.whiteA700);
},
child: Container(
padding: EdgeInsets.only(left: 20, top: 12, bottom: 12, right: 24),
width: 70,
decoration: BoxDecoration(
color: appTheme.blueA400,
borderRadius: BorderRadius.horizontal(left: Radius.circular(100)),
),
child: SvgPicture.asset(
'assets/icons/filter2.svg',
width: 24,
height: 16,
),
),
);
}
}

View File

@@ -1,24 +0,0 @@
import 'package:eatvio_app/theme/theme_helper.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
class FilterWidget extends StatelessWidget {
const FilterWidget({super.key});
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.only(left: 20, top: 12, bottom: 12, right: 24),
width: 70,
decoration: BoxDecoration(
color: appTheme.blueA400,
borderRadius: BorderRadius.horizontal(left: Radius.circular(100)),
),
child: SvgPicture.asset(
'assets/icons/filter2.svg',
width: 24,
height: 16,
),
);
}
}

View File

@@ -0,0 +1,97 @@
import 'package:eatvio_app/features/feed/presentation/widgets/choice_chip_widget.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/widgets.dart';
class TypeChoiceFilterWidget extends StatefulWidget {
const TypeChoiceFilterWidget({super.key});
@override
State<TypeChoiceFilterWidget> createState() => _TypeChoiceFilterWidgetState();
}
class _TypeChoiceFilterWidgetState extends State<TypeChoiceFilterWidget> {
final List<String> chipsLabelList = [];
int selectedChipIndex = 0;
@override
void didChangeDependencies() {
final s = S.of(context);
super.didChangeDependencies();
if (chipsLabelList.isEmpty) {
chipsLabelList.add(s.all);
chipsLabelList.add(s.vegan_type);
chipsLabelList.add(s.alko_type);
chipsLabelList.add(s.corb_type);
chipsLabelList.add(s.meat_type);
}
}
@override
Widget build(BuildContext context) {
return Wrap(
children: [
ChoiceChipWidget(
chipsLabelList: chipsLabelList,
index: 0,
selectedChipIndex: selectedChipIndex,
onSelected: (selectedValue) {
setState(() {
selectedChipIndex = 0;
});
},
),
SizedBox(
width: 8,
),
ChoiceChipWidget(
chipsLabelList: chipsLabelList,
index: 1,
selectedChipIndex: selectedChipIndex,
onSelected: (selectedValue) {
setState(() {
selectedChipIndex = 1;
});
},
),
SizedBox(
width: 8,
),
ChoiceChipWidget(
chipsLabelList: chipsLabelList,
index: 2,
selectedChipIndex: selectedChipIndex,
onSelected: (selectedValue) {
setState(() {
selectedChipIndex = 2;
});
},
),
SizedBox(
width: 8,
),
ChoiceChipWidget(
chipsLabelList: chipsLabelList,
index: 3,
selectedChipIndex: selectedChipIndex,
onSelected: (selectedValue) {
setState(() {
selectedChipIndex = 3;
});
},
),
SizedBox(
width: 8,
),
ChoiceChipWidget(
chipsLabelList: chipsLabelList,
index: 4,
selectedChipIndex: selectedChipIndex,
onSelected: (selectedValue) {
setState(() {
selectedChipIndex = 4;
});
},
),
],
);
}
}

View File

@@ -27,7 +27,7 @@ abstract class FeedService {
@GET('/api/diets/index')
Future<DietsPagination> getDiets({
@Query('page') required int page,
@Query('query_search') String? query,
@Query('query_search') String? query,
});
@GET('/api/blogPosts/{id}/comments/show')

View File

@@ -36,7 +36,7 @@ MessageLookupByLibrary? _findExact(String localeName) {
Future<bool> initializeMessages(String localeName) {
var availableLocale = Intl.verifiedLocale(
localeName, (locale) => _deferredLibraries[locale] != null,
onFailure: (_) => null,);
onFailure: (_) => null);
if (availableLocale == null) {
return new SynchronousFuture(false);
}

View File

@@ -32,7 +32,8 @@ class MessageLookup extends MessageLookupByLibrary {
"admin": MessageLookupByLibrary.simpleMessage("Админ"),
"alko_type": MessageLookupByLibrary.simpleMessage("Алкоголь"),
"alko_type_desc": MessageLookupByLibrary.simpleMessage(
"С некоторыми видами диет можно употреблять алкоголь в умеренных количествах. Рекомендуется совмещать вместе с низкокалорийными закусками.",),
"С некоторыми видами диет можно употреблять алкоголь в умеренных количествах. Рекомендуется совмещать вместе с низкокалорийными закусками."),
"all": MessageLookupByLibrary.simpleMessage("Все"),
"allergies_presence":
MessageLookupByLibrary.simpleMessage("Наличие аллергии"),
"another_form_of_comment":
@@ -42,7 +43,7 @@ class MessageLookup extends MessageLookupByLibrary {
"archived": MessageLookupByLibrary.simpleMessage("В архиве"),
"authentificate": MessageLookupByLibrary.simpleMessage("Войти"),
"average_pressure": MessageLookupByLibrary.simpleMessage(
"Средние показатели давления:",),
"Средние показатели давления:"),
"bodymass_chart": MessageLookupByLibrary.simpleMessage("График ИМТ"),
"calories": MessageLookupByLibrary.simpleMessage("Калории"),
"carbohydrates": MessageLookupByLibrary.simpleMessage("Углеводы"),
@@ -51,11 +52,11 @@ class MessageLookup extends MessageLookupByLibrary {
"chest_circumference":
MessageLookupByLibrary.simpleMessage("Обхват груди"),
"choose_start_diet_date": MessageLookupByLibrary.simpleMessage(
"Выберите дату старты диеты. Она отобразиться у Вас в дневнике",),
"Выберите дату старты диеты. Она отобразиться у Вас в дневнике"),
"circumference_empty": MessageLookupByLibrary.simpleMessage(
"Все замеры обязательны к заполнению",),
"Все замеры обязательны к заполнению"),
"circumference_is_not_a_number": MessageLookupByLibrary.simpleMessage(
"Все замеры должны быть числами",),
"Все замеры должны быть числами"),
"circumferences_chart":
MessageLookupByLibrary.simpleMessage("График обхватов"),
"city": MessageLookupByLibrary.simpleMessage("Город"),
@@ -70,7 +71,7 @@ class MessageLookup extends MessageLookupByLibrary {
"copy_link": MessageLookupByLibrary.simpleMessage("Скопировать ссылку"),
"corb_type": MessageLookupByLibrary.simpleMessage("Мучное"),
"corb_type_desc": MessageLookupByLibrary.simpleMessage(
"Если вы любите мучные продукты, но хотите придерживаться диеты, вам подходит диета с добавленим мучных блюд на основе цельнозерновых вариантов. В них больше сложных углеводов и клетчатки, что способствует длительному чувству сытости и поддерживает здоровье пищеварительной системы.",),
"Если вы любите мучные продукты, но хотите придерживаться диеты, вам подходит диета с добавленим мучных блюд на основе цельнозерновых вариантов. В них больше сложных углеводов и клетчатки, что способствует длительному чувству сытости и поддерживает здоровье пищеварительной системы."),
"country": MessageLookupByLibrary.simpleMessage("Страна"),
"date_of_birth": MessageLookupByLibrary.simpleMessage("Дата рождения"),
"day": MessageLookupByLibrary.simpleMessage("День"),
@@ -78,22 +79,24 @@ class MessageLookup extends MessageLookupByLibrary {
"diary": MessageLookupByLibrary.simpleMessage("Дневник"),
"diet_author": MessageLookupByLibrary.simpleMessage("Автор диеты"),
"diet_plan": MessageLookupByLibrary.simpleMessage("План диеты"),
"diet_time": MessageLookupByLibrary.simpleMessage("Время прохождения"),
"diet_type": MessageLookupByLibrary.simpleMessage("Тип диеты"),
"do_you_have_hypertension_tendency":
MessageLookupByLibrary.simpleMessage("Есть ли у вас гипертония?"),
"do_you_have_hypotension_tendency":
MessageLookupByLibrary.simpleMessage("Есть ли у вас гипотония?"),
"do_you_take_pills": MessageLookupByLibrary.simpleMessage(
"Принимаете ли вы лекарства или бады?",),
"Принимаете ли вы лекарства или бады?"),
"down_pressure":
MessageLookupByLibrary.simpleMessage("Нижние показатели давления:"),
"draft": MessageLookupByLibrary.simpleMessage("Черновик"),
"email": MessageLookupByLibrary.simpleMessage("Элетронная почта"),
"email_empty": MessageLookupByLibrary.simpleMessage(
"Необходимо ввести адрес электронной почты",),
"Необходимо ввести адрес электронной почты"),
"email_invalid": MessageLookupByLibrary.simpleMessage(
"Некорректный адрес электронной почты",),
"Некорректный адрес электронной почты"),
"enter_additional_information": MessageLookupByLibrary.simpleMessage(
"Введите любую дополнительную информацию на своё усмотрение:",),
"Введите любую дополнительную информацию на своё усмотрение:"),
"enter_information":
MessageLookupByLibrary.simpleMessage("Введите информацию..."),
"exit": MessageLookupByLibrary.simpleMessage("Выйти"),
@@ -102,16 +105,16 @@ class MessageLookup extends MessageLookupByLibrary {
"favorite_foods":
MessageLookupByLibrary.simpleMessage("Любимые продукты"),
"favorite_unfavorite_foods": MessageLookupByLibrary.simpleMessage(
"Ваши любимые/нелюбимые продукты питания?",),
"Ваши любимые/нелюбимые продукты питания?"),
"female": MessageLookupByLibrary.simpleMessage("Женский"),
"fill_in_information":
MessageLookupByLibrary.simpleMessage("Заполните информацию:"),
"finish": MessageLookupByLibrary.simpleMessage("Завершить"),
"first_name": MessageLookupByLibrary.simpleMessage("Имя"),
"first_name_empty": MessageLookupByLibrary.simpleMessage(
"Имя обязательно для заполнения",),
"Имя обязательно для заполнения"),
"first_name_short": MessageLookupByLibrary.simpleMessage(
"В имени должен быть минимум 1 символ",),
"В имени должен быть минимум 1 символ"),
"gr": MessageLookupByLibrary.simpleMessage("гр"),
"grams": MessageLookupByLibrary.simpleMessage("гр"),
"height_measurements":
@@ -126,9 +129,9 @@ class MessageLookup extends MessageLookupByLibrary {
"language": MessageLookupByLibrary.simpleMessage("Язык"),
"last_name": MessageLookupByLibrary.simpleMessage("Фамилия"),
"last_name_empty": MessageLookupByLibrary.simpleMessage(
"Фамилия обязательна для заполнения",),
"Фамилия обязательна для заполнения"),
"last_name_short": MessageLookupByLibrary.simpleMessage(
"В фамилии должен быть минимум 6 символов",),
"В фамилии должен быть минимум 6 символов"),
"lbl_english": MessageLookupByLibrary.simpleMessage("English"),
"link_was_copied":
MessageLookupByLibrary.simpleMessage("Ссылка успешно скопирована"),
@@ -137,7 +140,7 @@ class MessageLookup extends MessageLookupByLibrary {
"meal": MessageLookupByLibrary.simpleMessage("Приём пищи"),
"meat_type": MessageLookupByLibrary.simpleMessage("С мясом"),
"meat_type_desc": MessageLookupByLibrary.simpleMessage(
"Диета с мясом полезная и сбалансированная. Включает в себя нежирное мясо: курицу, индейку, кролика или говядину. В сочетании с овощами получаете все необходимые витамины и минералы. Не забывайте о порциях и старайтесь есть медленно, прислушиваясь к своему организму.",),
"Диета с мясом полезная и сбалансированная. Включает в себя нежирное мясо: курицу, индейку, кролика или говядину. В сочетании с овощами получаете все необходимые витамины и минералы. Не забывайте о порциях и старайтесь есть медленно, прислушиваясь к своему организму."),
"milliliter": MessageLookupByLibrary.simpleMessage("мл"),
"msg_network_err":
MessageLookupByLibrary.simpleMessage("Сетевая ошибка!"),
@@ -146,21 +149,22 @@ class MessageLookup extends MessageLookupByLibrary {
"my_blog": MessageLookupByLibrary.simpleMessage("Мой блог"),
"my_settings": MessageLookupByLibrary.simpleMessage("Мои настройки"),
"name_empty": MessageLookupByLibrary.simpleMessage(
"Имя пользователя обязательно для заполнения",),
"Имя пользователя обязательно для заполнения"),
"name_short": MessageLookupByLibrary.simpleMessage(
"В имени пользователя должно быть минимум 6 символов",),
"В имени пользователя должно быть минимум 6 символов"),
"no": MessageLookupByLibrary.simpleMessage("Нет"),
"no_account":
MessageLookupByLibrary.simpleMessage("У меня нет аккаунта"),
"order": MessageLookupByLibrary.simpleMessage("Порядок"),
"password": MessageLookupByLibrary.simpleMessage("Пароль"),
"password_confirmation":
MessageLookupByLibrary.simpleMessage("Подтвердите пароль"),
"password_confirmation_mismatch":
MessageLookupByLibrary.simpleMessage("Пароли не совпадают"),
"password_empty": MessageLookupByLibrary.simpleMessage(
"Пароль обязателен для заполнения",),
"Пароль обязателен для заполнения"),
"password_short": MessageLookupByLibrary.simpleMessage(
"В пароле должно быть минимум 6 символов",),
"В пароле должно быть минимум 6 символов"),
"pcs": MessageLookupByLibrary.simpleMessage("шт"),
"place_of_living":
MessageLookupByLibrary.simpleMessage("Место проживания"),
@@ -169,9 +173,9 @@ class MessageLookup extends MessageLookupByLibrary {
"preferred_language":
MessageLookupByLibrary.simpleMessage("Предпочитаемый язык общения"),
"pressure_empty": MessageLookupByLibrary.simpleMessage(
"Все показатели давления обязательны к заполнению",),
"Все показатели давления обязательны к заполнению"),
"pressure_is_not_a_number": MessageLookupByLibrary.simpleMessage(
"Все показатели давления должны быть числами",),
"Все показатели давления должны быть числами"),
"profile": MessageLookupByLibrary.simpleMessage("Профиль"),
"proteins": MessageLookupByLibrary.simpleMessage("Белки"),
"save": MessageLookupByLibrary.simpleMessage("Сохранить"),
@@ -180,11 +184,11 @@ class MessageLookup extends MessageLookupByLibrary {
"skip": MessageLookupByLibrary.simpleMessage("Пропустить"),
"startDiet": MessageLookupByLibrary.simpleMessage("Начать диету"),
"tell_your_schedule": MessageLookupByLibrary.simpleMessage(
"Расскажите в свободной форме о том как просыпаетесь, когда едите, когда ложитесь спать:",),
"Расскажите в свободной форме о том как просыпаетесь, когда едите, когда ложитесь спать:"),
"unfavorite_foods":
MessageLookupByLibrary.simpleMessage("Нелюбимые продукты"),
"up_pressure": MessageLookupByLibrary.simpleMessage(
"Верхние показатели давления:",),
"Верхние показатели давления:"),
"user_authorization":
MessageLookupByLibrary.simpleMessage("Аутентификация"),
"user_registration":
@@ -192,7 +196,7 @@ class MessageLookup extends MessageLookupByLibrary {
"username": MessageLookupByLibrary.simpleMessage("Имя пользователя"),
"vegan_type": MessageLookupByLibrary.simpleMessage("Вегетарианская"),
"vegan_type_desc": MessageLookupByLibrary.simpleMessage(
"Диета основана на потреблении растительной пищи и отказе от продуктов животного происхождения. Такой тип питания может быть разнообразным и вкусным, а также полезным для здоровья.",),
"Диета основана на потреблении растительной пищи и отказе от продуктов животного происхождения. Такой тип питания может быть разнообразным и вкусным, а также полезным для здоровья."),
"waist": MessageLookupByLibrary.simpleMessage("Талия"),
"waist_circumference":
MessageLookupByLibrary.simpleMessage("Обхват талии"),
@@ -203,7 +207,7 @@ class MessageLookup extends MessageLookupByLibrary {
"what_is_your_name":
MessageLookupByLibrary.simpleMessage("Как вас зовут?"),
"wrong_credentials": MessageLookupByLibrary.simpleMessage(
"Неверные данные пользователя",),
"Неверные данные пользователя"),
"your_blood_pressure":
MessageLookupByLibrary.simpleMessage("Ваше артериальное давление?"),
"your_favorite_sports":
@@ -211,6 +215,6 @@ class MessageLookup extends MessageLookupByLibrary {
"your_gender": MessageLookupByLibrary.simpleMessage("Ваш пол?"),
"your_height": MessageLookupByLibrary.simpleMessage("Ваш рост:"),
"your_schedule": MessageLookupByLibrary.simpleMessage("Ваш режим дня?"),
"your_weight": MessageLookupByLibrary.simpleMessage("Ваш вес:"),
"your_weight": MessageLookupByLibrary.simpleMessage("Ваш вес:")
};
}

View File

@@ -19,7 +19,7 @@ class S {
static S get current {
assert(_current != null,
'No instance of S was loaded. Try to initialize the S delegate before accessing S.current.',);
'No instance of S was loaded. Try to initialize the S delegate before accessing S.current.');
return _current!;
}
@@ -42,7 +42,7 @@ class S {
static S of(BuildContext context) {
final instance = S.maybeOf(context);
assert(instance != null,
'No instance of S present in the widget tree. Did you add S.delegate in localizationsDelegates?',);
'No instance of S present in the widget tree. Did you add S.delegate in localizationsDelegates?');
return instance!;
}
@@ -1369,6 +1369,46 @@ class S {
args: [],
);
}
/// `Порядок`
String get order {
return Intl.message(
'Порядок',
name: 'order',
desc: '',
args: [],
);
}
/// `Время прохождения`
String get diet_time {
return Intl.message(
'Время прохождения',
name: 'diet_time',
desc: '',
args: [],
);
}
/// `Все`
String get all {
return Intl.message(
'Все',
name: 'all',
desc: '',
args: [],
);
}
/// `Тип диеты`
String get diet_type {
return Intl.message(
'Тип диеты',
name: 'diet_type',
desc: '',
args: [],
);
}
}
class AppLocalizationDelegate extends LocalizationsDelegate<S> {

View File

@@ -130,5 +130,9 @@
"answer": "Ответить",
"link_was_copied": "Ссылка успешно скопирована",
"choose_start_diet_date": "Выберите дату старты диеты. Она отобразиться у Вас в дневнике",
"continue_": "Продолжить"
"continue_": "Продолжить",
"order": "Порядок",
"diet_time": "Время прохождения",
"all": "Все",
"diet_type": "Тип диеты"
}

View File

@@ -176,6 +176,8 @@ class PrimaryColors {
Color get lightGray => Color(0xFFCECECE);
Color get transparentBlue => Color(0xFF2F80ED).withOpacity(0.1);
Color get qwe => Color(0xFFF6F6F6);
}
PrimaryColors get appTheme => ThemeHelper().themeColor();