feat(feed): Dio, retrofit and freezed model stump

This commit is contained in:
2024-06-27 16:50:23 +03:00
parent b1508c3553
commit 58a7ca933d
13 changed files with 481 additions and 76 deletions

View File

@@ -1,11 +1,11 @@
import 'dart:async';
import 'package:dio/dio.dart';
import 'package:eatvio_app/app_router.dart';
import 'package:eatvio_app/locator_service.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:hive/hive.dart';
import 'package:http/http.dart' as http;
import 'package:image_picker/image_picker.dart';
import 'dart:convert';
import '/core/app_export.dart';
@@ -39,7 +39,7 @@ class BlogPostCreateBloc
blog_postModelObj: currentState.blog_postModelObj,
title: currentState.title,
description: currentState.description,
youtube_url: currentState.youtube_url,
youtubeURL: currentState.youtubeURL,
token: currentState.token,
),
);
@@ -49,7 +49,7 @@ class BlogPostCreateBloc
token: token,
title: TextEditingController(),
description: TextEditingController(),
youtube_url: TextEditingController(),
youtubeURL: TextEditingController(),
),
);
}
@@ -64,8 +64,9 @@ class BlogPostCreateBloc
var response = await sendBlogPost(
currentState.title?.text,
currentState.youtube_url?.text,
currentState.youtubeURL?.text,
currentState.description?.text,
currentState.image,
);
if (response.statusCode == 200) {
@@ -78,34 +79,35 @@ class BlogPostCreateBloc
}
}
FutureOr<http.Response> sendBlogPost(
String? title,
String? youtube_url,
String? description,
) async {
String url = '${dotenv.env['SERVER_ROOT']!}/feed/create';
final box = await Hive.openBox('token');
final token = box.get('token');
sendBlogPost(String? title, String? youtubeURL, String? description,
XFile? image) async {
String url = '/blog_post/create';
final Map<String, dynamic> credentials = {
'title': title,
'description': description,
'youtube_url': youtube_url,
'youtube_url': youtubeURL,
'status': 'active',
'image': image,
};
var response = await http.post(
Uri.parse(url),
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer $token',
},
body: json.encode(credentials),
encoding: utf8,
var response = await sl<Dio>().post(
url,
data: json.encode(credentials),
);
return response;
}
FutureOr<void> _uploadImages(
UploadImages event, Emitter<BlogPostCreateState> emit) {}
UploadImages event, Emitter<BlogPostCreateState> emit) {
if (state is BlogPostCreateLoadedState) {
var currentState = state as BlogPostCreateLoadedState;
emit(
currentState.copyWith(
image: event.image,
),
);
}
}
}

View File

@@ -27,33 +27,37 @@ class BlogPostCreateLoadedState extends BlogPostCreateState {
this.blog_postModelObj,
this.title,
this.description,
this.youtube_url,
this.youtubeURL,
this.image,
this.isLoading = false,
});
BlogPostCreateModel? blog_postModelObj;
TextEditingController? title;
TextEditingController? description;
TextEditingController? youtube_url;
TextEditingController? youtubeURL;
String? token;
bool isLoading;
XFile? image;
@override
List<Object?> get props =>
[blog_postModelObj, title, description, youtube_url];
[blog_postModelObj, title, description, youtubeURL];
BlogPostCreateLoadedState copyWith({
BlogPostCreateModel? blog_postModelObj,
TextEditingController? title,
TextEditingController? description,
TextEditingController? youtube_url,
TextEditingController? youtubeURL,
String? token,
XFile? image,
bool isLoading = false,
}) {
return BlogPostCreateLoadedState(
blog_postModelObj: blog_postModelObj ?? this.blog_postModelObj,
title: title ?? this.title,
description: description ?? this.description,
youtube_url: youtube_url ?? this.youtube_url,
youtubeURL: youtubeURL ?? this.youtubeURL,
token: token ?? this.token,
image: image ?? this.image,
isLoading: this.isLoading,
);
}

View File

