feature(registration): remove .env

This commit is contained in:
mso7nueh
2024-07-15 17:06:13 +03:00
parent 726b74a236
commit d859a15cfd
188 changed files with 2509 additions and 3147 deletions

View File

@@ -1,22 +1,21 @@
import 'dart:convert';
import 'package:eatvio_app/core/app_export.dart';
import 'package:eatvio_app/core/token_storage.dart';
import 'package:eatvio_app/widgets/app_bar/appbar_title_image.dart';
import 'package:eatvio_app/widgets/app_bar/custom_app_bar.dart';
import 'package:eatvio_app/widgets/custom_text_form_field.dart';
import 'package:eatvio_app/widgets/next_step_button.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:hive/hive.dart';
import 'package:http/http.dart' as http;
import 'package:validators/validators.dart';
import 'bloc/auth_bloc.dart';
import 'models/auth_model.dart';
import 'package:eatvio_app/core/app_export.dart';
import 'package:eatvio_app/widgets/app_bar/appbar_title_image.dart';
import 'package:eatvio_app/widgets/app_bar/custom_app_bar.dart';
import 'package:validators/validators.dart';
import 'package:eatvio_app/widgets/next_step_button.dart';
import 'package:eatvio_app/widgets/custom_text_form_field.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'dart:convert';
import 'package:hive/hive.dart';
import 'package:http/http.dart' as http;
class AuthScreen extends StatelessWidget {
class AuthScreen extends StatefulWidget {
static Widget builder(BuildContext context) {
return BlocProvider<AuthBloc>(
create: (context) => AuthBloc(
@@ -30,6 +29,13 @@ class AuthScreen extends StatelessWidget {
);
}
@override
State<AuthScreen> createState() => _AuthScreenState();
}
class _AuthScreenState extends State<AuthScreen> {
bool checkboxState = false;
@override
Widget build(BuildContext context) {
mediaQueryData = MediaQuery.of(context);
@@ -95,12 +101,6 @@ class AuthScreen extends StatelessWidget {
);
},
),
TextButton(
onPressed: () async {
await launchUrl(Uri.parse(dotenv.env['OFERTA']!));
},
child: Text('Пользовательское соглашение'),
),
],
),
),
@@ -117,7 +117,7 @@ class AuthScreen extends StatelessWidget {
'password': state.passwordController!.text,
};
final response = await http.post(
Uri.parse(dotenv.env['SERVER_ROOT']! + '/login'),
Uri.parse(baseUrl + '/api/login'),
headers: {'Content-Type': 'application/json'},
body: json.encode(credentials),
encoding: utf8,

View File

@@ -1,10 +1,10 @@
import 'package:eatvio_app/presentation/auth/auth_screen/models/auth_model.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import '/core/app_export.dart';
import 'package:eatvio_app/presentation/auth/auth_screen/models/auth_model.dart';
part 'auth_event.dart';
part 'auth_state.dart';
/// A bloc that manages the state of a Auth according to the event that is dispatched to it.

View File

@@ -1,14 +1,15 @@
import 'dart:async';
import 'package:intl/intl.dart';
import 'dart:convert';
import 'package:eatvio_app/presentation/diary/diary_edit_screen/models/diary_edit_model.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import '/core/app_export.dart';
import 'package:image_picker/image_picker.dart';
import 'package:eatvio_app/presentation/diary/diary_edit_screen/models/diary_edit_model.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:hive/hive.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:http/http.dart' as http;
import 'package:image_picker/image_picker.dart';
import 'package:intl/intl.dart';
import '/core/app_export.dart';
part 'diary_edit_event.dart';
part 'diary_edit_state.dart';
@@ -148,7 +149,7 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
fetchDiaryId(String token, Emitter<DiaryEditState> emit) async {
final response = await http.get(
Uri.parse(dotenv.env['SERVER_ROOT']! + '/diary/index'),
Uri.parse(baseUrl + '/api/diary/index'),
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer $token',
@@ -173,7 +174,7 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
fetchDiaryData(String token, int diaryId) async {
final response = await http.get(
Uri.parse(dotenv.env['SERVER_ROOT']! + '/diary/$diaryId/show/'),
Uri.parse(baseUrl + '/api/diary/$diaryId/show/'),
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer $token',
@@ -191,7 +192,7 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
) async {
final responseChart = await http.get(
Uri.parse(
dotenv.env['SERVER_ROOT']! + '/diary/$diaryID/dailyNutritionData/',
baseUrl + '/api/diary/$diaryID/dailyNutritionData/',
),
headers: {
'Content-Type': 'application/json',
@@ -234,7 +235,7 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
final box = await Hive.openBox('token');
final token = box.get('token');
final response = await http.get(
Uri.parse(dotenv.env['SERVER_ROOT']! + '/diary/index'),
Uri.parse(baseUrl + '/api/diary/index'),
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer $token',
@@ -247,7 +248,7 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
final int diaryId = data['id'];
final diaryResponse = await http.get(
Uri.parse(dotenv.env['SERVER_ROOT']! + '/diary/$diaryId/show/'),
Uri.parse(baseUrl + '/api/diary/$diaryId/show/'),
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer $token',
@@ -260,7 +261,7 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
final String diaryDate = diary['date'] ?? DateTime.now().toString();
final responseChart = await http.get(
Uri.parse(
dotenv.env['SERVER_ROOT']! + '/diary/$diaryId/dailyNutritionData/',
baseUrl + '/api/diary/$diaryId/dailyNutritionData/',
),
headers: {
'Content-Type': 'application/json',
@@ -371,7 +372,7 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
int? mealID,
int diaryID,
) async {
String url = dotenv.env['SERVER_ROOT']! + '/diary/$diaryID/meal/$mealID/satiety/fill';
String url = baseUrl + '/api/diary/$diaryID/meal/$mealID/satiety/fill';
final Map<String, dynamic> credentials = {
'satiety': selectedSatiety,
};
@@ -419,7 +420,7 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
int? mealID,
int diaryID,
) async {
String url = dotenv.env['SERVER_ROOT']! + '/diary/$diaryID/meal/$mealID/mood/fill';
String url = baseUrl + '/api/diary/$diaryID/meal/$mealID/mood/fill';
final Map<String, dynamic> credentials = {
'mood': selectedMood,
};
@@ -484,7 +485,7 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
TimeOfDay wakeup,
int diaryID,
) async {
String url = dotenv.env['SERVER_ROOT']! + '/diary/$diaryID/wakeUp/fill';
String url = baseUrl + '/api/diary/$diaryID/wakeUp/fill';
final formatter = DateFormat('HH:mm');
final wakeupString = formatter.format(DateTime(2022, 1, 1, wakeup.hour, wakeup.minute));
final Map<String, dynamic> credentials = {
@@ -509,7 +510,7 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
TimeOfDay retire,
int diaryID,
) async {
String url = dotenv.env['SERVER_ROOT']! + '/diary/$diaryID/retire/fill';
String url = baseUrl + '/api/diary/$diaryID/retire/fill';
final formatter = DateFormat('HH:mm');
final retireString = formatter.format(DateTime(2022, 1, 1, retire.hour, retire.minute));
final Map<String, dynamic> credentials = {
@@ -547,7 +548,7 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
int? mealID,
int diaryID,
) async {
String url = dotenv.env['SERVER_ROOT']! + '/diary/???';
String url = baseUrl + '/api/diary/???';
final Map<String, dynamic> credentials = {
'time': mealTime,
'meal_id': mealID,
@@ -674,7 +675,7 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
int? mealID,
int diaryID,
) async {
String url = '${dotenv.env['SERVER_ROOT']!}/diary/$diaryID/meal/$mealID/portions/create';
String url = '${baseUrl}/api/diary/$diaryID/meal/$mealID/portions/create';
final Map<String, dynamic> credentials = {
'product_name': name,
@@ -705,7 +706,7 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
final Map<String, dynamic> credentials = {'name': event.input};
final uri = Uri.parse(
'${dotenv.env['SERVER_ROOT']!}/diary/product_nutritional_values/filter?${Uri(queryParameters: credentials).query}',
'${baseUrl}/api/diary/product_nutritional_values/filter?${Uri(queryParameters: credentials).query}',
);
final response = await http.get(
@@ -767,7 +768,7 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
int? mealID,
int diaryID,
) async {
String url = '${dotenv.env['SERVER_ROOT']!}/diary/$diaryID/meal/$mealID/portions/$portionID/delete';
String url = '${baseUrl}/api/diary/$diaryID/meal/$mealID/portions/$portionID/delete';
var response = await http.delete(
Uri.parse(url),
@@ -813,7 +814,7 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
int? mealID,
int diaryID,
) async {
String url = '${dotenv.env['SERVER_ROOT']!}/diary/$diaryID/meal/$mealID/photo/delete';
String url = '${baseUrl}/api/diary/$diaryID/meal/$mealID/photo/delete';
final Map<String, dynamic> credentials = {
'id': photoID,
'path': path,
@@ -866,7 +867,7 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
'comment': comment ?? " ",
};
String url = '${dotenv.env['SERVER_ROOT']!}/diary/$diaryID/finishDay';
String url = '${baseUrl}/api/diary/$diaryID/finishDay';
var response = await http.post(
Uri.parse(url),
@@ -905,7 +906,7 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
int mealId,
String? token,
) async {
String url = '${dotenv.env['SERVER_ROOT']!}/diary/$diaryID/meal/$mealId/photo/upload';
String url = '${baseUrl}/api/diary/$diaryID/meal/$mealId/photo/upload';
var request = http.MultipartRequest('POST', Uri.parse(url));
request.headers['Authorization'] = 'Bearer $token';
@@ -958,9 +959,9 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
String? token,
) async {
final Uri url = Uri.parse(
'${dotenv.env['SERVER_ROOT']!}/diary/product_nutritional_values/sendToModeration',
'${baseUrl}/api/diary/product_nutritional_values/sendToModeration',
);
final Uri serverURL = Uri.parse('${dotenv.env['SERVER_ROOT']!}');
final Uri serverURL = Uri.parse('${baseUrl}');
String host = serverURL.host;
final Map<String, String> credentials = {
'name': name,
@@ -1020,7 +1021,7 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
String comment,
int diaryID,
) async {
String url = dotenv.env['SERVER_ROOT']! + '/diary/$diaryID/retire/fill';
String url = baseUrl + '/api/diary/$diaryID/retire/fill';
final Map<String, dynamic> credentials = {
'comment': comment,
};

View File

@@ -122,7 +122,6 @@ class ChangeMeal extends DiaryEditEvent {
List<Object?> get props => [value, meals];
}
class ClearInput extends DiaryEditEvent {
ClearInput({required this.value});
@@ -154,18 +153,18 @@ class RefreshEvent extends DiaryEditEvent {
}
class SendModeration extends DiaryEditEvent {
SendModeration(
{required this.name,
required this.energyOn100gr,
required this.proteinsOn100gr,
required this.fatsOn100gr,
required this.carbohydratesOn100gr,});
SendModeration({
required this.name,
required this.energyOn100gr,
required this.proteinsOn100gr,
required this.fatsOn100gr,
required this.carbohydratesOn100gr,
});
String name;
String energyOn100gr;
String proteinsOn100gr;
String fatsOn100gr;
String carbohydratesOn100gr;
@override
List<Object?> get props =>
[name, energyOn100gr, proteinsOn100gr, fatsOn100gr, carbohydratesOn100gr];
List<Object?> get props => [name, energyOn100gr, proteinsOn100gr, fatsOn100gr, carbohydratesOn100gr];
}

View File

@@ -1,19 +1,19 @@
// ignore_for_file: unused_local_variable
import 'bloc/diary_edit_bloc.dart';
import 'models/diary_edit_model.dart';
import 'package:carousel_slider/carousel_slider.dart';
import 'package:eatvio_app/core/app_export.dart';
import 'package:eatvio_app/widgets/custom_edit_drop_down.dart';
import 'package:eatvio_app/widgets/custom_edit_text_form_field.dart';
import 'package:eatvio_app/widgets/custom_outlined_button.dart';
import 'package:eatvio_app/widgets/custom_scale_element.dart';
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:fl_chart/fl_chart.dart';
import 'package:image_picker/image_picker.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:eatvio_app/core/app_export.dart';
import 'package:eatvio_app/widgets/custom_edit_text_form_field.dart';
import 'package:eatvio_app/widgets/custom_edit_drop_down.dart';
import 'package:eatvio_app/widgets/custom_outlined_button.dart';
import 'package:eatvio_app/widgets/custom_scale_element.dart';
import 'package:carousel_slider/carousel_slider.dart';
import 'bloc/diary_edit_bloc.dart';
import 'models/diary_edit_model.dart';
// ignore: must_be_immutable
class DiaryEditScreen extends StatelessWidget {
@@ -711,7 +711,7 @@ class DiaryEditScreen extends StatelessWidget {
borderRadius: BorderRadiusStyle.roundedBorder5,
),
child: Image.network(
dotenv.env['STORAGE_URL']! + photo['path'],
baseUrl + '/storage/' + photo['path'],
),
),
Positioned(

View File

@@ -1,7 +1,7 @@
// ignore_for_file: must_be_immutable
import 'package:equatable/equatable.dart';
import 'package:eatvio_app/data/models/selectionPopupModel/selection_popup_model.dart';
import 'package:equatable/equatable.dart';
/// This class defines the variables used in the [k0_page],
/// and is typically used to hold data that is passed between different parts of the application.

View File

@@ -1,15 +1,15 @@
import 'dart:async';
import 'dart:convert';
import 'package:eatvio_app/app_router.dart';
import 'package:eatvio_app/presentation/diets/diet_create_screen/models/diet_create_model.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:go_router/go_router.dart';
import 'package:hive/hive.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import '/core/app_export.dart';
import 'package:eatvio_app/presentation/diets/diet_create_screen/models/diet_create_model.dart';
part 'diet_create_event.dart';
part 'diet_create_state.dart';
@@ -32,21 +32,27 @@ class DietCreateBloc extends Bloc<DietCreateEvent, DietCreateState> {
}
if (state is DietCreateLoadedState) {
var currentState = state as DietCreateLoadedState;
emit(currentState.copyWith(
dietModelObj: currentState.dietModelObj,
name: currentState.name,
details: currentState.details,
),);
emit(
currentState.copyWith(
dietModelObj: currentState.dietModelObj,
name: currentState.name,
details: currentState.details,
),
);
} else {
emit(DietCreateLoadedState(
name: TextEditingController(),
details: TextEditingController(),
),);
emit(
DietCreateLoadedState(
name: TextEditingController(),
details: TextEditingController(),
),
);
}
}
FutureOr<void> _createDiet(
CreateDiet event, Emitter<DietCreateState> emit,) async {
CreateDiet event,
Emitter<DietCreateState> emit,
) async {
if (state is DietCreateLoadedState) {
var currentState = state as DietCreateLoadedState;
@@ -59,7 +65,8 @@ class DietCreateBloc extends Bloc<DietCreateEvent, DietCreateState> {
AppRouter.navigatorKey.currentContext?.goNamed('dietSelectScreen');
} else {
print(
'Failed to create blog post. Status Code: ${response.statusCode} ${response.body}',);
'Failed to create blog post. Status Code: ${response.statusCode} ${response.body}',
);
}
}
}
@@ -68,7 +75,7 @@ class DietCreateBloc extends Bloc<DietCreateEvent, DietCreateState> {
String? name,
String? details,
) async {
String url = '${dotenv.env['SERVER_ROOT']!}/diets/create';
String url = '${baseUrl}/api/diets/create';
final Map<String, dynamic> credentials = {
'name': name,

View File

@@ -60,6 +60,5 @@ class DietCreateErrorState extends DietCreateState {
}
class DietCreateInitialState extends DietCreateState {
DietCreateInitialState({required DietCreateModel dietModelObj})
: super(dietModelObj: dietModelObj);
DietCreateInitialState({required DietCreateModel dietModelObj}) : super(dietModelObj: dietModelObj);
}

View File

@@ -1,16 +1,18 @@
import 'bloc/diet_create_bloc.dart';
import 'models/diet_create_model.dart';
import 'package:eatvio_app/core/app_export.dart';
import 'package:flutter/material.dart';
import 'bloc/diet_create_bloc.dart';
import 'models/diet_create_model.dart';
class DietCreateScreen extends StatelessWidget {
const DietCreateScreen({Key? key}) : super(key: key);
static Widget builder(BuildContext context) {
final dietID = ModalRoute.of(context)!.settings.arguments;
return BlocProvider<DietCreateBloc>(
create: (context) => DietCreateBloc(DietCreateState(dietModelObj: DietCreateModel(dietID: dietID.toString())))..add(DietCreateInitialEvent()),
child: DietCreateScreen(),);
create: (context) => DietCreateBloc(DietCreateState(dietModelObj: DietCreateModel(dietID: dietID.toString())))..add(DietCreateInitialEvent()),
child: DietCreateScreen(),
);
}
Widget build(BuildContext context) {
@@ -43,47 +45,52 @@ class DietCreateScreen extends StatelessWidget {
body: Container(
width: double.maxFinite,
decoration: AppDecoration.fillGray,
child: ListView(children: [
SizedBox(height: 30.v),
Container(
margin: EdgeInsets.only(
left: 16.h,
right: 20.h,
bottom: 10.h,
child: ListView(
children: [
SizedBox(height: 30.v),
Container(
margin: EdgeInsets.only(
left: 16.h,
right: 20.h,
bottom: 10.h,
),
padding: EdgeInsets.all(12.h),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: const BorderRadius.all(Radius.circular(12)),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 2,
blurRadius: 5,
offset: Offset(0, 3),
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: 16.0),
TextFormField(
controller: state.name,
decoration: InputDecoration(labelText: 'Заголовок'),
),
SizedBox(height: 16.0),
TextFormField(
controller: state.details,
decoration: InputDecoration(labelText: 'Описание'),
),
SizedBox(height: 16.0),
// Здесь нужно добавить виджет для загрузки картинки
ElevatedButton(
onPressed: () => context.read<DietCreateBloc>().add(CreateDiet()),
child: Text('Создать'),
),
],
),
),
padding: EdgeInsets.all(12.h),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: const BorderRadius.all(Radius.circular(12)),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 2,
blurRadius: 5,
offset: Offset(0, 3),
),
],
),
child: Column(mainAxisSize: MainAxisSize.min, children: [
SizedBox(height: 16.0),
TextFormField(
controller: state.name,
decoration: InputDecoration(labelText: 'Заголовок'),
),
SizedBox(height: 16.0),
TextFormField(
controller: state.details,
decoration: InputDecoration(labelText: 'Описание'),
),
SizedBox(height: 16.0),
// Здесь нужно добавить виджет для загрузки картинки
ElevatedButton(
onPressed: () => context.read<DietCreateBloc>().add(CreateDiet()),
child: Text('Создать'),
),
],),
),
],),
],
),
),
),
);

View File

@@ -1,18 +1,17 @@
import 'dart:async';
import 'dart:convert';
import 'package:eatvio_app/app_router.dart';
import 'package:eatvio_app/presentation/diets/diet_edit_screen/models/diet_edit_model.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:go_router/go_router.dart';
import 'package:hive/hive.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import '/core/app_export.dart';
import 'package:eatvio_app/presentation/diets/diet_edit_screen/models/diet_edit_model.dart';
part 'diet_edit_event.dart';
part 'diet_edit_state.dart';
/// A bloc that manages the state of a DietEdit according to the event that is dispatched to it.
@@ -34,7 +33,7 @@ class DietEditBloc extends Bloc<DietEditEvent, DietEditState> {
}
final String? dietID = state.dietModelObj?.dietID;
final response = await http.get(
Uri.parse(dotenv.env['SERVER_ROOT']! + '/diets/show/$dietID'),
Uri.parse(baseUrl + '/api/diets/show/$dietID'),
headers: {
'Authorization': 'Bearer $token',
},
@@ -44,18 +43,24 @@ class DietEditBloc extends Bloc<DietEditEvent, DietEditState> {
if (data is Map<String, dynamic>) {
if (state is DietEditLoadedState) {
var currentState = state as DietEditLoadedState;
emit(currentState.copyWith(
emit(
currentState.copyWith(
dietModelObj: currentState.dietModelObj,
diet: currentState.diet,
name: currentState.name,
duration: currentState.duration,
details: currentState.details,),);
details: currentState.details,
),
);
} else {
emit(DietEditLoadedState(
emit(
DietEditLoadedState(
diet: data,
name: TextEditingController(text: data['name']),
details: TextEditingController(text: data['details']),
duration: TextEditingController(text: data['duration']),),);
duration: TextEditingController(text: data['duration']),
),
);
}
} else {
// Один или несколько ключевых полей отсутствуют, обрабатываем ошибку
@@ -96,7 +101,7 @@ class DietEditBloc extends Bloc<DietEditEvent, DietEditState> {
String? duration,
) async {
int id = diet?['id'];
String url = '${dotenv.env['SERVER_ROOT']!}/diets/update/$id';
String url = '${baseUrl}/api/diets/update/$id';
final Map<String, dynamic> credentials = {
'id': id,

View File

@@ -22,8 +22,13 @@ class DietEditState extends Equatable {
}
class DietEditLoadedState extends DietEditState {
DietEditLoadedState(
{this.dietModelObj, this.diet, this.name, this.details, this.duration,});
DietEditLoadedState({
this.dietModelObj,
this.diet,
this.name,
this.details,
this.duration,
});
DietEditModel? dietModelObj;
Map<String, dynamic>? diet;
@@ -59,6 +64,5 @@ class DietEditErrorState extends DietEditState {
}
class DietEditInitialState extends DietEditState {
DietEditInitialState({required DietEditModel dietModelObj})
: super(dietModelObj: dietModelObj);
DietEditInitialState({required DietEditModel dietModelObj}) : super(dietModelObj: dietModelObj);
}

View File

@@ -1,9 +1,9 @@
import 'bloc/diet_edit_bloc.dart';
import 'models/diet_edit_model.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:eatvio_app/core/app_export.dart';
import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'bloc/diet_edit_bloc.dart';
import 'models/diet_edit_model.dart';
class DietEditScreen extends StatelessWidget {
const DietEditScreen({Key? key}) : super(key: key);
@@ -11,8 +11,9 @@ class DietEditScreen extends StatelessWidget {
static Widget builder(BuildContext context) {
final dietID = ModalRoute.of(context)!.settings.arguments;
return BlocProvider<DietEditBloc>(
create: (context) => DietEditBloc(DietEditState(dietModelObj: DietEditModel(dietID: dietID.toString())))..add(DietEditInitialEvent()),
child: DietEditScreen(),);
create: (context) => DietEditBloc(DietEditState(dietModelObj: DietEditModel(dietID: dietID.toString())))..add(DietEditInitialEvent()),
child: DietEditScreen(),
);
}
Widget build(BuildContext context) {
@@ -71,7 +72,7 @@ class DietEditScreen extends StatelessWidget {
mainAxisSize: MainAxisSize.min,
children: [
CachedNetworkImage(
imageUrl: dotenv.env['STORAGE_URL']! + (state.diet?['image'] ?? ''),
imageUrl: baseUrl + '/storage/' + (state.diet?['image'] ?? ''),
imageBuilder: (context, imageProvider) => CircleAvatar(
backgroundImage: imageProvider,
radius: 25,

View File

@@ -1,17 +1,17 @@
//import 'package:eatvio_app/presentation/auth/auth_screen/auth_screen.dart';
import 'dart:async';
import 'dart:convert';
import 'package:eatvio_app/app_router.dart';
import 'package:eatvio_app/presentation/diets/diet_select_screen/models/diet_select_model.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import '/core/app_export.dart';
import 'package:eatvio_app/presentation/diets/diet_select_screen/models/diet_select_model.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:hive/hive.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:image_picker/image_picker.dart';
import 'package:go_router/go_router.dart';
import 'package:hive/hive.dart';
import 'package:http/http.dart' as http;
import 'package:image_picker/image_picker.dart';
import '/core/app_export.dart';
part 'diet_select_event.dart';
part 'diet_select_state.dart';
@@ -33,7 +33,7 @@ class DietSelectBloc extends Bloc<DietSelectEvent, DietSelectState> {
AppRouter.navigatorKey.currentContext?.goNamed('authScreen');
}
final response = await http.get(
Uri.parse(dotenv.env['SERVER_ROOT']! + '/diets/index'),
Uri.parse(baseUrl + '/api/diets/index'),
headers: {
'Authorization': 'Bearer $token',
},
@@ -47,15 +47,19 @@ class DietSelectBloc extends Bloc<DietSelectEvent, DietSelectState> {
if (state is DietSelectLoadedState) {
print('Got loaded');
var currentState = state as DietSelectLoadedState;
emit((currentState).copyWith(
posts: data,
myProfileModelObj: currentState.myProfileModelObj,
),);
emit(
(currentState).copyWith(
posts: data,
myProfileModelObj: currentState.myProfileModelObj,
),
);
} else {
print('Got unloaded');
emit(DietSelectLoadedState(
posts: data,
),);
emit(
DietSelectLoadedState(
posts: data,
),
);
}
} else {
// Один или несколько ключевых полей отсутствуют, обрабатываем ошибку

View File

@@ -12,14 +12,12 @@ class DietSelectState extends Equatable {
}
class DietSelectInitialState extends DietSelectState {
DietSelectInitialState({required DietSelectModel myProfileModelObj})
: super(myProfileModelObj: myProfileModelObj);
DietSelectInitialState({required DietSelectModel myProfileModelObj}) : super(myProfileModelObj: myProfileModelObj);
}
/// Represents the state of DietSelect in the application.
class DietSelectLoadedState extends DietSelectState {
DietSelectLoadedState({this.myProfileModelObj, this.posts})
: super(myProfileModelObj: null);
DietSelectLoadedState({this.myProfileModelObj, this.posts}) : super(myProfileModelObj: null);
DietSelectModel? myProfileModelObj;
List<dynamic>? posts;

View File

@@ -1,19 +1,20 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:eatvio_app/core/app_export.dart';
import 'package:eatvio_app/widgets/app_bar/appbar_leading_image.dart';
import 'package:eatvio_app/widgets/app_bar/appbar_subtitle.dart';
import 'package:eatvio_app/widgets/app_bar/custom_app_bar.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'bloc/diet_select_bloc.dart';
import 'models/diet_select_model.dart';
import 'package:eatvio_app/core/app_export.dart';
import 'package:eatvio_app/widgets/app_bar/appbar_subtitle.dart';
import 'package:eatvio_app/widgets/app_bar/appbar_leading_image.dart';
import 'package:eatvio_app/widgets/app_bar/custom_app_bar.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter/material.dart';
class DietSelectScreen extends StatelessWidget {
static Widget builder(BuildContext context) {
return BlocProvider<DietSelectBloc>(
create: (context) => DietSelectBloc(DietSelectState(myProfileModelObj: DietSelectModel()))..add(DietSelectInitialEvent()), child: DietSelectScreen(),);
create: (context) => DietSelectBloc(DietSelectState(myProfileModelObj: DietSelectModel()))..add(DietSelectInitialEvent()),
child: DietSelectScreen(),
);
}
Widget build(BuildContext context) {
@@ -118,50 +119,51 @@ class DietSelectScreen extends StatelessWidget {
itemBuilder: (context, index) {
final post = state.posts?[index];
return Container(
margin: EdgeInsets.only(bottom: 10),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: const BorderRadius.all(Radius.circular(12)),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 2,
blurRadius: 5,
offset: Offset(0, 3),
),
],
margin: EdgeInsets.only(bottom: 10),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: const BorderRadius.all(Radius.circular(12)),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 2,
blurRadius: 5,
offset: Offset(0, 3),
),
],
),
child: ListTile(
leading: CachedNetworkImage(
imageUrl: baseUrl + '/storage/' + post['image'],
imageBuilder: (context, imageProvider) => CircleAvatar(
backgroundImage: imageProvider,
radius: 25,
),
placeholder: (context, url) => CircularProgressIndicator(),
errorWidget: (context, url, error) => CircleAvatar(
child: Icon(Icons.person),
backgroundColor: Colors.white,
radius: 25,
),
),
child: ListTile(
leading: CachedNetworkImage(
imageUrl: dotenv.env['STORAGE_URL']! + post['image'],
imageBuilder: (context, imageProvider) => CircleAvatar(
backgroundImage: imageProvider,
radius: 25,
),
placeholder: (context, url) => CircularProgressIndicator(),
errorWidget: (context, url, error) => CircleAvatar(
child: Icon(Icons.person),
backgroundColor: Colors.white,
radius: 25,
),
),
title: Text(
post?['name'] ?? '',
style: CustomTextStyles.titleMediumSemiBold,
),
subtitle: Text(post?['details'] ?? ''),
title: Text(
post?['name'] ?? '',
style: CustomTextStyles.titleMediumSemiBold,
),
subtitle: Text(post?['details'] ?? ''),
onTap: () => GoRouter.of(context).goNamed(
'dietScreen',
extra: post?['id'].toString(),
),
trailing: GestureDetector(
onTap: () => GoRouter.of(context).goNamed(
'dietScreen',
'dietEditScreen',
extra: post?['id'].toString(),
),
trailing: GestureDetector(
onTap: () => GoRouter.of(context).goNamed(
'dietEditScreen',
extra: post?['id'].toString(),
),
child: Icon(Icons.edit),
),
),);
child: Icon(Icons.edit),
),
),
);
},
);
}

View File

@@ -1,18 +1,17 @@
import 'dart:async';
import 'dart:convert';
import 'package:eatvio_app/app_router.dart';
import 'package:eatvio_app/presentation/diets/diet_show_screen/models/diet_show_model.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:go_router/go_router.dart';
import 'package:hive/hive.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import '/core/app_export.dart';
import 'package:eatvio_app/presentation/diets/diet_show_screen/models/diet_show_model.dart';
import 'package:go_router/go_router.dart';
part 'diet_show_event.dart';
part 'diet_show_state.dart';
/// A bloc that manages the state of a Diet according to the event that is dispatched to it.
@@ -34,7 +33,7 @@ class DietBloc extends Bloc<DietEvent, DietState> {
}
final String? dietID = state.dietModelObj?.dietID;
final response = await http.get(
Uri.parse(dotenv.env['SERVER_ROOT']! + '/diets/show/$dietID'),
Uri.parse(baseUrl + '/api/diets/show/$dietID'),
headers: {
'Authorization': 'Bearer $token',
},
@@ -70,7 +69,7 @@ class DietBloc extends Bloc<DietEvent, DietState> {
}
final int id = event.id;
final response = await http.post(
Uri.parse(dotenv.env['SERVER_ROOT']! + '/diets/delete/$id'),
Uri.parse(baseUrl + '/api/diets/delete/$id'),
headers: {
'Authorization': 'Bearer $token',
},

View File

@@ -28,8 +28,7 @@ class DietLoadedState extends DietState {
Map<String, dynamic>? diet;
@override
List<Object?> get props => [dietModelObj, diet];
DietLoadedState copyWith(
{DietModel? dietModelObj, Map<String, dynamic>? diet}) {
DietLoadedState copyWith({DietModel? dietModelObj, Map<String, dynamic>? diet}) {
return DietLoadedState(
dietModelObj: dietModelObj ?? this.dietModelObj,
diet: diet ?? this.diet,
@@ -47,6 +46,5 @@ class DietErrorState extends DietState {
}
class DietInitialState extends DietState {
DietInitialState({required DietModel dietModelObj})
: super(dietModelObj: dietModelObj);
DietInitialState({required DietModel dietModelObj}) : super(dietModelObj: dietModelObj);
}

View File

@@ -1,11 +1,10 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:eatvio_app/core/app_export.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'bloc/diet_show_bloc.dart';
import 'models/diet_show_model.dart';
import 'package:eatvio_app/core/app_export.dart';
import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
class DietScreen extends StatelessWidget {
const DietScreen({Key? key}) : super(key: key);
@@ -13,7 +12,9 @@ class DietScreen extends StatelessWidget {
static Widget builder(BuildContext context) {
final dietID = ModalRoute.of(context)!.settings.arguments;
return BlocProvider<DietBloc>(
create: (context) => DietBloc(DietState(dietModelObj: DietModel(dietID: dietID.toString())))..add(DietInitialEvent()), child: DietScreen(),);
create: (context) => DietBloc(DietState(dietModelObj: DietModel(dietID: dietID.toString())))..add(DietInitialEvent()),
child: DietScreen(),
);
}
Widget build(BuildContext context) {
@@ -68,44 +69,47 @@ class DietScreen extends StatelessWidget {
),
],
),
child: Column(mainAxisSize: MainAxisSize.min, children: [
CachedNetworkImage(
imageUrl: dotenv.env['STORAGE_URL']! + (state.diet?['image'] ?? ''),
imageBuilder: (context, imageProvider) => CircleAvatar(
backgroundImage: imageProvider,
radius: 25,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
CachedNetworkImage(
imageUrl: baseUrl + '/storage/' + (state.diet?['image'] ?? ''),
imageBuilder: (context, imageProvider) => CircleAvatar(
backgroundImage: imageProvider,
radius: 25,
),
placeholder: (context, url) => CircularProgressIndicator(),
errorWidget: (context, url, error) => CircleAvatar(
child: Icon(Icons.person),
backgroundColor: Colors.white,
radius: 25,
),
),
placeholder: (context, url) => CircularProgressIndicator(),
errorWidget: (context, url, error) => CircleAvatar(
child: Icon(Icons.person),
backgroundColor: Colors.white,
radius: 25,
SizedBox(height: 16.0),
Text(state.diet?['name'], style: CustomTextStyles.titleMediumBlack),
SizedBox(height: 8.0),
Text(
state.diet?['details'],
),
),
SizedBox(height: 16.0),
Text(state.diet?['name'], style: CustomTextStyles.titleMediumBlack),
SizedBox(height: 8.0),
Text(
state.diet?['details'],
),
SizedBox(height: 16.0),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
ElevatedButton(
onPressed: () => GoRouter.of(context).goNamed(
'dietEditScreen',
extra: state.diet?['id'].toString(),
SizedBox(height: 16.0),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
ElevatedButton(
onPressed: () => GoRouter.of(context).goNamed(
'dietEditScreen',
extra: state.diet?['id'].toString(),
),
child: Text('Редактировать'),
),
child: Text('Редактировать'),
),
ElevatedButton(
onPressed: () => context.read<DietBloc>().add(DeleteEntry(state.diet?['id'])),
child: Text('Удалить'),
),
],
),
],),
ElevatedButton(
onPressed: () => context.read<DietBloc>().add(DeleteEntry(state.diet?['id'])),
child: Text('Удалить'),
),
],
),
],
),
),
],
),

View File

@@ -1,22 +1,23 @@
import 'dart:async';
import 'dart:convert';
import 'package:dio/dio.dart';
import 'package:eatvio_app/app_router.dart';
import 'package:eatvio_app/locator_service.dart';
import 'package:eatvio_app/presentation/feed/blog_post_create_screen/models/blog_post_create_model.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:hive/hive.dart';
import 'package:image_picker/image_picker.dart';
import 'dart:convert';
import '/core/app_export.dart';
import 'package:eatvio_app/presentation/feed/blog_post_create_screen/models/blog_post_create_model.dart';
import 'package:go_router/go_router.dart';
part 'blog_post_create_event.dart';
part 'blog_post_create_state.dart';
/// A bloc that manages the state of a BlogPostCreate according to the event that is dispatched to it.
class BlogPostCreateBloc
extends Bloc<BlogPostCreateEvent, BlogPostCreateState> {
class BlogPostCreateBloc extends Bloc<BlogPostCreateEvent, BlogPostCreateState> {
BlogPostCreateBloc(BlogPostCreateState initialState) : super(initialState) {
on<BlogPostCreateInitialEvent>(_onInitialize);
on<CreateBlogPost>(_createBlogPost);
@@ -79,8 +80,7 @@ class BlogPostCreateBloc
}
}
sendBlogPost(String? title, String? youtubeURL, String? description,
XFile? image) async {
sendBlogPost(String? title, String? youtubeURL, String? description, XFile? image) async {
String url = '/blog_post/create';
final Map<String, dynamic> credentials = {
@@ -99,8 +99,7 @@ class BlogPostCreateBloc
return response;
}
FutureOr<void> _uploadImages(
UploadImages event, Emitter<BlogPostCreateState> emit) {
FutureOr<void> _uploadImages(UploadImages event, Emitter<BlogPostCreateState> emit) {
if (state is BlogPostCreateLoadedState) {
var currentState = state as BlogPostCreateLoadedState;
emit(

View File

@@ -40,8 +40,7 @@ class BlogPostCreateLoadedState extends BlogPostCreateState {
bool isLoading;
XFile? image;
@override
List<Object?> get props =>
[blog_postModelObj, title, description, youtubeURL];
List<Object?> get props => [blog_postModelObj, title, description, youtubeURL];
BlogPostCreateLoadedState copyWith({
BlogPostCreateModel? blog_postModelObj,
TextEditingController? title,
@@ -73,6 +72,5 @@ class BlogPostCreateErrorState extends BlogPostCreateState {
}
class BlogPostCreateInitialState extends BlogPostCreateState {
BlogPostCreateInitialState({required BlogPostCreateModel blog_postModelObj})
: super(blog_postModelObj: blog_postModelObj);
BlogPostCreateInitialState({required BlogPostCreateModel blog_postModelObj}) : super(blog_postModelObj: blog_postModelObj);
}

View File

@@ -14,8 +14,7 @@ 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');
BlogPostCreateCredentialsModel _$BlogPostCreateCredentialsModelFromJson(
Map<String, dynamic> json) {
BlogPostCreateCredentialsModel _$BlogPostCreateCredentialsModelFromJson(Map<String, dynamic> json) {
return _BlogPostCreateCredentialsModel.fromJson(json);
}
@@ -29,29 +28,19 @@ mixin _$BlogPostCreateCredentialsModel {
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$BlogPostCreateCredentialsModelCopyWith<BlogPostCreateCredentialsModel>
get copyWith => throw _privateConstructorUsedError;
$BlogPostCreateCredentialsModelCopyWith<BlogPostCreateCredentialsModel> get copyWith => throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $BlogPostCreateCredentialsModelCopyWith<$Res> {
factory $BlogPostCreateCredentialsModelCopyWith(
BlogPostCreateCredentialsModel value,
$Res Function(BlogPostCreateCredentialsModel) then) =
_$BlogPostCreateCredentialsModelCopyWithImpl<$Res,
BlogPostCreateCredentialsModel>;
factory $BlogPostCreateCredentialsModelCopyWith(BlogPostCreateCredentialsModel value, $Res Function(BlogPostCreateCredentialsModel) then) =
_$BlogPostCreateCredentialsModelCopyWithImpl<$Res, BlogPostCreateCredentialsModel>;
@useResult
$Res call(
{String title,
String description,
@JsonKey(name: 'youtube_url') String youtubeUrl,
String status});
$Res call({String title, String description, @JsonKey(name: 'youtube_url') String youtubeUrl, String status});
}
/// @nodoc
class _$BlogPostCreateCredentialsModelCopyWithImpl<$Res,
$Val extends BlogPostCreateCredentialsModel>
implements $BlogPostCreateCredentialsModelCopyWith<$Res> {
class _$BlogPostCreateCredentialsModelCopyWithImpl<$Res, $Val extends BlogPostCreateCredentialsModel> implements $BlogPostCreateCredentialsModelCopyWith<$Res> {
_$BlogPostCreateCredentialsModelCopyWithImpl(this._value, this._then);
// ignore: unused_field
@@ -89,29 +78,18 @@ class _$BlogPostCreateCredentialsModelCopyWithImpl<$Res,
}
/// @nodoc
abstract class _$$BlogPostCreateCredentialsModelImplCopyWith<$Res>
implements $BlogPostCreateCredentialsModelCopyWith<$Res> {
factory _$$BlogPostCreateCredentialsModelImplCopyWith(
_$BlogPostCreateCredentialsModelImpl value,
$Res Function(_$BlogPostCreateCredentialsModelImpl) then) =
abstract class _$$BlogPostCreateCredentialsModelImplCopyWith<$Res> implements $BlogPostCreateCredentialsModelCopyWith<$Res> {
factory _$$BlogPostCreateCredentialsModelImplCopyWith(_$BlogPostCreateCredentialsModelImpl value, $Res Function(_$BlogPostCreateCredentialsModelImpl) then) =
__$$BlogPostCreateCredentialsModelImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{String title,
String description,
@JsonKey(name: 'youtube_url') String youtubeUrl,
String status});
$Res call({String title, String description, @JsonKey(name: 'youtube_url') String youtubeUrl, String status});
}
/// @nodoc
class __$$BlogPostCreateCredentialsModelImplCopyWithImpl<$Res>
extends _$BlogPostCreateCredentialsModelCopyWithImpl<$Res,
_$BlogPostCreateCredentialsModelImpl>
class __$$BlogPostCreateCredentialsModelImplCopyWithImpl<$Res> extends _$BlogPostCreateCredentialsModelCopyWithImpl<$Res, _$BlogPostCreateCredentialsModelImpl>
implements _$$BlogPostCreateCredentialsModelImplCopyWith<$Res> {
__$$BlogPostCreateCredentialsModelImplCopyWithImpl(
_$BlogPostCreateCredentialsModelImpl _value,
$Res Function(_$BlogPostCreateCredentialsModelImpl) _then)
__$$BlogPostCreateCredentialsModelImplCopyWithImpl(_$BlogPostCreateCredentialsModelImpl _value, $Res Function(_$BlogPostCreateCredentialsModelImpl) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@@ -145,17 +123,11 @@ class __$$BlogPostCreateCredentialsModelImplCopyWithImpl<$Res>
/// @nodoc
@JsonSerializable()
class _$BlogPostCreateCredentialsModelImpl
implements _BlogPostCreateCredentialsModel {
class _$BlogPostCreateCredentialsModelImpl implements _BlogPostCreateCredentialsModel {
const _$BlogPostCreateCredentialsModelImpl(
{required this.title,
required this.description,
@JsonKey(name: 'youtube_url') required this.youtubeUrl,
this.status = 'active'});
{required this.title, required this.description, @JsonKey(name: 'youtube_url') required this.youtubeUrl, this.status = 'active'});
factory _$BlogPostCreateCredentialsModelImpl.fromJson(
Map<String, dynamic> json) =>
_$$BlogPostCreateCredentialsModelImplFromJson(json);
factory _$BlogPostCreateCredentialsModelImpl.fromJson(Map<String, dynamic> json) => _$$BlogPostCreateCredentialsModelImplFromJson(json);
@override
final String title;
@@ -179,25 +151,20 @@ class _$BlogPostCreateCredentialsModelImpl
(other.runtimeType == runtimeType &&
other is _$BlogPostCreateCredentialsModelImpl &&
(identical(other.title, title) || other.title == title) &&
(identical(other.description, description) ||
other.description == description) &&
(identical(other.youtubeUrl, youtubeUrl) ||
other.youtubeUrl == youtubeUrl) &&
(identical(other.description, description) || other.description == description) &&
(identical(other.youtubeUrl, youtubeUrl) || other.youtubeUrl == youtubeUrl) &&
(identical(other.status, status) || other.status == status));
}
@JsonKey(ignore: true)
@override
int get hashCode =>
Object.hash(runtimeType, title, description, youtubeUrl, status);
int get hashCode => Object.hash(runtimeType, title, description, youtubeUrl, status);
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$BlogPostCreateCredentialsModelImplCopyWith<
_$BlogPostCreateCredentialsModelImpl>
get copyWith => __$$BlogPostCreateCredentialsModelImplCopyWithImpl<
_$BlogPostCreateCredentialsModelImpl>(this, _$identity);
_$$BlogPostCreateCredentialsModelImplCopyWith<_$BlogPostCreateCredentialsModelImpl> get copyWith =>
__$$BlogPostCreateCredentialsModelImplCopyWithImpl<_$BlogPostCreateCredentialsModelImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
@@ -207,16 +174,14 @@ class _$BlogPostCreateCredentialsModelImpl
}
}
abstract class _BlogPostCreateCredentialsModel
implements BlogPostCreateCredentialsModel {
abstract class _BlogPostCreateCredentialsModel implements BlogPostCreateCredentialsModel {
const factory _BlogPostCreateCredentialsModel(
{required final String title,
required final String description,
@JsonKey(name: 'youtube_url') required final String youtubeUrl,
final String status}) = _$BlogPostCreateCredentialsModelImpl;
factory _BlogPostCreateCredentialsModel.fromJson(Map<String, dynamic> json) =
_$BlogPostCreateCredentialsModelImpl.fromJson;
factory _BlogPostCreateCredentialsModel.fromJson(Map<String, dynamic> json) = _$BlogPostCreateCredentialsModelImpl.fromJson;
@override
String get title;
@@ -229,7 +194,5 @@ abstract class _BlogPostCreateCredentialsModel
String get status;
@override
@JsonKey(ignore: true)
_$$BlogPostCreateCredentialsModelImplCopyWith<
_$BlogPostCreateCredentialsModelImpl>
get copyWith => throw _privateConstructorUsedError;
_$$BlogPostCreateCredentialsModelImplCopyWith<_$BlogPostCreateCredentialsModelImpl> get copyWith => throw _privateConstructorUsedError;
}

View File

@@ -6,18 +6,14 @@ part of '../../../../features/blog_post_create/models/blog_post_create_credentia
// JsonSerializableGenerator
// **************************************************************************
_$BlogPostCreateCredentialsModelImpl
_$$BlogPostCreateCredentialsModelImplFromJson(Map<String, dynamic> json) =>
_$BlogPostCreateCredentialsModelImpl(
title: json['title'] as String,
description: json['description'] as String,
youtubeUrl: json['youtube_url'] as String,
status: json['status'] as String? ?? 'active',
);
_$BlogPostCreateCredentialsModelImpl _$$BlogPostCreateCredentialsModelImplFromJson(Map<String, dynamic> json) => _$BlogPostCreateCredentialsModelImpl(
title: json['title'] as String,
description: json['description'] as String,
youtubeUrl: json['youtube_url'] as String,
status: json['status'] as String? ?? 'active',
);
Map<String, dynamic> _$$BlogPostCreateCredentialsModelImplToJson(
_$BlogPostCreateCredentialsModelImpl instance) =>
<String, dynamic>{
Map<String, dynamic> _$$BlogPostCreateCredentialsModelImplToJson(_$BlogPostCreateCredentialsModelImpl instance) => <String, dynamic>{
'title': instance.title,
'description': instance.description,
'youtube_url': instance.youtubeUrl,

View File

@@ -8,8 +8,7 @@ import 'package:equatable/equatable.dart';
class BlogPostCreateModel extends Equatable {
final String blog_postID;
BlogPostCreateModel({required String blog_postID})
: blog_postID = blog_postID;
BlogPostCreateModel({required String blog_postID}) : blog_postID = blog_postID;
BlogPostCreateModel copyWith(String blog_postID) {
return BlogPostCreateModel(blog_postID: blog_postID);

View File

@@ -1,18 +1,17 @@
import 'dart:async';
import 'dart:convert';
import 'package:eatvio_app/app_router.dart';
import 'package:eatvio_app/presentation/feed/blog_post_edit_screen/models/blog_post_edit_model.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:go_router/go_router.dart';
import 'package:hive/hive.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import '/core/app_export.dart';
import 'package:eatvio_app/presentation/feed/blog_post_edit_screen/models/blog_post_edit_model.dart';
import 'package:go_router/go_router.dart';
part 'blog_post_edit_event.dart';
part 'blog_post_edit_state.dart';
/// A bloc that manages the state of a BlogPostEdit according to the event that is dispatched to it.
@@ -34,7 +33,7 @@ class BlogPostEditBloc extends Bloc<BlogPostEditEvent, BlogPostEditState> {
}
final String? blog_postID = state.blog_postModelObj?.blog_postID;
final response = await http.get(
Uri.parse(dotenv.env['SERVER_ROOT']! + '/feed/show/$blog_postID'),
Uri.parse(baseUrl + '/api/feed/show/$blog_postID'),
headers: {
'Authorization': 'Bearer $token',
},
@@ -65,8 +64,7 @@ class BlogPostEditBloc extends Bloc<BlogPostEditEvent, BlogPostEditState> {
}
} else {
// Один или несколько ключевых полей отсутствуют, обрабатываем ошибку
emit(BlogPostEditErrorState(
'Некоторые необходимые данные отсутствуют.'));
emit(BlogPostEditErrorState('Некоторые необходимые данные отсутствуют.'));
}
} else {
// Статус ответа не равен 200, обрабатываем ошибку
@@ -78,8 +76,7 @@ class BlogPostEditBloc extends Bloc<BlogPostEditEvent, BlogPostEditState> {
}
}
FutureOr<void> _editBlogPost(
EditBlogPost event, Emitter<BlogPostEditState> emit) async {
FutureOr<void> _editBlogPost(EditBlogPost event, Emitter<BlogPostEditState> emit) async {
if (state is BlogPostEditLoadedState) {
var currentState = state as BlogPostEditLoadedState;
@@ -91,11 +88,9 @@ class BlogPostEditBloc extends Bloc<BlogPostEditEvent, BlogPostEditState> {
);
if (response.statusCode == 200) {
AppRouter.navigatorKey.currentContext?.goNamed('blogPostScreen',
extra: currentState.blog_post?['id'].toString());
AppRouter.navigatorKey.currentContext?.goNamed('blogPostScreen', extra: currentState.blog_post?['id'].toString());
} else {
print(
'Failed to edit blog post. Status Code: ${response.statusCode} ${response.body}');
print('Failed to edit blog post. Status Code: ${response.statusCode} ${response.body}');
}
}
}
@@ -107,7 +102,7 @@ class BlogPostEditBloc extends Bloc<BlogPostEditEvent, BlogPostEditState> {
String? description,
) async {
int id = blog_post?['id'];
String url = '${dotenv.env['SERVER_ROOT']!}/feed/update/$id';
String url = '${baseUrl}/api/feed/update/$id';
final Map<String, dynamic> credentials = {
'id': id,

View File

@@ -36,8 +36,7 @@ class BlogPostEditLoadedState extends BlogPostEditState {
TextEditingController? description;
TextEditingController? youtubeURL;
@override
List<Object?> get props =>
[blog_postModelObj, blog_post, title, description, youtubeURL];
List<Object?> get props => [blog_postModelObj, blog_post, title, description, youtubeURL];
BlogPostEditLoadedState copyWith({
BlogPostEditModel? blog_postModelObj,
Map<String, dynamic>? blog_post,
@@ -65,6 +64,5 @@ class BlogPostEditErrorState extends BlogPostEditState {
}
class BlogPostEditInitialState extends BlogPostEditState {
BlogPostEditInitialState({required BlogPostEditModel blog_postModelObj})
: super(blog_postModelObj: blog_postModelObj);
BlogPostEditInitialState({required BlogPostEditModel blog_postModelObj}) : super(blog_postModelObj: blog_postModelObj);
}

View File

@@ -1,19 +1,17 @@
import 'bloc/blog_post_edit_bloc.dart';
import 'models/blog_post_edit_model.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:eatvio_app/core/app_export.dart';
import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'bloc/blog_post_edit_bloc.dart';
import 'models/blog_post_edit_model.dart';
class BlogPostEditScreen extends StatelessWidget {
const BlogPostEditScreen({Key? key}) : super(key: key);
static Widget builder(BuildContext context, blog_postID) {
return BlocProvider<BlogPostEditBloc>(
create: (context) => BlogPostEditBloc(BlogPostEditState(
blog_postModelObj:
BlogPostEditModel(blog_postID: blog_postID.toString())))
..add(BlogPostEditInitialEvent()),
create: (context) =>
BlogPostEditBloc(BlogPostEditState(blog_postModelObj: BlogPostEditModel(blog_postID: blog_postID.toString())))..add(BlogPostEditInitialEvent()),
child: BlogPostEditScreen(),
);
}
@@ -77,8 +75,7 @@ class BlogPostEditScreen extends StatelessWidget {
mainAxisSize: MainAxisSize.min,
children: [
CachedNetworkImage(
imageUrl: dotenv.env['STORAGE_URL']! +
(state.blog_post?['image'] ?? ''),
imageUrl: baseUrl + '/storage/' + (state.blog_post?['image'] ?? ''),
imageBuilder: (context, imageProvider) => CircleAvatar(
backgroundImage: imageProvider,
radius: 25,
@@ -108,8 +105,7 @@ class BlogPostEditScreen extends StatelessWidget {
SizedBox(height: 16.0),
// Здесь нужно добавить виджет для загрузки картинки
ElevatedButton(
onPressed: () =>
context.read<BlogPostEditBloc>().add(EditBlogPost()),
onPressed: () => context.read<BlogPostEditBloc>().add(EditBlogPost()),
child: Text('Сохранить'),
),
],

View File

@@ -1,7 +1,7 @@
// ignore_for_file: must_be_immutable
import 'package:equatable/equatable.dart';
import 'package:eatvio_app/presentation/feed/blog_post_screen/models/comment_model.dart';
import 'package:equatable/equatable.dart';
class BlogPostModel extends Equatable {
final String blog_postID;

View File

@@ -4,12 +4,13 @@ import 'package:eatvio_app/core/app_export.dart';
// ignore_for_file: must_be_immutable
class CommentModel {
CommentModel(
{this.userImage,
this.username,
this.dateText,
this.greetingText,
required this.id,}) {
CommentModel({
this.userImage,
this.username,
this.dateText,
this.greetingText,
required this.id,
}) {
userImage = userImage ?? ImageConstant.imageNotFound;
username = username;
dateText = dateText;

View File

@@ -1,10 +1,9 @@
// ignore_for_file: must_be_immutable
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:eatvio_app/core/app_export.dart';
import 'package:eatvio_app/presentation/feed/blog_post_screen/models/comment_model.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
class CommentWidget extends StatelessWidget {
CommentWidget(this.commentModelObj, {Key? key})
@@ -36,13 +35,11 @@ class CommentWidget extends StatelessWidget {
],
),
child: Row(
mainAxisAlignment: MainAxisAlignment
.spaceBetween, // Move the main axis alignment to space between
crossAxisAlignment:
CrossAxisAlignment.center, // Center the items vertically
mainAxisAlignment: MainAxisAlignment.spaceBetween, // Move the main axis alignment to space between
crossAxisAlignment: CrossAxisAlignment.center, // Center the items vertically
children: [
CachedNetworkImage(
imageUrl: dotenv.env['STORAGE_URL']! + commentModelObj.userImage!,
imageUrl: baseUrl + '/storage/' + commentModelObj.userImage!,
imageBuilder: (context, imageProvider) => CircleAvatar(
backgroundImage: imageProvider,
radius: 25,
@@ -69,10 +66,7 @@ class CommentWidget extends StatelessWidget {
child: Row(
children: [
Text(
commentModelObj.username!.length <= 15
? commentModelObj.username!
: commentModelObj.username!.substring(0, 15) +
'...',
commentModelObj.username!.length <= 15 ? commentModelObj.username! : commentModelObj.username!.substring(0, 15) + '...',
style: theme.textTheme.titleMedium,
),
Spacer(),
@@ -96,10 +90,11 @@ class CommentWidget extends StatelessWidget {
),
),
CustomImageView(
imagePath: ImageConstant.imgSend,
height: 30.v,
width: 30.h,
margin: EdgeInsets.only(left: 5),),
imagePath: ImageConstant.imgSend,
height: 30.v,
width: 30.h,
margin: EdgeInsets.only(left: 5),
),
],
),
);

View File

@@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
import 'package:eatvio_app/core/app_export.dart';
import 'package:flutter/material.dart';
class ScrollItem extends StatelessWidget {
final String title;

View File

@@ -1,31 +1,31 @@
import 'package:flutter/material.dart';
import 'package:eatvio_app/core/app_export.dart';
import 'package:flutter/material.dart';
class CustomSearchView extends StatelessWidget {
CustomSearchView(
{Key? key,
this.alignment,
this.width,
this.scrollPadding,
this.controller,
this.focusNode,
this.autofocus = false,
this.textStyle,
this.textInputType = TextInputType.text,
this.maxLines,
this.hintText,
this.hintStyle,
this.prefix,
this.prefixConstraints,
this.suffix,
this.suffixConstraints,
this.contentPadding,
this.borderDecoration,
this.fillColor,
this.filled = true,
this.validator,
this.onChanged,})
: super(
CustomSearchView({
Key? key,
this.alignment,
this.width,
this.scrollPadding,
this.controller,
this.focusNode,
this.autofocus = false,
this.textStyle,
this.textInputType = TextInputType.text,
this.maxLines,
this.hintText,
this.hintStyle,
this.prefix,
this.prefixConstraints,
this.suffix,
this.suffixConstraints,
this.contentPadding,
this.borderDecoration,
this.fillColor,
this.filled = true,
this.validator,
this.onChanged,
}) : super(
key: key,
);
@@ -76,15 +76,15 @@ class CustomSearchView extends StatelessWidget {
return alignment != null
? Align(
alignment: alignment ?? Alignment.center,
child: searchViewWidget(context),)
child: searchViewWidget(context),
)
: searchViewWidget(context);
}
searchViewWidget(BuildContext context) => SizedBox(
width: width ?? double.maxFinite,
child: TextFormField(
scrollPadding:
EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
scrollPadding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
controller: controller,
focusNode: focusNode,
onTapOutside: (event) => onTapOutside(),
@@ -112,8 +112,9 @@ class CustomSearchView extends StatelessWidget {
prefixIcon: prefix ??
SizedBox(
child: Padding(
padding: EdgeInsets.only(left: 10),
child: Icon(Icons.search),),
padding: EdgeInsets.only(left: 10),
child: Icon(Icons.search),
),
),
prefixIconConstraints: prefixConstraints ??
BoxConstraints(

View File

@@ -1,15 +1,16 @@
import 'dart:async';
import 'dart:convert';
import 'package:eatvio_app/app_router.dart';
import 'package:eatvio_app/presentation/glossary/glossary_screen/models/glossary_screen_model.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:go_router/go_router.dart';
import 'package:hive/hive.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import '/core/app_export.dart';
import 'package:eatvio_app/presentation/glossary/glossary_screen/models/glossary_screen_model.dart';
import 'package:go_router/go_router.dart';
part 'glossary_screen_event.dart';
part 'glossary_screen_state.dart';
@@ -31,7 +32,7 @@ class GlossaryBloc extends Bloc<GlossaryEvent, GlossaryState> {
AppRouter.navigatorKey.currentContext?.goNamed('authScreen');
}
final responseProductGroups = await http.get(
Uri.parse(dotenv.env['SERVER_ROOT']! + '/glossary/getProductGroups'),
Uri.parse(baseUrl + '/api/glossary/getProductGroups'),
headers: {
'Authorization': 'Bearer $token',
},
@@ -41,18 +42,23 @@ class GlossaryBloc extends Bloc<GlossaryEvent, GlossaryState> {
if (dataProductGroups is List<dynamic>) {
if (state is GlossaryLoadedState) {
var currentState = state as GlossaryLoadedState;
emit(currentState.copyWith(
emit(
currentState.copyWith(
glossaryModelObj: currentState.glossaryModelObj,
token: currentState.token,
productGroups: currentState.productGroups,
productList: currentState.productList,),);
productList: currentState.productList,
),
);
} else {
List<SelectionPopupModel> productGroupsModel =
_fillProductGroups(dataProductGroups);
emit(GlossaryLoadedState(
List<SelectionPopupModel> productGroupsModel = _fillProductGroups(dataProductGroups);
emit(
GlossaryLoadedState(
token: token,
productGroups: productGroupsModel,
productList: [],),);
productList: [],
),
);
await _getProductListById(token, emit, productGroupsModel[0].id);
}
} else {
@@ -61,8 +67,11 @@ class GlossaryBloc extends Bloc<GlossaryEvent, GlossaryState> {
}
} else {
// Статус ответа не равен 200, обрабатываем ошибку
emit(GlossaryErrorState(
'Ошибка сервера: ${responseProductGroups.statusCode}',),);
emit(
GlossaryErrorState(
'Ошибка сервера: ${responseProductGroups.statusCode}',
),
);
}
} catch (e) {
// Ошибка при декодировании JSON или другая ошибка выполнения
@@ -71,7 +80,9 @@ class GlossaryBloc extends Bloc<GlossaryEvent, GlossaryState> {
}
void _changeProductGroup(
ChangeProductGroup event, Emitter<GlossaryState> emit,) async {
ChangeProductGroup event,
Emitter<GlossaryState> emit,
) async {
if (state is GlossaryLoadedState) {
var currentState = state as GlossaryLoadedState;
await _getProductListById(currentState.token, emit, event.id);
@@ -79,9 +90,12 @@ class GlossaryBloc extends Bloc<GlossaryEvent, GlossaryState> {
}
Future<void> _getProductListById(
token, Emitter<GlossaryState> emit, int? productGroupID,) async {
token,
Emitter<GlossaryState> emit,
int? productGroupID,
) async {
final responseProductList = await http.get(
Uri.parse(dotenv.env['SERVER_ROOT']! + '/glossary/index/$productGroupID'),
Uri.parse(baseUrl + '/api/glossary/index/$productGroupID'),
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer $token',
@@ -90,10 +104,13 @@ class GlossaryBloc extends Bloc<GlossaryEvent, GlossaryState> {
final dataProductList = jsonDecode(responseProductList.body);
if (dataProductList is List<dynamic>) {
var currentState = state as GlossaryLoadedState;
emit(currentState.copyWith(
emit(
currentState.copyWith(
glossaryModelObj: currentState.glossaryModelObj,
productGroups: currentState.productGroups,
productList: dataProductList,),);
productList: dataProductList,
),
);
} else {
emit(GlossaryErrorState('Список продуктов некорректного формата'));
}

View File

@@ -22,24 +22,25 @@ class GlossaryState extends Equatable {
}
class GlossaryLoadedState extends GlossaryState {
GlossaryLoadedState(
{this.glossaryModelObj,
this.productGroups,
this.productList,
this.token,});
GlossaryLoadedState({
this.glossaryModelObj,
this.productGroups,
this.productList,
this.token,
});
GlossaryModel? glossaryModelObj;
String? token;
List<SelectionPopupModel>? productGroups;
List<dynamic>? productList;
@override
List<Object?> get props =>
[glossaryModelObj, productGroups, productList, token];
GlossaryLoadedState copyWith(
{GlossaryModel? glossaryModelObj,
String? token,
List<SelectionPopupModel>? productGroups,
List<dynamic>? productList,}) {
List<Object?> get props => [glossaryModelObj, productGroups, productList, token];
GlossaryLoadedState copyWith({
GlossaryModel? glossaryModelObj,
String? token,
List<SelectionPopupModel>? productGroups,
List<dynamic>? productList,
}) {
return GlossaryLoadedState(
glossaryModelObj: glossaryModelObj ?? this.glossaryModelObj,
productGroups: productGroups ?? this.productGroups,
@@ -59,6 +60,5 @@ class GlossaryErrorState extends GlossaryState {
}
class GlossaryInitialState extends GlossaryState {
GlossaryInitialState({required GlossaryModel glossaryModelObj})
: super(glossaryModelObj: glossaryModelObj);
GlossaryInitialState({required GlossaryModel glossaryModelObj}) : super(glossaryModelObj: glossaryModelObj);
}

View File

@@ -1,20 +1,22 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:eatvio_app/core/app_export.dart';
import 'package:eatvio_app/presentation/glossary/glossary_screen/widgets/glossary_scroll_item.dart';
import 'package:eatvio_app/presentation/glossary/glossary_screen/widgets/glossary_search_widget.dart';
import 'package:eatvio_app/widgets/custom_drop_down.dart';
import 'package:eatvio_app/widgets/custom_outlined_button.dart';
import 'package:flutter/material.dart';
import 'bloc/glossary_screen_bloc.dart';
import 'models/glossary_screen_model.dart';
import 'package:eatvio_app/core/app_export.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:eatvio_app/widgets/custom_outlined_button.dart';
import 'package:eatvio_app/widgets/custom_drop_down.dart';
import 'package:eatvio_app/presentation/glossary/glossary_screen/widgets/glossary_search_widget.dart';
import 'package:eatvio_app/presentation/glossary/glossary_screen/widgets/glossary_scroll_item.dart';
class GlossaryScreen extends StatelessWidget {
const GlossaryScreen({Key? key}) : super(key: key);
static Widget builder(BuildContext context) {
return BlocProvider<GlossaryBloc>(
create: (context) => GlossaryBloc(GlossaryState(glossaryModelObj: GlossaryModel()))..add(GlossaryInitialEvent()), child: GlossaryScreen(),);
create: (context) => GlossaryBloc(GlossaryState(glossaryModelObj: GlossaryModel()))..add(GlossaryInitialEvent()),
child: GlossaryScreen(),
);
}
Widget build(BuildContext context) {
@@ -125,24 +127,26 @@ class GlossaryScreen extends StatelessWidget {
),
],
),
child: Column(children: [
Align(
alignment: Alignment.centerLeft,
child: Text(
"Поиск по алфавиту",
style: CustomTextStyles.titleMediumSemiBold,
child: Column(
children: [
Align(
alignment: Alignment.centerLeft,
child: Text(
"Поиск по алфавиту",
style: CustomTextStyles.titleMediumSemiBold,
),
),
),
SizedBox(
height: 8,
),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: List.generate(33, _fillAlphabet),
SizedBox(
height: 8,
),
),
],),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: List.generate(33, _fillAlphabet),
),
),
],
),
);
}
@@ -162,42 +166,44 @@ class GlossaryScreen extends StatelessWidget {
),
],
),
child: Column(children: [
Align(
alignment: Alignment.centerLeft,
child: Text(
"Группа продуктов",
style: CustomTextStyles.titleMediumSemiBold,
child: Column(
children: [
Align(
alignment: Alignment.centerLeft,
child: Text(
"Группа продуктов",
style: CustomTextStyles.titleMediumSemiBold,
),
),
),
SizedBox(
height: 8,
),
CustomDropDown(
items: state.productGroups,
value: state.productGroups?[0],
fillColor: Colors.grey[200]!,
hintText: 'Выберите категорию',
borderDecoration: OutlineInputBorder(
borderRadius: const BorderRadius.all(Radius.circular(8)),
borderSide: BorderSide(color: Colors.grey[200]!, width: 1),
SizedBox(
height: 8,
),
contentPadding: EdgeInsets.symmetric(
horizontal: 10,
vertical: 9,
CustomDropDown(
items: state.productGroups,
value: state.productGroups?[0],
fillColor: Colors.grey[200]!,
hintText: 'Выберите категорию',
borderDecoration: OutlineInputBorder(
borderRadius: const BorderRadius.all(Radius.circular(8)),
borderSide: BorderSide(color: Colors.grey[200]!, width: 1),
),
contentPadding: EdgeInsets.symmetric(
horizontal: 10,
vertical: 9,
),
onChanged: _changeProductGroup(context, state),
width: MediaQuery.of(context).size.width, // Set a percentage value as per your layout
),
onChanged: _changeProductGroup(context, state),
width: MediaQuery.of(context).size.width, // Set a percentage value as per your layout
),
SizedBox(
height: 8,
),
Align(alignment: Alignment.centerLeft, child: Text("*КБЖУ указано за 100гр")),
SizedBox(
height: 8,
),
_buildProductList(context, state),
],),
SizedBox(
height: 8,
),
Align(alignment: Alignment.centerLeft, child: Text("*КБЖУ указано за 100гр")),
SizedBox(
height: 8,
),
_buildProductList(context, state),
],
),
);
}
@@ -218,23 +224,24 @@ class GlossaryScreen extends StatelessWidget {
_buildProductEntry(product, BuildContext context) {
return Container(
margin: EdgeInsets.only(bottom: 10),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: const BorderRadius.all(Radius.circular(12)),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 1,
blurRadius: 3,
offset: Offset(0, 3),
),
],
),
child: Column(children: [
margin: EdgeInsets.only(bottom: 10),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: const BorderRadius.all(Radius.circular(12)),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 1,
blurRadius: 3,
offset: Offset(0, 3),
),
],
),
child: Column(
children: [
ListTile(
trailing: CachedNetworkImage(
imageUrl: dotenv.env['STORAGE_URL']! + (product['image'] ?? ''),
imageUrl: baseUrl + '/storage/' + (product['image'] ?? ''),
imageBuilder: (context, imageProvider) => CircleAvatar(
backgroundImage: imageProvider,
radius: 25,
@@ -255,26 +262,34 @@ class GlossaryScreen extends StatelessWidget {
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
Row(children: [
Column(children: [
Text("Ккал - ${product['energy_on_100gr'].toString()}"),
SizedBox(height: 8),
Text("Белки - ${product['proteins_on_100gr'].toString()}"),
],),
Spacer(),
Padding(
padding: const EdgeInsets.only(right: 24.0),
child: Column(children: [
Text("Жиры - ${product['fats_on_100gr'].toString()}"),
SizedBox(height: 8),
Text("Углеводы - ${product['carbohydrates_on_100gr'].toString()}"),
],),
),
],),
Row(
children: [
Column(
children: [
Text("Ккал - ${product['energy_on_100gr'].toString()}"),
SizedBox(height: 8),
Text("Белки - ${product['proteins_on_100gr'].toString()}"),
],
),
Spacer(),
Padding(
padding: const EdgeInsets.only(right: 24.0),
child: Column(
children: [
Text("Жиры - ${product['fats_on_100gr'].toString()}"),
SizedBox(height: 8),
Text("Углеводы - ${product['carbohydrates_on_100gr'].toString()}"),
],
),
),
],
),
],
),
),
],),);
],
),
);
}
Widget _fillAlphabet(int index) {

View File

@@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
import 'package:eatvio_app/core/app_export.dart';
import 'package:flutter/material.dart';
class ScrollItem extends StatelessWidget {
final String title;

View File

@@ -1,31 +1,31 @@
import 'package:flutter/material.dart';
import 'package:eatvio_app/core/app_export.dart';
import 'package:flutter/material.dart';
class CustomSearchView extends StatelessWidget {
CustomSearchView(
{Key? key,
this.alignment,
this.width,
this.scrollPadding,
this.controller,
this.focusNode,
this.autofocus = false,
this.textStyle,
this.textInputType = TextInputType.text,
this.maxLines,
this.hintText,
this.hintStyle,
this.prefix,
this.prefixConstraints,
this.suffix,
this.suffixConstraints,
this.contentPadding,
this.borderDecoration,
this.fillColor,
this.filled = true,
this.validator,
this.onChanged,})
: super(
CustomSearchView({
Key? key,
this.alignment,
this.width,
this.scrollPadding,
this.controller,
this.focusNode,
this.autofocus = false,
this.textStyle,
this.textInputType = TextInputType.text,
this.maxLines,
this.hintText,
this.hintStyle,
this.prefix,
this.prefixConstraints,
this.suffix,
this.suffixConstraints,
this.contentPadding,
this.borderDecoration,
this.fillColor,
this.filled = true,
this.validator,
this.onChanged,
}) : super(
key: key,
);
@@ -76,15 +76,15 @@ class CustomSearchView extends StatelessWidget {
return alignment != null
? Align(
alignment: alignment ?? Alignment.center,
child: searchViewWidget(context),)
child: searchViewWidget(context),
)
: searchViewWidget(context);
}
searchViewWidget(BuildContext context) => SizedBox(
width: width ?? double.maxFinite,
child: TextFormField(
scrollPadding:
EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
scrollPadding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
controller: controller,
focusNode: focusNode,
onTapOutside: (event) => onTapOutside(),
@@ -112,8 +112,9 @@ class CustomSearchView extends StatelessWidget {
prefixIcon: prefix ??
SizedBox(
child: Padding(
padding: EdgeInsets.only(left: 10),
child: Icon(Icons.search),),
padding: EdgeInsets.only(left: 10),
child: Icon(Icons.search),
),
),
prefixIconConstraints: prefixConstraints ??
BoxConstraints(

View File

@@ -1,15 +1,16 @@
import 'dart:async';
import 'dart:convert';
import 'package:eatvio_app/app_router.dart';
import 'package:eatvio_app/presentation/plans/plan_create_screen/models/plan_create_model.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:go_router/go_router.dart';
import 'package:hive/hive.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import '/core/app_export.dart';
import 'package:eatvio_app/presentation/plans/plan_create_screen/models/plan_create_model.dart';
import 'package:go_router/go_router.dart';
part 'plan_create_event.dart';
part 'plan_create_state.dart';
@@ -31,21 +32,27 @@ class PlanCreateBloc extends Bloc<PlanCreateEvent, PlanCreateState> {
}
if (state is PlanCreateLoadedState) {
var currentState = state as PlanCreateLoadedState;
emit(currentState.copyWith(
planModelObj: currentState.planModelObj,
name: currentState.name,
details: currentState.details,
),);
emit(
currentState.copyWith(
planModelObj: currentState.planModelObj,
name: currentState.name,
details: currentState.details,
),
);
} else {
emit(PlanCreateLoadedState(
name: TextEditingController(),
details: TextEditingController(),
),);
emit(
PlanCreateLoadedState(
name: TextEditingController(),
details: TextEditingController(),
),
);
}
}
FutureOr<void> _createPlan(
CreatePlan event, Emitter<PlanCreateState> emit,) async {
CreatePlan event,
Emitter<PlanCreateState> emit,
) async {
if (state is PlanCreateLoadedState) {
var currentState = state as PlanCreateLoadedState;
@@ -58,7 +65,8 @@ class PlanCreateBloc extends Bloc<PlanCreateEvent, PlanCreateState> {
AppRouter.navigatorKey.currentContext?.goNamed('planSelectScreen');
} else {
print(
'Failed to create blog post. Status Code: ${response.statusCode} ${response.body}',);
'Failed to create blog post. Status Code: ${response.statusCode} ${response.body}',
);
}
}
}
@@ -67,7 +75,7 @@ class PlanCreateBloc extends Bloc<PlanCreateEvent, PlanCreateState> {
String? name,
String? details,
) async {
String url = '${dotenv.env['SERVER_ROOT']!}/plans/create';
String url = '${baseUrl}/api/plans/create';
final Map<String, dynamic> credentials = {
'name': name,

View File

@@ -60,6 +60,5 @@ class PlanCreateErrorState extends PlanCreateState {
}
class PlanCreateInitialState extends PlanCreateState {
PlanCreateInitialState({required PlanCreateModel planModelObj})
: super(planModelObj: planModelObj);
PlanCreateInitialState({required PlanCreateModel planModelObj}) : super(planModelObj: planModelObj);
}

View File

@@ -1,16 +1,18 @@
import 'bloc/plan_create_bloc.dart';
import 'models/plan_create_model.dart';
import 'package:eatvio_app/core/app_export.dart';
import 'package:flutter/material.dart';
import 'bloc/plan_create_bloc.dart';
import 'models/plan_create_model.dart';
class PlanCreateScreen extends StatelessWidget {
const PlanCreateScreen({Key? key}) : super(key: key);
static Widget builder(BuildContext context) {
final planID = ModalRoute.of(context)!.settings.arguments;
return BlocProvider<PlanCreateBloc>(
create: (context) => PlanCreateBloc(PlanCreateState(planModelObj: PlanCreateModel(planID: planID.toString())))..add(PlanCreateInitialEvent()),
child: PlanCreateScreen(),);
create: (context) => PlanCreateBloc(PlanCreateState(planModelObj: PlanCreateModel(planID: planID.toString())))..add(PlanCreateInitialEvent()),
child: PlanCreateScreen(),
);
}
Widget build(BuildContext context) {
@@ -65,24 +67,27 @@ class PlanCreateScreen extends StatelessWidget {
),
],
),
child: Column(mainAxisSize: MainAxisSize.min, children: [
SizedBox(height: 16.0),
TextFormField(
controller: state.name,
decoration: InputDecoration(labelText: 'Заголовок'),
),
SizedBox(height: 16.0),
TextFormField(
controller: state.details,
decoration: InputDecoration(labelText: 'Описание'),
),
SizedBox(height: 16.0),
// Здесь нужно добавить виджет для загрузки картинки
ElevatedButton(
onPressed: () => context.read<PlanCreateBloc>().add(CreatePlan()),
child: Text('Создать'),
),
],),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: 16.0),
TextFormField(
controller: state.name,
decoration: InputDecoration(labelText: 'Заголовок'),
),
SizedBox(height: 16.0),
TextFormField(
controller: state.details,
decoration: InputDecoration(labelText: 'Описание'),
),
SizedBox(height: 16.0),
// Здесь нужно добавить виджет для загрузки картинки
ElevatedButton(
onPressed: () => context.read<PlanCreateBloc>().add(CreatePlan()),
child: Text('Создать'),
),
],
),
),
],
),

View File

@@ -1,18 +1,17 @@
import 'dart:async';
import 'dart:convert';
import 'package:eatvio_app/app_router.dart';
import 'package:eatvio_app/presentation/plans/plan_edit_screen/models/plan_edit_model.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:go_router/go_router.dart';
import 'package:hive/hive.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import '/core/app_export.dart';
import 'package:eatvio_app/presentation/plans/plan_edit_screen/models/plan_edit_model.dart';
import 'package:go_router/go_router.dart';
part 'plan_edit_event.dart';
part 'plan_edit_state.dart';
/// A bloc that manages the state of a PlanEdit according to the event that is dispatched to it.
@@ -34,7 +33,7 @@ class PlanEditBloc extends Bloc<PlanEditEvent, PlanEditState> {
}
final String? planID = state.planModelObj?.planID;
final response = await http.get(
Uri.parse(dotenv.env['SERVER_ROOT']! + '/plans/show/$planID'),
Uri.parse(baseUrl + '/api/plans/show/$planID'),
headers: {
'Authorization': 'Bearer $token',
},
@@ -44,18 +43,24 @@ class PlanEditBloc extends Bloc<PlanEditEvent, PlanEditState> {
if (data is Map<String, dynamic>) {
if (state is PlanEditLoadedState) {
var currentState = state as PlanEditLoadedState;
emit(currentState.copyWith(
emit(
currentState.copyWith(
planModelObj: currentState.planModelObj,
plan: currentState.plan,
name: currentState.name,
duration: currentState.duration,
details: currentState.details,),);
details: currentState.details,
),
);
} else {
emit(PlanEditLoadedState(
emit(
PlanEditLoadedState(
plan: data,
name: TextEditingController(text: data['name']),
details: TextEditingController(text: data['details']),
duration: TextEditingController(text: data['duration'].toString()),),);
duration: TextEditingController(text: data['duration'].toString()),
),
);
}
} else {
// Один или несколько ключевых полей отсутствуют, обрабатываем ошибку
@@ -96,7 +101,7 @@ class PlanEditBloc extends Bloc<PlanEditEvent, PlanEditState> {
String? duration,
) async {
int id = plan?['id'];
String url = '${dotenv.env['SERVER_ROOT']!}/plans/update/$id';
String url = '${baseUrl}/api/plans/update/$id';
final Map<String, dynamic> credentials = {
'id': id,

View File

@@ -22,8 +22,13 @@ class PlanEditState extends Equatable {
}
class PlanEditLoadedState extends PlanEditState {
PlanEditLoadedState(
{this.planModelObj, this.plan, this.name, this.details, this.duration,});
PlanEditLoadedState({
this.planModelObj,
this.plan,
this.name,
this.details,
this.duration,
});
PlanEditModel? planModelObj;
Map<String, dynamic>? plan;
@@ -59,6 +64,5 @@ class PlanEditErrorState extends PlanEditState {
}
class PlanEditInitialState extends PlanEditState {
PlanEditInitialState({required PlanEditModel planModelObj})
: super(planModelObj: planModelObj);
PlanEditInitialState({required PlanEditModel planModelObj}) : super(planModelObj: planModelObj);
}

View File

@@ -1,9 +1,9 @@
import 'bloc/plan_edit_bloc.dart';
import 'models/plan_edit_model.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:eatvio_app/core/app_export.dart';
import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'bloc/plan_edit_bloc.dart';
import 'models/plan_edit_model.dart';
class PlanEditScreen extends StatelessWidget {
const PlanEditScreen({Key? key}) : super(key: key);
@@ -11,8 +11,9 @@ class PlanEditScreen extends StatelessWidget {
static Widget builder(BuildContext context) {
final planID = ModalRoute.of(context)!.settings.arguments;
return BlocProvider<PlanEditBloc>(
create: (context) => PlanEditBloc(PlanEditState(planModelObj: PlanEditModel(planID: planID.toString())))..add(PlanEditInitialEvent()),
child: PlanEditScreen(),);
create: (context) => PlanEditBloc(PlanEditState(planModelObj: PlanEditModel(planID: planID.toString())))..add(PlanEditInitialEvent()),
child: PlanEditScreen(),
);
}
Widget build(BuildContext context) {
@@ -71,7 +72,7 @@ class PlanEditScreen extends StatelessWidget {
mainAxisSize: MainAxisSize.min,
children: [
CachedNetworkImage(
imageUrl: dotenv.env['STORAGE_URL']! + (state.plan?['image'] ?? ''),
imageUrl: baseUrl + '/storage/' + (state.plan?['image'] ?? ''),
imageBuilder: (context, imageProvider) => CircleAvatar(
backgroundImage: imageProvider,
radius: 25,

View File

@@ -1,17 +1,17 @@
//import 'package:eatvio_app/presentation/auth/auth_screen/auth_screen.dart';
import 'dart:async';
import 'dart:convert';
import 'package:eatvio_app/app_router.dart';
import 'package:eatvio_app/presentation/plans/plan_select_screen/models/plan_select_model.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import '/core/app_export.dart';
import 'package:eatvio_app/presentation/plans/plan_select_screen/models/plan_select_model.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:hive/hive.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:image_picker/image_picker.dart';
import 'package:go_router/go_router.dart';
import 'package:hive/hive.dart';
import 'package:http/http.dart' as http;
import 'package:image_picker/image_picker.dart';
import '/core/app_export.dart';
part 'plan_select_event.dart';
part 'plan_select_state.dart';
@@ -33,7 +33,7 @@ class PlanSelectBloc extends Bloc<PlanSelectEvent, PlanSelectState> {
AppRouter.navigatorKey.currentContext?.goNamed('authScreen');
}
final response = await http.get(
Uri.parse(dotenv.env['SERVER_ROOT']! + '/plans/index'),
Uri.parse(baseUrl + '/api/plans/index'),
);
print('Got response');
if (response.statusCode == 200) {
@@ -44,15 +44,19 @@ class PlanSelectBloc extends Bloc<PlanSelectEvent, PlanSelectState> {
if (state is PlanSelectLoadedState) {
print('Got loaded');
var currentState = state as PlanSelectLoadedState;
emit((currentState).copyWith(
posts: data,
myProfileModelObj: currentState.myProfileModelObj,
),);
emit(
(currentState).copyWith(
posts: data,
myProfileModelObj: currentState.myProfileModelObj,
),
);
} else {
print('Got unloaded');
emit(PlanSelectLoadedState(
posts: data,
),);
emit(
PlanSelectLoadedState(
posts: data,
),
);
}
} else {
// Один или несколько ключевых полей отсутствуют, обрабатываем ошибку

View File

@@ -12,14 +12,12 @@ class PlanSelectState extends Equatable {
}
class PlanSelectInitialState extends PlanSelectState {
PlanSelectInitialState({required PlanSelectModel myProfileModelObj})
: super(myProfileModelObj: myProfileModelObj);
PlanSelectInitialState({required PlanSelectModel myProfileModelObj}) : super(myProfileModelObj: myProfileModelObj);
}
/// Represents the state of PlanSelect in the application.
class PlanSelectLoadedState extends PlanSelectState {
PlanSelectLoadedState({this.myProfileModelObj, this.posts})
: super(myProfileModelObj: null);
PlanSelectLoadedState({this.myProfileModelObj, this.posts}) : super(myProfileModelObj: null);
PlanSelectModel? myProfileModelObj;
List<dynamic>? posts;

View File

@@ -1,19 +1,20 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:eatvio_app/core/app_export.dart';
import 'package:eatvio_app/widgets/app_bar/appbar_leading_image.dart';
import 'package:eatvio_app/widgets/app_bar/appbar_subtitle.dart';
import 'package:eatvio_app/widgets/app_bar/custom_app_bar.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'bloc/plan_select_bloc.dart';
import 'models/plan_select_model.dart';
import 'package:eatvio_app/core/app_export.dart';
import 'package:eatvio_app/widgets/app_bar/appbar_subtitle.dart';
import 'package:eatvio_app/widgets/app_bar/appbar_leading_image.dart';
import 'package:eatvio_app/widgets/app_bar/custom_app_bar.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter/material.dart';
class PlanSelectScreen extends StatelessWidget {
static Widget builder(BuildContext context) {
return BlocProvider<PlanSelectBloc>(
create: (context) => PlanSelectBloc(PlanSelectState(myProfileModelObj: PlanSelectModel()))..add(PlanSelectInitialEvent()), child: PlanSelectScreen(),);
create: (context) => PlanSelectBloc(PlanSelectState(myProfileModelObj: PlanSelectModel()))..add(PlanSelectInitialEvent()),
child: PlanSelectScreen(),
);
}
Widget build(BuildContext context) {
@@ -133,7 +134,7 @@ class PlanSelectScreen extends StatelessWidget {
),
child: ListTile(
leading: CachedNetworkImage(
imageUrl: dotenv.env['STORAGE_URL']! + post['image'],
imageUrl: baseUrl + '/storage/' + post['image'],
imageBuilder: (context, imageProvider) => CircleAvatar(
backgroundImage: imageProvider,
radius: 25,

View File

@@ -1,18 +1,17 @@
import 'dart:async';
import 'dart:convert';
import 'package:eatvio_app/app_router.dart';
import 'package:eatvio_app/presentation/plans/plan_show_screen/models/plan_show_model.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:go_router/go_router.dart';
import 'package:hive/hive.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import '/core/app_export.dart';
import 'package:eatvio_app/presentation/plans/plan_show_screen/models/plan_show_model.dart';
import 'package:go_router/go_router.dart';
part 'plan_show_event.dart';
part 'plan_show_state.dart';
/// A bloc that manages the state of a Plan according to the event that is dispatched to it.
@@ -34,7 +33,7 @@ class PlanBloc extends Bloc<PlanEvent, PlanState> {
}
final String? planID = state.planModelObj?.planID;
final response = await http.get(
Uri.parse(dotenv.env['SERVER_ROOT']! + '/plans/show/$planID'),
Uri.parse(baseUrl + '/api/plans/show/$planID'),
headers: {
'Authorization': 'Bearer $token',
},
@@ -70,7 +69,7 @@ class PlanBloc extends Bloc<PlanEvent, PlanState> {
}
final int id = event.id;
final response = await http.post(
Uri.parse(dotenv.env['SERVER_ROOT']! + '/plans/delete/$id'),
Uri.parse(baseUrl + '/api/plans/delete/$id'),
headers: {
'Authorization': 'Bearer $token',
},

View File

@@ -28,8 +28,7 @@ class PlanLoadedState extends PlanState {
Map<String, dynamic>? plan;
@override
List<Object?> get props => [planModelObj, plan];
PlanLoadedState copyWith(
{PlanModel? planModelObj, Map<String, dynamic>? plan}) {
PlanLoadedState copyWith({PlanModel? planModelObj, Map<String, dynamic>? plan}) {
return PlanLoadedState(
planModelObj: planModelObj ?? this.planModelObj,
plan: plan ?? this.plan,
@@ -47,6 +46,5 @@ class PlanErrorState extends PlanState {
}
class PlanInitialState extends PlanState {
PlanInitialState({required PlanModel planModelObj})
: super(planModelObj: planModelObj);
PlanInitialState({required PlanModel planModelObj}) : super(planModelObj: planModelObj);
}

View File

@@ -1,12 +1,10 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:eatvio_app/core/app_export.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'bloc/plan_show_bloc.dart';
import 'models/plan_show_model.dart';
import 'package:eatvio_app/core/app_export.dart';
import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
class PlanScreen extends StatelessWidget {
const PlanScreen({Key? key}) : super(key: key);
@@ -14,7 +12,9 @@ class PlanScreen extends StatelessWidget {
static Widget builder(BuildContext context) {
final planID = ModalRoute.of(context)!.settings.arguments;
return BlocProvider<PlanBloc>(
create: (context) => PlanBloc(PlanState(planModelObj: PlanModel(planID: planID.toString())))..add(PlanInitialEvent()), child: PlanScreen(),);
create: (context) => PlanBloc(PlanState(planModelObj: PlanModel(planID: planID.toString())))..add(PlanInitialEvent()),
child: PlanScreen(),
);
}
Widget build(BuildContext context) {
@@ -69,43 +69,46 @@ class PlanScreen extends StatelessWidget {
),
],
),
child: Column(mainAxisSize: MainAxisSize.min, children: [
CachedNetworkImage(
imageUrl: dotenv.env['STORAGE_URL']! + (state.plan?['image'] ?? ''),
imageBuilder: (context, imageProvider) => CircleAvatar(
backgroundImage: imageProvider,
radius: 25,
),
placeholder: (context, url) => CircularProgressIndicator(),
errorWidget: (context, url, error) => CircleAvatar(
child: Icon(Icons.person),
backgroundColor: Colors.white,
radius: 25,
),
),
SizedBox(height: 16.0),
Text(state.plan?['name'], style: CustomTextStyles.titleMediumBlack),
SizedBox(height: 8.0),
Text(
state.plan?['details'],
),
SizedBox(height: 16.0),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
ElevatedButton(
onPressed: () {
// Действие для редактирования
},
child: Text('Редактировать'),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
CachedNetworkImage(
imageUrl: baseUrl + '/storage/' + (state.plan?['image'] ?? ''),
imageBuilder: (context, imageProvider) => CircleAvatar(
backgroundImage: imageProvider,
radius: 25,
),
ElevatedButton(
onPressed: () => context.read<PlanBloc>().add(DeleteEntry(state.plan?['id'])),
child: Text('Удалить'),
placeholder: (context, url) => CircularProgressIndicator(),
errorWidget: (context, url, error) => CircleAvatar(
child: Icon(Icons.person),
backgroundColor: Colors.white,
radius: 25,
),
],
),
],),
),
SizedBox(height: 16.0),
Text(state.plan?['name'], style: CustomTextStyles.titleMediumBlack),
SizedBox(height: 8.0),
Text(
state.plan?['details'],
),
SizedBox(height: 16.0),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
ElevatedButton(
onPressed: () {
// Действие для редактирования
},
child: Text('Редактировать'),
),
ElevatedButton(
onPressed: () => context.read<PlanBloc>().add(DeleteEntry(state.plan?['id'])),
child: Text('Удалить'),
),
],
),
],
),
),
],
),

View File

@@ -1,17 +1,17 @@
//import 'package:eatvio_app/presentation/auth/auth_screen/auth_screen.dart';
import 'dart:async';
import 'dart:convert';
import 'package:eatvio_app/app_router.dart';
import 'package:eatvio_app/presentation/profile/my_profile_screen/models/my_profile_model.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import '/core/app_export.dart';
import 'package:eatvio_app/presentation/profile/my_profile_screen/models/my_profile_model.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:hive/hive.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:image_picker/image_picker.dart';
import 'package:go_router/go_router.dart';
import 'package:hive/hive.dart';
import 'package:http/http.dart' as http;
import 'package:image_picker/image_picker.dart';
import '/core/app_export.dart';
part 'my_profile_event.dart';
part 'my_profile_state.dart';
@@ -36,7 +36,7 @@ class MyProfileBloc extends Bloc<MyProfileEvent, MyProfileState> {
//final token =
// "9|laravel_sanctum_KFXbccDcWEvTPfJmV99EQv1ne AggmpR2TidVGDPN34fa201a";
final response = await http.get(
Uri.parse(dotenv.env['SERVER_ROOT']! + '/profile/index'),
Uri.parse(baseUrl + '/api/profile/index'),
headers: {
'Authorization': 'Bearer $token',
},
@@ -278,7 +278,7 @@ class MyProfileBloc extends Bloc<MyProfileEvent, MyProfileState> {
}
Future<String> uploadImageToServer(String imagePath, String? token) async {
String url = '${dotenv.env['SERVER_ROOT']!}/profile/photo/upload';
String url = '${baseUrl}/api/profile/photo/upload';
print(url);
var request = http.MultipartRequest('POST', Uri.parse(url));
request.headers['Authorization'] = 'Bearer $token';

View File

@@ -12,49 +12,48 @@ class MyProfileState extends Equatable {
}
class MyProfileInitialState extends MyProfileState {
MyProfileInitialState({required MyProfileModel myProfileModelObj})
: super(myProfileModelObj: myProfileModelObj);
MyProfileInitialState({required MyProfileModel myProfileModelObj}) : super(myProfileModelObj: myProfileModelObj);
}
/// Represents the state of MyProfile in the application.
class MyProfileLoadedState extends MyProfileState {
MyProfileLoadedState(
{this.chestCircumferenceController,
this.waistCircumferenceController,
this.hipCircumferenceController,
this.myProfileModelObj,
this.weightController,
this.user,
this.userDetails,
this.chartPoints,
this.chartDates,
this.plans,
this.token,
this.selectedCountry,
this.selectedCity,
this.selectedActivity,
this.chestCircumferenceFormController,
this.waistCircumferenceFormController,
this.hipCircumferenceFormController,
this.firstNameFormController,
this.lastNameFormController,
this.countryFormController,
this.dateFormController,
this.cityFormController,
this.heightFormController,
this.weightFormController,
this.upPressureFormController,
this.downPressureFormController,
this.allergiesFormController,
this.pillsFormController,
this.sportsFormController,
this.favoriteFormController,
this.unfavoriteFormController,
this.additionalFormController,
this.hypoFormController,
this.hyperFormController,
this.circumferences,})
: super(myProfileModelObj: null);
MyProfileLoadedState({
this.chestCircumferenceController,
this.waistCircumferenceController,
this.hipCircumferenceController,
this.myProfileModelObj,
this.weightController,
this.user,
this.userDetails,
this.chartPoints,
this.chartDates,
this.plans,
this.token,
this.selectedCountry,
this.selectedCity,
this.selectedActivity,
this.chestCircumferenceFormController,
this.waistCircumferenceFormController,
this.hipCircumferenceFormController,
this.firstNameFormController,
this.lastNameFormController,
this.countryFormController,
this.dateFormController,
this.cityFormController,
this.heightFormController,
this.weightFormController,
this.upPressureFormController,
this.downPressureFormController,
this.allergiesFormController,
this.pillsFormController,
this.sportsFormController,
this.favoriteFormController,
this.unfavoriteFormController,
this.additionalFormController,
this.hypoFormController,
this.hyperFormController,
this.circumferences,
}) : super(myProfileModelObj: null);
MyProfileModel? myProfileModelObj;
Map<String, dynamic>? user;
@@ -174,42 +173,27 @@ class MyProfileLoadedState extends MyProfileState {
chartPoints: chartPoints ?? this.chartPoints,
chartDates: chartDates ?? this.chartDates,
weightController: weightController ?? this.weightController,
chestCircumferenceController:
chestCircumferenceController ?? this.chestCircumferenceController,
waistCircumferenceController:
waistCircumferenceController ?? this.waistCircumferenceController,
hipCircumferenceController:
hipCircumferenceController ?? this.hipCircumferenceController,
chestCircumferenceFormController: chestCircumferenceFormController ??
this.chestCircumferenceFormController,
waistCircumferenceFormController: waistCircumferenceFormController ??
this.waistCircumferenceFormController,
hipCircumferenceFormController:
hipCircumferenceFormController ?? this.hipCircumferenceFormController,
firstNameFormController:
firstNameFormController ?? this.firstNameFormController,
lastNameFormController:
lastNameFormController ?? this.lastNameFormController,
countryFormController:
countryFormController ?? this.countryFormController,
chestCircumferenceController: chestCircumferenceController ?? this.chestCircumferenceController,
waistCircumferenceController: waistCircumferenceController ?? this.waistCircumferenceController,
hipCircumferenceController: hipCircumferenceController ?? this.hipCircumferenceController,
chestCircumferenceFormController: chestCircumferenceFormController ?? this.chestCircumferenceFormController,
waistCircumferenceFormController: waistCircumferenceFormController ?? this.waistCircumferenceFormController,
hipCircumferenceFormController: hipCircumferenceFormController ?? this.hipCircumferenceFormController,
firstNameFormController: firstNameFormController ?? this.firstNameFormController,
lastNameFormController: lastNameFormController ?? this.lastNameFormController,
countryFormController: countryFormController ?? this.countryFormController,
dateFormController: dateFormController ?? this.dateFormController,
cityFormController: cityFormController ?? this.cityFormController,
heightFormController: heightFormController ?? this.heightFormController,
weightFormController: weightFormController ?? this.weightFormController,
upPressureFormController:
upPressureFormController ?? this.upPressureFormController,
downPressureFormController:
downPressureFormController ?? this.downPressureFormController,
allergiesFormController:
allergiesFormController ?? this.allergiesFormController,
upPressureFormController: upPressureFormController ?? this.upPressureFormController,
downPressureFormController: downPressureFormController ?? this.downPressureFormController,
allergiesFormController: allergiesFormController ?? this.allergiesFormController,
pillsFormController: pillsFormController ?? this.pillsFormController,
sportsFormController: sportsFormController ?? this.sportsFormController,
favoriteFormController:
favoriteFormController ?? this.favoriteFormController,
unfavoriteFormController:
unfavoriteFormController ?? this.unfavoriteFormController,
additionalFormController:
additionalFormController ?? this.additionalFormController,
favoriteFormController: favoriteFormController ?? this.favoriteFormController,
unfavoriteFormController: unfavoriteFormController ?? this.unfavoriteFormController,
additionalFormController: additionalFormController ?? this.additionalFormController,
token: token ?? this.token,
selectedCountry: selectedCountry ?? this.selectedCountry,
selectedCity: selectedCity ?? this.selectedCity,

View File

@@ -1,21 +1,21 @@
import 'package:go_router/go_router.dart';
import 'dart:convert';
import 'dart:math';
import 'bloc/my_profile_bloc.dart';
import 'models/my_profile_model.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:carousel_slider/carousel_slider.dart';
import 'package:eatvio_app/core/app_export.dart';
import 'package:eatvio_app/widgets/app_bar/appbar_subtitle.dart';
import 'package:eatvio_app/widgets/custom_text_form_field.dart';
import 'package:carousel_slider/carousel_slider.dart';
import 'package:image_picker/image_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter/services.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:http/http.dart' as http;
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:go_router/go_router.dart';
import 'package:http/http.dart' as http;
import 'package:image_picker/image_picker.dart';
import 'package:intl/intl.dart';
import 'dart:math';
import 'dart:convert';
import 'bloc/my_profile_bloc.dart';
import 'models/my_profile_model.dart';
class MyProfileScreen extends StatelessWidget {
static Widget builder(BuildContext context) {
@@ -156,7 +156,7 @@ class MyProfileScreen extends StatelessWidget {
GestureDetector(
onTap: () => addImage(context),
child: CachedNetworkImage(
imageUrl: dotenv.env['STORAGE_URL']! + (state.user?['profile_photo_path'] ?? ''),
imageUrl: baseUrl + '/storage/' + (state.user?['profile_photo_path'] ?? ''),
imageBuilder: (context, imageProvider) => CircleAvatar(
backgroundImage: imageProvider,
radius: 60,
@@ -720,7 +720,7 @@ class MyProfileScreen extends StatelessWidget {
child: Container(
margin: EdgeInsets.only(bottom: 12.h),
child: Image.network(
dotenv.env['STORAGE_URL']! + imagePaths?[i],
baseUrl + '/storage/' + imagePaths?[i],
height: 25.adaptSize,
width: 25.adaptSize,
),
@@ -849,7 +849,7 @@ class MyProfileScreen extends StatelessWidget {
try {
final token = state.token;
final response = await http.post(
Uri.parse(dotenv.env['SERVER_ROOT']! + '/profile/addCircumference'),
Uri.parse(baseUrl + '/api/profile/addCircumference'),
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer $token',
@@ -941,7 +941,7 @@ class MyProfileScreen extends StatelessWidget {
try {
final token = state.token;
final response = await http.post(
Uri.parse(dotenv.env['SERVER_ROOT']! + '/profile/addWeight'),
Uri.parse(baseUrl + '/api/profile/addWeight'),
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer $token',

View File

@@ -1,22 +1,24 @@
import 'package:eatvio_app/core/app_export.dart';
import 'package:eatvio_app/widgets/app_bar/appbar_leading_image.dart';
import 'package:eatvio_app/widgets/app_bar/appbar_subtitle.dart';
import 'package:eatvio_app/widgets/app_bar/custom_app_bar.dart';
import 'package:eatvio_app/widgets/custom_about_list.dart';
import 'package:eatvio_app/widgets/custom_expandable_list.dart';
import 'package:eatvio_app/widgets/custom_language_drop_down.dart';
import 'package:eatvio_app/widgets/custom_notification_screen.dart';
import 'package:eatvio_app/widgets/custom_section_title.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'bloc/my_profile_bloc.dart';
import 'models/my_profile_model.dart';
import 'package:eatvio_app/core/app_export.dart';
import 'package:eatvio_app/widgets/app_bar/appbar_subtitle.dart';
import 'package:eatvio_app/widgets/app_bar/appbar_leading_image.dart';
import 'package:eatvio_app/widgets/app_bar/custom_app_bar.dart';
import 'package:eatvio_app/widgets/custom_about_list.dart';
import 'package:eatvio_app/widgets/custom_expandable_list.dart';
import 'package:eatvio_app/widgets/custom_notification_screen.dart';
import 'package:eatvio_app/widgets/custom_section_title.dart';
import 'package:eatvio_app/widgets/custom_language_drop_down.dart';
import 'package:flutter/material.dart';
class MySettingsScreen extends StatelessWidget {
static Widget builder(BuildContext context) {
return BlocProvider<MyProfileBloc>(
create: (context) => MyProfileBloc(MyProfileState(myProfileModelObj: MyProfileModel()))..add(MyProfileInitialEvent()), child: MySettingsScreen(),);
create: (context) => MyProfileBloc(MyProfileState(myProfileModelObj: MyProfileModel()))..add(MyProfileInitialEvent()),
child: MySettingsScreen(),
);
}
Widget build(BuildContext context) {
@@ -45,7 +47,6 @@ class MySettingsScreen extends StatelessWidget {
}
_buildSetingsScreen(BuildContext context, MyProfileLoadedState state) {
return Scaffold(
appBar: AppBar(
toolbarHeight: 0,
@@ -64,30 +65,32 @@ class MySettingsScreen extends StatelessWidget {
children: [
SizedBox(height: 30),
Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: const BorderRadius.all(Radius.circular(12)),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 2,
blurRadius: 5,
offset: Offset(0, 3),
),
],
),
child: CustomAppBar(
height: 54.v,
leadingWidth: 28.h,
leading: AppbarLeadingImage(
imagePath: ImageConstant.imgArrowLeft,
margin: EdgeInsets.only(left: 16.h, top: 15.v, bottom: 15.v),
onTap: () => GoRouter.of(context).pop(),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: const BorderRadius.all(Radius.circular(12)),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 2,
blurRadius: 5,
offset: Offset(0, 3),
),
title: Padding(
padding: EdgeInsets.only(left: 20.h),
child: Row(children: [AppbarSubtitle(text: state.user?['name'], margin: EdgeInsets.only(left: 80.h, top: 12.v, bottom: 9.v))]),),
),),
],
),
child: CustomAppBar(
height: 54.v,
leadingWidth: 28.h,
leading: AppbarLeadingImage(
imagePath: ImageConstant.imgArrowLeft,
margin: EdgeInsets.only(left: 16.h, top: 15.v, bottom: 15.v),
onTap: () => GoRouter.of(context).pop(),
),
title: Padding(
padding: EdgeInsets.only(left: 20.h),
child: Row(children: [AppbarSubtitle(text: state.user?['name'], margin: EdgeInsets.only(left: 80.h, top: 12.v, bottom: 9.v))]),
),
),
),
SizedBox(height: 20),
Container(
padding: EdgeInsets.symmetric(

View File

@@ -1,15 +1,16 @@
import 'dart:async';
import 'dart:convert';
import 'package:eatvio_app/app_router.dart';
import 'package:eatvio_app/presentation/recipe/recipe_create_screen/models/recipe_create_model.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:go_router/go_router.dart';
import 'package:hive/hive.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import '/core/app_export.dart';
import 'package:eatvio_app/presentation/recipe/recipe_create_screen/models/recipe_create_model.dart';
import 'package:go_router/go_router.dart';
part 'recipe_create_event.dart';
part 'recipe_create_state.dart';
@@ -34,7 +35,8 @@ class RecipeCreateBloc extends Bloc<RecipeCreateEvent, RecipeCreateState> {
}
if (state is RecipeCreateLoadedState) {
var currentState = state as RecipeCreateLoadedState;
emit(currentState.copyWith(
emit(
currentState.copyWith(
recipeModelObj: currentState.recipeModelObj,
title: currentState.title,
token: currentState.token,
@@ -42,62 +44,79 @@ class RecipeCreateBloc extends Bloc<RecipeCreateEvent, RecipeCreateState> {
video_url: currentState.video_url,
steps: currentState.steps,
stepDescriptions: currentState.stepDescriptions,
tags: currentState.tags,),);
tags: currentState.tags,
),
);
} else {
final Map<String, dynamic> step = {
'description': '',
'image': '',
};
emit(RecipeCreateLoadedState(
emit(
RecipeCreateLoadedState(
token: token,
title: TextEditingController(),
description: TextEditingController(),
video_url: TextEditingController(),
tags: TextEditingController(),
stepDescriptions: [TextEditingController()],
steps: [step],),);
steps: [step],
),
);
}
}
FutureOr<void> _addStep(
AddStep event, Emitter<RecipeCreateState> emit,) async {
AddStep event,
Emitter<RecipeCreateState> emit,
) async {
if (state is RecipeCreateLoadedState) {
var currentState = state as RecipeCreateLoadedState;
List<dynamic>? updatedSteps = currentState.steps;
List<TextEditingController>? updatedStepDescriptions =
currentState.stepDescriptions;
List<TextEditingController>? updatedStepDescriptions = currentState.stepDescriptions;
updatedSteps!.add({
'description': '',
'image': '',
});
updatedStepDescriptions!.add(TextEditingController());
emit(currentState.copyWith(
steps: updatedSteps, stepDescriptions: updatedStepDescriptions,),);
emit(
currentState.copyWith(
steps: updatedSteps,
stepDescriptions: updatedStepDescriptions,
),
);
await _reloadScreen(event, emit);
}
}
FutureOr<void> _removeStep(
RemoveStep event, Emitter<RecipeCreateState> emit,) async {
RemoveStep event,
Emitter<RecipeCreateState> emit,
) async {
if (state is RecipeCreateLoadedState) {
var currentState = state as RecipeCreateLoadedState;
List<dynamic>? updatedSteps = currentState.steps;
List<TextEditingController>? updatedStepDescriptions =
currentState.stepDescriptions;
List<TextEditingController>? updatedStepDescriptions = currentState.stepDescriptions;
updatedSteps!.removeAt(event.id);
updatedStepDescriptions!.removeAt(event.id);
emit(currentState.copyWith(
steps: updatedSteps, stepDescriptions: updatedStepDescriptions,),);
emit(
currentState.copyWith(
steps: updatedSteps,
stepDescriptions: updatedStepDescriptions,
),
);
await _reloadScreen(event, emit);
}
}
FutureOr<void> _createRecipe(
CreateRecipe event, Emitter<RecipeCreateState> emit,) async {
CreateRecipe event,
Emitter<RecipeCreateState> emit,
) async {
if (state is RecipeCreateLoadedState) {
var currentState = state as RecipeCreateLoadedState;
@@ -112,14 +131,19 @@ class RecipeCreateBloc extends Bloc<RecipeCreateEvent, RecipeCreateState> {
AppRouter.navigatorKey.currentContext?.goNamed('recipeSelectScreen');
} else {
print(
'Failed to create blog post. Status Code: ${response.statusCode} ${response.body}',);
'Failed to create blog post. Status Code: ${response.statusCode} ${response.body}',
);
}
}
}
FutureOr<http.Response> sendRecipe(String? title, String? video_url,
String? description, List<dynamic>? steps,) async {
String url = '${dotenv.env['SERVER_ROOT']!}/recipe/create';
FutureOr<http.Response> sendRecipe(
String? title,
String? video_url,
String? description,
List<dynamic>? steps,
) async {
String url = '${baseUrl}/api/recipe/create';
final Map<String, dynamic> credentials = {
'title': title,
'description': description,
@@ -141,7 +165,9 @@ class RecipeCreateBloc extends Bloc<RecipeCreateEvent, RecipeCreateState> {
}
FutureOr<void> _reloadScreen(
RecipeCreateEvent? event, Emitter<RecipeCreateState> emit,) async {
RecipeCreateEvent? event,
Emitter<RecipeCreateState> emit,
) async {
print('Reloading..');
final box = await Hive.openBox('token');
final token = box.get('token');
@@ -150,7 +176,8 @@ class RecipeCreateBloc extends Bloc<RecipeCreateEvent, RecipeCreateState> {
}
if (state is RecipeCreateLoadedState) {
var currentState = state as RecipeCreateLoadedState;
emit(currentState.copyWith(
emit(
currentState.copyWith(
recipeModelObj: currentState.recipeModelObj,
title: currentState.title,
token: currentState.token,
@@ -158,7 +185,9 @@ class RecipeCreateBloc extends Bloc<RecipeCreateEvent, RecipeCreateState> {
video_url: currentState.video_url,
steps: currentState.steps,
stepDescriptions: currentState.stepDescriptions,
tags: currentState.tags,),);
tags: currentState.tags,
),
);
}
}
}

View File

@@ -22,15 +22,16 @@ class RecipeCreateState extends Equatable {
}
class RecipeCreateLoadedState extends RecipeCreateState {
RecipeCreateLoadedState(
{this.recipeModelObj,
this.token,
this.title,
this.description,
this.video_url,
this.tags,
this.stepDescriptions,
this.steps,});
RecipeCreateLoadedState({
this.recipeModelObj,
this.token,
this.title,
this.description,
this.video_url,
this.tags,
this.stepDescriptions,
this.steps,
});
RecipeCreateModel? recipeModelObj;
String? token;
@@ -62,14 +63,15 @@ class RecipeCreateLoadedState extends RecipeCreateState {
List<TextEditingController>? stepDescriptions,
}) {
return RecipeCreateLoadedState(
recipeModelObj: recipeModelObj ?? this.recipeModelObj,
title: title ?? this.title,
description: description ?? this.description,
video_url: video_url ?? this.video_url,
token: token ?? this.token,
steps: steps ?? this.steps,
tags: tags ?? this.tags,
stepDescriptions: stepDescriptions ?? this.stepDescriptions,);
recipeModelObj: recipeModelObj ?? this.recipeModelObj,
title: title ?? this.title,
description: description ?? this.description,
video_url: video_url ?? this.video_url,
token: token ?? this.token,
steps: steps ?? this.steps,
tags: tags ?? this.tags,
stepDescriptions: stepDescriptions ?? this.stepDescriptions,
);
}
}
@@ -83,6 +85,5 @@ class RecipeCreateErrorState extends RecipeCreateState {
}
class RecipeCreateInitialState extends RecipeCreateState {
RecipeCreateInitialState({required RecipeCreateModel recipeModelObj})
: super(recipeModelObj: recipeModelObj);
RecipeCreateInitialState({required RecipeCreateModel recipeModelObj}) : super(recipeModelObj: recipeModelObj);
}

View File

@@ -1,14 +1,14 @@
import 'package:eatvio_app/core/app_export.dart';
import 'package:eatvio_app/widgets/app_bar/appbar_leading_image.dart';
import 'package:eatvio_app/widgets/app_bar/appbar_subtitle.dart';
import 'package:eatvio_app/widgets/app_bar/custom_app_bar.dart';
import 'package:eatvio_app/widgets/custom_outlined_button.dart';
import 'package:eatvio_app/widgets/custom_text_form_field.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'bloc/recipe_create_bloc.dart';
import 'models/recipe_create_model.dart';
import 'package:eatvio_app/core/app_export.dart';
import 'package:flutter/material.dart';
import 'package:eatvio_app/widgets/app_bar/custom_app_bar.dart';
import 'package:eatvio_app/widgets/app_bar/appbar_leading_image.dart';
import 'package:eatvio_app/widgets/app_bar/appbar_subtitle.dart';
import 'package:eatvio_app/widgets/custom_text_form_field.dart';
import 'package:eatvio_app/widgets/custom_outlined_button.dart';
class RecipeCreateScreen extends StatelessWidget {
const RecipeCreateScreen({Key? key}) : super(key: key);
@@ -16,9 +16,10 @@ class RecipeCreateScreen extends StatelessWidget {
static Widget builder(BuildContext context) {
final recipeID = ModalRoute.of(context)!.settings.arguments;
return BlocProvider<RecipeCreateBloc>(
create: (context) =>
RecipeCreateBloc(RecipeCreateState(recipeModelObj: RecipeCreateModel(recipeID: recipeID.toString())))..add(RecipeCreateInitialEvent()),
child: RecipeCreateScreen(),);
create: (context) =>
RecipeCreateBloc(RecipeCreateState(recipeModelObj: RecipeCreateModel(recipeID: recipeID.toString())))..add(RecipeCreateInitialEvent()),
child: RecipeCreateScreen(),
);
}
Widget build(BuildContext context) {
@@ -53,29 +54,33 @@ class RecipeCreateScreen extends StatelessWidget {
}
return RefreshIndicator(
onRefresh: refreshPage(context),
child: Scaffold(
appBar: AppBar(
toolbarHeight: 0,
automaticallyImplyLeading: false,
),
resizeToAvoidBottomInset: false,
body: Container(
width: double.maxFinite,
decoration: BoxDecoration(color: Colors.grey[200]),
child: ListView(children: [
_buldAppBar(context),
SizedBox(height: 20.v),
_buildRecipeInfo(state, context),
SizedBox(height: 20.v),
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: steps,
),
SizedBox(height: 20.v),
_buildAdditional(context, state),
],),
),),);
onRefresh: refreshPage(context),
child: Scaffold(
appBar: AppBar(
toolbarHeight: 0,
automaticallyImplyLeading: false,
),
resizeToAvoidBottomInset: false,
body: Container(
width: double.maxFinite,
decoration: BoxDecoration(color: Colors.grey[200]),
child: ListView(
children: [
_buldAppBar(context),
SizedBox(height: 20.v),
_buildRecipeInfo(state, context),
SizedBox(height: 20.v),
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: steps,
),
SizedBox(height: 20.v),
_buildAdditional(context, state),
],
),
),
),
);
}
_buldAppBar(BuildContext context) {
@@ -102,10 +107,11 @@ class RecipeCreateScreen extends StatelessWidget {
onTap: onTapBack(context),
),
title: Align(
alignment: Alignment.center,
child: AppbarSubtitle(
text: "Добавление рецепта",
),),
alignment: Alignment.center,
child: AppbarSubtitle(
text: "Добавление рецепта",
),
),
),
);
}
@@ -130,41 +136,44 @@ class RecipeCreateScreen extends StatelessWidget {
),
],
),
child: Column(mainAxisSize: MainAxisSize.min, children: [
SizedBox(height: 16.0),
Align(alignment: Alignment.centerLeft, child: Text("Изображение")),
_buildAddPhoto(context, state),
SizedBox(height: 16.0),
Align(alignment: Alignment.centerLeft, child: Text("Название рецепта")),
CustomTextFormField(
controller: state.title,
hintText: "Введите название рецепта",
fillColor: appTheme.gray50,
borderDecoration: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.h),
borderSide: BorderSide(
color: appTheme.gray50,
width: 1,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: 16.0),
Align(alignment: Alignment.centerLeft, child: Text("Изображение")),
_buildAddPhoto(context, state),
SizedBox(height: 16.0),
Align(alignment: Alignment.centerLeft, child: Text("Название рецепта")),
CustomTextFormField(
controller: state.title,
hintText: "Введите название рецепта",
fillColor: appTheme.gray50,
borderDecoration: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.h),
borderSide: BorderSide(
color: appTheme.gray50,
width: 1,
),
),
),
),
SizedBox(height: 16.0),
Align(alignment: Alignment.centerLeft, child: Text("Описание")),
CustomTextFormField(
controller: state.description,
hintText: "Введите описание рецепта",
maxLines: 4,
fillColor: appTheme.gray50,
borderDecoration: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.h),
borderSide: BorderSide(
color: appTheme.gray50,
width: 1,
SizedBox(height: 16.0),
Align(alignment: Alignment.centerLeft, child: Text("Описание")),
CustomTextFormField(
controller: state.description,
hintText: "Введите описание рецепта",
maxLines: 4,
fillColor: appTheme.gray50,
borderDecoration: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.h),
borderSide: BorderSide(
color: appTheme.gray50,
width: 1,
),
),
),
),
SizedBox(height: 16.0),
],),
SizedBox(height: 16.0),
],
),
);
}
@@ -254,71 +263,74 @@ class RecipeCreateScreen extends StatelessWidget {
),
],
),
child: Column(mainAxisSize: MainAxisSize.min, children: [
Align(
alignment: Alignment.centerLeft,
child: Text(
"Шаг ${i + 1}",
style: CustomTextStyles.titleMediumSemiBold,
),
),
SizedBox(height: 8.0),
CustomTextFormField(
controller: state.stepDescriptions?[i],
hintText: "Введите описание шага",
maxLines: 4,
fillColor: appTheme.gray50,
borderDecoration: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.h),
borderSide: BorderSide(
color: appTheme.gray50,
width: 1,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Align(
alignment: Alignment.centerLeft,
child: Text(
"Шаг ${i + 1}",
style: CustomTextStyles.titleMediumSemiBold,
),
),
),
SizedBox(height: 16.0),
_buildAddPhoto(context, state),
SizedBox(height: 16.0),
if (isFirstStep && isLastStep)
CustomOutlinedButton(
onPressed: () => context.read<RecipeCreateBloc>().add(AddStep()),
text: "Добавить шаг",
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
),
if (isLastStep && !isFirstStep) // Добавляем кнопку "Добавить шаг" только для последнего шага
Column(
children: [
SizedBox(height: 16.0),
Row(
children: [
Flexible(
flex: 5,
child: CustomOutlinedButton(
onPressed: () => context.read<RecipeCreateBloc>().add(AddStep()),
text: "Добавить шаг",
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
),
),
SizedBox(width: 8), // Add some spacing between the buttons
Flexible(
flex: 1,
child: CustomOutlinedButton(
text: "х",
onPressed: () => context.read<RecipeCreateBloc>().add(RemoveStep(i)),
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
),
),
],
SizedBox(height: 8.0),
CustomTextFormField(
controller: state.stepDescriptions?[i],
hintText: "Введите описание шага",
maxLines: 4,
fillColor: appTheme.gray50,
borderDecoration: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.h),
borderSide: BorderSide(
color: appTheme.gray50,
width: 1,
),
],
),
),
if (!isLastStep && !isFirstStep)
CustomOutlinedButton(
onPressed: () => context.read<RecipeCreateBloc>().add(RemoveStep(i)),
text: "x",
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
),
],),
SizedBox(height: 16.0),
_buildAddPhoto(context, state),
SizedBox(height: 16.0),
if (isFirstStep && isLastStep)
CustomOutlinedButton(
onPressed: () => context.read<RecipeCreateBloc>().add(AddStep()),
text: "Добавить шаг",
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
),
if (isLastStep && !isFirstStep) // Добавляем кнопку "Добавить шаг" только для последнего шага
Column(
children: [
SizedBox(height: 16.0),
Row(
children: [
Flexible(
flex: 5,
child: CustomOutlinedButton(
onPressed: () => context.read<RecipeCreateBloc>().add(AddStep()),
text: "Добавить шаг",
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
),
),
SizedBox(width: 8), // Add some spacing between the buttons
Flexible(
flex: 1,
child: CustomOutlinedButton(
text: "х",
onPressed: () => context.read<RecipeCreateBloc>().add(RemoveStep(i)),
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
),
),
],
),
],
),
if (!isLastStep && !isFirstStep)
CustomOutlinedButton(
onPressed: () => context.read<RecipeCreateBloc>().add(RemoveStep(i)),
text: "x",
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
),
],
),
);
}
@@ -342,40 +354,43 @@ class RecipeCreateScreen extends StatelessWidget {
),
],
),
child: Column(mainAxisSize: MainAxisSize.min, children: [
Align(alignment: Alignment.centerLeft, child: Text("Теги")),
CustomTextFormField(
controller: state.tags,
hintText: "Введите теги, через запятую",
fillColor: appTheme.gray50,
borderDecoration: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.h),
borderSide: BorderSide(
color: appTheme.gray50,
width: 1,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Align(alignment: Alignment.centerLeft, child: Text("Теги")),
CustomTextFormField(
controller: state.tags,
hintText: "Введите теги, через запятую",
fillColor: appTheme.gray50,
borderDecoration: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.h),
borderSide: BorderSide(
color: appTheme.gray50,
width: 1,
),
),
),
),
SizedBox(height: 8.0),
Align(alignment: Alignment.centerLeft, child: Text("Ссылка на видео (необязательно)")),
CustomTextFormField(
controller: state.video_url,
hintText: "YouTube",
fillColor: appTheme.gray50,
borderDecoration: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.h),
borderSide: BorderSide(
color: appTheme.gray50,
width: 1,
SizedBox(height: 8.0),
Align(alignment: Alignment.centerLeft, child: Text("Ссылка на видео (необязательно)")),
CustomTextFormField(
controller: state.video_url,
hintText: "YouTube",
fillColor: appTheme.gray50,
borderDecoration: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.h),
borderSide: BorderSide(
color: appTheme.gray50,
width: 1,
),
),
),
),
SizedBox(height: 16.0),
CustomOutlinedButton(
text: "Добавить рецепт",
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
),
],),
SizedBox(height: 16.0),
CustomOutlinedButton(
text: "Добавить рецепт",
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
),
],
),
);
}

View File

@@ -1,18 +1,17 @@
import 'dart:async';
import 'dart:convert';
import 'package:eatvio_app/app_router.dart';
import 'package:eatvio_app/presentation/recipe/recipe_edit_screen/models/recipe_edit_model.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:go_router/go_router.dart';
import 'package:hive/hive.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import '/core/app_export.dart';
import 'package:eatvio_app/presentation/recipe/recipe_edit_screen/models/recipe_edit_model.dart';
import 'package:go_router/go_router.dart';
part 'recipe_edit_event.dart';
part 'recipe_edit_state.dart';
/// A bloc that manages the state of a RecipeEdit according to the event that is dispatched to it.
@@ -37,7 +36,7 @@ class RecipeEditBloc extends Bloc<RecipeEditEvent, RecipeEditState> {
}
final String? recipeID = state.recipeModelObj?.recipeID;
final response = await http.get(
Uri.parse(dotenv.env['SERVER_ROOT']! + '/recipes/show/$recipeID'),
Uri.parse(baseUrl + '/api/recipes/show/$recipeID'),
headers: {
'Authorization': 'Bearer $token',
},
@@ -166,7 +165,7 @@ class RecipeEditBloc extends Bloc<RecipeEditEvent, RecipeEditState> {
String? description,
) async {
int id = recipe?['id'];
String url = '${dotenv.env['SERVER_ROOT']!}/recipe/update/$id';
String url = '${baseUrl}/api/recipe/update/$id';
final Map<String, dynamic> credentials = {
'id': id,
@@ -200,7 +199,7 @@ class RecipeEditBloc extends Bloc<RecipeEditEvent, RecipeEditState> {
}
final response = await http.get(
Uri.parse(dotenv.env['SERVER_ROOT']! + '/recipes/getTags/$id'),
Uri.parse(baseUrl + '/api/recipes/getTags/$id'),
headers: {
'Authorization': 'Bearer $token',
},

View File

@@ -22,17 +22,18 @@ class RecipeEditState extends Equatable {
}
class RecipeEditLoadedState extends RecipeEditState {
RecipeEditLoadedState(
{this.recipeModelObj,
this.token,
this.recipe,
this.title,
this.description,
this.video_url,
this.tagsController,
this.stepDescriptions,
this.tags,
this.steps,});
RecipeEditLoadedState({
this.recipeModelObj,
this.token,
this.recipe,
this.title,
this.description,
this.video_url,
this.tagsController,
this.stepDescriptions,
this.tags,
this.steps,
});
RecipeEditModel? recipeModelObj;
Map<String, dynamic>? recipe;
@@ -45,8 +46,7 @@ class RecipeEditLoadedState extends RecipeEditState {
List<dynamic>? tags;
List<TextEditingController>? stepDescriptions;
@override
List<Object?> get props =>
[recipeModelObj, recipe, title, description, video_url];
List<Object?> get props => [recipeModelObj, recipe, title, description, video_url];
RecipeEditLoadedState copyWith({
RecipeEditModel? recipeModelObj,
Map<String, dynamic>? recipe,
@@ -84,6 +84,5 @@ class RecipeEditErrorState extends RecipeEditState {
}
class RecipeEditInitialState extends RecipeEditState {
RecipeEditInitialState({required RecipeEditModel recipeModelObj})
: super(recipeModelObj: recipeModelObj);
RecipeEditInitialState({required RecipeEditModel recipeModelObj}) : super(recipeModelObj: recipeModelObj);
}

View File

@@ -1,16 +1,15 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:eatvio_app/core/app_export.dart';
import 'package:eatvio_app/widgets/app_bar/appbar_leading_image.dart';
import 'package:eatvio_app/widgets/app_bar/appbar_subtitle.dart';
import 'package:eatvio_app/widgets/app_bar/custom_app_bar.dart';
import 'package:eatvio_app/widgets/custom_outlined_button.dart';
import 'package:eatvio_app/widgets/custom_text_form_field.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'bloc/recipe_edit_bloc.dart';
import 'models/recipe_edit_model.dart';
import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:eatvio_app/core/app_export.dart';
import 'package:eatvio_app/widgets/app_bar/custom_app_bar.dart';
import 'package:eatvio_app/widgets/app_bar/appbar_leading_image.dart';
import 'package:eatvio_app/widgets/app_bar/appbar_subtitle.dart';
import 'package:eatvio_app/widgets/custom_text_form_field.dart';
import 'package:eatvio_app/widgets/custom_outlined_button.dart';
class RecipeEditScreen extends StatelessWidget {
const RecipeEditScreen({Key? key}) : super(key: key);
@@ -18,8 +17,9 @@ class RecipeEditScreen extends StatelessWidget {
static Widget builder(BuildContext context) {
final recipeID = ModalRoute.of(context)!.settings.arguments;
return BlocProvider<RecipeEditBloc>(
create: (context) => RecipeEditBloc(RecipeEditState(recipeModelObj: RecipeEditModel(recipeID: recipeID.toString())))..add(RecipeEditInitialEvent()),
child: RecipeEditScreen(),);
create: (context) => RecipeEditBloc(RecipeEditState(recipeModelObj: RecipeEditModel(recipeID: recipeID.toString())))..add(RecipeEditInitialEvent()),
child: RecipeEditScreen(),
);
}
Widget build(BuildContext context) {
@@ -109,10 +109,11 @@ class RecipeEditScreen extends StatelessWidget {
onTap: onTapBack(context),
),
title: Align(
alignment: Alignment.center,
child: AppbarSubtitle(
text: "Редактирование рецепта",
),),
alignment: Alignment.center,
child: AppbarSubtitle(
text: "Редактирование рецепта",
),
),
),
);
}
@@ -137,52 +138,55 @@ class RecipeEditScreen extends StatelessWidget {
),
],
),
child: Column(mainAxisSize: MainAxisSize.min, children: [
SizedBox(height: 16.0),
Align(alignment: Alignment.centerLeft, child: Text("Изображение")),
if (state.recipe?['image'] != null)
CachedNetworkImage(
imageUrl: dotenv.env['STORAGE_URL']! + (state.recipe?['image']),
width: double.infinity,
// Установите ширину равной ширине родительского элемента
height: 200.0,
fit: BoxFit.cover,
placeholder: (context, url) => CircularProgressIndicator(),
errorWidget: (context, url, error) => Icon(Icons.error),
)
else
_buildAddPhoto(context, state),
SizedBox(height: 16.0),
Align(alignment: Alignment.centerLeft, child: Text("Название рецепта")),
CustomTextFormField(
controller: state.title,
hintText: "Введите название рецепта",
fillColor: appTheme.gray50,
borderDecoration: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.h),
borderSide: BorderSide(
color: appTheme.gray50,
width: 1,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: 16.0),
Align(alignment: Alignment.centerLeft, child: Text("Изображение")),
if (state.recipe?['image'] != null)
CachedNetworkImage(
imageUrl: baseUrl + '/storage/' + (state.recipe?['image']),
width: double.infinity,
// Установите ширину равной ширине родительского элемента
height: 200.0,
fit: BoxFit.cover,
placeholder: (context, url) => CircularProgressIndicator(),
errorWidget: (context, url, error) => Icon(Icons.error),
)
else
_buildAddPhoto(context, state),
SizedBox(height: 16.0),
Align(alignment: Alignment.centerLeft, child: Text("Название рецепта")),
CustomTextFormField(
controller: state.title,
hintText: "Введите название рецепта",
fillColor: appTheme.gray50,
borderDecoration: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.h),
borderSide: BorderSide(
color: appTheme.gray50,
width: 1,
),
),
),
),
SizedBox(height: 16.0),
Align(alignment: Alignment.centerLeft, child: Text("Описание")),
CustomTextFormField(
controller: state.description,
hintText: "Введите описание рецепта",
maxLines: 4,
fillColor: appTheme.gray50,
borderDecoration: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.h),
borderSide: BorderSide(
color: appTheme.gray50,
width: 1,
SizedBox(height: 16.0),
Align(alignment: Alignment.centerLeft, child: Text("Описание")),
CustomTextFormField(
controller: state.description,
hintText: "Введите описание рецепта",
maxLines: 4,
fillColor: appTheme.gray50,
borderDecoration: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.h),
borderSide: BorderSide(
color: appTheme.gray50,
width: 1,
),
),
),
),
SizedBox(height: 16.0),
],),
SizedBox(height: 16.0),
],
),
);
}
@@ -272,83 +276,86 @@ class RecipeEditScreen extends StatelessWidget {
),
],
),
child: Column(mainAxisSize: MainAxisSize.min, children: [
Align(
alignment: Alignment.centerLeft,
child: Text(
"Шаг ${i + 1}",
style: CustomTextStyles.titleMediumSemiBold,
),
),
SizedBox(height: 8.0),
CustomTextFormField(
controller: state.stepDescriptions?[i],
hintText: "Введите описание шага",
maxLines: 4,
fillColor: appTheme.gray50,
borderDecoration: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.h),
borderSide: BorderSide(
color: appTheme.gray50,
width: 1,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Align(
alignment: Alignment.centerLeft,
child: Text(
"Шаг ${i + 1}",
style: CustomTextStyles.titleMediumSemiBold,
),
),
),
SizedBox(height: 16.0),
if (state.steps?[i]['image'] != null)
CachedNetworkImage(
imageUrl: dotenv.env['STORAGE_URL']! + (state.steps?[i]['image']),
width: double.infinity,
// Установите ширину равной ширине родительского элемента
height: 200.0,
// Задайте постоянную высоту элемента
fit: BoxFit.cover,
placeholder: (context, url) => CircularProgressIndicator(),
errorWidget: (context, url, error) => Icon(Icons.error),
)
else
_buildAddPhoto(context, state),
SizedBox(height: 16.0),
if (isFirstStep && isLastStep)
CustomOutlinedButton(
onPressed: () => context.read<RecipeEditBloc>().add(AddStep()),
text: "Добавить шаг",
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
),
if (isLastStep && !isFirstStep) // Добавляем кнопку "Добавить шаг" только для последнего шага
Column(
children: [
SizedBox(height: 16.0),
Row(
children: [
Flexible(
flex: 5,
child: CustomOutlinedButton(
onPressed: () => context.read<RecipeEditBloc>().add(AddStep()),
text: "Добавить шаг",
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
),
),
SizedBox(width: 8), // Add some spacing between the buttons
Flexible(
flex: 1,
child: CustomOutlinedButton(
text: "х",
onPressed: () => context.read<RecipeEditBloc>().add(RemoveStep(i)),
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
),
),
],
SizedBox(height: 8.0),
CustomTextFormField(
controller: state.stepDescriptions?[i],
hintText: "Введите описание шага",
maxLines: 4,
fillColor: appTheme.gray50,
borderDecoration: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.h),
borderSide: BorderSide(
color: appTheme.gray50,
width: 1,
),
],
),
),
if (!isLastStep && !isFirstStep)
CustomOutlinedButton(
onPressed: () => context.read<RecipeEditBloc>().add(RemoveStep(i)),
text: "x",
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
),
],),
SizedBox(height: 16.0),
if (state.steps?[i]['image'] != null)
CachedNetworkImage(
imageUrl: baseUrl + '/storage/' + (state.steps?[i]['image']),
width: double.infinity,
// Установите ширину равной ширине родительского элемента
height: 200.0,
// Задайте постоянную высоту элемента
fit: BoxFit.cover,
placeholder: (context, url) => CircularProgressIndicator(),
errorWidget: (context, url, error) => Icon(Icons.error),
)
else
_buildAddPhoto(context, state),
SizedBox(height: 16.0),
if (isFirstStep && isLastStep)
CustomOutlinedButton(
onPressed: () => context.read<RecipeEditBloc>().add(AddStep()),
text: "Добавить шаг",
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
),
if (isLastStep && !isFirstStep) // Добавляем кнопку "Добавить шаг" только для последнего шага
Column(
children: [
SizedBox(height: 16.0),
Row(
children: [
Flexible(
flex: 5,
child: CustomOutlinedButton(
onPressed: () => context.read<RecipeEditBloc>().add(AddStep()),
text: "Добавить шаг",
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
),
),
SizedBox(width: 8), // Add some spacing between the buttons
Flexible(
flex: 1,
child: CustomOutlinedButton(
text: "х",
onPressed: () => context.read<RecipeEditBloc>().add(RemoveStep(i)),
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
),
),
],
),
],
),
if (!isLastStep && !isFirstStep)
CustomOutlinedButton(
onPressed: () => context.read<RecipeEditBloc>().add(RemoveStep(i)),
text: "x",
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
),
],
),
);
}
@@ -372,29 +379,32 @@ class RecipeEditScreen extends StatelessWidget {
),
],
),
child: Column(mainAxisSize: MainAxisSize.min, children: [
Align(alignment: Alignment.centerLeft, child: Text("Теги")),
_buildEditableTags(state.tags),
SizedBox(height: 8.0),
Align(alignment: Alignment.centerLeft, child: Text("Ссылка на видео (необязательно)")),
CustomTextFormField(
controller: state.video_url,
hintText: "YouTube",
fillColor: appTheme.gray50,
borderDecoration: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.h),
borderSide: BorderSide(
color: appTheme.gray50,
width: 1,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Align(alignment: Alignment.centerLeft, child: Text("Теги")),
_buildEditableTags(state.tags),
SizedBox(height: 8.0),
Align(alignment: Alignment.centerLeft, child: Text("Ссылка на видео (необязательно)")),
CustomTextFormField(
controller: state.video_url,
hintText: "YouTube",
fillColor: appTheme.gray50,
borderDecoration: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.h),
borderSide: BorderSide(
color: appTheme.gray50,
width: 1,
),
),
),
),
SizedBox(height: 16.0),
CustomOutlinedButton(
text: "Добавить рецепт",
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
),
],),
SizedBox(height: 16.0),
CustomOutlinedButton(
text: "Добавить рецепт",
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
),
],
),
);
}

View File

@@ -3,14 +3,14 @@ import 'dart:async';
import 'package:dio/dio.dart';
import 'package:eatvio_app/app_router.dart';
import 'package:eatvio_app/locator_service.dart';
import 'package:eatvio_app/presentation/recipe/recipe_select_screen/models/recipe_select_model.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import '/core/app_export.dart';
import 'package:eatvio_app/presentation/recipe/recipe_select_screen/models/recipe_select_model.dart';
import 'package:hive/hive.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:image_picker/image_picker.dart';
import 'package:go_router/go_router.dart';
import 'package:hive/hive.dart';
import 'package:image_picker/image_picker.dart';
import '/core/app_export.dart';
part 'recipe_select_event.dart';
part 'recipe_select_state.dart';
@@ -31,7 +31,7 @@ class RecipeSelectBloc extends Bloc<RecipeSelectEvent, RecipeSelectState> {
if (token == null) {
AppRouter.navigatorKey.currentContext?.goNamed('authScreen');
}
final response = await sl<Dio>().get(dotenv.env['SERVER_ROOT']! + '/recipes/index');
final response = await sl<Dio>().get(baseUrl + '/api/recipes/index');
print('Got response');
if (response.statusCode == 200) {
print('Got 200');

View File

@@ -1,20 +1,20 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:eatvio_app/core/app_export.dart';
import 'package:eatvio_app/widgets/app_bar/appbar_leading_image.dart';
import 'package:eatvio_app/widgets/app_bar/appbar_subtitle.dart';
import 'package:eatvio_app/widgets/app_bar/custom_app_bar.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'bloc/recipe_select_bloc.dart';
import 'models/recipe_select_model.dart';
import 'package:eatvio_app/core/app_export.dart';
import 'package:eatvio_app/widgets/app_bar/appbar_subtitle.dart';
import 'package:eatvio_app/widgets/app_bar/appbar_leading_image.dart';
import 'package:eatvio_app/widgets/app_bar/custom_app_bar.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter/material.dart';
class RecipeSelectScreen extends StatelessWidget {
static Widget builder(BuildContext context) {
return BlocProvider<RecipeSelectBloc>(
create: (context) => RecipeSelectBloc(RecipeSelectState(myProfileModelObj: RecipeSelectModel()))..add(RecipeSelectInitialEvent()),
child: RecipeSelectScreen(),);
create: (context) => RecipeSelectBloc(RecipeSelectState(myProfileModelObj: RecipeSelectModel()))..add(RecipeSelectInitialEvent()),
child: RecipeSelectScreen(),
);
}
Widget build(BuildContext context) {
@@ -118,50 +118,51 @@ class RecipeSelectScreen extends StatelessWidget {
itemBuilder: (context, index) {
final post = state.posts?[index];
return Container(
margin: EdgeInsets.only(bottom: 10),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: const BorderRadius.all(Radius.circular(12)),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 2,
blurRadius: 5,
offset: Offset(0, 3),
),
],
margin: EdgeInsets.only(bottom: 10),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: const BorderRadius.all(Radius.circular(12)),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 2,
blurRadius: 5,
offset: Offset(0, 3),
),
],
),
child: ListTile(
leading: CachedNetworkImage(
imageUrl: baseUrl + '/storage/' + post['image'],
imageBuilder: (context, imageProvider) => CircleAvatar(
backgroundImage: imageProvider,
radius: 25,
),
placeholder: (context, url) => CircularProgressIndicator(),
errorWidget: (context, url, error) => CircleAvatar(
child: Icon(Icons.person),
backgroundColor: Colors.white,
radius: 25,
),
),
child: ListTile(
leading: CachedNetworkImage(
imageUrl: dotenv.env['STORAGE_URL']! + post['image'],
imageBuilder: (context, imageProvider) => CircleAvatar(
backgroundImage: imageProvider,
radius: 25,
),
placeholder: (context, url) => CircularProgressIndicator(),
errorWidget: (context, url, error) => CircleAvatar(
child: Icon(Icons.person),
backgroundColor: Colors.white,
radius: 25,
),
),
title: Text(
post?['title'] ?? '',
style: CustomTextStyles.titleMediumSemiBold,
),
subtitle: Text(post?['description'] ?? ''),
title: Text(
post?['title'] ?? '',
style: CustomTextStyles.titleMediumSemiBold,
),
subtitle: Text(post?['description'] ?? ''),
onTap: () => GoRouter.of(context).goNamed(
'recipeScreen',
extra: post?['id'].toString(),
),
trailing: GestureDetector(
onTap: () => GoRouter.of(context).goNamed(
'recipeScreen',
'recipeEditScreen',
extra: post?['id'].toString(),
),
trailing: GestureDetector(
onTap: () => GoRouter.of(context).goNamed(
'recipeEditScreen',
extra: post?['id'].toString(),
),
child: Icon(Icons.edit),
),
),);
child: Icon(Icons.edit),
),
),
);
},
);
}

View File

@@ -1,20 +1,19 @@
import 'dart:async';
import 'dart:convert';
import 'package:eatvio_app/app_router.dart';
import 'package:eatvio_app/presentation/feed/blog_post_screen/models/comment_model.dart';
import 'package:eatvio_app/presentation/recipe/recipe_show_screen/models/recipe_show_model.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:go_router/go_router.dart';
import 'package:hive/hive.dart';
import 'package:http/http.dart' as http;
import 'package:intl/intl.dart';
import 'dart:convert';
import '/core/app_export.dart';
import 'package:eatvio_app/presentation/recipe/recipe_show_screen/models/recipe_show_model.dart';
import 'package:eatvio_app/presentation/feed/blog_post_screen/models/comment_model.dart';
import 'package:go_router/go_router.dart';
part 'recipe_show_event.dart';
part 'recipe_show_state.dart';
/// A bloc that manages the state of a Recipe according to the event that is dispatched to it.
@@ -37,7 +36,7 @@ class RecipeBloc extends Bloc<RecipeEvent, RecipeState> {
}
final String? recipeID = state.recipeModelObj?.recipeID;
final response = await http.get(
Uri.parse(dotenv.env['SERVER_ROOT']! + '/recipes/show/$recipeID'),
Uri.parse(baseUrl + '/api/recipes/show/$recipeID'),
headers: {
'Authorization': 'Bearer $token',
},
@@ -93,7 +92,7 @@ class RecipeBloc extends Bloc<RecipeEvent, RecipeState> {
}
final int id = event.id;
final response = await http.post(
Uri.parse(dotenv.env['SERVER_ROOT']! + '/recipes/delete/$id'),
Uri.parse(baseUrl + '/api/recipes/delete/$id'),
headers: {
'Authorization': 'Bearer $token',
},
@@ -131,7 +130,7 @@ class RecipeBloc extends Bloc<RecipeEvent, RecipeState> {
}
final response = await http.get(
Uri.parse(dotenv.env['SERVER_ROOT']! + '/recipes/getAuthor/$id'),
Uri.parse(baseUrl + '/api/recipes/getAuthor/$id'),
headers: {
'Authorization': 'Bearer $token',
},
@@ -147,7 +146,7 @@ class RecipeBloc extends Bloc<RecipeEvent, RecipeState> {
AppRouter.navigatorKey.currentContext?.goNamed('authScreen');
}
final response = await http.get(
Uri.parse(dotenv.env['SERVER_ROOT']! + '/recipes/getComments/$id'),
Uri.parse(baseUrl + '/api/recipes/getComments/$id'),
headers: {
'Authorization': 'Bearer $token',
},
@@ -165,7 +164,7 @@ class RecipeBloc extends Bloc<RecipeEvent, RecipeState> {
}
final response = await http.get(
Uri.parse(dotenv.env['SERVER_ROOT']! + '/recipes/getTags/$id'),
Uri.parse(baseUrl + '/api/recipes/getTags/$id'),
headers: {
'Authorization': 'Bearer $token',
},
@@ -191,7 +190,7 @@ class RecipeBloc extends Bloc<RecipeEvent, RecipeState> {
}
Future<http.Response> _sendCommentData(String? token, String body, int id) async {
String url = dotenv.env['SERVER_ROOT']! + '/recipes/createComment/$id';
String url = baseUrl + '/api/recipes/createComment/$id';
final Map<String, dynamic> credentials = {
'id': id,
'body': body,

View File

@@ -22,15 +22,16 @@ class RecipeState extends Equatable {
}
class RecipeLoadedState extends RecipeState {
RecipeLoadedState(
{this.recipeModelObj,
this.recipe,
this.author,
this.comments,
this.token,
this.commentController,
this.tags,
this.steps,});
RecipeLoadedState({
this.recipeModelObj,
this.recipe,
this.author,
this.comments,
this.token,
this.commentController,
this.tags,
this.steps,
});
RecipeModel? recipeModelObj;
Map<String, dynamic>? recipe;
@@ -50,15 +51,16 @@ class RecipeLoadedState extends RecipeState {
commentController,
steps,
];
RecipeLoadedState copyWith(
{RecipeModel? recipeModelObj,
Map<String, dynamic>? recipe,
Map<String, dynamic>? author,
TextEditingController? commentController,
String? token,
List<dynamic>? tags,
List<dynamic>? steps,
List<dynamic>? comments,}) {
RecipeLoadedState copyWith({
RecipeModel? recipeModelObj,
Map<String, dynamic>? recipe,
Map<String, dynamic>? author,
TextEditingController? commentController,
String? token,
List<dynamic>? tags,
List<dynamic>? steps,
List<dynamic>? comments,
}) {
return RecipeLoadedState(
recipeModelObj: recipeModelObj ?? this.recipeModelObj,
recipe: recipe ?? this.recipe,
@@ -82,6 +84,5 @@ class RecipeErrorState extends RecipeState {
}
class RecipeInitialState extends RecipeState {
RecipeInitialState({required RecipeModel recipeModelObj})
: super(recipeModelObj: recipeModelObj);
RecipeInitialState({required RecipeModel recipeModelObj}) : super(recipeModelObj: recipeModelObj);
}

View File

@@ -1,15 +1,14 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:eatvio_app/core/app_export.dart';
import 'package:eatvio_app/presentation/feed/blog_post_screen/widgets/comment_widget.dart';
import 'package:eatvio_app/widgets/custom_edit_text_form_field.dart';
import 'package:eatvio_app/widgets/custom_outlined_button.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:intl/intl.dart';
import 'bloc/recipe_show_bloc.dart';
import 'models/recipe_show_model.dart';
import 'package:eatvio_app/presentation/feed/blog_post_screen/widgets/comment_widget.dart';
import 'package:eatvio_app/core/app_export.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:eatvio_app/widgets/custom_edit_text_form_field.dart';
import 'package:eatvio_app/widgets/custom_outlined_button.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
class RecipeScreen extends StatelessWidget {
const RecipeScreen({Key? key}) : super(key: key);
@@ -17,8 +16,9 @@ class RecipeScreen extends StatelessWidget {
static Widget builder(BuildContext context) {
final recipeID = ModalRoute.of(context)!.settings.arguments;
return BlocProvider<RecipeBloc>(
create: (context) => RecipeBloc(RecipeState(recipeModelObj: RecipeModel(recipeID: recipeID.toString())))..add(RecipeInitialEvent()),
child: RecipeScreen(),);
create: (context) => RecipeBloc(RecipeState(recipeModelObj: RecipeModel(recipeID: recipeID.toString())))..add(RecipeInitialEvent()),
child: RecipeScreen(),
);
}
Widget build(BuildContext context) {
@@ -52,32 +52,33 @@ class RecipeScreen extends StatelessWidget {
}
}
return Scaffold(
appBar: AppBar(
toolbarHeight: 0,
automaticallyImplyLeading: false,
appBar: AppBar(
toolbarHeight: 0,
automaticallyImplyLeading: false,
),
resizeToAvoidBottomInset: false,
body: Container(
width: double.maxFinite,
decoration: AppDecoration.fillGray,
child: ListView(
children: [
SizedBox(height: 15.v),
_buildHeaderRow(context, state),
SizedBox(height: 15.v),
_buildRecipeData(context, state),
SizedBox(height: 15.v),
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: steps,
),
SizedBox(height: 15.v),
_buildTextField(context, state),
SizedBox(height: 15.v),
_buildCommentSection(context, state),
],
),
resizeToAvoidBottomInset: false,
body: Container(
width: double.maxFinite,
decoration: AppDecoration.fillGray,
child: ListView(
children: [
SizedBox(height: 15.v),
_buildHeaderRow(context, state),
SizedBox(height: 15.v),
_buildRecipeData(context, state),
SizedBox(height: 15.v),
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: steps,
),
SizedBox(height: 15.v),
_buildTextField(context, state),
SizedBox(height: 15.v),
_buildCommentSection(context, state),
],
),
),);
),
);
}
_buildHeaderRow(BuildContext context, RecipeLoadedState state) {
@@ -119,7 +120,7 @@ class RecipeScreen extends StatelessWidget {
),
),
CachedNetworkImage(
imageUrl: dotenv.env['STORAGE_URL']! + (state.author?['profile_photo_path'] ?? ''),
imageUrl: baseUrl + '/storage/' + (state.author?['profile_photo_path'] ?? ''),
imageBuilder: (context, imageProvider) => CircleAvatar(
backgroundImage: imageProvider,
radius: 25,
@@ -159,75 +160,86 @@ class RecipeScreen extends StatelessWidget {
),
],
),
child: Column(mainAxisSize: MainAxisSize.min, children: [
Row(
children: [
Text(
state.recipe?['title'],
style: CustomTextStyles.titleMediumBlack,
),
Spacer(),
PopupMenuButton<String>(
onSelected: (value) => tapMenu(value, context, state),
itemBuilder: (BuildContext context) => <PopupMenuEntry<String>>[
PopupMenuItem<String>(
value: 'edit',
child: Text('Редактировать'),
),
PopupMenuItem<String>(
value: 'delete',
child: Text('Удалить'),
),
],
),
],
),
SizedBox(height: 8.0),
_buildImage(state),
SizedBox(height: 16.0),
Text(
state.recipe?['description'],
style: CustomTextStyles.bodyMediumBluegray800,
),
SizedBox(height: 16.0),
Align(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
children: [
Text(
state.recipe?['title'],
style: CustomTextStyles.titleMediumBlack,
),
Spacer(),
PopupMenuButton<String>(
onSelected: (value) => tapMenu(value, context, state),
itemBuilder: (BuildContext context) => <PopupMenuEntry<String>>[
PopupMenuItem<String>(
value: 'edit',
child: Text('Редактировать'),
),
PopupMenuItem<String>(
value: 'delete',
child: Text('Удалить'),
),
],
),
],
),
SizedBox(height: 8.0),
_buildImage(state),
SizedBox(height: 16.0),
Text(
state.recipe?['description'],
style: CustomTextStyles.bodyMediumBluegray800,
),
SizedBox(height: 16.0),
Align(
alignment: Alignment.topLeft,
child: Text(
DateFormat('dd.MM.yyyy').format(DateTime.parse(state.recipe?['updated_at'])),
style: theme.textTheme.bodyMedium,
),),
SizedBox(height: 16.0),
_buildTags(state.tags),
SizedBox(height: 16.0),
Row(children: [
Padding(
padding: EdgeInsets.only(left: 24),
child: Row(children: [
Padding(
padding: EdgeInsets.only(right: 8),
child: Icon(Icons.favorite_outline),
),
Text(
"Нравится",
style: CustomTextStyles.titleMediumSemiBold,
),
],),
),
),
Spacer(),
Padding(
padding: EdgeInsets.only(right: 24),
child: Row(children: [
Padding(
padding: const EdgeInsets.only(right: 8),
child: Icon(Icons.share_outlined),
SizedBox(height: 16.0),
_buildTags(state.tags),
SizedBox(height: 16.0),
Row(
children: [
Padding(
padding: EdgeInsets.only(left: 24),
child: Row(
children: [
Padding(
padding: EdgeInsets.only(right: 8),
child: Icon(Icons.favorite_outline),
),
Text(
"Нравится",
style: CustomTextStyles.titleMediumSemiBold,
),
],
),
Text(
"Поделиться",
style: CustomTextStyles.titleMediumSemiBold,
),
Spacer(),
Padding(
padding: EdgeInsets.only(right: 24),
child: Row(
children: [
Padding(
padding: const EdgeInsets.only(right: 8),
child: Icon(Icons.share_outlined),
),
Text(
"Поделиться",
style: CustomTextStyles.titleMediumSemiBold,
),
],
),
],),),
],),
],),
),
],
),
],
),
);
}
@@ -245,7 +257,7 @@ class RecipeScreen extends StatelessWidget {
_buildImage(RecipeLoadedState state) {
return CachedNetworkImage(
imageUrl: dotenv.env['STORAGE_URL']! + (state.recipe?['image'] ?? ''),
imageUrl: baseUrl + '/storage/' + (state.recipe?['image'] ?? ''),
imageBuilder: (context, imageProvider) => Stack(
children: [
ClipRRect(
@@ -431,28 +443,31 @@ class RecipeScreen extends StatelessWidget {
),
],
),
child: Column(mainAxisSize: MainAxisSize.min, children: [
Align(
alignment: Alignment.centerLeft,
child: Text(
"Шаг ${i + 1}",
style: CustomTextStyles.titleMediumSemiBold,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Align(
alignment: Alignment.centerLeft,
child: Text(
"Шаг ${i + 1}",
style: CustomTextStyles.titleMediumSemiBold,
),
),
),
SizedBox(height: 16.0),
CachedNetworkImage(
imageUrl: dotenv.env['STORAGE_URL']! + (state.steps?[i]['image']),
width: double.infinity,
// Установите ширину равной ширине родительского элемента
height: 200.0,
// Задайте постоянную высоту элемента
fit: BoxFit.cover,
placeholder: (context, url) => CircularProgressIndicator(),
errorWidget: (context, url, error) => Icon(Icons.error),
),
SizedBox(height: 16.0),
Text(state.steps?[i]['description']),
],),
SizedBox(height: 16.0),
CachedNetworkImage(
imageUrl: baseUrl + '/storage/' + (state.steps?[i]['image']),
width: double.infinity,
// Установите ширину равной ширине родительского элемента
height: 200.0,
// Задайте постоянную высоту элемента
fit: BoxFit.cover,
placeholder: (context, url) => CircularProgressIndicator(),
errorWidget: (context, url, error) => Icon(Icons.error),
),
SizedBox(height: 16.0),
Text(state.steps?[i]['description']),
],
),
);
}
}

View File

@@ -1,10 +1,9 @@
// ignore_for_file: must_be_immutable
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:eatvio_app/core/app_export.dart';
import 'package:eatvio_app/presentation/feed/blog_post_screen/models/comment_model.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
class CommentWidget extends StatelessWidget {
CommentWidget(this.commentModelObj, {Key? key})
@@ -36,13 +35,11 @@ class CommentWidget extends StatelessWidget {
],
),
child: Row(
mainAxisAlignment: MainAxisAlignment
.spaceBetween, // Move the main axis alignment to space between
crossAxisAlignment:
CrossAxisAlignment.center, // Center the items vertically
mainAxisAlignment: MainAxisAlignment.spaceBetween, // Move the main axis alignment to space between
crossAxisAlignment: CrossAxisAlignment.center, // Center the items vertically
children: [
CachedNetworkImage(
imageUrl: dotenv.env['STORAGE_URL']! + commentModelObj.userImage!,
imageUrl: baseUrl + '/storage/' + commentModelObj.userImage!,
imageBuilder: (context, imageProvider) => CircleAvatar(
backgroundImage: imageProvider,
radius: 25,
@@ -69,10 +66,7 @@ class CommentWidget extends StatelessWidget {
child: Row(
children: [
Text(
commentModelObj.username!.length <= 15
? commentModelObj.username!
: commentModelObj.username!.substring(0, 15) +
'...',
commentModelObj.username!.length <= 15 ? commentModelObj.username! : commentModelObj.username!.substring(0, 15) + '...',
style: theme.textTheme.titleMedium,
),
Spacer(),
@@ -96,10 +90,11 @@ class CommentWidget extends StatelessWidget {
),
),
CustomImageView(
imagePath: ImageConstant.imgSend,
height: 30.v,
width: 30.h,
margin: EdgeInsets.only(left: 5),),
imagePath: ImageConstant.imgSend,
height: 30.v,
width: 30.h,
margin: EdgeInsets.only(left: 5),
),
],
),
);

View File

@@ -1,16 +1,16 @@
//import 'package:eatvio_app/presentation/auth/auth_screen/auth_screen.dart';
import 'dart:async';
import 'dart:convert';
import 'package:eatvio_app/app_router.dart';
import 'package:eatvio_app/presentation/users/subscribers_screen/models/subscribers_model.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import '/core/app_export.dart';
import 'package:eatvio_app/presentation/users/subscribers_screen/models/subscribers_model.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:hive/hive.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:go_router/go_router.dart';
import 'package:hive/hive.dart';
import 'package:http/http.dart' as http;
import '/core/app_export.dart';
part 'subscribers_event.dart';
part 'subscribers_state.dart';
@@ -32,7 +32,7 @@ class SubscribersBloc extends Bloc<SubscribersEvent, SubscribersState> {
AppRouter.navigatorKey.currentContext?.goNamed('authScreen');
}
final response = await http.get(
Uri.parse(dotenv.env['SERVER_ROOT']! + '/profile/subscribers'),
Uri.parse(baseUrl + '/api/profile/subscribers'),
headers: {
'Authorization': 'Bearer $token',
},

View File

@@ -12,14 +12,12 @@ class SubscribersState extends Equatable {
}
class SubscribersInitialState extends SubscribersState {
SubscribersInitialState({required SubscribersModel myProfileModelObj})
: super(myProfileModelObj: myProfileModelObj);
SubscribersInitialState({required SubscribersModel myProfileModelObj}) : super(myProfileModelObj: myProfileModelObj);
}
/// Represents the state of Subscribers in the application.
class SubscribersLoadedState extends SubscribersState {
SubscribersLoadedState({this.myProfileModelObj, this.subscribers})
: super(myProfileModelObj: null);
SubscribersLoadedState({this.myProfileModelObj, this.subscribers}) : super(myProfileModelObj: null);
SubscribersModel? myProfileModelObj;
dynamic subscribers;

View File

@@ -1,23 +1,20 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:eatvio_app/core/app_export.dart';
import 'package:eatvio_app/presentation/feed/feed_screen/widgets/feed_search_view.dart';
import 'package:eatvio_app/widgets/app_bar/appbar_leading_image.dart';
import 'package:eatvio_app/widgets/app_bar/appbar_subtitle.dart';
import 'package:eatvio_app/widgets/app_bar/custom_app_bar.dart';
import 'package:eatvio_app/widgets/custom_outlined_button.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'bloc/subscribers_bloc.dart';
import 'models/subscribers_model.dart';
import 'package:eatvio_app/core/app_export.dart';
import 'package:eatvio_app/presentation/feed/feed_screen/widgets/feed_search_view.dart';
import 'package:eatvio_app/widgets/app_bar/appbar_subtitle.dart';
import 'package:eatvio_app/widgets/app_bar/appbar_leading_image.dart';
import 'package:eatvio_app/widgets/app_bar/custom_app_bar.dart';
import 'package:eatvio_app/widgets/custom_outlined_button.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter/material.dart';
class SubscribersScreen extends StatelessWidget {
static Widget builder(BuildContext context) {
return BlocProvider<SubscribersBloc>(
create: (context) => SubscribersBloc(
SubscribersState(myProfileModelObj: SubscribersModel()))
..add(SubscribersInitialEvent()),
create: (context) => SubscribersBloc(SubscribersState(myProfileModelObj: SubscribersModel()))..add(SubscribersInitialEvent()),
child: SubscribersScreen());
}
@@ -73,8 +70,7 @@ class SubscribersScreen extends StatelessWidget {
leadingWidth: 28.h,
leading: AppbarLeadingImage(
imagePath: ImageConstant.imgArrowLeft,
margin:
EdgeInsets.only(left: 16.h, top: 15.v, bottom: 15.v),
margin: EdgeInsets.only(left: 16.h, top: 15.v, bottom: 15.v),
onTap: () => GoRouter.of(context).pop(),
),
title: Padding(
@@ -83,14 +79,12 @@ class SubscribersScreen extends StatelessWidget {
children: [
AppbarSubtitle(
text: "Мои подписчики",
margin: EdgeInsets.only(
left: 80.h, top: 12.v, bottom: 9.v),
margin: EdgeInsets.only(left: 80.h, top: 12.v, bottom: 9.v),
),
Spacer(),
Padding(
padding: const EdgeInsets.only(right: 20.0),
child: GestureDetector(
child: Icon(Icons.create_outlined)),
child: GestureDetector(child: Icon(Icons.create_outlined)),
)
],
),
@@ -145,8 +139,7 @@ class SubscribersScreen extends StatelessWidget {
flex: 1,
child: CustomOutlinedButton(
text: "+",
onPressed: () =>
GoRouter.of(context).goNamed('blogPostCreateScreen'),
onPressed: () => GoRouter.of(context).goNamed('blogPostCreateScreen'),
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
),
),
@@ -190,7 +183,7 @@ class SubscribersScreen extends StatelessWidget {
),
child: ListTile(
leading: CachedNetworkImage(
imageUrl: dotenv.env['STORAGE_URL']! + subscriber['image'],
imageUrl: baseUrl + '/storage/' + subscriber['image'],
imageBuilder: (context, imageProvider) => CircleAvatar(
backgroundImage: imageProvider,
radius: 25,