Merge remote-tracking branch 'refs/remotes/origin/feature/feed'

# Conflicts:
#	lib/app_router.dart
#	lib/locator_service.dart
This commit is contained in:
mso7nueh
2024-08-13 16:34:48 +03:00
12 changed files with 1232 additions and 85 deletions

View File

@@ -11,7 +11,6 @@ import 'package:eatvio_app/features/diary/presentation/pages/diary_screen.dart';
import 'package:eatvio_app/features/feed/models/recipes_model.dart';
import 'package:eatvio_app/features/feed/presentation/pages/feed_screen.dart';
import 'package:eatvio_app/features/recipes/presentation/pages/about_recipes_screen.dart';
import 'package:eatvio_app/features/recipes/presentation/pages/recipe_create_screen.dart';
import 'package:eatvio_app/features/registration/presentation/pages/registration_screen.dart';
import 'package:eatvio_app/presentation/auth/auth_screen/auth_screen.dart';
import 'package:eatvio_app/presentation/diets/diet_create_screen/diet_create_screen.dart';
@@ -94,9 +93,18 @@ class AppRouter {
),
),
GoRoute(
path: '/recipe-create-screen',
name: 'recipeCreateScreen',
builder: (context, state) => RecipeCreateScreen(),
path: '/profile-screen',
name: 'profileScreen',
builder: (context, state) => ProfileScreen(
author: state.extra as Author,
),
),
GoRoute(
path: '/about-diet-screen',
name: 'aboutDietScreen',
builder: (context, state) => AboutDietScreen(
datum: state.extra as Datum,
),
),
GoRoute(
path: '/blog-post-create-screen',

View File

@@ -49,7 +49,7 @@ class _BlogPostCreateScreenState extends State<BlogPostCreateScreen> {
_buildBlogPostCreate(BuildContext context, BlogPostCreateLoadedState state) {
return Scaffold(
resizeToAvoidBottomInset: false,
resizeToAvoidBottomInset: true,
appBar: AppBar(),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
floatingActionButton: SizedBox(
@@ -62,8 +62,12 @@ class _BlogPostCreateScreenState extends State<BlogPostCreateScreen> {
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
backgroundColor: appTheme.blueA400,
onPressed: () => _submitForm(context, state.title!.text, state.description!.text, state.youtubeURL!.text),
backgroundColor: state.image != null || state.title!.text.isNotEmpty || state.description!.text.isNotEmpty || state.youtubeURL!.text.isNotEmpty
? appTheme.blueA400
: Colors.grey,
onPressed: state.image != null || state.title!.text.isNotEmpty || state.description!.text.isNotEmpty || state.youtubeURL!.text.isNotEmpty
? () => _submitForm(context, state.title!.text, state.description!.text, state.youtubeURL!.text)
: null,
label: Text(
'Создать',
style: theme.textTheme.titleSmall,
@@ -75,32 +79,29 @@ class _BlogPostCreateScreenState extends State<BlogPostCreateScreen> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (state.youtubeURL!.text.isEmpty) Text('Изображение поста'),
if (state.youtubeURL!.text.isEmpty) SizedBox(height: 8),
if (state.youtubeURL!.text.isEmpty) _buildAddPhoto(context, state),
SizedBox(height: 16),
Text('Заголовок'),
SizedBox(height: 8),
CustomTextFormField(
controller: state.title,
validator: _validateTitle,
),
SizedBox(height: 16),
Text('Описание'),
SizedBox(height: 8),
CustomTextFormField(
controller: state.description,
validator: _validateDescription,
maxLines: 6,
minLines: 1,
),
SizedBox(height: 16),
Text('Ссылка на видео'),
SizedBox(height: 8),
CustomTextFormField(
if (state.image == null) Text('Ссылка на видео'),
if (state.image == null) SizedBox(height: 8),
if (state.image == null) CustomTextFormField(
controller: state.youtubeURL,
validator: _validateYouTubeLink,
),
SizedBox(height: 16),
Text('Изображение поста'),
SizedBox(height: 8),
_buildAddPhoto(context, state),
SizedBox(height: 80),
],
),
@@ -110,20 +111,7 @@ class _BlogPostCreateScreenState extends State<BlogPostCreateScreen> {
}
_submitForm(BuildContext context, String title, String description, String youtubeURL) async {
String? titleError = _validateTitle(title);
String? descriptionError = _validateDescription(description);
String? youtubeURLError = _validateYouTubeLink(youtubeURL);
if (titleError != null || descriptionError != null || youtubeURLError != null) {
// Display error messages or handle invalid input as needed
// For example, show a snackbar with the error message
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(titleError ?? descriptionError ?? youtubeURLError!)),
);
} else {
// Proceed with form submission
context.read<BlogPostCreateBloc>().add(CreateBlogPost());
}
context.read<BlogPostCreateBloc>().add(CreateBlogPost());
}
_buildAddPhoto(BuildContext context, BlogPostCreateLoadedState state) {
@@ -179,31 +167,3 @@ addImage(BuildContext context, bool isLoading) {
};
}
}
String? _validateTitle(value) {
if (value!.isEmpty) {
return 'Пожалуйста, введите заголовок';
}
return null;
}
String? _validateDescription(value) {
if (value!.isEmpty) {
return 'Пожалуйста, введите описание';
}
return null;
}
String? _validateYouTubeLink(value) {
if (value!.isEmpty) {
return 'Пожалуйста, введите ссылку';
} else if (!_isValidUrl(value)) {
return 'Пожалуйста, введите корректную ссылку';
}
return null;
}
bool _isValidUrl(String url) {
final RegExp urlRegExp = new RegExp(r"^(http|https):\/\/([\w-]+\.)+[\w-]+(\/[\w-./?%&=]*)?$");
return urlRegExp.hasMatch(url);
}

View File

@@ -0,0 +1,250 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:eatvio_app/core/constants/constants.dart';
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
import 'package:eatvio_app/theme/theme_helper.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
class AboutDietScreen extends StatefulWidget {
final Datum datum;
late final List<int> selectedIndexes;
AboutDietScreen({super.key, required this.datum}) {
selectedIndexes = List.generate(datum.dailySchedule.length, (_) => 0);
}
@override
State<AboutDietScreen> createState() => _AboutDietScreenState();
}
class _AboutDietScreenState extends State<AboutDietScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
floatingActionButton: SizedBox(
width: MediaQuery.of(context).size.width - 32,
child: FloatingActionButton.extended(
heroTag: 'nextButtonRegistration',
highlightElevation: 0,
elevation: 0,
focusElevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
backgroundColor: appTheme.greenA700,
onPressed: () {},
label: Text(
'Начать диету',
style: theme.textTheme.titleSmall,
),
),
),
appBar: AppBar(
actions: [
InkWell(
borderRadius: BorderRadius.circular(16),
onTap: () => GoRouter.of(context).pushNamed('profileScreen', extra: widget.datum.author),
child: Ink(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircleAvatar(
maxRadius: 16,
minRadius: 16,
backgroundImage: NetworkImage(widget.datum.author.authorProfileIcon),
),
SizedBox(height: 4),
Text(
widget.datum.author.authorName,
style: TextStyle(
color: Colors.black,
fontSize: 14,
fontWeight: FontWeight.w400,
height: 1,
),
),
],
),
),
),
],
),
body: SingleChildScrollView(
padding: EdgeInsets.only(bottom: 80),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Text(
widget.datum.name,
style: TextStyle(
color: Colors.black,
fontSize: 24,
fontWeight: FontWeight.w500,
height: 1,
),
),
),
SizedBox(height: 12),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
children: [
Icon(Icons.timer_outlined),
SizedBox(width: 4),
Text(
widget.datum.duration.toString() + ' дней',
style: TextStyle(
color: Colors.black,
fontSize: 12,
fontWeight: FontWeight.w400,
height: 1,
),
),
],
),
),
SizedBox(height: 12),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Text(
widget.datum.details,
style: TextStyle(
color: Colors.black,
fontSize: 14,
fontWeight: FontWeight.w400,
height: 1.2,
),
),
),
SizedBox(height: 12),
CachedNetworkImage(
imageUrl: baseUrl + '/storage/' + widget.datum.image,
),
SizedBox(height: 12),
ListView.separated(
padding: EdgeInsets.all(16),
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemBuilder: (context, index) {
return _dayOfPlan(widget.datum.dailySchedule[index], index);
},
separatorBuilder: (context, index) => Padding(
padding: const EdgeInsets.symmetric(vertical: 16),
child: Container(
height: 0.4,
decoration: BoxDecoration(
color: Colors.grey,
borderRadius: BorderRadius.circular(16),
),
),
),
itemCount: widget.datum.dailySchedule.length,
),
SizedBox(height: 32),
],
),
),
);
}
_dayOfPlan(List<List<DailySchedule>> day, int dayNumber) {
_choiceChipWidget(int index) {
return ChoiceChip(
selectedColor: appTheme.blueA400,
pressElevation: 0,
showCheckmark: false,
side: BorderSide.none,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
label: Text((index + 1).toString()),
labelStyle: TextStyle(
color: index == widget.selectedIndexes[dayNumber] ? Colors.white : appTheme.gray600,
fontWeight: FontWeight.w500,
fontSize: 12,
),
selected: widget.selectedIndexes[dayNumber] == index,
backgroundColor: appTheme.gray100,
onSelected: (bool selected) {
setState(() {
widget.selectedIndexes[dayNumber] = index;
});
},
);
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'День ' + (dayNumber + 1).toString(),
style: TextStyle(
color: Colors.black,
fontSize: 16,
fontWeight: FontWeight.w500,
height: 1,
),
),
SizedBox(height: 4),
Text(
'Приём пищи',
style: TextStyle(
color: Colors.black,
fontSize: 14,
fontWeight: FontWeight.w400,
height: 1,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_choiceChipWidget(0),
_choiceChipWidget(1),
_choiceChipWidget(2),
_choiceChipWidget(3),
_choiceChipWidget(4),
],
),
SizedBox(height: 8),
ListView.separated(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, index) => _daily(day[widget.selectedIndexes[dayNumber]][index]),
separatorBuilder: (context, index) => SizedBox(height: 4),
itemCount: day[widget.selectedIndexes[dayNumber]].length,
),
],
);
}
_daily(DailySchedule daily) {
return Row(
children: [
Text(
'• ' + daily.name,
style: TextStyle(
color: Colors.black,
fontSize: 14,
fontWeight: FontWeight.w400,
height: 1.2,
),
),
Spacer(),
Text(
daily.quantity.toString() + ' ' + daily.portion.name,
style: TextStyle(
color: Colors.black,
fontSize: 14,
fontWeight: FontWeight.w400,
height: 1.2,
),
),
],
);
}
}

