feat(profile): subscribers and subscriptions

This commit is contained in:
mso7nueh
2024-11-22 15:27:51 +03:00
parent f805505cb3
commit c909c75d47
45 changed files with 2790 additions and 451 deletions

View File

@@ -0,0 +1,29 @@
import 'package:eatvio_app/features/profile/presentation/bloc/sub_cubit/sub_state.dart';
import 'package:eatvio_app/features/profile/repositories/profile_repository.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class SubCubit extends Cubit<SubState> {
final ProfileRepository profileRepository;
SubCubit({
required this.profileRepository,
}) : super(const SubState());
getSubscriptions(String slug) async {
emit(const SubState.loading());
try {
emit(SubState.loaded(subList: await profileRepository.getSubscriptionsBySlug(slug)));
} catch (e) {
emit(SubState.error(errorMessage: e.toString()));
}
}
getSubscribers(String slug) async {
emit(const SubState.loading());
try {
emit(SubState.loaded(subList: await profileRepository.getSubscribersBySlug(slug)));
} catch (e) {
emit(SubState.error(errorMessage: e.toString()));
}
}
}

View File

@@ -0,0 +1,19 @@
import 'package:eatvio_app/features/profile/models/sub_model.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'sub_state.freezed.dart';
@freezed
abstract class SubState with _$SubState {
const factory SubState() = _SubState;
const factory SubState.loading() = SubStateLoading;
const factory SubState.loaded({
required List<SubModel> subList,
}) = SubStateLoaded;
const factory SubState.error({
required String errorMessage,
}) = SubStateError;
}

View File