@@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:eatvio_app/widgets/custom_text_form_field.dart';
import 'package:eatvio_app/widgets/custom_outlined_button.dart';
import 'dart:io';
class BlogPostCreateScreen extends StatelessWidget {
const BlogPostCreateScreen({Key? key}) : super(key: key);
@@ -43,6 +44,7 @@ class BlogPostCreateScreen extends StatelessWidget {
}
_buildBlogPostCreate(BuildContext context, BlogPostCreateLoadedState state) {
print(state.image);
return Scaffold(
resizeToAvoidBottomInset: false,
body: Container(
@@ -85,7 +87,7 @@ class BlogPostCreateScreen extends StatelessWidget {
Text("Ссылка на YouTube",
style: theme.textTheme.bodyLarge),
CustomTextFormField(
controller: state.youtube_url,
controller: state.youtubeURL,
validator: _validateYouTubeLink,
),
SizedBox(height: 8.0),
@@ -101,7 +103,7 @@ class BlogPostCreateScreen extends StatelessWidget {
// Add widget for image upload here
CustomOutlinedButton(
onPressed: () => _submitForm(context, state.title!.text,
state.description!.text, state.youtube_url!.text),
state.description!.text, state.youtubeURL!.text),
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
text: 'Создать',
),
@@ -144,7 +146,7 @@ _buildAddPhoto(BuildContext context, BlogPostCreateLoadedState state) {
onTap: addImage(context, isLoading),
child: Stack(
children: [
if (!isLoading)
if (state.image == null)
Container(
margin: EdgeInsets.symmetric(horizontal: 10.h),
padding: EdgeInsets.symmetric(
@@ -158,8 +160,6 @@ _buildAddPhoto(BuildContext context, BlogPostCreateLoadedState state) {
mainAxisAlignment: MainAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: [
// Показывайте прелоадер, если загрузка активна
Padding(
padding: EdgeInsets.only(
left: 7.h,
@@ -178,7 +178,7 @@ _buildAddPhoto(BuildContext context, BlogPostCreateLoadedState state) {
],
),
),
if (isLoading)
if (state.image != null)
Container(
margin: EdgeInsets.symmetric(horizontal: 10.h),
padding: EdgeInsets.symmetric(
@@ -188,12 +188,8 @@ _buildAddPhoto(BuildContext context, BlogPostCreateLoadedState state) {
decoration: AppDecoration.fillGray.copyWith(
borderRadius: BorderRadiusStyle.roundedBorder5,
),
child: Center(
child: CircularProgressIndicator(
color: appTheme
.blueGray300), // Замените на свой прелоадер при необходимости
),
),
child: Image(image: FileImage(File(state.image!.path))),
)
],
),
);

View File

@@ -0,0 +1,16 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'blog_post_create_credentials_model.freezed.dart';
part 'blog_post_create_credentials_model.g.dart';
@freezed
class BlogPostCreateCredentialsModel with _$BlogPostCreateCredentialsModel {
const factory BlogPostCreateCredentialsModel({
required String title,
required String description,
@JsonKey(name: 'youtube_url') required String youtubeUrl,
@Default('active') String status,
}) = _BlogPostCreateCredentialsModel;
factory BlogPostCreateCredentialsModel.fromJson(Map<String, Object?> json) =>
_$BlogPostCreateCredentialsModelFromJson(json);
}

View File

@@ -0,0 +1,235 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'blog_post_create_credentials_model.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
BlogPostCreateCredentialsModel _$BlogPostCreateCredentialsModelFromJson(
Map<String, dynamic> json) {
return _BlogPostCreateCredentialsModel.fromJson(json);
}
/// @nodoc
mixin _$BlogPostCreateCredentialsModel {
String get title => throw _privateConstructorUsedError;
String get description => throw _privateConstructorUsedError;
@JsonKey(name: 'youtube_url')
String get youtubeUrl => throw _privateConstructorUsedError;
String get status => throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$BlogPostCreateCredentialsModelCopyWith<BlogPostCreateCredentialsModel>
get copyWith => throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $BlogPostCreateCredentialsModelCopyWith<$Res> {
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});
}
/// @nodoc
class _$BlogPostCreateCredentialsModelCopyWithImpl<$Res,
$Val extends BlogPostCreateCredentialsModel>
implements $BlogPostCreateCredentialsModelCopyWith<$Res> {
_$BlogPostCreateCredentialsModelCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
@pragma('vm:prefer-inline')
@override
$Res call({
Object? title = null,
Object? description = null,
Object? youtubeUrl = null,
Object? status = null,
}) {
return _then(_value.copyWith(
title: null == title
? _value.title
: title // ignore: cast_nullable_to_non_nullable
as String,
description: null == description
? _value.description
: description // ignore: cast_nullable_to_non_nullable
as String,
youtubeUrl: null == youtubeUrl
? _value.youtubeUrl
: youtubeUrl // ignore: cast_nullable_to_non_nullable
as String,
status: null == status
? _value.status
: status // ignore: cast_nullable_to_non_nullable
as String,
) as $Val);
}
}
/// @nodoc
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});
}
/// @nodoc
class __$$BlogPostCreateCredentialsModelImplCopyWithImpl<$Res>
extends _$BlogPostCreateCredentialsModelCopyWithImpl<$Res,
_$BlogPostCreateCredentialsModelImpl>
implements _$$BlogPostCreateCredentialsModelImplCopyWith<$Res> {
__$$BlogPostCreateCredentialsModelImplCopyWithImpl(
_$BlogPostCreateCredentialsModelImpl _value,
$Res Function(_$BlogPostCreateCredentialsModelImpl) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@override
$Res call({
Object? title = null,
Object? description = null,
Object? youtubeUrl = null,
Object? status = null,
}) {
return _then(_$BlogPostCreateCredentialsModelImpl(
title: null == title
? _value.title
: title // ignore: cast_nullable_to_non_nullable
as String,
description: null == description
? _value.description
: description // ignore: cast_nullable_to_non_nullable
as String,
youtubeUrl: null == youtubeUrl
? _value.youtubeUrl
: youtubeUrl // ignore: cast_nullable_to_non_nullable
as String,
status: null == status
? _value.status
: status // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
@JsonSerializable()
class _$BlogPostCreateCredentialsModelImpl
implements _BlogPostCreateCredentialsModel {
const _$BlogPostCreateCredentialsModelImpl(
{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);
@override
final String title;
@override
final String description;
@override
@JsonKey(name: 'youtube_url')
final String youtubeUrl;
@override
@JsonKey()
final String status;
@override
String toString() {
return 'BlogPostCreateCredentialsModel(title: $title, description: $description, youtubeUrl: $youtubeUrl, status: $status)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(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.status, status) || other.status == status));
}
@JsonKey(ignore: true)
@override
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);
@override
Map<String, dynamic> toJson() {
return _$$BlogPostCreateCredentialsModelImplToJson(
this,
);
}
}
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;
@override
String get title;
@override
String get description;
@override
@JsonKey(name: 'youtube_url')
String get youtubeUrl;
@override
String get status;
@override
@JsonKey(ignore: true)
_$$BlogPostCreateCredentialsModelImplCopyWith<
_$BlogPostCreateCredentialsModelImpl>
get copyWith => throw _privateConstructorUsedError;
}

View File

@@ -0,0 +1,25 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'blog_post_create_credentials_model.dart';
// **************************************************************************
// 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',
);
Map<String, dynamic> _$$BlogPostCreateCredentialsModelImplToJson(
_$BlogPostCreateCredentialsModelImpl instance) =>
<String, dynamic>{
'title': instance.title,
'description': instance.description,
'youtube_url': instance.youtubeUrl,
'status': instance.status,
};

View File

@@ -0,0 +1,20 @@
import 'package:eatvio_app/presentation/feed/blog_post_create_screen/models/blog_post_create_credentials_model.dart';
import 'package:eatvio_app/presentation/feed/blog_post_create_screen/services/blog_service.dart';
class BlogRepository {
final BlogService blogService;
BlogRepository({
required this.blogService,
});
Future<BlogPostCreateCredentialsModel> createBlogPost(
BlogPostCreateCredentialsModel model) async {
try {
return (await blogService.createBlogPost(model));
} catch (e) {
print(e);
throw (e);
}
}
}

View File

@@ -0,0 +1,14 @@
import 'package:eatvio_app/presentation/feed/blog_post_create_screen/models/blog_post_create_credentials_model.dart';
import 'package:dio/dio.dart';
import 'package:retrofit/retrofit.dart';
part 'blog_service.g.dart';
@RestApi()
abstract class BlogService {
factory BlogService(Dio dio, {String baseUrl}) = _BlogService;
@POST('/blog_post/create')
Future<BlogPostCreateCredentialsModel> createBlogPost(
BlogPostCreateCredentialsModel model);
}

View File

@@ -0,0 +1,78 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'blog_service.dart';
// **************************************************************************
// RetrofitGenerator
// **************************************************************************
// ignore_for_file: unnecessary_brace_in_string_interps,no_leading_underscores_for_local_identifiers
class _BlogService implements BlogService {
_BlogService(
this._dio, {
this.baseUrl,
});
final Dio _dio;
String? baseUrl;
@override
Future<BlogPostCreateCredentialsModel> createBlogPost(
BlogPostCreateCredentialsModel model) async {
final _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{};
final _headers = <String, dynamic>{};
const Map<String, dynamic>? _data = null;
final _result = await _dio.fetch<Map<String, dynamic>>(
_setStreamType<BlogPostCreateCredentialsModel>(Options(
method: 'POST',
headers: _headers,
extra: _extra,
)
.compose(
_dio.options,
'/blog_post/create',
queryParameters: queryParameters,
data: _data,
)
.copyWith(
baseUrl: _combineBaseUrls(
_dio.options.baseUrl,
baseUrl,
))));
final value = BlogPostCreateCredentialsModel.fromJson(_result.data!);
return value;
}
RequestOptions _setStreamType<T>(RequestOptions requestOptions) {
if (T != dynamic &&
!(requestOptions.responseType == ResponseType.bytes ||
requestOptions.responseType == ResponseType.stream)) {
if (T == String) {
requestOptions.responseType = ResponseType.plain;
} else {
requestOptions.responseType = ResponseType.json;
}
}
return requestOptions;
}
String _combineBaseUrls(
String dioBaseUrl,
String? baseUrl,
) {
if (baseUrl == null || baseUrl.trim().isEmpty) {
return dioBaseUrl;
}
final url = Uri.parse(baseUrl);
if (url.isAbsolute) {
return url.toString();
}
return Uri.parse(dioBaseUrl).resolveUri(url).toString();
}
}