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

@@ -3,7 +3,7 @@ import 'package:eatvio_app/locator_service.dart';
import 'package:hive_flutter/hive_flutter.dart';
abstract class TokenStorage {
static addTokenToDioHeader(String token) {
static _addTokenToDioHeader(String token) {
sl<Dio>().options.headers['Authorization'] = 'Bearer $token';
}
@@ -13,20 +13,19 @@ abstract class TokenStorage {
if (token == null) {
return false;
} else {
addTokenToDioHeader(token);
_addTokenToDioHeader(token);
return true;
}
}
static getTokenFromStorage() async => await Hive.openBox('token')
..get('token');
static Future<String> getTokenFromStorage() async =>
(await Hive.openBox('token')).get('token');
static saveTokenInStorage(String token) async {
await Hive.openBox('token')
..put('token', token);
addTokenToDioHeader(token);
(await Hive.openBox('token')).put('token', token);
_addTokenToDioHeader(token);
}
static removeTokenInStorage(String token) async => await Hive.openBox('token')
..delete('token');
static removeTokenInStorage() async =>
(await Hive.openBox('token')).delete('token');
}

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();
}
}

View File

@@ -44,22 +44,29 @@ class BlogPostEditBloc extends Bloc<BlogPostEditEvent, BlogPostEditState> {
if (data is Map<String, dynamic>) {
if (state is BlogPostEditLoadedState) {
var currentState = state as BlogPostEditLoadedState;
emit(currentState.copyWith(
emit(
currentState.copyWith(
blog_postModelObj: currentState.blog_postModelObj,
blog_post: currentState.blog_post,
title: currentState.title,
description: currentState.description,
youtube_url: currentState.youtube_url,),);
youtubeURL: currentState.youtubeURL,
),
);
} else {
emit(BlogPostEditLoadedState(
emit(
BlogPostEditLoadedState(
blog_post: data,
title: TextEditingController(text: data['title']),
description: TextEditingController(text: data['description']),
youtube_url: TextEditingController(text: data['youtube_url']),),);
youtubeURL: TextEditingController(text: data['youtubeURL']),
),
);
}
} else {
// Один или несколько ключевых полей отсутствуют, обрабатываем ошибку
emit(BlogPostEditErrorState('Некоторые необходимые данные отсутствуют.'));
emit(BlogPostEditErrorState(
'Некоторые необходимые данные отсутствуют.'));
}
} else {
// Статус ответа не равен 200, обрабатываем ошибку
@@ -71,21 +78,24 @@ 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;
var response = await sendBlogPost(
currentState.blog_post,
currentState.title?.text,
currentState.youtube_url?.text,
currentState.youtubeURL?.text,
currentState.description?.text,
);
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}');
}
}
}
@@ -93,7 +103,7 @@ class BlogPostEditBloc extends Bloc<BlogPostEditEvent, BlogPostEditState> {
FutureOr<http.Response> sendBlogPost(
Map<String, dynamic>? blog_post,
String? title,
String? youtube_url,
String? youtubeURL,
String? description,
) async {
int id = blog_post?['id'];
@@ -103,7 +113,7 @@ class BlogPostEditBloc extends Bloc<BlogPostEditEvent, BlogPostEditState> {
'id': id,
'title': title,
'description': description,
'youtube_url': youtube_url,
'youtubeURL': youtubeURL,
};
var response = await http.post(

View File

@@ -22,34 +22,35 @@ class BlogPostEditState extends Equatable {
}
class BlogPostEditLoadedState extends BlogPostEditState {
BlogPostEditLoadedState(
{this.blog_postModelObj,
BlogPostEditLoadedState({
this.blog_postModelObj,
this.blog_post,
this.title,
this.description,
this.youtube_url,});
this.youtubeURL,
});
BlogPostEditModel? blog_postModelObj;
Map<String, dynamic>? blog_post;
TextEditingController? title;
TextEditingController? description;
TextEditingController? youtube_url;
TextEditingController? youtubeURL;
@override
List<Object?> get props =>
[blog_postModelObj, blog_post, title, description, youtube_url];
[blog_postModelObj, blog_post, title, description, youtubeURL];
BlogPostEditLoadedState copyWith({
BlogPostEditModel? blog_postModelObj,
Map<String, dynamic>? blog_post,
TextEditingController? title,
TextEditingController? description,
TextEditingController? youtube_url,
TextEditingController? youtubeURL,
}) {
return BlogPostEditLoadedState(
blog_postModelObj: blog_postModelObj ?? this.blog_postModelObj,
blog_post: blog_post ?? this.blog_post,
title: title ?? this.title,
description: description ?? this.description,
youtube_url: youtube_url ?? this.youtube_url,
youtubeURL: youtubeURL ?? this.youtubeURL,
);
}
}

View File

@@ -10,9 +10,12 @@ class BlogPostEditScreen extends StatelessWidget {
static Widget builder(BuildContext context, blog_postID) {
return BlocProvider<BlogPostEditBloc>(
create: (context) =>
BlogPostEditBloc(BlogPostEditState(blog_postModelObj: BlogPostEditModel(blog_postID: blog_postID.toString())))..add(BlogPostEditInitialEvent()),
child: BlogPostEditScreen(),);
create: (context) => BlogPostEditBloc(BlogPostEditState(
blog_postModelObj:
BlogPostEditModel(blog_postID: blog_postID.toString())))
..add(BlogPostEditInitialEvent()),
child: BlogPostEditScreen(),
);
}
Widget build(BuildContext context) {
@@ -74,7 +77,8 @@ class BlogPostEditScreen extends StatelessWidget {
mainAxisSize: MainAxisSize.min,
children: [
CachedNetworkImage(
imageUrl: dotenv.env['STORAGE_URL']! + (state.blog_post?['image'] ?? ''),
imageUrl: dotenv.env['STORAGE_URL']! +
(state.blog_post?['image'] ?? ''),
imageBuilder: (context, imageProvider) => CircleAvatar(
backgroundImage: imageProvider,
radius: 25,
@@ -98,13 +102,14 @@ class BlogPostEditScreen extends StatelessWidget {
),
SizedBox(height: 16.0),
TextFormField(
controller: state.youtube_url,
controller: state.youtubeURL,
decoration: InputDecoration(labelText: 'Ссылка'),
),
SizedBox(height: 16.0),
// Здесь нужно добавить виджет для загрузки картинки
ElevatedButton(
onPressed: () => context.read<BlogPostEditBloc>().add(EditBlogPost()),
onPressed: () =>
context.read<BlogPostEditBloc>().add(EditBlogPost()),
child: Text('Сохранить'),
),
],