@@ -0,0 +1,638 @@
// 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 'sub_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 _$SubState {
@optionalTypeArgs
TResult when<TResult extends Object?>(
TResult Function() $default, {
required TResult Function() loading,
required TResult Function(List<SubModel> subList) loaded,
required TResult Function(String errorMessage) error,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>(
TResult? Function()? $default, {
TResult? Function()? loading,
TResult? Function(List<SubModel> subList)? loaded,
TResult? Function(String errorMessage)? error,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>(
TResult Function()? $default, {
TResult Function()? loading,
TResult Function(List<SubModel> subList)? loaded,
TResult Function(String errorMessage)? error,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult map<TResult extends Object?>(
TResult Function(_SubState value) $default, {
required TResult Function(SubStateLoading value) loading,
required TResult Function(SubStateLoaded value) loaded,
required TResult Function(SubStateError value) error,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>(
TResult? Function(_SubState value)? $default, {
TResult? Function(SubStateLoading value)? loading,
TResult? Function(SubStateLoaded value)? loaded,
TResult? Function(SubStateError value)? error,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>(
TResult Function(_SubState value)? $default, {
TResult Function(SubStateLoading value)? loading,
TResult Function(SubStateLoaded value)? loaded,
TResult Function(SubStateError value)? error,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $SubStateCopyWith<$Res> {
factory $SubStateCopyWith(SubState value, $Res Function(SubState) then) =
_$SubStateCopyWithImpl<$Res, SubState>;
}
/// @nodoc
class _$SubStateCopyWithImpl<$Res, $Val extends SubState>
implements $SubStateCopyWith<$Res> {
_$SubStateCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of SubState
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
abstract class _$$SubStateImplCopyWith<$Res> {
factory _$$SubStateImplCopyWith(
_$SubStateImpl value, $Res Function(_$SubStateImpl) then) =
__$$SubStateImplCopyWithImpl<$Res>;
}
/// @nodoc
class __$$SubStateImplCopyWithImpl<$Res>
extends _$SubStateCopyWithImpl<$Res, _$SubStateImpl>
implements _$$SubStateImplCopyWith<$Res> {
__$$SubStateImplCopyWithImpl(
_$SubStateImpl _value, $Res Function(_$SubStateImpl) _then)
: super(_value, _then);
/// Create a copy of SubState
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
class _$SubStateImpl implements _SubState {
const _$SubStateImpl();
@override
String toString() {
return 'SubState()';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _$SubStateImpl);
}
@override
int get hashCode => runtimeType.hashCode;
@override
@optionalTypeArgs
TResult when<TResult extends Object?>(
TResult Function() $default, {
required TResult Function() loading,
required TResult Function(List<SubModel> subList) loaded,
required TResult Function(String errorMessage) error,
}) {
return $default();
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>(
TResult? Function()? $default, {
TResult? Function()? loading,
TResult? Function(List<SubModel> subList)? loaded,
TResult? Function(String errorMessage)? error,
}) {
return $default?.call();
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>(
TResult Function()? $default, {
TResult Function()? loading,
TResult Function(List<SubModel> subList)? 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(_SubState value) $default, {
required TResult Function(SubStateLoading value) loading,
required TResult Function(SubStateLoaded value) loaded,
required TResult Function(SubStateError value) error,
}) {
return $default(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>(
TResult? Function(_SubState value)? $default, {
TResult? Function(SubStateLoading value)? loading,
TResult? Function(SubStateLoaded value)? loaded,
TResult? Function(SubStateError value)? error,
}) {
return $default?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>(
TResult Function(_SubState value)? $default, {
TResult Function(SubStateLoading value)? loading,
TResult Function(SubStateLoaded value)? loaded,
TResult Function(SubStateError value)? error,
required TResult orElse(),
}) {
if ($default != null) {
return $default(this);
}
return orElse();
}
}
abstract class _SubState implements SubState {
const factory _SubState() = _$SubStateImpl;
}
/// @nodoc
abstract class _$$SubStateLoadingImplCopyWith<$Res> {
factory _$$SubStateLoadingImplCopyWith(_$SubStateLoadingImpl value,
$Res Function(_$SubStateLoadingImpl) then) =
__$$SubStateLoadingImplCopyWithImpl<$Res>;
}
/// @nodoc
class __$$SubStateLoadingImplCopyWithImpl<$Res>
extends _$SubStateCopyWithImpl<$Res, _$SubStateLoadingImpl>
implements _$$SubStateLoadingImplCopyWith<$Res> {
__$$SubStateLoadingImplCopyWithImpl(
_$SubStateLoadingImpl _value, $Res Function(_$SubStateLoadingImpl) _then)
: super(_value, _then);
/// Create a copy of SubState
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
class _$SubStateLoadingImpl implements SubStateLoading {
const _$SubStateLoadingImpl();
@override
String toString() {
return 'SubState.loading()';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _$SubStateLoadingImpl);
}
@override
int get hashCode => runtimeType.hashCode;
@override
@optionalTypeArgs
TResult when<TResult extends Object?>(
TResult Function() $default, {
required TResult Function() loading,
required TResult Function(List<SubModel> subList) loaded,
required TResult Function(String errorMessage) error,
}) {
return loading();
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>(
TResult? Function()? $default, {
TResult? Function()? loading,
TResult? Function(List<SubModel> subList)? loaded,
TResult? Function(String errorMessage)? error,
}) {
return loading?.call();
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>(
TResult Function()? $default, {
TResult Function()? loading,
TResult Function(List<SubModel> subList)? 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(_SubState value) $default, {
required TResult Function(SubStateLoading value) loading,
required TResult Function(SubStateLoaded value) loaded,
required TResult Function(SubStateError value) error,
}) {
return loading(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>(
TResult? Function(_SubState value)? $default, {
TResult? Function(SubStateLoading value)? loading,
TResult? Function(SubStateLoaded value)? loaded,
TResult? Function(SubStateError value)? error,
}) {
return loading?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>(
TResult Function(_SubState value)? $default, {
TResult Function(SubStateLoading value)? loading,
TResult Function(SubStateLoaded value)? loaded,
TResult Function(SubStateError value)? error,
required TResult orElse(),
}) {
if (loading != null) {
return loading(this);
}
return orElse();
}
}
abstract class SubStateLoading implements SubState {
const factory SubStateLoading() = _$SubStateLoadingImpl;
}
/// @nodoc
abstract class _$$SubStateLoadedImplCopyWith<$Res> {
factory _$$SubStateLoadedImplCopyWith(_$SubStateLoadedImpl value,
$Res Function(_$SubStateLoadedImpl) then) =
__$$SubStateLoadedImplCopyWithImpl<$Res>;
@useResult
$Res call({List<SubModel> subList});
}
/// @nodoc
class __$$SubStateLoadedImplCopyWithImpl<$Res>
extends _$SubStateCopyWithImpl<$Res, _$SubStateLoadedImpl>
implements _$$SubStateLoadedImplCopyWith<$Res> {
__$$SubStateLoadedImplCopyWithImpl(
_$SubStateLoadedImpl _value, $Res Function(_$SubStateLoadedImpl) _then)
: super(_value, _then);
/// Create a copy of SubState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? subList = null,
}) {
return _then(_$SubStateLoadedImpl(
subList: null == subList
? _value._subList
: subList // ignore: cast_nullable_to_non_nullable
as List<SubModel>,
));
}
}
/// @nodoc
class _$SubStateLoadedImpl implements SubStateLoaded {
const _$SubStateLoadedImpl({required final List<SubModel> subList})
: _subList = subList;
final List<SubModel> _subList;
@override
List<SubModel> get subList {
if (_subList is EqualUnmodifiableListView) return _subList;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_subList);
}
@override
String toString() {
return 'SubState.loaded(subList: $subList)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$SubStateLoadedImpl &&
const DeepCollectionEquality().equals(other._subList, _subList));
}
@override
int get hashCode =>
Object.hash(runtimeType, const DeepCollectionEquality().hash(_subList));
/// Create a copy of SubState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$SubStateLoadedImplCopyWith<_$SubStateLoadedImpl> get copyWith =>
__$$SubStateLoadedImplCopyWithImpl<_$SubStateLoadedImpl>(
this, _$identity);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>(
TResult Function() $default, {
required TResult Function() loading,
required TResult Function(List<SubModel> subList) loaded,
required TResult Function(String errorMessage) error,
}) {
return loaded(subList);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>(
TResult? Function()? $default, {
TResult? Function()? loading,
TResult? Function(List<SubModel> subList)? loaded,
TResult? Function(String errorMessage)? error,
}) {
return loaded?.call(subList);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>(
TResult Function()? $default, {
TResult Function()? loading,
TResult Function(List<SubModel> subList)? loaded,
TResult Function(String errorMessage)? error,
required TResult orElse(),
}) {
if (loaded != null) {
return loaded(subList);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>(
TResult Function(_SubState value) $default, {
required TResult Function(SubStateLoading value) loading,
required TResult Function(SubStateLoaded value) loaded,
required TResult Function(SubStateError value) error,
}) {
return loaded(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>(
TResult? Function(_SubState value)? $default, {
TResult? Function(SubStateLoading value)? loading,
TResult? Function(SubStateLoaded value)? loaded,
TResult? Function(SubStateError value)? error,
}) {
return loaded?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>(
TResult Function(_SubState value)? $default, {
TResult Function(SubStateLoading value)? loading,
TResult Function(SubStateLoaded value)? loaded,
TResult Function(SubStateError value)? error,
required TResult orElse(),
}) {
if (loaded != null) {
return loaded(this);
}
return orElse();
}
}
abstract class SubStateLoaded implements SubState {
const factory SubStateLoaded({required final List<SubModel> subList}) =
_$SubStateLoadedImpl;
List<SubModel> get subList;
/// Create a copy of SubState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
_$$SubStateLoadedImplCopyWith<_$SubStateLoadedImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class _$$SubStateErrorImplCopyWith<$Res> {
factory _$$SubStateErrorImplCopyWith(
_$SubStateErrorImpl value, $Res Function(_$SubStateErrorImpl) then) =
__$$SubStateErrorImplCopyWithImpl<$Res>;
@useResult
$Res call({String errorMessage});
}
/// @nodoc
class __$$SubStateErrorImplCopyWithImpl<$Res>
extends _$SubStateCopyWithImpl<$Res, _$SubStateErrorImpl>
implements _$$SubStateErrorImplCopyWith<$Res> {
__$$SubStateErrorImplCopyWithImpl(
_$SubStateErrorImpl _value, $Res Function(_$SubStateErrorImpl) _then)
: super(_value, _then);
/// Create a copy of SubState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? errorMessage = null,
}) {
return _then(_$SubStateErrorImpl(
errorMessage: null == errorMessage
? _value.errorMessage
: errorMessage // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
class _$SubStateErrorImpl implements SubStateError {
const _$SubStateErrorImpl({required this.errorMessage});
@override
final String errorMessage;
@override
String toString() {
return 'SubState.error(errorMessage: $errorMessage)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$SubStateErrorImpl &&
(identical(other.errorMessage, errorMessage) ||
other.errorMessage == errorMessage));
}
@override
int get hashCode => Object.hash(runtimeType, errorMessage);
/// Create a copy of SubState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$SubStateErrorImplCopyWith<_$SubStateErrorImpl> get copyWith =>
__$$SubStateErrorImplCopyWithImpl<_$SubStateErrorImpl>(this, _$identity);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>(
TResult Function() $default, {
required TResult Function() loading,
required TResult Function(List<SubModel> subList) 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(List<SubModel> subList)? 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(List<SubModel> subList)? 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(_SubState value) $default, {
required TResult Function(SubStateLoading value) loading,
required TResult Function(SubStateLoaded value) loaded,
required TResult Function(SubStateError value) error,
}) {
return error(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>(
TResult? Function(_SubState value)? $default, {
TResult? Function(SubStateLoading value)? loading,
TResult? Function(SubStateLoaded value)? loaded,
TResult? Function(SubStateError value)? error,
}) {
return error?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>(
TResult Function(_SubState value)? $default, {
TResult Function(SubStateLoading value)? loading,
TResult Function(SubStateLoaded value)? loaded,
TResult Function(SubStateError value)? error,
required TResult orElse(),
}) {
if (error != null) {
return error(this);
}
return orElse();
}
}
abstract class SubStateError implements SubState {
const factory SubStateError({required final String errorMessage}) =
_$SubStateErrorImpl;
String get errorMessage;
/// Create a copy of SubState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
_$$SubStateErrorImplCopyWith<_$SubStateErrorImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@@ -1,11 +1,13 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:eatvio_app/core/const.dart';
import 'package:eatvio_app/core/widgets/loader.dart';
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
import 'package:eatvio_app/features/feed/presentation/widgets/blog_post_widget.dart';
import 'package:eatvio_app/features/profile/models/circumference_model.dart';
import 'package:eatvio_app/features/profile/models/weight_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/features/profile/presentation/pages/subs_screen.dart';
import 'package:eatvio_app/features/profile/presentation/widgets/circumference_chart_widget.dart';
import 'package:eatvio_app/features/profile/presentation/widgets/imt_chart_widget.dart';
import 'package:eatvio_app/features/profile/presentation/widgets/weight_chart_widget.dart';
@@ -14,7 +16,6 @@ import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
import 'package:rive/rive.dart';
import 'package:flutter_svg/flutter_svg.dart';
class ProfileScreen extends StatefulWidget {
@@ -56,7 +57,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
circumferencePoints = state.circumferencePoints!;
weightPoints = state.weightPoints!;
}
return _profileScreen(state);
return widget.author == null ? _myProfileScreen(state) : _profileScreen(state);
} else if (state is ProfileStateError) {
return Scaffold(
appBar: AppBar(),
@@ -70,15 +71,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
} else {
return Scaffold(
appBar: AppBar(),
body: Center(
child: SizedBox(
height: 32,
width: 32,
child: RiveAnimation.asset(
'assets/rive_animations/loading.riv',
),
),
),
body: const Loader(),
);
}
},
@@ -88,7 +81,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
_profileScreen(ProfileStateLoaded state) {
return Scaffold(
backgroundColor: Colors.grey.shade200,
appBar: widget.author == null ? AppBar(toolbarHeight: 0) : AppBar(),
appBar: AppBar(),
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -105,26 +98,56 @@ class _ProfileScreenState extends State<ProfileScreen> {
children: [
_header(state),
if (state.profileDetails.additionalInfo.isNotEmpty) _additionalInfo(state),
if (widget.author != null) const SizedBox(height: 8),
if (widget.author != null) _messageAndSubscribe(),
if (widget.author != null) const SizedBox(height: 16),
const SizedBox(height: 8),
_messageAndSubscribe(),
const SizedBox(height: 16),
],
),
),
_subscriptionsAndSubscribers(state),
if(state.profile.badges.isNotEmpty) _badges(state),
if (widget.author == null) const SizedBox(height: 16),
if (state.profile.badges.isNotEmpty) _badges(state),
_socialLinks(state),
if (widget.author == null) const SizedBox(height: 16),
_education(state),
if (widget.author == null) const SizedBox(height: 16),
_diplomasAndCertificates(state),
if (widget.author == null) const SizedBox(height: 16),
if (widget.author == null) _charts(),
if (widget.author == null) const SizedBox(height: 16),
_activePlans(state),
if (widget.author == null) const SizedBox(height: 16),
if (widget.author == null) _newBlogPostButton(),
_blogPosts(state),
],
),
),
);
}
_myProfileScreen(ProfileStateLoaded state) {
return Scaffold(
backgroundColor: Colors.grey.shade200,
appBar: AppBar(toolbarHeight: 0),
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(
bottom: Radius.circular(16),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
_header(state),
if (state.profileDetails.additionalInfo.isNotEmpty) _additionalInfo(state),
],
),
),
_subscriptionsAndSubscribers(state),
if (state.profile.badges.isNotEmpty) Padding(padding: EdgeInsets.only(bottom: 16), child: _badges(state)),
if (state.profile.role == 'consultant') Padding(padding: EdgeInsets.only(bottom: 16), child: _socialLinks(state)),
if (state.profile.role == 'consultant') Padding(padding: EdgeInsets.only(bottom: 16), child: _education(state)),
if (state.profile.role == 'consultant') Padding(padding: EdgeInsets.only(bottom: 16), child: _diplomasAndCertificates(state)),
Padding(padding: EdgeInsets.only(bottom: 16), child: _charts()),
if (state.profile.role == 'consultant') Padding(padding: EdgeInsets.only(bottom: 16), child: _activePlans(state)),
_newBlogPostButton(),
_blogPosts(state),
],
),
@@ -511,7 +534,13 @@ class _ProfileScreenState extends State<ProfileScreen> {
child: Row(
children: [
InkWell(
onTap: () {},
onTap: () => GoRouter.of(context).pushNamed(
'subsScreen',
extra: SubType.Subscriptions,
pathParameters: {
'slug': state.profile.slug,
},
),
borderRadius: BorderRadius.circular(12),
child: Ink(
padding: const EdgeInsets.all(8),
@@ -555,7 +584,13 @@ class _ProfileScreenState extends State<ProfileScreen> {
),
SizedBox(width: 4),
InkWell(
onTap: () {},
onTap: () => GoRouter.of(context).pushNamed(
'subsScreen',
extra: SubType.Subscribers,
pathParameters: {
'slug': state.profile.slug,
},
),
borderRadius: BorderRadius.circular(12),
child: Ink(
padding: const EdgeInsets.all(8),

View File

@@ -0,0 +1,69 @@
import 'package:eatvio_app/core/locator_service.dart';
import 'package:eatvio_app/core/widgets/loader.dart';
import 'package:eatvio_app/features/profile/presentation/bloc/sub_cubit/sub_cubit.dart';
import 'package:eatvio_app/features/profile/presentation/bloc/sub_cubit/sub_state.dart';
import 'package:eatvio_app/features/profile/presentation/widgets/sub_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
enum SubType { Subscribers, Subscriptions }
class SubsScreen extends StatefulWidget {
final SubType subType;
final String slug;
const SubsScreen({
super.key,
required this.slug,
required this.subType,
});
@override
State<SubsScreen> createState() => _SubsScreenState();
}
class _SubsScreenState extends State<SubsScreen> {
@override
Widget build(BuildContext context) {
return BlocProvider<SubCubit>(
create: (BuildContext context) => widget.subType == SubType.Subscriptions
? (sl<SubCubit>()
..getSubscriptions(
widget.slug,
))
: (sl<SubCubit>()
..getSubscribers(
widget.slug,
)),
child: BlocBuilder<SubCubit, SubState>(
builder: (BuildContext context, SubState state) {
return Scaffold(
appBar: AppBar(
title: Text(
widget.subType == SubType.Subscriptions ? 'Подписки' : 'Подписчики',
),
),
body: _buildStates(state),
);
},
),
);
}
_buildStates(SubState state) {
if (state is SubStateLoading) {
return Loader();
}
if (state is SubStateError) {
return Text(state.errorMessage);
}
if (state is SubStateLoaded) {
return ListView.builder(
itemBuilder: (context, index) => SubWidget(
subModel: state.subList[index],
),
itemCount: state.subList.length,
);
}
}
}

View File

@@ -0,0 +1,63 @@
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:flutter/material.dart';
enum SubActionType { SendMessage, Subscribe }
class SubActionButtonWidget extends StatefulWidget {
final SubActionType subActionType;
const SubActionButtonWidget({
super.key,
required this.subActionType,
});
@override
State<SubActionButtonWidget> createState() => _SubActionButtonWidgetState();
}
class _SubActionButtonWidgetState extends State<SubActionButtonWidget> {
@override
Widget build(BuildContext context) {
if (widget.subActionType == SubActionType.Subscribe) {
return InkWell(
onTap: () {},
borderRadius: BorderRadius.circular(8),
child: Ink(
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 4),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: appTheme.blueA400,
),
child: Text(
'Подписаться',
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w500,
color: Colors.white,
),
),
),
);
} else {
return InkWell(
onTap: () {},
borderRadius: BorderRadius.circular(8),
child: Ink(
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 4),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: Color(0xFFF1F2F6),
),
child: Text(
'Сообщение',
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w500,
color: Color(0xFF6D6F71),
),
),
),
);
}
}
}

View File

@@ -0,0 +1,65 @@
import 'package:eatvio_app/features/profile/models/sub_model.dart';
import 'package:eatvio_app/features/profile/presentation/widgets/sub_action_button_widget.dart';
import 'package:flutter/material.dart';
class SubWidget extends StatefulWidget {
final SubModel subModel;
const SubWidget({
super.key,
required this.subModel,
});
@override
State<SubWidget> createState() => _SubWidgetState();
}
class _SubWidgetState extends State<SubWidget> {
@override
Widget build(BuildContext context) {
return InkWell(
onTap: () {},
child: Ink(
padding: EdgeInsets.symmetric(vertical: 8, horizontal: 16),
child: Row(
children: [
CircleAvatar(
backgroundImage: NetworkImage(widget.subModel.profilePhotoUrl),
radius: 28,
),
const SizedBox(width: 12),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
widget.subModel.name,
style: TextStyle(
color: Color(0xFF323F4B),
fontSize: 16,
fontWeight: FontWeight.w600,
height: 1,
),
),
const SizedBox(height: 4),
Text(
'${DateTime.now().year - widget.subModel.birthdate.year - (DateTime.now().isBefore(DateTime(DateTime.now().year, widget.subModel.birthdate.month, widget.subModel.birthdate.day)) ? 1 : 0)} лет, ${widget.subModel.city}',
style: TextStyle(
color: Color(0xFF6D6F71),
fontSize: 12,
fontWeight: FontWeight.w500,
height: 1,
),
),
],
),
const Spacer(),
SubActionButtonWidget(
subActionType: widget.subModel.isSubscribe ? SubActionType.SendMessage : SubActionType.Subscribe,
),
],
),
),
);
}
}