diff --git a/aait/mobile/group-1/blog_app/lib/Injection/article_injection.dart b/aait/mobile/group-1/blog_app/lib/Injection/article_injection.dart new file mode 100644 index 000000000..44f0e87b8 --- /dev/null +++ b/aait/mobile/group-1/blog_app/lib/Injection/article_injection.dart @@ -0,0 +1,45 @@ +// import 'package:blog_app/features/Article/domain/usecases/get_article.dart'; +// import 'package:blog_app/features/Article/presentation/bloc/article_bloc/article_bloc.dart'; +// import 'package:blog_app/features/blog/data/datasources/remote_remote_data_source.dart'; +// import 'package:blog_app/features/blog/data/repository/article_repository_implimentation.dart'; +// import 'package:blog_app/features/blog/domain/entities/create_article_entity.dart'; +// import 'package:blog_app/features/blog/domain/repositories/article_repository.dart'; +// import 'package:blog_app/features/blog/domain/usecases/create_article.dart'; +// import 'package:blog_app/features/blog/domain/usecases/update_article.dart'; +// import 'package:get_it/get_it.dart'; +// import 'package:internet_connection_checker/internet_connection_checker.dart'; +// import 'package:shared_preferences/shared_preferences.dart'; + +// import '../core/network/network_info.dart'; +// import '../features/Article/domain/entities/article_enitity.dart'; + +// final sl = GetIt.instance; + +// Future init() async { +// //! Features - Task Management +// // Bloc +// sl.registerFactory(() => ArticleBloc()); + +// // Use cases +// sl.registerLazySingleton(() => GetArticle(sl())); +// sl.registerLazySingleton(() => CreateArticle(sl())); +// sl.registerLazySingleton(() => UpdateArticle(sl())); + +// // Repository +// sl.registerLazySingleton( +// () => ArticleRemoteDataSourceImpl()); +// sl.registerLazySingleton( +// () => ArticleRepositoryImpl(networkInfo: sl(), remoteDataSource: sl())); + +// // classes +// sl.registerLazySingleton(() =>
[]); +// sl.registerLazySingleton(() => []); +// //! Core +// sl.registerLazySingleton(() => NetworkInfoImpl(sl())); + +// //! External +// sl.registerLazySingleton(() => InternetConnectionChecker()); + +// final sharedPreferences = await SharedPreferences.getInstance(); +// sl.registerLazySingleton(() => sharedPreferences); +// } diff --git a/aait/mobile/group-1/blog_app/lib/features/Article/data/datasources/remote_remote_data_source.dart b/aait/mobile/group-1/blog_app/lib/features/Article/data/datasources/remote_remote_data_source.dart index 8a3108c9d..28cfe90f6 100644 --- a/aait/mobile/group-1/blog_app/lib/features/Article/data/datasources/remote_remote_data_source.dart +++ b/aait/mobile/group-1/blog_app/lib/features/Article/data/datasources/remote_remote_data_source.dart @@ -1,10 +1,13 @@ import 'dart:convert'; +import 'dart:io'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:http/http.dart' as http; import '../../../../core/utils/constants.dart'; import '../models/article_model.dart'; import '../models/create_article_model.dart'; +import 'package:http_parser/http_parser.dart'; +import 'package:mime/mime.dart'; abstract class ArticleRemoteDataSource { Future postArticle(CreateArticleModel articleModel); @@ -15,23 +18,75 @@ abstract class ArticleRemoteDataSource { class ArticleRemoteDataSourceImpl extends ArticleRemoteDataSource { @override Future postArticle(CreateArticleModel articleModel) async { - final String? token = await getToken(); - final response = await http.post(Uri.parse('$baseApi/article'), - headers: {'Content-Type': 'application/json', "token": token!}, - body: json.encode(articleModel.toJson())); + String? token = await getToken(); + final uri = Uri.parse('$baseApi/article'); + var request = http.MultipartRequest('POST', uri); - return ArticleModel.fromJson(jsonDecode(response.body)); + Map headers = { + HttpHeaders.authorizationHeader: + 'Bearer $token', // Add your authentication token here + }; + + request.headers.addAll(headers); + File imageFile = File(articleModel.image); + final String fileType = MimeTypeResolver().lookup(imageFile.path) ?? ""; + + request.files.add(http.MultipartFile( + 'photo', imageFile.readAsBytes().asStream(), imageFile.lengthSync(), + filename: imageFile.path.split("/").last, + contentType: MediaType.parse(fileType))); + + request.fields['title'] = articleModel.title; + request.fields['subTitle'] = articleModel.subTitle; + request.fields['tags'] = jsonEncode(articleModel.tags); + request.fields['content'] = articleModel.content; + request.fields['estimatedReadTime'] = articleModel.estimatedtime; + + var response = await request.send(); + + if (response.statusCode == 200) { + return articleModel as ArticleModel; + } + throw ("Couldn't Post Article ${response.statusCode}"); } @override Future updateArticle(CreateArticleModel articleModel) async { - final String? token = await getToken(); + String? token = await getToken(); + print("something wrong"); + if (articleModel.id == null) { + throw ("Couldn't Post Article"); + } final id = articleModel.id; - final response = await http.post(Uri.parse('$baseApi/article/$id'), - headers: {'Content-Type': 'application/json', "token": token!}, - body: json.encode(articleModel.toJson())); + final uri = Uri.parse('$baseApi/article/$id'); + var request = http.MultipartRequest('PUT', uri); - return ArticleModel.fromJson(jsonDecode(response.body)); + Map headers = { + HttpHeaders.authorizationHeader: + 'Bearer $token', // Add your authentication token here + }; + + request.headers.addAll(headers); + File imageFile = File(articleModel.image); + final String fileType = MimeTypeResolver().lookup(imageFile.path) ?? ""; + + request.files.add(http.MultipartFile( + 'photo', imageFile.readAsBytes().asStream(), imageFile.lengthSync(), + filename: imageFile.path.split("/").last, + contentType: MediaType.parse(fileType))); + + request.fields['title'] = articleModel.title; + request.fields['subTitle'] = articleModel.subTitle; + request.fields['tags'] = jsonEncode(articleModel.tags); + request.fields['content'] = articleModel.content; + request.fields['estimatedReadTime'] = articleModel.estimatedtime; + + var response = await request.send(); + + if (response.statusCode == 200) { + return articleModel as ArticleModel; + } + throw ("Couldn't Post Article ${response.statusCode}"); } @override diff --git a/aait/mobile/group-1/blog_app/lib/features/Article/data/models/article_model.dart b/aait/mobile/group-1/blog_app/lib/features/Article/data/models/article_model.dart index 49cffd156..e89484892 100644 --- a/aait/mobile/group-1/blog_app/lib/features/Article/data/models/article_model.dart +++ b/aait/mobile/group-1/blog_app/lib/features/Article/data/models/article_model.dart @@ -12,8 +12,8 @@ class ArticleModel extends Article implements Equatable { required this.tags, this.user, required this.content, - this.image, - this.estimatedtime, + required this.image, + required this.estimatedtime, this.imageCloudinaryPublicId, this.createdAt}) : super( @@ -42,9 +42,9 @@ class ArticleModel extends Article implements Equatable { @override final String content; @override - final String? image; + final String image; @override - final String? estimatedtime; + final String estimatedtime; @override final String? imageCloudinaryPublicId; @override @@ -69,7 +69,9 @@ class ArticleModel extends Article implements Equatable { 'title': articleModel.title, 'subTitle': articleModel.subTitle, 'content': articleModel.content, - 'tags': jsonEncode(articleModel.tags) + 'tags': jsonEncode(articleModel.tags), + 'image': articleModel.image, + 'estimatedReadTime': articleModel.estimatedtime }; } diff --git a/aait/mobile/group-1/blog_app/lib/features/Article/data/models/create_article_model.dart b/aait/mobile/group-1/blog_app/lib/features/Article/data/models/create_article_model.dart index b2909459a..5db2617ea 100644 --- a/aait/mobile/group-1/blog_app/lib/features/Article/data/models/create_article_model.dart +++ b/aait/mobile/group-1/blog_app/lib/features/Article/data/models/create_article_model.dart @@ -11,8 +11,8 @@ class CreateArticleModel extends CreateArticleEntity implements Equatable { required this.tags, required this.content, this.id, - this.image, - this.estimatedtime, + required this.image, + required this.estimatedtime, }) : super( title: title, subTitle: subTitle, @@ -33,9 +33,9 @@ class CreateArticleModel extends CreateArticleEntity implements Equatable { @override final String content; @override - final String? image; + final String image; @override - final String? estimatedtime; + final String estimatedtime; factory CreateArticleModel.fromJson(Map json) { return CreateArticleModel( @@ -52,7 +52,9 @@ class CreateArticleModel extends CreateArticleEntity implements Equatable { 'title': title, 'subTitle': subTitle, 'content': content, - 'tags': tags + 'tags': tags, + 'image': image, + 'estimatedReadTime': estimatedtime }; } diff --git a/aait/mobile/group-1/blog_app/lib/features/Article/data/repository/article_repository_implimentation.dart b/aait/mobile/group-1/blog_app/lib/features/Article/data/repository/article_repository_implimentation.dart index 0527fb144..707dd5eb0 100644 --- a/aait/mobile/group-1/blog_app/lib/features/Article/data/repository/article_repository_implimentation.dart +++ b/aait/mobile/group-1/blog_app/lib/features/Article/data/repository/article_repository_implimentation.dart @@ -22,7 +22,9 @@ class ArticleRepositoryImpl extends ArticleRepository { title: article.title, subTitle: article.subTitle, tags: article.tags, - content: article.content); + content: article.content, + image: article.image, + estimatedtime: article.estimatedtime); final isConnected = await networkInfo.isConnected; if (isConnected) { final article = await remoteDataSource.postArticle(createArticleModel); @@ -39,7 +41,9 @@ class ArticleRepositoryImpl extends ArticleRepository { title: article.title, subTitle: article.subTitle, tags: article.tags, - content: article.content); + content: article.content, + image: article.image, + estimatedtime: article.estimatedtime); final isConnected = await networkInfo.isConnected; if (isConnected) { final article = await remoteDataSource.updateArticle(createArticleModel); diff --git a/aait/mobile/group-1/blog_app/lib/features/Article/domain/entities/article_enitity.dart b/aait/mobile/group-1/blog_app/lib/features/Article/domain/entities/article_enitity.dart index 3877ff705..20596fc7c 100644 --- a/aait/mobile/group-1/blog_app/lib/features/Article/domain/entities/article_enitity.dart +++ b/aait/mobile/group-1/blog_app/lib/features/Article/domain/entities/article_enitity.dart @@ -5,8 +5,8 @@ class Article { final String? user; final List tags; final String content; - final String? image; - final String? estimatedtime; + final String image; + final String estimatedtime; final String? imageCloudinaryPublicId; final DateTime? createdAt; @@ -17,8 +17,8 @@ class Article { required this.tags, this.user, required this.content, - this.image, - this.estimatedtime, + required this.image, + required this.estimatedtime, this.imageCloudinaryPublicId, this.createdAt}); } diff --git a/aait/mobile/group-1/blog_app/lib/features/Article/domain/entities/create_article_entity.dart b/aait/mobile/group-1/blog_app/lib/features/Article/domain/entities/create_article_entity.dart index 8e63111cd..d980247e9 100644 --- a/aait/mobile/group-1/blog_app/lib/features/Article/domain/entities/create_article_entity.dart +++ b/aait/mobile/group-1/blog_app/lib/features/Article/domain/entities/create_article_entity.dart @@ -4,8 +4,8 @@ class CreateArticleEntity { final String subTitle; final List tags; final String content; - final String? image; - final String? estimatedtime; + final String image; + final String estimatedtime; CreateArticleEntity( {this.id, @@ -13,6 +13,6 @@ class CreateArticleEntity { required this.subTitle, required this.tags, required this.content, - this.image, - this.estimatedtime}); + required this.image, + required this.estimatedtime}); } diff --git a/aait/mobile/group-1/blog_app/lib/features/Article/presentation/bloc/article_bloc/article_bloc.dart b/aait/mobile/group-1/blog_app/lib/features/Article/presentation/bloc/article_bloc/article_bloc.dart index 61c04ff2a..3e38fb183 100644 --- a/aait/mobile/group-1/blog_app/lib/features/Article/presentation/bloc/article_bloc/article_bloc.dart +++ b/aait/mobile/group-1/blog_app/lib/features/Article/presentation/bloc/article_bloc/article_bloc.dart @@ -3,7 +3,7 @@ import 'package:blog_app/features/Article/domain/usecases/create_article.dart'; import 'package:blog_app/features/Article/domain/usecases/get_article.dart'; import 'package:blog_app/features/Article/domain/usecases/update_article.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; - +import '../../../../../Injection/article_injection.dart'; import '../../../../../Injection/injection_container.dart'; import '../../../../../core/network/network_info.dart'; import '../../../data/datasources/remote_remote_data_source.dart'; @@ -22,12 +22,16 @@ class ArticleBloc extends Bloc { on((CreateArticleEvent event, Emitter emit) async { CreateArticle usecase = CreateArticle(repository); + emit(Loading()); await usecase.repository.createArticle(event.article); + emit(Idle()); }); on((UpdateArticleEvent event, Emitter emit) async { UpdateArticle usecase = UpdateArticle(repository); + emit(Loading()); await usecase.repository.updateArticle(event.article); + emit(Idle()); }); on((GetArticleEvent event, Emitter emit) async { diff --git a/aait/mobile/group-1/blog_app/lib/features/Article/presentation/pages/create_article.dart b/aait/mobile/group-1/blog_app/lib/features/Article/presentation/pages/create_article.dart index c18948501..3e1fe5b07 100644 --- a/aait/mobile/group-1/blog_app/lib/features/Article/presentation/pages/create_article.dart +++ b/aait/mobile/group-1/blog_app/lib/features/Article/presentation/pages/create_article.dart @@ -5,11 +5,15 @@ import 'package:blog_app/features/Article/presentation/bloc/article_bloc/article import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import '../../../../core/utils/colors.dart'; +import '../../../authentication_and_authorization/presentation/pages/circular_indicator.dart'; import '../bloc/article_bloc/article_bloc.dart'; import '../bloc/article_bloc/article_state.dart'; +import 'package:image_picker/image_picker.dart'; +import 'dart:io'; class ArticlePage extends StatefulWidget { - const ArticlePage({super.key, this.id}); + const ArticlePage({super.key, this.id = "64eda97cb00b3d3d2fe86ec5"}); final String? id; @override @@ -23,38 +27,42 @@ class _ArticlePageState extends State { } List tags = [ - "Sports", - "Tech", - "Politics", - "Art", - "Design", - "Culture", - "Production", - "Others", + "sports", + "tech", + "politics", + "art", + "design", + "culture", + "production", + "others", ]; // List selected = List.generate(tags.length, (index) => false); Map tagsMap = { - "Sports": false, - "Tech": false, - "Politics": false, - "Art": false, - "Design": false, - "Culture": false, - "Production": false, - "Others": false, + "sports": false, + "tech": false, + "politics": false, + "art": false, + "design": false, + "culture": false, + "production": false, + "others": false, }; - @override - Widget build(BuildContext context) { - Article? initialArticle; - bool tonalSelected = true; + Article? initialArticle; + bool tonalSelected = true; - final formKey = GlobalKey(); + final formKey = GlobalKey(); - final titleController = TextEditingController(); - final subTitleController = TextEditingController(); - final articleContent = TextEditingController(); + final titleController = TextEditingController(); + final subTitleController = TextEditingController(); + final articleContent = TextEditingController(); + final estimatedReadTimeContoller = TextEditingController(); + final ImagePicker picker = ImagePicker(); + Widget articleImage = Icon(Icons.place); + var imagePath; + @override + Widget build(BuildContext context) { return Scaffold( body: BlocBuilder( builder: (context, state) { @@ -68,207 +76,370 @@ class _ArticlePageState extends State { titleController.text = state.article.title; subTitleController.text = state.article.subTitle; articleContent.text = state.article.subTitle; + estimatedReadTimeContoller.text = state.article.estimatedtime; for (String tag in state.article.tags) { tagsMap[tag] = true; } }); } - return SafeArea( - child: Container( - padding: const EdgeInsets.all(25), - height: MediaQuery.of(context).size.height, - child: ListView( - children: [ - Row( - children: [ - IconButton.filledTonal( - isSelected: tonalSelected, - icon: const Icon(Icons.arrow_back_ios_new_rounded), - onPressed: () { - Navigator.of(context).pop(); + if (state is Idle) { + return SafeArea( + child: Container( + width: MediaQuery.of(context).size.width, + padding: const EdgeInsets.all(25), + height: MediaQuery.of(context).size.height, + child: ListView( + children: [ + Row( + children: [ + IconButton.filledTonal( + isSelected: tonalSelected, + icon: const Icon(Icons.arrow_back_ios_new_rounded), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + SizedBox( + width: MediaQuery.of(context).size.width * 0.1, + ), + const Text( + "New Article", + style: TextStyle( + fontSize: 23, fontWeight: FontWeight.w600), + ), + ], + ), + Container( + width: MediaQuery.of(context).size.width * 0.5, + margin: const EdgeInsets.only(top: 5, bottom: 10), + child: TextButton( + onPressed: () async { + await showDialog( + context: context, + builder: (BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + margin: EdgeInsets.only(bottom: 8), + child: TextButton( + style: ButtonStyle( + padding: + const MaterialStatePropertyAll( + EdgeInsets.symmetric( + vertical: 15, + horizontal: 25)), + backgroundColor: + MaterialStatePropertyAll( + Colors.grey[200])), + onPressed: () async { + final XFile? image = + await picker.pickImage( + source: + ImageSource.gallery); + + setState(() { + articleImage = + Image.file(File(image!.path)); + imagePath = image.path; + }); + }, + child: Text( + "Gallery", + style: TextStyle( + fontSize: 22, + color: Colors.grey[800], + fontWeight: FontWeight.w500, + ), + ), + ), + ), + TextButton( + style: ButtonStyle( + padding: MaterialStatePropertyAll( + EdgeInsets.symmetric( + vertical: 15, + horizontal: 25)), + backgroundColor: + MaterialStatePropertyAll( + Colors.grey[200])), + onPressed: () async { + final XFile? photo = + await picker.pickImage( + source: ImageSource.camera); + + setState(() { + articleImage = + Image.file(File(photo!.path)); + imagePath = photo.path; + }); + }, + child: Text( + "Camera", + style: TextStyle( + fontSize: 22, + color: Colors.grey[800], + fontWeight: FontWeight.w500, + ), + ), + ), + ]); + }); }, + child: Stack( + alignment: Alignment.center, + children: [ + SizedBox( + width: 80, height: 80, child: articleImage), + Stack( + alignment: Alignment.bottomCenter, + children: [ + Container( + width: 92, + height: 92, + decoration: ShapeDecoration( + shape: RoundedRectangleBorder( + side: const BorderSide( + width: 1, color: kLightBlue), + borderRadius: BorderRadius.circular(28), + ), + ), + ), + ], + ), + ], + ), ), - SizedBox( - width: MediaQuery.of(context).size.width * 0.1, - ), - const Text( - "New Article", - style: TextStyle( - fontSize: 23, fontWeight: FontWeight.w600), - ), - ], - ), - Padding( - padding: - const EdgeInsets.symmetric(vertical: 7, horizontal: 15), - child: TextFormField( - controller: titleController, - keyboardType: TextInputType.name, - decoration: const InputDecoration( - hintText: "Add title", - hintStyle: TextStyle( - color: Colors.grey, - fontSize: 20.0, - fontWeight: FontWeight.w300), + ), + Padding( + padding: const EdgeInsets.symmetric( + vertical: 7, horizontal: 15), + child: TextFormField( + controller: titleController, + keyboardType: TextInputType.name, + decoration: const InputDecoration( + hintText: "Add title", + hintStyle: TextStyle( + color: Colors.grey, + fontSize: 20.0, + fontWeight: FontWeight.w300), + ), + style: const TextStyle( + color: Colors.black, + fontSize: 22.0, + fontWeight: FontWeight.w500), + validator: (String? name) { + if (name == null || name.isEmpty) { + return "Name can not be empty"; + } + return null; + }, ), - style: const TextStyle( - color: Colors.black, - fontSize: 22.0, - fontWeight: FontWeight.w500), - validator: (String? name) { - if (name == null || name.isEmpty) { - return "Name can not be empty"; - } - return null; - }, ), - ), - Padding( - padding: - const EdgeInsets.symmetric(vertical: 7, horizontal: 15), - child: TextFormField( - controller: subTitleController, - keyboardType: TextInputType.name, - decoration: const InputDecoration( - hintText: "Add subtitle", - hintStyle: TextStyle( - color: Colors.grey, - fontSize: 20.0, - fontWeight: FontWeight.w300), + Padding( + padding: const EdgeInsets.symmetric( + vertical: 7, horizontal: 15), + child: TextFormField( + controller: subTitleController, + keyboardType: TextInputType.name, + decoration: const InputDecoration( + hintText: "Add subtitle", + hintStyle: TextStyle( + color: Colors.grey, + fontSize: 20.0, + fontWeight: FontWeight.w300), + ), + style: const TextStyle( + color: Colors.black, + fontSize: 21.0, + fontWeight: FontWeight.w400), + validator: (String? name) { + if (name == null || name.isEmpty) { + return "Name can not be empty"; + } + return null; + }, ), - style: const TextStyle( - color: Colors.black, - fontSize: 21.0, - fontWeight: FontWeight.w400), - validator: (String? name) { - if (name == null || name.isEmpty) { - return "Name can not be empty"; - } - return null; - }, ), - ), - Padding( - padding: - const EdgeInsets.symmetric(vertical: 7, horizontal: 15), - child: Wrap( - spacing: 8.0, - runSpacing: 4.0, - children: List.generate(tags.length, (index) { - return ChoiceChip( - label: Text(tags[index]), - selected: tagsMap[tags[index]]!, - onSelected: (isSelected) { - setState(() { - tagsMap[tags[index]] = isSelected; - }); - }, - elevation: 0, - backgroundColor: tagsMap[tags[index]]! - ? const Color(0xFF376AED) - : Colors.grey[300], - selectedColor: const Color(0xFF376AED), - labelStyle: TextStyle( - color: tagsMap[tags[index]]! - ? Colors.white - : Colors.black, + Padding( + padding: const EdgeInsets.symmetric( + vertical: 7, horizontal: 15), + child: Wrap( + spacing: 8.0, + runSpacing: 4.0, + children: List.generate(tags.length, (index) { + return ChoiceChip( + label: Text(tags[index]), + selected: tagsMap[tags[index]]!, + onSelected: (isSelected) { + setState(() { + tagsMap[tags[index]] = isSelected; + }); + }, + elevation: 0, + backgroundColor: tagsMap[tags[index]]! + ? const Color(0xFF376AED) + : Colors.grey[300], + selectedColor: const Color(0xFF376AED), + labelStyle: TextStyle( + color: tagsMap[tags[index]]! + ? Colors.white + : Colors.black, + ), + ); + })), + ), + Padding( + padding: const EdgeInsets.symmetric( + vertical: 7, horizontal: 15), + child: TextFormField( + controller: articleContent, + maxLines: 11, + keyboardType: TextInputType.name, + decoration: const InputDecoration( + border: OutlineInputBorder( + borderRadius: + BorderRadius.all(Radius.circular(10.0)), + borderSide: + BorderSide(color: Colors.grey, width: 0)), + hintText: "article content", + hintStyle: TextStyle( + color: Colors.grey, + fontSize: 17.0, + fontWeight: FontWeight.w300), + ), + style: const TextStyle( + // Style for input text + color: Colors.black, // Color of input text + fontSize: 19.0, + fontWeight: + FontWeight.w400 // Font size of input text ), - ); - })), - ), - Padding( - padding: - const EdgeInsets.symmetric(vertical: 7, horizontal: 15), - child: TextFormField( - controller: articleContent, - maxLines: 11, - keyboardType: TextInputType.name, - decoration: const InputDecoration( - border: OutlineInputBorder( - borderRadius: - BorderRadius.all(Radius.circular(10.0)), - borderSide: - BorderSide(color: Colors.grey, width: 0)), - hintText: "article content", - hintStyle: TextStyle( - color: Colors.grey, - fontSize: 17.0, - fontWeight: FontWeight.w300), + validator: (String? name) { + if (name == null || name.isEmpty) { + return "Name can not be empty"; + } + return null; + }, ), - style: const TextStyle( - // Style for input text - color: Colors.black, // Color of input text - fontSize: 19.0, - fontWeight: FontWeight.w400 // Font size of input text - ), - validator: (String? name) { - if (name == null || name.isEmpty) { - return "Name can not be empty"; - } - return null; - }, ), - ), - Padding( - padding: const EdgeInsets.symmetric( - vertical: 10, horizontal: 15), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const SizedBox(), - ElevatedButton( - onPressed: () async { - final formValid = formKey.currentState!.validate(); - if (!formValid) { - return; - } - - final bloc = BlocProvider.of(context); - final tags = tagsMap.keys - .where((key) => tagsMap[key]!) - .toList(); - CreateArticleEntity article = CreateArticleEntity( - id: initialArticle!.id, - title: titleController.text, - subTitle: subTitleController.text, - tags: tags, - content: articleContent.text); - if (widget.id != null && initialArticle != null) { - bloc.add(UpdateArticleEvent(article)); - } else { - bloc.add(CreateArticleEvent(article)); + Padding( + padding: const EdgeInsets.symmetric( + vertical: 7, horizontal: 15), + child: Column(children: [ + const Text("Estimated read time:"), + TextFormField( + controller: articleContent, + keyboardType: TextInputType.number, + decoration: const InputDecoration( + border: OutlineInputBorder( + borderRadius: + BorderRadius.all(Radius.circular(10.0)), + borderSide: + BorderSide(color: Colors.grey, width: 0)), + hintText: "5 min", + hintStyle: TextStyle( + color: Colors.grey, + fontSize: 15.0, + fontWeight: FontWeight.w300), + ), + style: const TextStyle( + // Style for input text + color: Colors.black, // Color of input text + fontSize: 19.0, + fontWeight: + FontWeight.w400 // Font size of input text + ), + validator: (String? name) { + if (name == null || name.isEmpty) { + return "Name can not be empty"; } - Navigator.pop(context); + return null; }, - style: ButtonStyle( - backgroundColor: MaterialStateProperty.all( - const Color(0xFF376AED)), - shape: MaterialStateProperty.all< - RoundedRectangleBorder>( - const RoundedRectangleBorder( - borderRadius: - BorderRadius.all(Radius.circular(50)), + ), + ]), + ), + Padding( + padding: const EdgeInsets.symmetric( + vertical: 10, horizontal: 15), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const SizedBox(), + ElevatedButton( + onPressed: () async { + if (formKey.currentState != null) { + final formValid = + formKey.currentState!.validate(); + if (!formValid) { + return; + } + } + + final bloc = + BlocProvider.of(context); + final tags = tagsMap.keys + .where((key) => tagsMap[key]!) + .toList(); + + if (widget.id != null && initialArticle != null) { + CreateArticleEntity article = CreateArticleEntity( + id: initialArticle!.id, + image: imagePath, + title: titleController.text, + subTitle: subTitleController.text, + tags: tags, + content: articleContent.text, + estimatedtime: + "${estimatedReadTimeContoller.text}min"); + bloc.add(UpdateArticleEvent(article)); + } else { + CreateArticleEntity article = + CreateArticleEntity( + // id: initialArticle!.id, + title: titleController.text, + image: imagePath, + subTitle: subTitleController.text, + tags: tags, + content: articleContent.text, + estimatedtime: + "${estimatedReadTimeContoller.text}min"); + bloc.add(CreateArticleEvent(article)); + } + Navigator.pop(context); + }, + style: ButtonStyle( + backgroundColor: MaterialStateProperty.all( + const Color(0xFF376AED)), + shape: MaterialStateProperty.all< + RoundedRectangleBorder>( + const RoundedRectangleBorder( + borderRadius: + BorderRadius.all(Radius.circular(50)), + ), ), ), - ), - child: const Padding( - padding: EdgeInsets.symmetric( - vertical: 8.0, horizontal: 15.0), - child: Text( - style: - TextStyle(fontSize: 17, color: Colors.white), - "Publish", + child: const Padding( + padding: EdgeInsets.symmetric( + vertical: 8.0, horizontal: 15.0), + child: Text( + style: TextStyle( + fontSize: 17, color: Colors.white), + "Publish", + ), ), ), - ), - const SizedBox() - ], - ), - ) - ], + const SizedBox() + ], + ), + ) + ], + ), ), - ), - ); + ); + } else { + return const CircularIndicator(); + } }, ), );