View File

@@ -0,0 +1,18 @@
import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_state.dart';
import 'package:eatvio_app/features/feed/repositories/feed_repository.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class DietsCubit extends Cubit<DietsState> {
final FeedRepository feedRepository;
DietsCubit({required this.feedRepository}) : super(const DietsState.loading());
loadDiets() async {
try {
final diets = await feedRepository.getDiets();
emit(DietsState.loaded(diets: diets));
} catch (e) {
emit(DietsState.error(errorMessage: e.toString()));
}
}
}

View File

@@ -0,0 +1,19 @@
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'diets_state.freezed.dart';
@freezed
abstract class DietsState with _$DietsState {
const factory DietsState() = _DietsState;
const factory DietsState.loading() = DietsStateLoading;
const factory DietsState.loaded({
required DietsPagination diets,
}) = DietsStateLoaded;
const factory DietsState.error({
required String errorMessage,
}) = DietsStateError;
}

View File

@@ -0,0 +1,620 @@
// 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 'diets_state.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');
/// @nodoc
mixin _$DietsState {
@optionalTypeArgs
TResult when<TResult extends Object?>(
TResult Function() $default, {
required TResult Function() loading,
required TResult Function(DietsPagination diets) loaded,
required TResult Function(String errorMessage) error,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>(
TResult? Function()? $default, {
TResult? Function()? loading,
TResult? Function(DietsPagination diets)? loaded,
TResult? Function(String errorMessage)? error,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>(
TResult Function()? $default, {
TResult Function()? loading,
TResult Function(DietsPagination diets)? loaded,
TResult Function(String errorMessage)? error,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult map<TResult extends Object?>(
TResult Function(_DietsState value) $default, {
required TResult Function(DietsStateLoading value) loading,
required TResult Function(DietsStateLoaded value) loaded,
required TResult Function(DietsStateError value) error,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>(
TResult? Function(_DietsState value)? $default, {
TResult? Function(DietsStateLoading value)? loading,
TResult? Function(DietsStateLoaded value)? loaded,
TResult? Function(DietsStateError value)? error,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>(
TResult Function(_DietsState value)? $default, {
TResult Function(DietsStateLoading value)? loading,
TResult Function(DietsStateLoaded value)? loaded,
TResult Function(DietsStateError value)? error,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $DietsStateCopyWith<$Res> {
factory $DietsStateCopyWith(
DietsState value, $Res Function(DietsState) then) =
_$DietsStateCopyWithImpl<$Res, DietsState>;
}
/// @nodoc
class _$DietsStateCopyWithImpl<$Res, $Val extends DietsState>
implements $DietsStateCopyWith<$Res> {
_$DietsStateCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
}
/// @nodoc
abstract class _$$DietsStateImplCopyWith<$Res> {
factory _$$DietsStateImplCopyWith(
_$DietsStateImpl value, $Res Function(_$DietsStateImpl) then) =
__$$DietsStateImplCopyWithImpl<$Res>;
}
/// @nodoc
class __$$DietsStateImplCopyWithImpl<$Res>
extends _$DietsStateCopyWithImpl<$Res, _$DietsStateImpl>
implements _$$DietsStateImplCopyWith<$Res> {
__$$DietsStateImplCopyWithImpl(
_$DietsStateImpl _value, $Res Function(_$DietsStateImpl) _then)
: super(_value, _then);
}
/// @nodoc
class _$DietsStateImpl implements _DietsState {
const _$DietsStateImpl();
@override
String toString() {
return 'DietsState()';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _$DietsStateImpl);
}
@override
int get hashCode => runtimeType.hashCode;
@override
@optionalTypeArgs
TResult when<TResult extends Object?>(
TResult Function() $default, {
required TResult Function() loading,
required TResult Function(DietsPagination diets) loaded,
required TResult Function(String errorMessage) error,
}) {
return $default();
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>(
TResult? Function()? $default, {
TResult? Function()? loading,
TResult? Function(DietsPagination diets)? loaded,
TResult? Function(String errorMessage)? error,
}) {
return $default?.call();
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>(
TResult Function()? $default, {
TResult Function()? loading,
TResult Function(DietsPagination diets)? loaded,
TResult Function(String errorMessage)? error,
required TResult orElse(),
}) {
if ($default != null) {
return $default();
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>(
TResult Function(_DietsState value) $default, {
required TResult Function(DietsStateLoading value) loading,
required TResult Function(DietsStateLoaded value) loaded,
required TResult Function(DietsStateError value) error,
}) {
return $default(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>(
TResult? Function(_DietsState value)? $default, {
TResult? Function(DietsStateLoading value)? loading,
TResult? Function(DietsStateLoaded value)? loaded,
TResult? Function(DietsStateError value)? error,
}) {
return $default?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>(
TResult Function(_DietsState value)? $default, {
TResult Function(DietsStateLoading value)? loading,
TResult Function(DietsStateLoaded value)? loaded,
TResult Function(DietsStateError value)? error,
required TResult orElse(),
}) {
if ($default != null) {
return $default(this);
}
return orElse();
}
}
abstract class _DietsState implements DietsState {
const factory _DietsState() = _$DietsStateImpl;
}
/// @nodoc
abstract class _$$DietsStateLoadingImplCopyWith<$Res> {
factory _$$DietsStateLoadingImplCopyWith(_$DietsStateLoadingImpl value,
$Res Function(_$DietsStateLoadingImpl) then) =
__$$DietsStateLoadingImplCopyWithImpl<$Res>;
}
/// @nodoc
class __$$DietsStateLoadingImplCopyWithImpl<$Res>
extends _$DietsStateCopyWithImpl<$Res, _$DietsStateLoadingImpl>
implements _$$DietsStateLoadingImplCopyWith<$Res> {
__$$DietsStateLoadingImplCopyWithImpl(_$DietsStateLoadingImpl _value,
$Res Function(_$DietsStateLoadingImpl) _then)
: super(_value, _then);
}
/// @nodoc
class _$DietsStateLoadingImpl implements DietsStateLoading {
const _$DietsStateLoadingImpl();
@override
String toString() {
return 'DietsState.loading()';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _$DietsStateLoadingImpl);
}
@override
int get hashCode => runtimeType.hashCode;
@override
@optionalTypeArgs
TResult when<TResult extends Object?>(
TResult Function() $default, {
required TResult Function() loading,
required TResult Function(DietsPagination diets) loaded,
required TResult Function(String errorMessage) error,
}) {
return loading();
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>(
TResult? Function()? $default, {
TResult? Function()? loading,
TResult? Function(DietsPagination diets)? loaded,
TResult? Function(String errorMessage)? error,
}) {
return loading?.call();
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>(
TResult Function()? $default, {
TResult Function()? loading,
TResult Function(DietsPagination diets)? loaded,
TResult Function(String errorMessage)? error,
required TResult orElse(),
}) {
if (loading != null) {
return loading();
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>(
TResult Function(_DietsState value) $default, {
required TResult Function(DietsStateLoading value) loading,
required TResult Function(DietsStateLoaded value) loaded,
required TResult Function(DietsStateError value) error,
}) {
return loading(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>(
TResult? Function(_DietsState value)? $default, {
TResult? Function(DietsStateLoading value)? loading,
TResult? Function(DietsStateLoaded value)? loaded,
TResult? Function(DietsStateError value)? error,
}) {
return loading?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>(
TResult Function(_DietsState value)? $default, {
TResult Function(DietsStateLoading value)? loading,
TResult Function(DietsStateLoaded value)? loaded,
TResult Function(DietsStateError value)? error,
required TResult orElse(),
}) {
if (loading != null) {
return loading(this);
}
return orElse();
}
}
abstract class DietsStateLoading implements DietsState {
const factory DietsStateLoading() = _$DietsStateLoadingImpl;
}
/// @nodoc
abstract class _$$DietsStateLoadedImplCopyWith<$Res> {
factory _$$DietsStateLoadedImplCopyWith(_$DietsStateLoadedImpl value,
$Res Function(_$DietsStateLoadedImpl) then) =
__$$DietsStateLoadedImplCopyWithImpl<$Res>;
@useResult
$Res call({DietsPagination diets});
$DietsPaginationCopyWith<$Res> get diets;
}
/// @nodoc
class __$$DietsStateLoadedImplCopyWithImpl<$Res>
extends _$DietsStateCopyWithImpl<$Res, _$DietsStateLoadedImpl>
implements _$$DietsStateLoadedImplCopyWith<$Res> {
__$$DietsStateLoadedImplCopyWithImpl(_$DietsStateLoadedImpl _value,
$Res Function(_$DietsStateLoadedImpl) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@override
$Res call({
Object? diets = null,
}) {
return _then(_$DietsStateLoadedImpl(
diets: null == diets
? _value.diets
: diets // ignore: cast_nullable_to_non_nullable
as DietsPagination,
));
}
@override
@pragma('vm:prefer-inline')
$DietsPaginationCopyWith<$Res> get diets {
return $DietsPaginationCopyWith<$Res>(_value.diets, (value) {
return _then(_value.copyWith(diets: value));
});
}
}
/// @nodoc
class _$DietsStateLoadedImpl implements DietsStateLoaded {
const _$DietsStateLoadedImpl({required this.diets});
@override
final DietsPagination diets;
@override
String toString() {
return 'DietsState.loaded(diets: $diets)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$DietsStateLoadedImpl &&
(identical(other.diets, diets) || other.diets == diets));
}
@override
int get hashCode => Object.hash(runtimeType, diets);
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$DietsStateLoadedImplCopyWith<_$DietsStateLoadedImpl> get copyWith =>
__$$DietsStateLoadedImplCopyWithImpl<_$DietsStateLoadedImpl>(
this, _$identity);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>(
TResult Function() $default, {
required TResult Function() loading,
required TResult Function(DietsPagination diets) loaded,
required TResult Function(String errorMessage) error,
}) {
return loaded(diets);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>(
TResult? Function()? $default, {
TResult? Function()? loading,
TResult? Function(DietsPagination diets)? loaded,
TResult? Function(String errorMessage)? error,
}) {
return loaded?.call(diets);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>(
TResult Function()? $default, {
TResult Function()? loading,
TResult Function(DietsPagination diets)? loaded,
TResult Function(String errorMessage)? error,
required TResult orElse(),
}) {
if (loaded != null) {
return loaded(diets);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>(
TResult Function(_DietsState value) $default, {
required TResult Function(DietsStateLoading value) loading,
required TResult Function(DietsStateLoaded value) loaded,
required TResult Function(DietsStateError value) error,
}) {
return loaded(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>(
TResult? Function(_DietsState value)? $default, {
TResult? Function(DietsStateLoading value)? loading,
TResult? Function(DietsStateLoaded value)? loaded,
TResult? Function(DietsStateError value)? error,
}) {
return loaded?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>(
TResult Function(_DietsState value)? $default, {
TResult Function(DietsStateLoading value)? loading,
TResult Function(DietsStateLoaded value)? loaded,
TResult Function(DietsStateError value)? error,
required TResult orElse(),
}) {
if (loaded != null) {
return loaded(this);
}
return orElse();
}
}
abstract class DietsStateLoaded implements DietsState {
const factory DietsStateLoaded({required final DietsPagination diets}) =
_$DietsStateLoadedImpl;
DietsPagination get diets;
@JsonKey(ignore: true)
_$$DietsStateLoadedImplCopyWith<_$DietsStateLoadedImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class _$$DietsStateErrorImplCopyWith<$Res> {
factory _$$DietsStateErrorImplCopyWith(_$DietsStateErrorImpl value,
$Res Function(_$DietsStateErrorImpl) then) =
__$$DietsStateErrorImplCopyWithImpl<$Res>;
@useResult
$Res call({String errorMessage});
}
/// @nodoc
class __$$DietsStateErrorImplCopyWithImpl<$Res>
extends _$DietsStateCopyWithImpl<$Res, _$DietsStateErrorImpl>
implements _$$DietsStateErrorImplCopyWith<$Res> {
__$$DietsStateErrorImplCopyWithImpl(
_$DietsStateErrorImpl _value, $Res Function(_$DietsStateErrorImpl) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@override
$Res call({
Object? errorMessage = null,
}) {
return _then(_$DietsStateErrorImpl(
errorMessage: null == errorMessage
? _value.errorMessage
: errorMessage // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
class _$DietsStateErrorImpl implements DietsStateError {
const _$DietsStateErrorImpl({required this.errorMessage});
@override
final String errorMessage;
@override
String toString() {
return 'DietsState.error(errorMessage: $errorMessage)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$DietsStateErrorImpl &&
(identical(other.errorMessage, errorMessage) ||
other.errorMessage == errorMessage));
}
@override
int get hashCode => Object.hash(runtimeType, errorMessage);
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$DietsStateErrorImplCopyWith<_$DietsStateErrorImpl> get copyWith =>
__$$DietsStateErrorImplCopyWithImpl<_$DietsStateErrorImpl>(
this, _$identity);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>(
TResult Function() $default, {
required TResult Function() loading,
required TResult Function(DietsPagination diets) loaded,
required TResult Function(String errorMessage) error,
}) {
return error(errorMessage);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>(
TResult? Function()? $default, {
TResult? Function()? loading,
TResult? Function(DietsPagination diets)? loaded,
TResult? Function(String errorMessage)? error,
}) {
return error?.call(errorMessage);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>(
TResult Function()? $default, {
TResult Function()? loading,
TResult Function(DietsPagination diets)? loaded,
TResult Function(String errorMessage)? error,
required TResult orElse(),
}) {
if (error != null) {
return error(errorMessage);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>(
TResult Function(_DietsState value) $default, {
required TResult Function(DietsStateLoading value) loading,
required TResult Function(DietsStateLoaded value) loaded,
required TResult Function(DietsStateError value) error,
}) {
return error(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>(
TResult? Function(_DietsState value)? $default, {
TResult? Function(DietsStateLoading value)? loading,
TResult? Function(DietsStateLoaded value)? loaded,
TResult? Function(DietsStateError value)? error,
}) {
return error?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>(
TResult Function(_DietsState value)? $default, {
TResult Function(DietsStateLoading value)? loading,
TResult Function(DietsStateLoaded value)? loaded,
TResult Function(DietsStateError value)? error,
required TResult orElse(),
}) {
if (error != null) {
return error(this);
}
return orElse();
}
}
abstract class DietsStateError implements DietsState {
const factory DietsStateError({required final String errorMessage}) =
_$DietsStateErrorImpl;
String get errorMessage;
@JsonKey(ignore: true)
_$$DietsStateErrorImplCopyWith<_$DietsStateErrorImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@@ -1,8 +1,11 @@
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/feed_cubit/feed_cubit.dart';
import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_state.dart';
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/diets_widget.dart';
import 'package:eatvio_app/features/feed/presentation/widgets/recipes_widget.dart';
import 'package:eatvio_app/theme/theme_helper.dart';
import 'package:flutter/cupertino.dart';
@@ -59,14 +62,57 @@ class _FeedScreenState extends State<FeedScreen> {
builder: (BuildContext context) {
if (_selectedChipIndex == 0)
return _buildAllBlogPosts();
else
else if (_selectedChipIndex == 1)
return _buildAllRecipes();
else if (_selectedChipIndex == 2)
return _buildDiets();
else
return Center(child: Text(chipsLabelList[_selectedChipIndex]));
},
),
),
);
}
_buildDiets() {
return RefreshIndicator(
onRefresh: () => context.read<DietsCubit>().loadDiets(),
child: BlocBuilder<DietsCubit, DietsState>(
builder: (BuildContext context, DietsState state) {
if (state is DietsStateLoaded) {
return ListView.separated(
padding: EdgeInsets.symmetric(vertical: 8),
shrinkWrap: true,
physics: BouncingScrollPhysics(),
itemBuilder: (context, index) {
return DietsWidget(datum: state.diets.data[index]);
},
separatorBuilder: (context, index) {
return SizedBox(height: 8);
},
itemCount: state.diets.data.length,
);
} else if (state is DietsStateError) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(16),
child: Center(
child: Text(state.errorMessage),
),
),
);
} else {
return Scaffold(
body: Center(
child: CupertinoActivityIndicator(),
),
);
}
},
),
);
}
_buildAllBlogPosts() {
return RefreshIndicator(
onRefresh: () => context.read<FeedCubit>().loadFeedBlogPostList(),

View File

@@ -0,0 +1,187 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:dismissible_page/dismissible_page.dart';
import 'package:eatvio_app/core/app_export.dart';
import 'package:eatvio_app/features/chat/presentation/pages/photo_view_screen.dart';
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
class DietsWidget extends StatefulWidget {
final Datum datum;
const DietsWidget({super.key, required this.datum});
@override
State<DietsWidget> createState() => _DietsWidgetState();
}
class _DietsWidgetState extends State<DietsWidget> {
@override
Widget build(BuildContext context) {
return InkWell(
borderRadius: BorderRadius.circular(12),
onTap: () => GoRouter.of(context).pushNamed('aboutDietScreen', extra: widget.datum),
child: Ink(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: Colors.white,
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Stack(
children: [
ClipRRect(
borderRadius: BorderRadius.vertical(top: Radius.circular(12)),
child: GestureDetector(
onTap: () {
context.pushTransparentRoute(
rootNavigator: true,
PhotoViewScreen(
imageUrls: [baseUrl + '/storage/' + widget.datum.image],
index: 0,
),
);
},
child: Hero(
tag: baseUrl + '/storage/' + widget.datum.image,
child: CachedNetworkImage(
imageUrl: baseUrl + '/storage/' + widget.datum.image,
),
),
),
),
Positioned(
left: 8,
bottom: 8,
child: Wrap(
runSpacing: 8,
children: [
_iconButton(Icons.fastfood, '139.9 ккал'),
SizedBox(width: 8),
_iconButton(Icons.timer_rounded, widget.datum.state),
SizedBox(width: 8),
_iconButton(Icons.fast_forward_rounded, widget.datum.duration.toString()),
],
),
),
Positioned(
left: 8,
top: 8,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.8),
borderRadius: BorderRadius.circular(16),
),
child: Row(
children: [
Icon(
Icons.favorite_rounded,
size: 16,
color: Colors.black,
),
SizedBox(width: 4),
Text(
widget.datum.totalLikes.toString(),
style: TextStyle(
fontSize: 12,
color: Colors.black,
fontWeight: FontWeight.w400,
),
),
],
),
),
),
Positioned(
right: 8,
top: 8,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.8),
borderRadius: BorderRadius.circular(16),
),
child: Row(
children: [
Icon(
Icons.visibility_rounded,
size: 16,
color: Colors.black,
),
SizedBox(width: 4),
Text(
widget.datum.totalViews.toString(),
style: TextStyle(
fontSize: 12,
color: Colors.black,
fontWeight: FontWeight.w400,
),
),
],
),
),
),
],
),
SizedBox(height: 12),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: Text(
widget.datum.name,
style: TextStyle(
fontWeight: FontWeight.w600,
color: Colors.black87,
),
),
),
SizedBox(height: 8),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: Text(
widget.datum.details,
style: TextStyle(
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),
if (text != null)
Text(
text,
style: TextStyle(
fontSize: 12,
color: Colors.black,
fontWeight: FontWeight.w500,
),
),
],
),
);
}
}

View File

@@ -0,0 +1,34 @@
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
import 'package:flutter/material.dart';
class ProfileScreen extends StatelessWidget {
final Author author;
const ProfileScreen({super.key, required this.author});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Column(
children: [
CircleAvatar(
maxRadius: 128,
minRadius: 128,
backgroundImage: NetworkImage(author.authorProfileIcon),
),
SizedBox(height: 32),
Text(
author.authorName,
style: TextStyle(
color: Colors.black,
fontSize: 24,
fontWeight: FontWeight.w400,
height: 1,
),
),
],
),
);
}
}

View File

@@ -61,6 +61,19 @@ class AboutRecipesScreen extends StatelessWidget {
),
),
SizedBox(height: 12),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Text(
recipes.description,
style: TextStyle(
color: Colors.black,
fontSize: 14,
fontWeight: FontWeight.w400,
height: 1.2,
),
),
),
SizedBox(height: 12),
CachedNetworkImage(
imageUrl: baseUrl + '/storage/' + recipes.image,
),
@@ -124,7 +137,7 @@ class AboutRecipesScreen extends StatelessWidget {
_ingredient(Ingredient ingredient) {
return Text(
'• '+ ingredient.name + ' ' + ingredient.portion + ' ' + (ingredient.unit ?? ''),
'• ' + ingredient.name + ' ' + ingredient.portion + ' ' + (ingredient.unit ?? ''),
style: TextStyle(
color: Colors.black,
fontSize: 14,

View File

@@ -15,6 +15,7 @@ import 'package:eatvio_app/features/chat/services/chat_service.dart';
import 'package:eatvio_app/features/diary/presentation/bloc/diary_bloc.dart';
import 'package:eatvio_app/features/diary/repositories/diary_repository.dart';
import 'package:eatvio_app/features/diary/services/diary_service.dart';
import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart';
import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart';
import 'package:eatvio_app/features/feed/presentation/bloc/recipes_cubit/recipes_cubit.dart';
import 'package:eatvio_app/features/feed/repositories/feed_repository.dart';
@@ -28,43 +29,32 @@ final sl = GetIt.instance;
Future<void> init() async {
// BLoC / Cubit
sl.registerLazySingleton<InitializationCubit>(
() => InitializationCubit(chatRepository: sl()));
sl.registerLazySingleton<InitializationCubit>(() => InitializationCubit(chatRepository: sl()));
sl.registerLazySingleton<NewMessagesCubit>(() => NewMessagesCubit());
sl.registerLazySingleton<NotificationsCubit>(() => NotificationsCubit());
sl.registerFactory<RegistrationCubit>(
() => RegistrationCubit(registrationRepository: sl()));
sl.registerFactory<ChatRoomCubit>(
() => ChatRoomCubit(chatRepository: sl(), initializationCubit: sl()));
sl.registerFactory<RegistrationCubit>(() => RegistrationCubit(registrationRepository: sl()));
sl.registerFactory<ChatRoomCubit>(() => ChatRoomCubit(chatRepository: sl(), initializationCubit: sl()));
sl.registerFactory<FeedCubit>(() => FeedCubit(feedRepository: sl()));
sl.registerFactory<RecipesCubit>(() => RecipesCubit(feedRepository: sl()));
sl.registerFactory<BlogPostCubit>(
() => BlogPostCubit(blogPostRepository: sl()));
sl.registerFactory<DietsCubit>(() => DietsCubit(feedRepository: sl()));
sl.registerFactory<BlogPostCubit>(() => BlogPostCubit(blogPostRepository: sl()));
sl.registerFactory<DiaryBloc>(() => DiaryBloc(diaryRepository: sl()));
sl.registerFactory<CalendarCubit>(
() => CalendarCubit(calendarRepository: sl()));
sl.registerFactory<CalendarCubit>(() => CalendarCubit(calendarRepository: sl()));
// Repository
sl.registerLazySingleton<ChatRepository>(
() => ChatRepository(chatService: sl()));
sl.registerLazySingleton<FeedRepository>(
() => FeedRepository(feedService: sl()));
sl.registerLazySingleton<BlogPostRepository>(
() => BlogPostRepository(blogPostService: sl()));
sl.registerLazySingleton<RegistrationRepository>(
() => RegistrationRepository(registrationService: sl()));
sl.registerLazySingleton<DiaryRepository>(
() => DiaryRepository(diaryService: sl()));
sl.registerLazySingleton<CalendarRepository>(
() => CalendarRepository(calendarService: sl()));
sl.registerLazySingleton<ChatRepository>(() => ChatRepository(chatService: sl()));
sl.registerLazySingleton<FeedRepository>(() => FeedRepository(feedService: sl()));
sl.registerLazySingleton<BlogPostRepository>(() => BlogPostRepository(blogPostService: sl()));
sl.registerLazySingleton<RegistrationRepository>(() => RegistrationRepository(registrationService: sl()));
sl.registerLazySingleton<DiaryRepository>(() => DiaryRepository(diaryService: sl()));
sl.registerLazySingleton<CalendarRepository>(() => CalendarRepository(calendarService: sl()));
// Services
sl.registerLazySingleton<ChatService>(() => ChatService(sl()));
sl.registerLazySingleton<FeedService>(() => FeedService(sl()));
sl.registerLazySingleton<BlogPostService>(() => BlogPostService(sl()));
sl.registerLazySingleton<RegistrationService>(
() => RegistrationService(sl()));
sl.registerLazySingleton<RegistrationService>(() => RegistrationService(sl()));
sl.registerLazySingleton<DiaryService>(() => DiaryService(sl()));
sl.registerLazySingleton<CalendarService>(() => CalendarService(sl()));

View File

@@ -7,6 +7,7 @@ import 'package:eatvio_app/features/calendar/presentation/bloc/calendar_cubit.da
import 'package:eatvio_app/features/chat/presentation/pages/chat_select_screen/bloc/chat_select_bloc.dart';
import 'package:eatvio_app/features/chat/presentation/pages/chat_select_screen/models/chat_select_model.dart';
import 'package:eatvio_app/features/diary/presentation/bloc/diary_bloc.dart';
import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart';
import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart';
import 'package:eatvio_app/features/feed/presentation/bloc/recipes_cubit/recipes_cubit.dart';
import 'package:eatvio_app/locator_service.dart' as di;
@@ -51,6 +52,7 @@ class MyApp extends StatelessWidget {
BlocProvider<ThemeBloc>(create: (context) => ThemeBloc(ThemeState(themeType: PrefUtils().getThemeData()))),
BlocProvider<FeedCubit>(create: (context) => sl<FeedCubit>()..loadFeedBlogPostList()),
BlocProvider<RecipesCubit>(create: (context) => sl<RecipesCubit>()..loadRecipesList()),
BlocProvider<DietsCubit>(create: (context) => sl<DietsCubit>()..loadDiets()),
BlocProvider<MyProfileBloc>(create: (context) => MyProfileBloc(MyProfileState(myProfileModelObj: MyProfileModel()))..add(MyProfileInitialEvent())),
BlocProvider<DiaryBloc>(create: (context) => sl<DiaryBloc>()..add(LoadDiary())),
BlocProvider<ChatSelectBloc>(