diff --git a/assets/icons/calendar.svg b/assets/icons/calendar.svg
new file mode 100644
index 00000000..e9d97952
--- /dev/null
+++ b/assets/icons/calendar.svg
@@ -0,0 +1,29 @@
+
+
+
+
diff --git a/assets/icons/diets.svg b/assets/icons/diets.svg
new file mode 100644
index 00000000..7714b3fa
--- /dev/null
+++ b/assets/icons/diets.svg
@@ -0,0 +1,24 @@
+
+
+
+
diff --git a/lib/app_router.dart b/lib/app_router.dart
index 57923180..3a662763 100644
--- a/lib/app_router.dart
+++ b/lib/app_router.dart
@@ -24,7 +24,6 @@ import 'package:eatvio_app/presentation/diets/diet_select_screen/diet_select_scr
import 'package:eatvio_app/presentation/feed/blog_post_edit_screen/blog_post_edit_screen.dart';
import 'package:eatvio_app/presentation/glossary/glossary_screen/glossary_screen.dart';
import 'package:eatvio_app/presentation/plans/plan_select_screen/plan_select_screen.dart';
-import 'package:eatvio_app/presentation/profile/my_profile_screen/my_profile_screen.dart';
import 'package:eatvio_app/presentation/profile/my_profile_screen/my_settings_screen.dart';
import 'package:eatvio_app/presentation/recipe/recipe_select_screen/recipe_select_screen.dart';
import 'package:flutter/material.dart';
@@ -62,7 +61,7 @@ class AppRouter {
},
routes: [
GoRoute(
- pageBuilder: (context, state) => NoTransitionPage(child: MyProfileScreen()),
+ pageBuilder: (context, state) => NoTransitionPage(child: ProfileScreen()),
path: '/my-profile-screen',
name: 'myProfileScreen',
redirect: (context, state) async {
diff --git a/lib/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_cubit.dart b/lib/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_cubit.dart
new file mode 100644
index 00000000..e2bfd48b
--- /dev/null
+++ b/lib/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_cubit.dart
@@ -0,0 +1,22 @@
+import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_state.dart';
+import 'package:eatvio_app/features/profile/repositories/profile_repository.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+class ProfileAvatarCubit extends Cubit {
+ final ProfileRepository profileRepository;
+
+ ProfileAvatarCubit({
+ required this.profileRepository,
+ }) : super(const ProfileAvatarState.loading());
+
+ getMyProfile() async {
+ try {
+ emit(ProfileAvatarState.loaded(
+ profile: await profileRepository.getMyProfile(),
+ ));
+ } catch (e) {
+ emit(ProfileAvatarState.error(errorMessage: e.toString()));
+ print(e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/lib/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_state.dart b/lib/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_state.dart
new file mode 100644
index 00000000..fe90fb6f
--- /dev/null
+++ b/lib/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_state.dart
@@ -0,0 +1,19 @@
+import 'package:eatvio_app/features/profile/models/profile_model.dart';
+import 'package:freezed_annotation/freezed_annotation.dart';
+
+part 'profile_avatar_state.freezed.dart';
+
+@freezed
+abstract class ProfileAvatarState with _$ProfileAvatarState {
+ const factory ProfileAvatarState() = _ProfileAvatarState;
+
+ const factory ProfileAvatarState.loading() = ProfileAvatarStateLoading;
+
+ const factory ProfileAvatarState.loaded({
+ required ProfileModel profile,
+ }) = ProfileAvatarStateLoaded;
+
+ const factory ProfileAvatarState.error({
+ required String errorMessage,
+ }) = ProfileAvatarStateError;
+}
diff --git a/lib/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_state.freezed.dart b/lib/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_state.freezed.dart
new file mode 100644
index 00000000..5d8de7ff
--- /dev/null
+++ b/lib/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_state.freezed.dart
@@ -0,0 +1,630 @@
+// 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 'profile_avatar_state.dart';
+
+// **************************************************************************
+// FreezedGenerator
+// **************************************************************************
+
+T _$identity(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 _$ProfileAvatarState {
+ @optionalTypeArgs
+ TResult when(
+ TResult Function() $default, {
+ required TResult Function() loading,
+ required TResult Function(ProfileModel profile) loaded,
+ required TResult Function(String errorMessage) error,
+ }) =>
+ throw _privateConstructorUsedError;
+ @optionalTypeArgs
+ TResult? whenOrNull(
+ TResult? Function()? $default, {
+ TResult? Function()? loading,
+ TResult? Function(ProfileModel profile)? loaded,
+ TResult? Function(String errorMessage)? error,
+ }) =>
+ throw _privateConstructorUsedError;
+ @optionalTypeArgs
+ TResult maybeWhen(
+ TResult Function()? $default, {
+ TResult Function()? loading,
+ TResult Function(ProfileModel profile)? loaded,
+ TResult Function(String errorMessage)? error,
+ required TResult orElse(),
+ }) =>
+ throw _privateConstructorUsedError;
+ @optionalTypeArgs
+ TResult map(
+ TResult Function(_ProfileAvatarState value) $default, {
+ required TResult Function(ProfileAvatarStateLoading value) loading,
+ required TResult Function(ProfileAvatarStateLoaded value) loaded,
+ required TResult Function(ProfileAvatarStateError value) error,
+ }) =>
+ throw _privateConstructorUsedError;
+ @optionalTypeArgs
+ TResult? mapOrNull(
+ TResult? Function(_ProfileAvatarState value)? $default, {
+ TResult? Function(ProfileAvatarStateLoading value)? loading,
+ TResult? Function(ProfileAvatarStateLoaded value)? loaded,
+ TResult? Function(ProfileAvatarStateError value)? error,
+ }) =>
+ throw _privateConstructorUsedError;
+ @optionalTypeArgs
+ TResult maybeMap(
+ TResult Function(_ProfileAvatarState value)? $default, {
+ TResult Function(ProfileAvatarStateLoading value)? loading,
+ TResult Function(ProfileAvatarStateLoaded value)? loaded,
+ TResult Function(ProfileAvatarStateError value)? error,
+ required TResult orElse(),
+ }) =>
+ throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $ProfileAvatarStateCopyWith<$Res> {
+ factory $ProfileAvatarStateCopyWith(
+ ProfileAvatarState value, $Res Function(ProfileAvatarState) then) =
+ _$ProfileAvatarStateCopyWithImpl<$Res, ProfileAvatarState>;
+}
+
+/// @nodoc
+class _$ProfileAvatarStateCopyWithImpl<$Res, $Val extends ProfileAvatarState>
+ implements $ProfileAvatarStateCopyWith<$Res> {
+ _$ProfileAvatarStateCopyWithImpl(this._value, this._then);
+
+ // ignore: unused_field
+ final $Val _value;
+ // ignore: unused_field
+ final $Res Function($Val) _then;
+}
+
+/// @nodoc
+abstract class _$$ProfileAvatarStateImplCopyWith<$Res> {
+ factory _$$ProfileAvatarStateImplCopyWith(_$ProfileAvatarStateImpl value,
+ $Res Function(_$ProfileAvatarStateImpl) then) =
+ __$$ProfileAvatarStateImplCopyWithImpl<$Res>;
+}
+
+/// @nodoc
+class __$$ProfileAvatarStateImplCopyWithImpl<$Res>
+ extends _$ProfileAvatarStateCopyWithImpl<$Res, _$ProfileAvatarStateImpl>
+ implements _$$ProfileAvatarStateImplCopyWith<$Res> {
+ __$$ProfileAvatarStateImplCopyWithImpl(_$ProfileAvatarStateImpl _value,
+ $Res Function(_$ProfileAvatarStateImpl) _then)
+ : super(_value, _then);
+}
+
+/// @nodoc
+
+class _$ProfileAvatarStateImpl implements _ProfileAvatarState {
+ const _$ProfileAvatarStateImpl();
+
+ @override
+ String toString() {
+ return 'ProfileAvatarState()';
+ }
+
+ @override
+ bool operator ==(Object other) {
+ return identical(this, other) ||
+ (other.runtimeType == runtimeType && other is _$ProfileAvatarStateImpl);
+ }
+
+ @override
+ int get hashCode => runtimeType.hashCode;
+
+ @override
+ @optionalTypeArgs
+ TResult when(
+ TResult Function() $default, {
+ required TResult Function() loading,
+ required TResult Function(ProfileModel profile) loaded,
+ required TResult Function(String errorMessage) error,
+ }) {
+ return $default();
+ }
+
+ @override
+ @optionalTypeArgs
+ TResult? whenOrNull(
+ TResult? Function()? $default, {
+ TResult? Function()? loading,
+ TResult? Function(ProfileModel profile)? loaded,
+ TResult? Function(String errorMessage)? error,
+ }) {
+ return $default?.call();
+ }
+
+ @override
+ @optionalTypeArgs
+ TResult maybeWhen(
+ TResult Function()? $default, {
+ TResult Function()? loading,
+ TResult Function(ProfileModel profile)? loaded,
+ TResult Function(String errorMessage)? error,
+ required TResult orElse(),
+ }) {
+ if ($default != null) {
+ return $default();
+ }
+ return orElse();
+ }
+
+ @override
+ @optionalTypeArgs
+ TResult map(
+ TResult Function(_ProfileAvatarState value) $default, {
+ required TResult Function(ProfileAvatarStateLoading value) loading,
+ required TResult Function(ProfileAvatarStateLoaded value) loaded,
+ required TResult Function(ProfileAvatarStateError value) error,
+ }) {
+ return $default(this);
+ }
+
+ @override
+ @optionalTypeArgs
+ TResult? mapOrNull(
+ TResult? Function(_ProfileAvatarState value)? $default, {
+ TResult? Function(ProfileAvatarStateLoading value)? loading,
+ TResult? Function(ProfileAvatarStateLoaded value)? loaded,
+ TResult? Function(ProfileAvatarStateError value)? error,
+ }) {
+ return $default?.call(this);
+ }
+
+ @override
+ @optionalTypeArgs
+ TResult maybeMap(
+ TResult Function(_ProfileAvatarState value)? $default, {
+ TResult Function(ProfileAvatarStateLoading value)? loading,
+ TResult Function(ProfileAvatarStateLoaded value)? loaded,
+ TResult Function(ProfileAvatarStateError value)? error,
+ required TResult orElse(),
+ }) {
+ if ($default != null) {
+ return $default(this);
+ }
+ return orElse();
+ }
+}
+
+abstract class _ProfileAvatarState implements ProfileAvatarState {
+ const factory _ProfileAvatarState() = _$ProfileAvatarStateImpl;
+}
+
+/// @nodoc
+abstract class _$$ProfileAvatarStateLoadingImplCopyWith<$Res> {
+ factory _$$ProfileAvatarStateLoadingImplCopyWith(
+ _$ProfileAvatarStateLoadingImpl value,
+ $Res Function(_$ProfileAvatarStateLoadingImpl) then) =
+ __$$ProfileAvatarStateLoadingImplCopyWithImpl<$Res>;
+}
+
+/// @nodoc
+class __$$ProfileAvatarStateLoadingImplCopyWithImpl<$Res>
+ extends _$ProfileAvatarStateCopyWithImpl<$Res,
+ _$ProfileAvatarStateLoadingImpl>
+ implements _$$ProfileAvatarStateLoadingImplCopyWith<$Res> {
+ __$$ProfileAvatarStateLoadingImplCopyWithImpl(
+ _$ProfileAvatarStateLoadingImpl _value,
+ $Res Function(_$ProfileAvatarStateLoadingImpl) _then)
+ : super(_value, _then);
+}
+
+/// @nodoc
+
+class _$ProfileAvatarStateLoadingImpl implements ProfileAvatarStateLoading {
+ const _$ProfileAvatarStateLoadingImpl();
+
+ @override
+ String toString() {
+ return 'ProfileAvatarState.loading()';
+ }
+
+ @override
+ bool operator ==(Object other) {
+ return identical(this, other) ||
+ (other.runtimeType == runtimeType &&
+ other is _$ProfileAvatarStateLoadingImpl);
+ }
+
+ @override
+ int get hashCode => runtimeType.hashCode;
+
+ @override
+ @optionalTypeArgs
+ TResult when(
+ TResult Function() $default, {
+ required TResult Function() loading,
+ required TResult Function(ProfileModel profile) loaded,
+ required TResult Function(String errorMessage) error,
+ }) {
+ return loading();
+ }
+
+ @override
+ @optionalTypeArgs
+ TResult? whenOrNull(
+ TResult? Function()? $default, {
+ TResult? Function()? loading,
+ TResult? Function(ProfileModel profile)? loaded,
+ TResult? Function(String errorMessage)? error,
+ }) {
+ return loading?.call();
+ }
+
+ @override
+ @optionalTypeArgs
+ TResult maybeWhen(
+ TResult Function()? $default, {
+ TResult Function()? loading,
+ TResult Function(ProfileModel profile)? loaded,
+ TResult Function(String errorMessage)? error,
+ required TResult orElse(),
+ }) {
+ if (loading != null) {
+ return loading();
+ }
+ return orElse();
+ }
+
+ @override
+ @optionalTypeArgs
+ TResult map(
+ TResult Function(_ProfileAvatarState value) $default, {
+ required TResult Function(ProfileAvatarStateLoading value) loading,
+ required TResult Function(ProfileAvatarStateLoaded value) loaded,
+ required TResult Function(ProfileAvatarStateError value) error,
+ }) {
+ return loading(this);
+ }
+
+ @override
+ @optionalTypeArgs
+ TResult? mapOrNull(
+ TResult? Function(_ProfileAvatarState value)? $default, {
+ TResult? Function(ProfileAvatarStateLoading value)? loading,
+ TResult? Function(ProfileAvatarStateLoaded value)? loaded,
+ TResult? Function(ProfileAvatarStateError value)? error,
+ }) {
+ return loading?.call(this);
+ }
+
+ @override
+ @optionalTypeArgs
+ TResult maybeMap(
+ TResult Function(_ProfileAvatarState value)? $default, {
+ TResult Function(ProfileAvatarStateLoading value)? loading,
+ TResult Function(ProfileAvatarStateLoaded value)? loaded,
+ TResult Function(ProfileAvatarStateError value)? error,
+ required TResult orElse(),
+ }) {
+ if (loading != null) {
+ return loading(this);
+ }
+ return orElse();
+ }
+}
+
+abstract class ProfileAvatarStateLoading implements ProfileAvatarState {
+ const factory ProfileAvatarStateLoading() = _$ProfileAvatarStateLoadingImpl;
+}
+
+/// @nodoc
+abstract class _$$ProfileAvatarStateLoadedImplCopyWith<$Res> {
+ factory _$$ProfileAvatarStateLoadedImplCopyWith(
+ _$ProfileAvatarStateLoadedImpl value,
+ $Res Function(_$ProfileAvatarStateLoadedImpl) then) =
+ __$$ProfileAvatarStateLoadedImplCopyWithImpl<$Res>;
+ @useResult
+ $Res call({ProfileModel profile});
+
+ $ProfileModelCopyWith<$Res> get profile;
+}
+
+/// @nodoc
+class __$$ProfileAvatarStateLoadedImplCopyWithImpl<$Res>
+ extends _$ProfileAvatarStateCopyWithImpl<$Res,
+ _$ProfileAvatarStateLoadedImpl>
+ implements _$$ProfileAvatarStateLoadedImplCopyWith<$Res> {
+ __$$ProfileAvatarStateLoadedImplCopyWithImpl(
+ _$ProfileAvatarStateLoadedImpl _value,
+ $Res Function(_$ProfileAvatarStateLoadedImpl) _then)
+ : super(_value, _then);
+
+ @pragma('vm:prefer-inline')
+ @override
+ $Res call({
+ Object? profile = null,
+ }) {
+ return _then(_$ProfileAvatarStateLoadedImpl(
+ profile: null == profile
+ ? _value.profile
+ : profile // ignore: cast_nullable_to_non_nullable
+ as ProfileModel,
+ ));
+ }
+
+ @override
+ @pragma('vm:prefer-inline')
+ $ProfileModelCopyWith<$Res> get profile {
+ return $ProfileModelCopyWith<$Res>(_value.profile, (value) {
+ return _then(_value.copyWith(profile: value));
+ });
+ }
+}
+
+/// @nodoc
+
+class _$ProfileAvatarStateLoadedImpl implements ProfileAvatarStateLoaded {
+ const _$ProfileAvatarStateLoadedImpl({required this.profile});
+
+ @override
+ final ProfileModel profile;
+
+ @override
+ String toString() {
+ return 'ProfileAvatarState.loaded(profile: $profile)';
+ }
+
+ @override
+ bool operator ==(Object other) {
+ return identical(this, other) ||
+ (other.runtimeType == runtimeType &&
+ other is _$ProfileAvatarStateLoadedImpl &&
+ (identical(other.profile, profile) || other.profile == profile));
+ }
+
+ @override
+ int get hashCode => Object.hash(runtimeType, profile);
+
+ @JsonKey(ignore: true)
+ @override
+ @pragma('vm:prefer-inline')
+ _$$ProfileAvatarStateLoadedImplCopyWith<_$ProfileAvatarStateLoadedImpl>
+ get copyWith => __$$ProfileAvatarStateLoadedImplCopyWithImpl<
+ _$ProfileAvatarStateLoadedImpl>(this, _$identity);
+
+ @override
+ @optionalTypeArgs
+ TResult when(
+ TResult Function() $default, {
+ required TResult Function() loading,
+ required TResult Function(ProfileModel profile) loaded,
+ required TResult Function(String errorMessage) error,
+ }) {
+ return loaded(profile);
+ }
+
+ @override
+ @optionalTypeArgs
+ TResult? whenOrNull(
+ TResult? Function()? $default, {
+ TResult? Function()? loading,
+ TResult? Function(ProfileModel profile)? loaded,
+ TResult? Function(String errorMessage)? error,
+ }) {
+ return loaded?.call(profile);
+ }
+
+ @override
+ @optionalTypeArgs
+ TResult maybeWhen(
+ TResult Function()? $default, {
+ TResult Function()? loading,
+ TResult Function(ProfileModel profile)? loaded,
+ TResult Function(String errorMessage)? error,
+ required TResult orElse(),
+ }) {
+ if (loaded != null) {
+ return loaded(profile);
+ }
+ return orElse();
+ }
+
+ @override
+ @optionalTypeArgs
+ TResult map(
+ TResult Function(_ProfileAvatarState value) $default, {
+ required TResult Function(ProfileAvatarStateLoading value) loading,
+ required TResult Function(ProfileAvatarStateLoaded value) loaded,
+ required TResult Function(ProfileAvatarStateError value) error,
+ }) {
+ return loaded(this);
+ }
+
+ @override
+ @optionalTypeArgs
+ TResult? mapOrNull(
+ TResult? Function(_ProfileAvatarState value)? $default, {
+ TResult? Function(ProfileAvatarStateLoading value)? loading,
+ TResult? Function(ProfileAvatarStateLoaded value)? loaded,
+ TResult? Function(ProfileAvatarStateError value)? error,
+ }) {
+ return loaded?.call(this);
+ }
+
+ @override
+ @optionalTypeArgs
+ TResult maybeMap(
+ TResult Function(_ProfileAvatarState value)? $default, {
+ TResult Function(ProfileAvatarStateLoading value)? loading,
+ TResult Function(ProfileAvatarStateLoaded value)? loaded,
+ TResult Function(ProfileAvatarStateError value)? error,
+ required TResult orElse(),
+ }) {
+ if (loaded != null) {
+ return loaded(this);
+ }
+ return orElse();
+ }
+}
+
+abstract class ProfileAvatarStateLoaded implements ProfileAvatarState {
+ const factory ProfileAvatarStateLoaded(
+ {required final ProfileModel profile}) = _$ProfileAvatarStateLoadedImpl;
+
+ ProfileModel get profile;
+ @JsonKey(ignore: true)
+ _$$ProfileAvatarStateLoadedImplCopyWith<_$ProfileAvatarStateLoadedImpl>
+ get copyWith => throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class _$$ProfileAvatarStateErrorImplCopyWith<$Res> {
+ factory _$$ProfileAvatarStateErrorImplCopyWith(
+ _$ProfileAvatarStateErrorImpl value,
+ $Res Function(_$ProfileAvatarStateErrorImpl) then) =
+ __$$ProfileAvatarStateErrorImplCopyWithImpl<$Res>;
+ @useResult
+ $Res call({String errorMessage});
+}
+
+/// @nodoc
+class __$$ProfileAvatarStateErrorImplCopyWithImpl<$Res>
+ extends _$ProfileAvatarStateCopyWithImpl<$Res,
+ _$ProfileAvatarStateErrorImpl>
+ implements _$$ProfileAvatarStateErrorImplCopyWith<$Res> {
+ __$$ProfileAvatarStateErrorImplCopyWithImpl(
+ _$ProfileAvatarStateErrorImpl _value,
+ $Res Function(_$ProfileAvatarStateErrorImpl) _then)
+ : super(_value, _then);
+
+ @pragma('vm:prefer-inline')
+ @override
+ $Res call({
+ Object? errorMessage = null,
+ }) {
+ return _then(_$ProfileAvatarStateErrorImpl(
+ errorMessage: null == errorMessage
+ ? _value.errorMessage
+ : errorMessage // ignore: cast_nullable_to_non_nullable
+ as String,
+ ));
+ }
+}
+
+/// @nodoc
+
+class _$ProfileAvatarStateErrorImpl implements ProfileAvatarStateError {
+ const _$ProfileAvatarStateErrorImpl({required this.errorMessage});
+
+ @override
+ final String errorMessage;
+
+ @override
+ String toString() {
+ return 'ProfileAvatarState.error(errorMessage: $errorMessage)';
+ }
+
+ @override
+ bool operator ==(Object other) {
+ return identical(this, other) ||
+ (other.runtimeType == runtimeType &&
+ other is _$ProfileAvatarStateErrorImpl &&
+ (identical(other.errorMessage, errorMessage) ||
+ other.errorMessage == errorMessage));
+ }
+
+ @override
+ int get hashCode => Object.hash(runtimeType, errorMessage);
+
+ @JsonKey(ignore: true)
+ @override
+ @pragma('vm:prefer-inline')
+ _$$ProfileAvatarStateErrorImplCopyWith<_$ProfileAvatarStateErrorImpl>
+ get copyWith => __$$ProfileAvatarStateErrorImplCopyWithImpl<
+ _$ProfileAvatarStateErrorImpl>(this, _$identity);
+
+ @override
+ @optionalTypeArgs
+ TResult when(
+ TResult Function() $default, {
+ required TResult Function() loading,
+ required TResult Function(ProfileModel profile) loaded,
+ required TResult Function(String errorMessage) error,
+ }) {
+ return error(errorMessage);
+ }
+
+ @override
+ @optionalTypeArgs
+ TResult? whenOrNull(
+ TResult? Function()? $default, {
+ TResult? Function()? loading,
+ TResult? Function(ProfileModel profile)? loaded,
+ TResult? Function(String errorMessage)? error,
+ }) {
+ return error?.call(errorMessage);
+ }
+
+ @override
+ @optionalTypeArgs
+ TResult maybeWhen(
+ TResult Function()? $default, {
+ TResult Function()? loading,
+ TResult Function(ProfileModel profile)? loaded,
+ TResult Function(String errorMessage)? error,
+ required TResult orElse(),
+ }) {
+ if (error != null) {
+ return error(errorMessage);
+ }
+ return orElse();
+ }
+
+ @override
+ @optionalTypeArgs
+ TResult map(
+ TResult Function(_ProfileAvatarState value) $default, {
+ required TResult Function(ProfileAvatarStateLoading value) loading,
+ required TResult Function(ProfileAvatarStateLoaded value) loaded,
+ required TResult Function(ProfileAvatarStateError value) error,
+ }) {
+ return error(this);
+ }
+
+ @override
+ @optionalTypeArgs
+ TResult? mapOrNull(
+ TResult? Function(_ProfileAvatarState value)? $default, {
+ TResult? Function(ProfileAvatarStateLoading value)? loading,
+ TResult? Function(ProfileAvatarStateLoaded value)? loaded,
+ TResult? Function(ProfileAvatarStateError value)? error,
+ }) {
+ return error?.call(this);
+ }
+
+ @override
+ @optionalTypeArgs
+ TResult maybeMap(
+ TResult Function(_ProfileAvatarState value)? $default, {
+ TResult Function(ProfileAvatarStateLoading value)? loading,
+ TResult Function(ProfileAvatarStateLoaded value)? loaded,
+ TResult Function(ProfileAvatarStateError value)? error,
+ required TResult orElse(),
+ }) {
+ if (error != null) {
+ return error(this);
+ }
+ return orElse();
+ }
+}
+
+abstract class ProfileAvatarStateError implements ProfileAvatarState {
+ const factory ProfileAvatarStateError({required final String errorMessage}) =
+ _$ProfileAvatarStateErrorImpl;
+
+ String get errorMessage;
+ @JsonKey(ignore: true)
+ _$$ProfileAvatarStateErrorImplCopyWith<_$ProfileAvatarStateErrorImpl>
+ get copyWith => throw _privateConstructorUsedError;
+}
diff --git a/lib/features/bottom_navigation_bar/presentation/widgets/bottom_navigation_bar_widget.dart b/lib/features/bottom_navigation_bar/presentation/widgets/bottom_navigation_bar_widget.dart
index b3f74075..a7e12393 100644
--- a/lib/features/bottom_navigation_bar/presentation/widgets/bottom_navigation_bar_widget.dart
+++ b/lib/features/bottom_navigation_bar/presentation/widgets/bottom_navigation_bar_widget.dart
@@ -4,6 +4,8 @@ import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/new_
import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/new_messages_cubit/new_messages_state.dart';
import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/notifications_cubit/notifications_cubit.dart';
import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/notifications_cubit/notifications_state.dart';
+import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_cubit.dart';
+import 'package:eatvio_app/features/bottom_navigation_bar/presentation/bloc/profile_avatar_cubit/profile_avatar_state.dart';
import 'package:eatvio_app/widgets/custom_navigation_menu.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
@@ -68,119 +70,154 @@ class _BottomNavigationBarWidgetState extends State {
Widget build(BuildContext context) {
return Scaffold(
body: widget.child,
- bottomNavigationBar: BottomNavigationBar(
- type: BottomNavigationBarType.fixed,
- onTap: _onItemTapped,
- selectedFontSize: 12,
- unselectedFontSize: 12,
- selectedItemColor: appTheme.blueA400,
- currentIndex: currentBottomNavBarIndex,
- items: [
- BottomNavigationBarItem(
- label: '',
- icon: SvgPicture.asset(
- ImageConstant.imgLogo,
- ),
- ),
- BottomNavigationBarItem(
- label: 'Лента',
- icon: SvgPicture.asset(
- colorFilter: ColorFilter.mode(Colors.grey, BlendMode.srcIn),
- width: 24,
- height: 24,
- ImageConstant.icnFeed,
- ),
- activeIcon: SvgPicture.asset(
- ImageConstant.icnFeed,
- width: 24,
- height: 24,
- colorFilter: ColorFilter.mode(
- appTheme.blueA400,
- BlendMode.srcIn,
+ bottomNavigationBar: SizedBox(
+ height: 58,
+ child: BottomNavigationBar(
+ type: BottomNavigationBarType.fixed,
+ onTap: _onItemTapped,
+ selectedFontSize: 10,
+ unselectedFontSize: 10,
+ selectedItemColor: appTheme.blueA400,
+ currentIndex: currentBottomNavBarIndex,
+ items: [
+ BottomNavigationBarItem(
+ label: '',
+ icon: SvgPicture.asset(
+ ImageConstant.imgLogo,
+ height: 32,
),
),
- ),
- BottomNavigationBarItem(
- label: "diary".tr,
- icon: SvgPicture.asset(
- colorFilter: ColorFilter.mode(Colors.grey, BlendMode.srcIn),
- ImageConstant.icnDiary,
- width: 24,
- height: 24,
- ),
- activeIcon: SvgPicture.asset(
- ImageConstant.icnDiary,
- width: 24,
- height: 24,
- colorFilter: ColorFilter.mode(
- appTheme.blueA400,
- BlendMode.srcIn,
+ BottomNavigationBarItem(
+ label: 'Лента',
+ icon: Padding(
+ padding: const EdgeInsets.only(bottom: 2),
+ child: SvgPicture.asset(
+ colorFilter: ColorFilter.mode(Colors.grey, BlendMode.srcIn),
+ width: 20,
+ height: 20,
+ ImageConstant.icnFeed,
+ ),
+ ),
+ activeIcon: Padding(
+ padding: const EdgeInsets.only(bottom: 2),
+ child: SvgPicture.asset(
+ ImageConstant.icnFeed,
+ width: 20,
+ height: 20,
+ colorFilter: ColorFilter.mode(
+ appTheme.blueA400,
+ BlendMode.srcIn,
+ ),
+ ),
),
),
- ),
- BottomNavigationBarItem(
- label: "chat".tr,
- icon: BlocBuilder(
- builder: (BuildContext context, NewMessagesState state) {
- return badges.Badge(
- position: badges.BadgePosition.topEnd(),
- badgeContent: Text(
- (state is NewMessagesStateLoaded) ? state.messagesCount.toString() : '0',
- style: TextStyle(
- color: Colors.white,
+ BottomNavigationBarItem(
+ label: "diary".tr,
+ icon: Padding(
+ padding: const EdgeInsets.only(bottom: 2),
+ child: SvgPicture.asset(
+ colorFilter: ColorFilter.mode(Colors.grey, BlendMode.srcIn),
+ ImageConstant.icnDiary,
+ width: 20,
+ height: 20,
+ ),
+ ),
+ activeIcon: Padding(
+ padding: const EdgeInsets.only(bottom: 2),
+ child: SvgPicture.asset(
+ ImageConstant.icnDiary,
+ width: 20,
+ height: 20,
+ colorFilter: ColorFilter.mode(
+ appTheme.blueA400,
+ BlendMode.srcIn,
+ ),
+ ),
+ ),
+ ),
+ BottomNavigationBarItem(
+ label: "chat".tr,
+ icon: BlocBuilder(
+ builder: (BuildContext context, NewMessagesState state) {
+ return badges.Badge(
+ position: badges.BadgePosition.topEnd(),
+ badgeAnimation: badges.BadgeAnimation.fade(),
+ badgeContent: Text(
+ (state is NewMessagesStateLoaded) ? state.messagesCount.toString() : '0',
+ style: TextStyle(
+ color: Colors.white,
+ fontSize: 8,
+ ),
),
- ),
- child: SvgPicture.asset(
- ImageConstant.icnChat,
- width: 24,
- height: 24,
- colorFilter: currentBottomNavBarIndex == 3
- ? ColorFilter.mode(
- appTheme.blueA400,
- BlendMode.srcIn,
- )
- : ColorFilter.mode(Colors.grey, BlendMode.srcIn),
- ),
- );
- },
- ),
- ),
- BottomNavigationBarItem(
- label: '',
- icon: BlocBuilder(
- builder: (BuildContext context, NotificationsState state) {
- return badges.Badge(
- position: badges.BadgePosition.topEnd(),
- badgeContent: Text(
- (state is NotificationsStateLoaded) ? state.notificationsCount.toString() : '0',
- style: TextStyle(
- color: Colors.white,
+ child: Padding(
+ padding: const EdgeInsets.only(bottom: 2),
+ child: SvgPicture.asset(
+ ImageConstant.icnChat,
+ width: 20,
+ height: 20,
+ colorFilter: currentBottomNavBarIndex == 3
+ ? ColorFilter.mode(
+ appTheme.blueA400,
+ BlendMode.srcIn,
+ )
+ : ColorFilter.mode(Colors.grey, BlendMode.srcIn),
+ ),
),
- ),
- child: SvgPicture.asset(
- ImageConstant.icnBell,
- colorFilter: currentBottomNavBarIndex == 4
- ? ColorFilter.mode(
- appTheme.blueA400,
- BlendMode.srcIn,
- )
- : ColorFilter.mode(Colors.grey, BlendMode.srcIn),
- width: 24,
- height: 24,
- ),
- );
- },
+ );
+ },
+ ),
),
- ),
- BottomNavigationBarItem(
- label: '',
- icon: Image.asset(
- ImageConstant.imgEllipse1,
- width: 32,
- height: 32,
+ BottomNavigationBarItem(
+ label: 'Уведомления',
+ icon: BlocBuilder(
+ builder: (BuildContext context, NotificationsState state) {
+ return badges.Badge(
+ position: badges.BadgePosition.topEnd(),
+ badgeAnimation: badges.BadgeAnimation.fade(),
+ badgeContent: Text(
+ (state is NotificationsStateLoaded) ? state.notificationsCount.toString() : '0',
+ style: TextStyle(
+ color: Colors.white,
+ fontSize: 8,
+ ),
+ ),
+ child: Padding(
+ padding: const EdgeInsets.only(bottom: 2),
+ child: SvgPicture.asset(
+ ImageConstant.icnBell,
+ colorFilter: currentBottomNavBarIndex == 4
+ ? ColorFilter.mode(
+ appTheme.blueA400,
+ BlendMode.srcIn,
+ )
+ : ColorFilter.mode(Colors.grey, BlendMode.srcIn),
+ width: 20,
+ height: 20,
+ ),
+ ),
+ );
+ },
+ ),
),
- ),
- ],
+ BottomNavigationBarItem(
+ label: '',
+ icon: BlocBuilder(
+ builder: (BuildContext context, ProfileAvatarState state) {
+ if (state is ProfileAvatarStateLoaded && state.profile.user.profilePhotoUrl.isNotEmpty) {
+ return CircleAvatar(
+ backgroundImage: NetworkImage(state.profile.user.profilePhotoUrl),
+ radius: 16,
+ );
+ }
+ return CircleAvatar(
+ radius: 16,
+ backgroundColor: Colors.black12,
+ );
+ },
+ ),
+ ),
+ ],
+ ),
),
);
}
diff --git a/lib/features/chat/presentation/widgets/chat_bubble_widget.dart b/lib/features/chat/presentation/widgets/chat_bubble_widget.dart
index f65fb3bc..eeba8f1e 100644
--- a/lib/features/chat/presentation/widgets/chat_bubble_widget.dart
+++ b/lib/features/chat/presentation/widgets/chat_bubble_widget.dart
@@ -1,8 +1,7 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:dismissible_page/dismissible_page.dart';
-import 'package:eatvio_app/core/constants/constants.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/theme/app_decoration.dart';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
@@ -29,12 +28,17 @@ class ChatBubbleWidget extends StatelessWidget {
child: DecoratedBox(
decoration: BoxDecoration(
color: isCurrentUser ? Color(0xFF7F9CF5) : Colors.white,
- borderRadius: BorderRadiusStyle.circleBorder17,
+ borderRadius: mediaData.length > 0
+ ? BorderRadius.circular(24)
+ : BorderRadius.horizontal(
+ left: isCurrentUser ? Radius.circular(32) : Radius.circular(16),
+ right: isCurrentUser ? Radius.circular(16) : Radius.circular(24),
+ ),
),
child: Padding(
- padding: const EdgeInsets.all(12),
+ padding: mediaData.length > 0 ? EdgeInsets.all(8) : EdgeInsets.only(top: 8, bottom: 8, left: isCurrentUser ? 16 : 8, right: isCurrentUser ? 8 : 16),
child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
+ crossAxisAlignment: isCurrentUser ? CrossAxisAlignment.end : CrossAxisAlignment.start,
children: [
if (mediaData.isNotEmpty)
CustomScrollView(
@@ -86,7 +90,7 @@ class ChatBubbleWidget extends StatelessWidget {
tag: baseUrl + '/storage/' + '${mediaData[index]}',
transitionOnUserGestures: true,
child: ClipRRect(
- borderRadius: BorderRadiusStyle.roundedBorder8,
+ borderRadius: BorderRadius.circular(16),
child: CachedNetworkImage(
imageUrl: baseUrl + '/storage/' + '${mediaData[index]}',
),
@@ -103,10 +107,17 @@ class ChatBubbleWidget extends StatelessWidget {
if (text.isNotEmpty)
Text(
text,
- style: TextStyle(
- color: isCurrentUser ? Colors.white : Color(0xFF323F4B),
- ),
+ style: TextStyle(color: isCurrentUser ? Colors.white : Color(0xFF323F4B), height: 1),
),
+ SizedBox(height: 4),
+ Text(
+ DateTime.parse(sendTime).format('hh:mm'),
+ style: TextStyle(
+ color: isCurrentUser ? Colors.white.withOpacity(0.4) : Color(0xFF323F4B).withOpacity(0.4),
+ height: 1,
+ fontSize: 10,
+ ),
+ ),
],
),
),
diff --git a/lib/features/chat/presentation/widgets/chat_search_bar_widget.dart b/lib/features/chat/presentation/widgets/chat_search_bar_widget.dart
index 62199d0f..4889fdfd 100644
--- a/lib/features/chat/presentation/widgets/chat_search_bar_widget.dart
+++ b/lib/features/chat/presentation/widgets/chat_search_bar_widget.dart
@@ -1,5 +1,4 @@
import 'package:eatvio_app/core/app_export.dart';
-import 'package:eatvio_app/features/chat/presentation/bloc/chat_select_cubit/chat_select_cubit.dart';
import 'package:flutter/material.dart';
class ChatSearchBarWidget extends StatelessWidget {
diff --git a/lib/features/profile/presentation/bloc/profile_cubit/profile_cubit.dart b/lib/features/profile/presentation/bloc/profile_cubit/profile_cubit.dart
index a1ee1be7..34f6385e 100644
--- a/lib/features/profile/presentation/bloc/profile_cubit/profile_cubit.dart
+++ b/lib/features/profile/presentation/bloc/profile_cubit/profile_cubit.dart
@@ -30,4 +30,20 @@ class ProfileCubit extends Cubit {
print(e);
}
}
+
+ addCircumference({
+ required int chestCircumference,
+ required int waistCircumference,
+ required int hipCircumference,
+ }) async {
+ try {
+ await profileRepository.addCircumference(
+ chestCircumference: chestCircumference,
+ waistCircumference: waistCircumference,
+ hipCircumference: hipCircumference,
+ );
+ } catch (e) {
+ emit(ProfileState.error(errorMessage: e.toString()));
+ }
+ }
}
diff --git a/lib/features/profile/presentation/pages/profile_screen.dart b/lib/features/profile/presentation/pages/profile_screen.dart
index d4cec48f..5bd5154a 100644
--- a/lib/features/profile/presentation/pages/profile_screen.dart
+++ b/lib/features/profile/presentation/pages/profile_screen.dart
@@ -1,46 +1,84 @@
import 'package:eatvio_app/core/app_export.dart';
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
+import 'package:eatvio_app/features/profile/models/profile_model.dart';
import 'package:eatvio_app/features/profile/presentation/bloc/profile_cubit/profile_cubit.dart';
import 'package:eatvio_app/features/profile/presentation/bloc/profile_cubit/profile_state.dart';
import 'package:eatvio_app/locator_service.dart';
import 'package:flutter/material.dart';
+import 'package:intl/intl.dart';
+import 'package:syncfusion_flutter_charts/charts.dart';
-class ProfileScreen extends StatelessWidget {
- final Author author;
+class ProfileScreen extends StatefulWidget {
+ final Author? author;
- const ProfileScreen({super.key, required this.author});
+ const ProfileScreen({super.key, this.author});
+
+ @override
+ State createState() => _ProfileScreenState();
+}
+
+class _ProfileScreenState extends State {
+ List chartData = [];
@override
Widget build(BuildContext context) {
return BlocProvider(
- create: (BuildContext context) => sl()..getProfileBySlug(author.authorSlug),
+ create: (BuildContext context) {
+ if (widget.author == null)
+ return sl()..getMyProfile();
+ else
+ return sl()..getProfileBySlug(widget.author!.authorSlug);
+ },
child: BlocBuilder(
builder: (BuildContext context, ProfileState state) {
if (state is ProfileStateLoaded) {
+ chartData = state.profile.circumferences;
return Scaffold(
appBar: AppBar(
- centerTitle: true,
- title: Text(
- state.profile.user.name,
- style: TextStyle(
- color: Colors.black,
- fontSize: 24,
- fontWeight: FontWeight.w400,
- height: 1,
- ),
- ),
+ centerTitle: false,
+ title: Text(state.profile.user.name),
),
body: SingleChildScrollView(
- padding: EdgeInsets.all(16),
child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
children: [
- Center(
+ Padding(
+ padding: const EdgeInsets.only(left: 16, bottom: 32),
child: CircleAvatar(
- maxRadius: 128,
- minRadius: 128,
+ radius: 32,
backgroundImage: NetworkImage(state.profile.user.profilePhotoUrl),
),
),
+ SfCartesianChart(
+ margin: EdgeInsets.zero,
+ enableAxisAnimation: true,
+ enableSideBySideSeriesPlacement: true,
+ legend: const Legend(
+ isVisible: true,
+ opacity: 0.7,
+ position: LegendPosition.bottom,
+ ),
+ plotAreaBorderWidth: 0,
+ trackballBehavior: TrackballBehavior(
+ enable: true,
+ activationMode: ActivationMode.singleTap,
+ ),
+ primaryXAxis: DateTimeAxis(
+ dateFormat: DateFormat.MMMd('ru'),
+ axisLine: AxisLine(
+ width: 1,
+ ),
+ ),
+ primaryYAxis: NumericAxis(
+ labelFormat: '{value} см',
+ axisLine: AxisLine(
+ width: 1,
+ ),
+ majorTickLines: MajorTickLines(size: 0),
+ ),
+ series: _getSplineAreaSeries(),
+ tooltipBehavior: TooltipBehavior(enable: true),
+ ),
],
),
),
@@ -67,4 +105,64 @@ class ProfileScreen extends StatelessWidget {
),
);
}
+
+ List> _getSplineAreaSeries() {
+ final Color seriesColor1 = Colors.red;
+ final Color seriesColor2 = Colors.blue;
+ final Color seriesColor3 = Colors.green;
+
+ return >[
+ SplineSeries(
+ markerSettings: MarkerSettings(
+ isVisible: true,
+ color: seriesColor1,
+ height: 12,
+ width: 12,
+ ),
+ dataLabelSettings: DataLabelSettings(
+ labelAlignment: ChartDataLabelAlignment.top,
+ isVisible: true,
+ ),
+ dataSource: chartData,
+ color: seriesColor1,
+ name: 'Грудь',
+ xValueMapper: (Circumference value, int index) => value.createdAt,
+ yValueMapper: (Circumference value, int index) => value.chestCircumference,
+ ),
+ SplineSeries(
+ markerSettings: MarkerSettings(
+ isVisible: true,
+ color: seriesColor2,
+ height: 12,
+ width: 12,
+ ),
+ dataLabelSettings: DataLabelSettings(
+ labelAlignment: ChartDataLabelAlignment.top,
+ isVisible: true,
+ ),
+ dataSource: chartData,
+ color: seriesColor2,
+ name: 'Талия',
+ xValueMapper: (Circumference value, int index) => value.createdAt,
+ yValueMapper: (Circumference value, int index) => value.waistCircumference,
+ ),
+ SplineSeries(
+ markerSettings: MarkerSettings(
+ isVisible: true,
+ color: seriesColor3,
+ height: 12,
+ width: 12,
+ ),
+ dataLabelSettings: DataLabelSettings(
+ labelAlignment: ChartDataLabelAlignment.top,
+ isVisible: true,
+ ),
+ dataSource: chartData,
+ color: seriesColor3,
+ name: 'Бёдра',
+ xValueMapper: (Circumference value, int index) => value.createdAt,
+ yValueMapper: (Circumference value, int index) => value.hipCircumference,
+ ),
+ ];
+ }
}
diff --git a/lib/features/profile/repositories/profile_repository.dart b/lib/features/profile/repositories/profile_repository.dart
index b9dbdf1e..a57be856 100644
--- a/lib/features/profile/repositories/profile_repository.dart
+++ b/lib/features/profile/repositories/profile_repository.dart
@@ -21,4 +21,22 @@ class ProfileRepository {
rethrow;
}
}
+
+ Future addCircumference({
+ required int chestCircumference,
+ required int waistCircumference,
+ required int hipCircumference,
+ }) async {
+ try {
+ return (await profileService.addCircumference(
+ chestCircumference: chestCircumference,
+ waistCircumference: waistCircumference,
+ hipCircumference: hipCircumference,
+ ))
+ .values
+ .first;
+ } catch (e) {
+ rethrow;
+ }
+ }
}
diff --git a/lib/features/profile/services/profile_service.dart b/lib/features/profile/services/profile_service.dart
index 5f5657b9..dc9f2864 100644
--- a/lib/features/profile/services/profile_service.dart
+++ b/lib/features/profile/services/profile_service.dart
@@ -15,4 +15,11 @@ abstract class ProfileService {
@GET('/api/profile/index')
Future getMyProfile();
+
+ @POST('/api/profile/addCircumference')
+ Future