elCaribe app - customization and branding
This commit is contained in:
1428
news-app/lib/ui/screens/AddEditNews/AddNews.dart
Normal file
1428
news-app/lib/ui/screens/AddEditNews/AddNews.dart
Normal file
File diff suppressed because it is too large
Load Diff
164
news-app/lib/ui/screens/AddEditNews/ManageUserNews.dart
Normal file
164
news-app/lib/ui/screens/AddEditNews/ManageUserNews.dart
Normal file
@@ -0,0 +1,164 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:news/cubits/Auth/authCubit.dart';
|
||||
import 'package:news/cubits/GetUserDraftedNewsCubit.dart';
|
||||
import 'package:news/data/repositories/Settings/settingsLocalDataRepository.dart';
|
||||
import 'package:news/ui/screens/AddEditNews/Widgets/userAllNews.dart';
|
||||
import 'package:news/ui/screens/AddEditNews/Widgets/userDrafterNews.dart';
|
||||
import 'package:news/ui/widgets/customTextLabel.dart';
|
||||
import 'package:shimmer/shimmer.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:news/cubits/getUserNewsCubit.dart';
|
||||
import 'package:news/app/routes.dart';
|
||||
import 'package:news/utils/uiUtils.dart';
|
||||
|
||||
class ManageUserNews extends StatefulWidget {
|
||||
const ManageUserNews({super.key});
|
||||
|
||||
@override
|
||||
ManageUserNewsState createState() => ManageUserNewsState();
|
||||
}
|
||||
|
||||
class ManageUserNewsState extends State<ManageUserNews> with TickerProviderStateMixin {
|
||||
final bool _isButtonExtended = true;
|
||||
late final ScrollController controller = ScrollController()..addListener(hasMoreNewsScrollListener);
|
||||
late final ScrollController draftController = ScrollController()..addListener(hasMoreDraftedNewsScrollListener);
|
||||
|
||||
Set<String> get locationValue => SettingsLocalDataRepository().getLocationCityValues();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
getNews();
|
||||
getUserDraftedNews();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void getUserDraftedNews() {
|
||||
context.read<GetUserDraftedNewsCubit>().getGetUserDraftedNews(userId: int.parse(context.read<AuthCubit>().getUserId()));
|
||||
}
|
||||
|
||||
void getNews() {
|
||||
context.read<GetUserNewsCubit>().getGetUserNews(latitude: locationValue.first, longitude: locationValue.last);
|
||||
}
|
||||
|
||||
void getMoreNews() {
|
||||
context.read<GetUserNewsCubit>().getMoreGetUserNews(latitude: locationValue.first, longitude: locationValue.last);
|
||||
}
|
||||
|
||||
void hasMoreNewsScrollListener() {
|
||||
if (controller.position.maxScrollExtent == controller.offset) {
|
||||
if (context.read<GetUserNewsCubit>().hasMoreGetUserNews()) {
|
||||
getMoreNews();
|
||||
} else {}
|
||||
}
|
||||
}
|
||||
|
||||
void hasMoreDraftedNewsScrollListener() {
|
||||
if (draftController.position.maxScrollExtent == draftController.offset) {
|
||||
if (context.read<GetUserDraftedNewsCubit>().hasMoreGetUserDraftedNews()) {
|
||||
getUserDraftedNews();
|
||||
} else {}
|
||||
}
|
||||
}
|
||||
|
||||
getAppBar() {
|
||||
return PreferredSize(
|
||||
preferredSize: const Size(double.infinity, 45),
|
||||
child: UiUtils.applyBoxShadow(
|
||||
context: context,
|
||||
child: AppBar(
|
||||
centerTitle: false,
|
||||
backgroundColor: Colors.transparent,
|
||||
title: Transform(
|
||||
transform: Matrix4.translationValues(-20.0, 0.0, 0.0),
|
||||
child: CustomTextLabel(
|
||||
text: 'manageNewsLbl',
|
||||
textStyle: Theme.of(context).textTheme.titleLarge?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer, fontWeight: FontWeight.w600, letterSpacing: 0.5))),
|
||||
leading: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10.0),
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
splashColor: Colors.transparent,
|
||||
highlightColor: Colors.transparent,
|
||||
child: Icon(Icons.arrow_back, color: UiUtils.getColorScheme(context).primaryContainer)),
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
newsAddBtn() {
|
||||
return Column(mainAxisAlignment: MainAxisAlignment.end, children: [
|
||||
FloatingActionButton(
|
||||
isExtended: _isButtonExtended,
|
||||
backgroundColor: UiUtils.getColorScheme(context).surface,
|
||||
child: Icon(Icons.add, size: 32, color: UiUtils.getColorScheme(context).primaryContainer),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pushNamed(Routes.addNews, arguments: {"isEdit": false, "from": "myNews"});
|
||||
}),
|
||||
const SizedBox(height: 10)
|
||||
]);
|
||||
}
|
||||
|
||||
contentShimmer(BuildContext context) {
|
||||
return Shimmer.fromColors(
|
||||
baseColor: Colors.grey.withOpacity(0.6),
|
||||
highlightColor: Colors.grey,
|
||||
child: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
padding: const EdgeInsetsDirectional.only(start: 20, end: 20),
|
||||
itemBuilder: (_, i) =>
|
||||
Container(decoration: BoxDecoration(borderRadius: BorderRadius.circular(10.0), color: Colors.grey.withOpacity(0.6)), margin: const EdgeInsets.only(top: 20), height: 190.0),
|
||||
itemCount: 6));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DefaultTabController(
|
||||
length: 2,
|
||||
child: Scaffold(
|
||||
appBar: getAppBar(),
|
||||
floatingActionButton: newsAddBtn(),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(10), color: UiUtils.getColorScheme(context).surface),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 8),
|
||||
margin: const EdgeInsetsDirectional.only(start: 10, end: 10),
|
||||
child: TabBar(
|
||||
indicator: BoxDecoration(
|
||||
color: UiUtils.getColorScheme(context).primaryContainer,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
labelColor: UiUtils.getColorScheme(context).surface,
|
||||
unselectedLabelColor: UiUtils.getColorScheme(context).primaryContainer,
|
||||
tabs: [
|
||||
Tab(text: UiUtils.getTranslatedLabel(context, 'manageNewsAllLbl')),
|
||||
Tab(text: UiUtils.getTranslatedLabel(context, 'manageNewsDraftLbl')),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: TabBarView(
|
||||
children: [
|
||||
UserAllNewsTab(controller: controller, contentShimmer: contentShimmer(context), fetchNews: getNews, fetchMoreNews: getMoreNews),
|
||||
UserDrafterNewsTab(controller: draftController, contentShimmer: contentShimmer(context), fetchDraftedNews: getUserDraftedNews, fetchMoreDraftedNews: getUserDraftedNews),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
302
news-app/lib/ui/screens/AddEditNews/NewsDescription.dart
Normal file
302
news-app/lib/ui/screens/AddEditNews/NewsDescription.dart
Normal file
@@ -0,0 +1,302 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:html_editor_plus/html_editor.dart';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:news/cubits/appSystemSettingCubit.dart';
|
||||
import 'package:news/ui/screens/AddEditNews/Widgets/geminiService.dart';
|
||||
import 'package:news/ui/widgets/SnackBarWidget.dart';
|
||||
import 'package:news/ui/widgets/customTextLabel.dart';
|
||||
import 'package:news/utils/uiUtils.dart';
|
||||
import 'package:shimmer/shimmer.dart';
|
||||
import 'package:news/ui/styles/colors.dart';
|
||||
|
||||
class NewsDescription extends StatefulWidget {
|
||||
String? description;
|
||||
Function changeDesc;
|
||||
Function? validateDesc;
|
||||
int? from;
|
||||
String? summDescription;
|
||||
Function? isDraftNews;
|
||||
|
||||
NewsDescription(this.description, this.summDescription, this.changeDesc, this.validateDesc, this.from, this.isDraftNews, {super.key});
|
||||
|
||||
@override
|
||||
NewsDescriptionState createState() => NewsDescriptionState();
|
||||
}
|
||||
|
||||
class NewsDescriptionState extends State<NewsDescription> {
|
||||
String result = '';
|
||||
bool isLoading = true;
|
||||
bool isSubmitted = false;
|
||||
|
||||
final HtmlEditorController controller = HtmlEditorController();
|
||||
|
||||
final TextEditingController summaryController = TextEditingController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
setValue();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
setValue() async {
|
||||
Future.delayed(
|
||||
const Duration(seconds: 4),
|
||||
() {
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
getAppBar() {
|
||||
return PreferredSize(
|
||||
preferredSize: const Size(double.infinity, 45),
|
||||
child: UiUtils.applyBoxShadow(
|
||||
context: context,
|
||||
child: AppBar(
|
||||
centerTitle: false,
|
||||
backgroundColor: Colors.transparent,
|
||||
title: Transform(
|
||||
transform: Matrix4.translationValues(-20.0, 0.0, 0.0),
|
||||
child: CustomTextLabel(
|
||||
text: widget.from == 2 ? 'editNewsLbl' : 'createNewsLbl',
|
||||
textStyle: Theme.of(context).textTheme.titleLarge?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer, fontWeight: FontWeight.w600, letterSpacing: 0.5),
|
||||
),
|
||||
),
|
||||
leading: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10.0),
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
controller.getText().then((value) {
|
||||
widget.changeDesc(value, summaryController.text, false);
|
||||
});
|
||||
},
|
||||
splashColor: Colors.transparent,
|
||||
highlightColor: Colors.transparent,
|
||||
child: Icon(Icons.arrow_back, color: UiUtils.getColorScheme(context).primaryContainer),
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
Container(
|
||||
padding: const EdgeInsetsDirectional.only(end: 20),
|
||||
alignment: Alignment.center,
|
||||
child: CustomTextLabel(text: 'step2of2Lbl', textStyle: Theme.of(context).textTheme.bodySmall!.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.6))),
|
||||
)
|
||||
],
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
Widget nextBtn() {
|
||||
return (isSubmitted)
|
||||
? UiUtils.showCircularProgress(true, Theme.of(context).primaryColor)
|
||||
: Padding(
|
||||
padding: const EdgeInsetsDirectional.all(10),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: InkWell(
|
||||
onTap: () => validateTextAndSubmit(isDraft: 1),
|
||||
splashColor: Colors.transparent,
|
||||
child: Container(
|
||||
height: 45.0,
|
||||
width: MediaQuery.of(context).size.width * 0.4,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(7.0),
|
||||
border: Border.all(color: UiUtils.getColorScheme(context).primaryContainer),
|
||||
),
|
||||
child: CustomTextLabel(
|
||||
text: 'saveAsDraftLbl',
|
||||
textStyle: Theme.of(context)
|
||||
.textTheme
|
||||
.titleLarge
|
||||
?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer, fontWeight: FontWeight.w600, fontSize: 18, letterSpacing: 0.6))),
|
||||
)),
|
||||
SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: InkWell(
|
||||
splashColor: Colors.transparent,
|
||||
child: Container(
|
||||
height: 45.0,
|
||||
width: MediaQuery.of(context).size.width * 0.4,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(color: UiUtils.getColorScheme(context).primaryContainer, borderRadius: BorderRadius.circular(7.0)),
|
||||
child: CustomTextLabel(
|
||||
text: 'publishBtnLbl',
|
||||
textStyle: Theme.of(context).textTheme.titleLarge?.copyWith(color: UiUtils.getColorScheme(context).surface, fontWeight: FontWeight.w600, fontSize: 18, letterSpacing: 0.6),
|
||||
),
|
||||
),
|
||||
onTap: () => validateTextAndSubmit(isDraft: 0),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void validateTextAndSubmit({required int isDraft}) {
|
||||
widget.isDraftNews!(isDraft);
|
||||
controller.getText().then((value) {
|
||||
isSubmitted = true;
|
||||
widget.validateDesc!(value, summaryController.text);
|
||||
});
|
||||
}
|
||||
|
||||
Widget shimmer() {
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
child: Shimmer.fromColors(
|
||||
baseColor: Colors.grey[300]!,
|
||||
highlightColor: Colors.grey[100]!,
|
||||
child: Container(
|
||||
height: MediaQuery.of(context).size.height * 0.741,
|
||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(10), color: Theme.of(context).cardColor),
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (widget.description != null && widget.description != "") controller.setText(widget.description!); //incase of Edit
|
||||
return Scaffold(
|
||||
appBar: getAppBar(),
|
||||
bottomNavigationBar: nextBtn(),
|
||||
body: PopScope(
|
||||
canPop: false,
|
||||
onPopInvoked: (bool isTrue) {
|
||||
controller.getText().then((value) {
|
||||
widget.changeDesc(value, summaryController.text, false);
|
||||
});
|
||||
},
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
if (!kIsWeb) {
|
||||
FocusScope.of(context).unfocus(); //dismiss keyboard
|
||||
}
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsetsDirectional.all(20),
|
||||
child: isLoading
|
||||
? shimmer()
|
||||
: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: MediaQuery.of(context).size.height * 0.45,
|
||||
child: Theme(
|
||||
data: Theme.of(context).copyWith(textTheme: TextTheme(titleSmall: Theme.of(context).textTheme.titleMedium!.copyWith(color: Colors.orange))),
|
||||
child: HtmlEditor(
|
||||
controller: controller,
|
||||
htmlEditorOptions: HtmlEditorOptions(
|
||||
hint: UiUtils.getTranslatedLabel(context, 'descLbl'),
|
||||
adjustHeightForKeyboard: true,
|
||||
autoAdjustHeight: true,
|
||||
shouldEnsureVisible: true,
|
||||
spellCheck: true,
|
||||
disabled: false),
|
||||
htmlToolbarOptions: HtmlToolbarOptions(
|
||||
toolbarPosition: ToolbarPosition.aboveEditor,
|
||||
toolbarType: ToolbarType.nativeExpandable,
|
||||
gridViewHorizontalSpacing: 0,
|
||||
gridViewVerticalSpacing: 0,
|
||||
toolbarItemHeight: 30,
|
||||
buttonColor: UiUtils.getColorScheme(context).primaryContainer,
|
||||
buttonFocusColor: Theme.of(context).primaryColor,
|
||||
buttonBorderColor: Colors.red,
|
||||
buttonFillColor: secondaryColor,
|
||||
dropdownIconColor: Theme.of(context).primaryColor,
|
||||
dropdownIconSize: 26,
|
||||
textStyle: Theme.of(context).textTheme.titleMedium!.copyWith(color: UiUtils.getColorScheme(context).primaryContainer),
|
||||
onButtonPressed: (ButtonType type, bool? status, Function? updateStatus) {
|
||||
return true;
|
||||
},
|
||||
onDropdownChanged: (DropdownType type, dynamic changed, Function(dynamic)? updateSelectedItem) {
|
||||
return true;
|
||||
},
|
||||
mediaLinkInsertInterceptor: (String url, InsertFileType type) {
|
||||
return true;
|
||||
},
|
||||
mediaUploadInterceptor: (PlatformFile file, InsertFileType type) async {
|
||||
return true;
|
||||
},
|
||||
),
|
||||
otherOptions: OtherOptions(
|
||||
height: MediaQuery.of(context).size.height * 0.725,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
color: UiUtils.getColorScheme(context).surface,
|
||||
),
|
||||
),
|
||||
callbacks: Callbacks(
|
||||
onChangeCodeview: (String? changed) {
|
||||
result = changed!;
|
||||
},
|
||||
onImageUploadError: (
|
||||
FileUpload? file,
|
||||
String? base64Str,
|
||||
UploadError error,
|
||||
) {},
|
||||
onNavigationRequestMobile: (String url) {
|
||||
return NavigationActionPolicy.ALLOW;
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
summarySection()
|
||||
],
|
||||
),
|
||||
),
|
||||
)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget summarySection() {
|
||||
if (widget.summDescription != null && widget.summDescription!.isNotEmpty) summaryController.text = widget.summDescription!;
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(height: 20),
|
||||
CustomTextLabel(text: UiUtils.getTranslatedLabel(context, 'summarizedDescription'), textStyle: Theme.of(context).textTheme.titleMedium),
|
||||
const SizedBox(height: 10),
|
||||
TextField(
|
||||
controller: summaryController,
|
||||
maxLines: 3,
|
||||
readOnly: true,
|
||||
decoration: InputDecoration(
|
||||
hintText: UiUtils.getTranslatedLabel(context, 'clickSummarizeDescription'),
|
||||
border: OutlineInputBorder(borderRadius: BorderRadius.circular(10)),
|
||||
filled: true,
|
||||
contentPadding: EdgeInsets.all(5),
|
||||
fillColor: UiUtils.getColorScheme(context).surface)),
|
||||
const SizedBox(height: 10),
|
||||
Center(
|
||||
child: ElevatedButton(
|
||||
onPressed: () async {
|
||||
String fullText = await controller.getText();
|
||||
if (fullText.trim().isEmpty) return showSnackBar('enterDescriptionFirst', context);
|
||||
String summary = await GeminiService.summarizeDescription(fullText, context.read<AppConfigurationCubit>().getGeminiAPiKey());
|
||||
setState(() {
|
||||
summaryController.text = summary;
|
||||
});
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: UiUtils.getColorScheme(context).primaryContainer,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(7)),
|
||||
),
|
||||
child: Text(
|
||||
UiUtils.getTranslatedLabel(context, 'summarizedDescription'),
|
||||
style: Theme.of(context).textTheme.titleMedium?.copyWith(color: UiUtils.getColorScheme(context).surface, fontWeight: FontWeight.w600),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
251
news-app/lib/ui/screens/AddEditNews/Widgets/UserNewsWidgets.dart
Normal file
251
news-app/lib/ui/screens/AddEditNews/Widgets/UserNewsWidgets.dart
Normal file
@@ -0,0 +1,251 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:news/app/routes.dart';
|
||||
import 'package:news/cubits/deleteUserNewsCubit.dart';
|
||||
import 'package:news/cubits/getUserNewsCubit.dart';
|
||||
import 'package:news/cubits/themeCubit.dart';
|
||||
import 'package:news/data/models/NewsModel.dart';
|
||||
import 'package:news/ui/styles/appTheme.dart';
|
||||
import 'package:news/ui/styles/colors.dart';
|
||||
import 'package:news/ui/widgets/SnackBarWidget.dart';
|
||||
import 'package:news/ui/widgets/customTextBtn.dart';
|
||||
import 'package:news/ui/widgets/customTextLabel.dart';
|
||||
import 'package:news/ui/widgets/networkImage.dart';
|
||||
import 'package:news/ui/screens/auth/Widgets/svgPictureWidget.dart';
|
||||
import 'package:news/utils/hiveBoxKeys.dart';
|
||||
import 'package:news/utils/uiUtils.dart';
|
||||
|
||||
class UsernewsWidgets {
|
||||
static final labelKeys = {"standard_post": 'stdPostLbl', "video_youtube": 'videoYoutubeLbl', "video_other": 'videoOtherUrlLbl', "video_upload": 'videoUploadLbl'};
|
||||
|
||||
static buildNewsContainer(
|
||||
{required BuildContext context,
|
||||
required NewsModel model,
|
||||
required int index,
|
||||
required int totalCurrentNews,
|
||||
required bool hasMoreNewsFetchError,
|
||||
required bool hasMore,
|
||||
required Function fetchMoreNews}) {
|
||||
if (index == totalCurrentNews - 1 && index != 0) {
|
||||
if (hasMore) {
|
||||
if (hasMoreNewsFetchError) {
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 8.0),
|
||||
child: IconButton(onPressed: () => fetchMoreNews, icon: Icon(Icons.error, color: Theme.of(context).primaryColor)),
|
||||
));
|
||||
} else {
|
||||
return Center(child: Padding(padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 8.0), child: UiUtils.showCircularProgress(true, Theme.of(context).primaryColor)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return InkWell(
|
||||
splashColor: Colors.transparent,
|
||||
highlightColor: Colors.transparent,
|
||||
onTap: () {
|
||||
Navigator.of(context).pushNamed(Routes.newsDetails, arguments: {"model": model, "isFromBreak": false, "fromShowMore": false});
|
||||
},
|
||||
child: Container(
|
||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(10.0), color: UiUtils.getColorScheme(context).surface),
|
||||
padding: const EdgeInsetsDirectional.all(15),
|
||||
margin: const EdgeInsets.only(top: 20),
|
||||
child: SizedBox(
|
||||
width: MediaQuery.of(context).size.width * 0.24,
|
||||
height: MediaQuery.of(context).size.height * 0.26,
|
||||
child: Column(mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
newsImage(imageURL: model.image!, context: context),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 10.0),
|
||||
child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||
categoryName(context: context, categoryName: (model.categoryName != null && model.categoryName!.trim().isNotEmpty) ? model.categoryName! : ""),
|
||||
setDate(context: context, dateValue: model.date!)
|
||||
]),
|
||||
),
|
||||
Spacer(),
|
||||
deleteAndEditButton(context: context, isEdit: true, onTap: () => Navigator.of(context).pushNamed(Routes.addNews, arguments: {"model": model, "isEdit": true, "from": "myNews"})),
|
||||
deleteAndEditButton(context: context, isEdit: false, onTap: () => deleteNewsDialogue(context, model.id!, index))
|
||||
],
|
||||
),
|
||||
Divider(thickness: 2),
|
||||
Expanded(
|
||||
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||
CustomTextLabel(
|
||||
text: model.title!,
|
||||
maxLines: 3,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
softWrap: true,
|
||||
textStyle: Theme.of(context).textTheme.titleMedium!.copyWith(color: UiUtils.getColorScheme(context).primaryContainer, fontWeight: FontWeight.w700)),
|
||||
contentTypeView(context: context, model: model),
|
||||
])),
|
||||
Divider(thickness: 2),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
(model.isExpired == 1)
|
||||
? Container(
|
||||
child: Row(
|
||||
children: [
|
||||
SvgPictureWidget(
|
||||
assetName: 'expiredNews',
|
||||
assetColor:
|
||||
(context.read<ThemeCubit>().state.appTheme == AppTheme.Dark ? ColorFilter.mode(darkIconColor, BlendMode.srcIn) : ColorFilter.mode(iconColor, BlendMode.srcIn))),
|
||||
SizedBox(width: 2.5),
|
||||
Text(
|
||||
UiUtils.getTranslatedLabel(context, 'expiredKey'),
|
||||
style: TextStyle(color: (context.read<ThemeCubit>().state.appTheme == AppTheme.Dark ? darkIconColor : iconColor), fontWeight: FontWeight.w500),
|
||||
),
|
||||
],
|
||||
))
|
||||
: SizedBox.shrink(),
|
||||
(model.status == "0")
|
||||
? Container(
|
||||
padding: EdgeInsets.symmetric(vertical: 3, horizontal: 5),
|
||||
child: Tooltip(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 20),
|
||||
decoration: BoxDecoration(color: UiUtils.getColorScheme(context).primaryContainer, borderRadius: BorderRadius.circular(10)),
|
||||
textStyle: Theme.of(context).textTheme.bodyMedium?.copyWith(color: UiUtils.getColorScheme(context).secondary, fontSize: 10),
|
||||
message: UiUtils.getTranslatedLabel(context, 'newsCreatedSuccessfully'),
|
||||
child: Row(
|
||||
children: [
|
||||
SvgPictureWidget(
|
||||
assetName: 'deactivatedNews',
|
||||
assetColor: (context.read<ThemeCubit>().state.appTheme == AppTheme.Dark
|
||||
? const ColorFilter.mode(darkIconColor, BlendMode.srcIn)
|
||||
: const ColorFilter.mode(iconColor, BlendMode.srcIn))),
|
||||
const SizedBox(width: 2.5),
|
||||
Text(
|
||||
UiUtils.getTranslatedLabel(context, 'deactivatedKey'),
|
||||
style: TextStyle(color: (context.read<ThemeCubit>().state.appTheme == AppTheme.Dark ? darkIconColor : iconColor), fontWeight: FontWeight.bold),
|
||||
),
|
||||
],
|
||||
),
|
||||
))
|
||||
: SizedBox.shrink(),
|
||||
],
|
||||
),
|
||||
]),
|
||||
)));
|
||||
}
|
||||
|
||||
static Widget newsImage({required BuildContext context, required String imageURL}) {
|
||||
return ClipRRect(
|
||||
borderRadius: BorderRadius.circular(45),
|
||||
child: CustomNetworkImage(networkImageUrl: imageURL, fit: BoxFit.cover, height: MediaQuery.of(context).size.width * 0.18, isVideo: false, width: MediaQuery.of(context).size.width * 0.18));
|
||||
}
|
||||
|
||||
static Widget categoryName({required BuildContext context, required String categoryName}) {
|
||||
return (categoryName.trim().isNotEmpty)
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(top: 4),
|
||||
child: CustomTextLabel(
|
||||
text: categoryName,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
softWrap: true,
|
||||
textStyle: Theme.of(context).textTheme.bodyLarge!.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.9), fontSize: 16, fontWeight: FontWeight.w600)),
|
||||
)
|
||||
: const SizedBox.shrink();
|
||||
}
|
||||
|
||||
static Widget deleteAndEditButton({required BuildContext context, required bool isEdit, required void Function()? onTap}) {
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
padding: const EdgeInsetsDirectional.only(top: 3, bottom: 3, start: 5),
|
||||
alignment: Alignment.center,
|
||||
child: SvgPictureWidget(
|
||||
assetName: (isEdit) ? 'editMyNews' : 'deleteMyNews',
|
||||
height: 30,
|
||||
width: 30,
|
||||
fit: BoxFit.contain,
|
||||
assetColor: (isEdit) ? ColorFilter.mode(UiUtils.getColorScheme(context).onPrimary, BlendMode.srcIn) : null),
|
||||
));
|
||||
}
|
||||
|
||||
static Widget setDate({required BuildContext context, required String dateValue}) {
|
||||
DateTime time = DateTime.parse(dateValue);
|
||||
var newFormat = DateFormat("dd-MMM-yyyy", Hive.box(settingsBoxKey).get(currentLanguageCodeKey));
|
||||
final newNewsDate = newFormat.format(time);
|
||||
|
||||
return CustomTextLabel(
|
||||
text: newNewsDate,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
softWrap: true,
|
||||
textStyle: Theme.of(context).textTheme.bodySmall!.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.8)));
|
||||
}
|
||||
|
||||
static deleteNewsDialogue(BuildContext mainContext, String id, int index) async {
|
||||
await showDialog(
|
||||
context: mainContext,
|
||||
builder: (BuildContext context) {
|
||||
return StatefulBuilder(builder: (BuildContext context, StateSetter setStater) {
|
||||
return AlertDialog(
|
||||
backgroundColor: UiUtils.getColorScheme(context).surface,
|
||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5.0))),
|
||||
content: CustomTextLabel(text: 'doYouReallyNewsLbl', textStyle: Theme.of(context).textTheme.titleMedium),
|
||||
title: const CustomTextLabel(text: 'delNewsLbl'),
|
||||
titleTextStyle: Theme.of(context).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.w600),
|
||||
actions: <Widget>[
|
||||
CustomTextButton(
|
||||
textWidget: CustomTextLabel(
|
||||
text: 'noLbl', textStyle: Theme.of(context).textTheme.titleSmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer, fontWeight: FontWeight.bold)),
|
||||
onTap: () {
|
||||
Navigator.of(context).pop(false);
|
||||
}),
|
||||
BlocConsumer<DeleteUserNewsCubit, DeleteUserNewsState>(
|
||||
bloc: context.read<DeleteUserNewsCubit>(),
|
||||
listener: (context, state) {
|
||||
if (state is DeleteUserNewsSuccess) {
|
||||
context.read<GetUserNewsCubit>().deleteNews(index);
|
||||
showSnackBar(state.message, context);
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
builder: (context, state) {
|
||||
return CustomTextButton(
|
||||
textWidget: CustomTextLabel(
|
||||
text: 'yesLbl', textStyle: Theme.of(context).textTheme.titleSmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer, fontWeight: FontWeight.bold)),
|
||||
onTap: () async {
|
||||
context.read<DeleteUserNewsCubit>().setDeleteUserNews(newsId: id);
|
||||
});
|
||||
})
|
||||
],
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
static Widget contentTypeView({required BuildContext context, required NewsModel model}) {
|
||||
String contType = "";
|
||||
|
||||
final key = labelKeys[model.contentType];
|
||||
if (key != null) {
|
||||
contType = UiUtils.getTranslatedLabel(context, key);
|
||||
}
|
||||
return (model.contentType != "")
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(top: 7),
|
||||
child: Row(crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||
CustomTextLabel(
|
||||
text: 'contentTypeLbl',
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
softWrap: true,
|
||||
textStyle: Theme.of(context).textTheme.bodyLarge!.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withAlpha((0.3 * 255).round()))),
|
||||
CustomTextLabel(text: " : ", textStyle: Theme.of(context).textTheme.bodyLarge!.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withAlpha((0.3 * 255).round()))),
|
||||
CustomTextLabel(
|
||||
text: contType,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
softWrap: true,
|
||||
textStyle: Theme.of(context).textTheme.bodyMedium!.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withAlpha((0.3 * 255).round())))
|
||||
]),
|
||||
)
|
||||
: SizedBox.shrink();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:news/cubits/categoryCubit.dart';
|
||||
import 'package:news/cubits/locationCityCubit.dart';
|
||||
import 'package:news/cubits/updateBottomsheetContentCubit.dart';
|
||||
import 'package:news/cubits/tagCubit.dart';
|
||||
import 'package:news/utils/uiUtils.dart';
|
||||
import 'package:news/ui/widgets/customTextLabel.dart';
|
||||
|
||||
class CustomBottomsheet extends StatefulWidget {
|
||||
final BuildContext context;
|
||||
final String titleTxt;
|
||||
final int listLength;
|
||||
final String language_id;
|
||||
final NullableIndexedWidgetBuilder listViewChild;
|
||||
|
||||
const CustomBottomsheet({super.key, required this.context, required this.titleTxt, required this.listLength, required this.listViewChild, required this.language_id});
|
||||
|
||||
@override
|
||||
CustomBottomsheetState createState() => CustomBottomsheetState();
|
||||
}
|
||||
|
||||
class CustomBottomsheetState extends State<CustomBottomsheet> {
|
||||
late final ScrollController locationScrollController = ScrollController();
|
||||
late final ScrollController languageScrollController = ScrollController();
|
||||
late final ScrollController categoryScrollController = ScrollController();
|
||||
late final ScrollController subcategoryScrollController = ScrollController();
|
||||
late final ScrollController tagScrollController = ScrollController();
|
||||
|
||||
ScrollController scController = ScrollController();
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
initScrollController();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
disposeScrollController();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void initScrollController() {
|
||||
switch (widget.titleTxt) {
|
||||
case 'chooseLanLbl':
|
||||
scController = languageScrollController;
|
||||
break;
|
||||
case 'selCatLbl':
|
||||
scController = categoryScrollController;
|
||||
break;
|
||||
case 'selSubCatLbl':
|
||||
scController = subcategoryScrollController;
|
||||
break;
|
||||
case 'selTagLbl':
|
||||
scController = tagScrollController;
|
||||
break;
|
||||
case 'selLocationLbl':
|
||||
scController = locationScrollController;
|
||||
break;
|
||||
}
|
||||
scController.addListener(() => hasMoreLocationScrollListener());
|
||||
}
|
||||
|
||||
disposeScrollController() {
|
||||
switch (widget.titleTxt) {
|
||||
case 'chooseLanLbl':
|
||||
languageScrollController.dispose();
|
||||
break;
|
||||
case 'selCatLbl':
|
||||
categoryScrollController.dispose();
|
||||
break;
|
||||
case 'selSubCatLbl':
|
||||
subcategoryScrollController.dispose();
|
||||
break;
|
||||
case 'selTagLbl':
|
||||
tagScrollController.dispose();
|
||||
break;
|
||||
case 'selLocationLbl':
|
||||
locationScrollController.dispose();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void hasMoreLocationScrollListener() {
|
||||
if (scController.offset >= scController.position.maxScrollExtent && !scController.position.outOfRange) {
|
||||
switch (widget.titleTxt) {
|
||||
case 'selCatLbl':
|
||||
if (context.read<CategoryCubit>().hasMoreCategory()) {
|
||||
context.read<CategoryCubit>().getMoreCategory(langId: widget.language_id);
|
||||
}
|
||||
break;
|
||||
case 'selTagLbl':
|
||||
if (context.read<TagCubit>().hasMoreTags()) {
|
||||
context.read<TagCubit>().getMoreTags(langId: widget.language_id);
|
||||
}
|
||||
break;
|
||||
case 'selLocationLbl':
|
||||
if (context.read<LocationCityCubit>().hasMoreLocation()) {
|
||||
context.read<LocationCityCubit>().getMoreLocationCity();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Builder(
|
||||
builder: (BuildContext context) => BlocBuilder<BottomSheetCubit, BottomSheetState>(
|
||||
builder: (context, state) {
|
||||
int listLength = widget.listLength;
|
||||
switch (widget.titleTxt) {
|
||||
case 'selLocationLbl':
|
||||
listLength = state.locationData.length;
|
||||
break;
|
||||
case 'selTagLbl':
|
||||
listLength = state.tagsData.length;
|
||||
break;
|
||||
case 'chooseLanLbl':
|
||||
listLength = state.languageData.length;
|
||||
break;
|
||||
case 'selCatLbl':
|
||||
listLength = state.categoryData.length;
|
||||
}
|
||||
return DraggableScrollableSheet(
|
||||
snap: true,
|
||||
snapSizes: const [0.5, 0.9],
|
||||
expand: false,
|
||||
builder: (_, controller) {
|
||||
controller = scController;
|
||||
return Container(
|
||||
padding: const EdgeInsetsDirectional.only(bottom: 15.0, top: 15.0, start: 20.0, end: 20.0),
|
||||
decoration: BoxDecoration(borderRadius: const BorderRadius.only(topLeft: Radius.circular(30), topRight: Radius.circular(30)), color: UiUtils.getColorScheme(context).surface),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
CustomTextLabel(
|
||||
text: widget.titleTxt,
|
||||
textStyle: Theme.of(context).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold, color: UiUtils.getColorScheme(context).primaryContainer)),
|
||||
const SizedBox(height: 10),
|
||||
Expanded(
|
||||
child: ListView.builder(
|
||||
controller: controller,
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
padding: const EdgeInsetsDirectional.only(top: 10.0, bottom: 25.0),
|
||||
itemCount: listLength,
|
||||
itemBuilder: widget.listViewChild)),
|
||||
],
|
||||
));
|
||||
});
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
149
news-app/lib/ui/screens/AddEditNews/Widgets/geminiService.dart
Normal file
149
news-app/lib/ui/screens/AddEditNews/Widgets/geminiService.dart
Normal file
@@ -0,0 +1,149 @@
|
||||
import 'dart:convert';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:news/utils/api.dart';
|
||||
|
||||
/// AI Service Module using Google Gemini API
|
||||
/// This module contains all AI-related API calls for the application
|
||||
|
||||
class GeminiService {
|
||||
static const String _geminiModel = "gemini-2.0-flash"; // Or gemini-1.5-pro
|
||||
|
||||
/// Helper function to call Gemini API
|
||||
static Future<Map<String, dynamic>> _callGeminiAPI(String prompt, String apiKey) async {
|
||||
try {
|
||||
final requestBody = {
|
||||
"contents": [
|
||||
{
|
||||
"parts": [
|
||||
{"text": prompt}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
final url = Uri.parse("${Api.geminiMetaInfoApi}$_geminiModel:generateContent?key=$apiKey");
|
||||
|
||||
final response = await http.post(
|
||||
url,
|
||||
headers: {"Content-Type": "application/json"},
|
||||
body: jsonEncode(requestBody),
|
||||
);
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
final errorData = jsonDecode(response.body);
|
||||
throw Exception(errorData["error"]?["message"] ?? "Failed to generate content");
|
||||
}
|
||||
|
||||
return jsonDecode(response.body);
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate content
|
||||
static Future<String> generateContent({
|
||||
String? title,
|
||||
String? category,
|
||||
String? language,
|
||||
String? languageCode,
|
||||
required String apiKey,
|
||||
}) async {
|
||||
try {
|
||||
String fullPrompt = "You are a skilled news article writer. Create engaging and informative content.";
|
||||
|
||||
if (title != null) {
|
||||
fullPrompt += "\n\nWrite an article with the title: \"$title\"";
|
||||
}
|
||||
|
||||
if (category != null) {
|
||||
fullPrompt += "\nCategory: $category";
|
||||
}
|
||||
|
||||
if (language != null && languageCode != null) {
|
||||
fullPrompt += "\n\nIMPORTANT: Generate all content in $language language ($languageCode). The response MUST be in $language.";
|
||||
}
|
||||
|
||||
fullPrompt += "\n\nRequest: \n\nArticle:";
|
||||
|
||||
final response = await _callGeminiAPI(fullPrompt, apiKey);
|
||||
|
||||
return response["candidates"][0]["content"]["parts"][0]["text"];
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate meta info
|
||||
static Future<Map<String, dynamic>> generateMetaInfo({required String title, String? language, String? languageCode, required String apiKey}) async {
|
||||
try {
|
||||
String languageInstruction = "";
|
||||
if (language != null && languageCode != null) {
|
||||
languageInstruction = "\n\nIMPORTANT: Generate all content in $language language ($languageCode). The response MUST be in same language as title.";
|
||||
}
|
||||
|
||||
final prompt = """
|
||||
You are an SEO expert. Generate meta title, description, keywords, and a slug for this news article titled: "$title".$languageInstruction
|
||||
|
||||
Return ONLY a JSON object with these fields:
|
||||
- meta_title
|
||||
- meta_description
|
||||
- meta_keywords
|
||||
- slug
|
||||
|
||||
Response must be valid JSON.
|
||||
""";
|
||||
|
||||
final response = await _callGeminiAPI(prompt, apiKey);
|
||||
final responseText = response["candidates"][0]["content"]["parts"][0]["text"].trim();
|
||||
|
||||
try {
|
||||
return jsonDecode(responseText);
|
||||
} catch (_) {
|
||||
final match = RegExp(r"\{[\s\S]*\}").firstMatch(responseText);
|
||||
if (match != null) {
|
||||
return jsonDecode(match.group(0)!);
|
||||
}
|
||||
return {
|
||||
"meta_title": title,
|
||||
"meta_description": "Read about $title in our latest news article.",
|
||||
"meta_keywords": title.toLowerCase().split(" ").join(","),
|
||||
"slug": title.toLowerCase().replaceAll(RegExp(r'[^a-z0-9]+'), "-").replaceAll(RegExp(r'^-|-$'), ""),
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
/// Summarize description
|
||||
static Future<String> summarizeDescription(String description, String apiKey, {String language = "English", String languageCode = "en"}) async {
|
||||
try {
|
||||
if (description.trim().isEmpty) return "";
|
||||
|
||||
final cleanContent = description.replaceAll(RegExp(r"<[^>]*>"), "").trim();
|
||||
if (cleanContent.isEmpty) return "";
|
||||
|
||||
final prompt = """
|
||||
You are a skilled content summarizer. Summarize the following news content:
|
||||
|
||||
Content: "$cleanContent"
|
||||
|
||||
Instructions:
|
||||
- 200-250 words
|
||||
- Maintain key facts
|
||||
- Professional news style
|
||||
- No explanations, only summary
|
||||
- IMPORTANT: Generate in $language ($languageCode).
|
||||
|
||||
Summary:""";
|
||||
|
||||
final response = await _callGeminiAPI(prompt, apiKey);
|
||||
final summary = response["candidates"][0]["content"]["parts"][0]["text"].trim();
|
||||
|
||||
String finalSummary = summary.replaceAll(RegExp(r"^['\']+|['\']+$"), '').trim();
|
||||
return finalSummary;
|
||||
} catch (e) {
|
||||
return description.replaceAll(RegExp(r"<[^>]*>"), "").substring(0, 150) + "...";
|
||||
}
|
||||
}
|
||||
}
|
||||
49
news-app/lib/ui/screens/AddEditNews/Widgets/userAllNews.dart
Normal file
49
news-app/lib/ui/screens/AddEditNews/Widgets/userAllNews.dart
Normal file
@@ -0,0 +1,49 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:news/cubits/getUserNewsCubit.dart';
|
||||
import 'package:news/ui/screens/AddEditNews/Widgets/UserNewsWidgets.dart';
|
||||
import 'package:news/ui/widgets/errorContainerWidget.dart';
|
||||
import 'package:news/utils/ErrorMessageKeys.dart';
|
||||
import 'package:news/utils/uiUtils.dart';
|
||||
|
||||
class UserAllNewsTab extends StatelessWidget {
|
||||
final ScrollController controller;
|
||||
final Widget contentShimmer;
|
||||
final Function fetchNews;
|
||||
final Function fetchMoreNews;
|
||||
|
||||
UserAllNewsTab({super.key, required this.controller, required this.contentShimmer, required this.fetchNews, required this.fetchMoreNews});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<GetUserNewsCubit, GetUserNewsState>(builder: (context, state) {
|
||||
if (state is GetUserNewsFetchSuccess) {
|
||||
return RefreshIndicator(
|
||||
onRefresh: () async => fetchNews,
|
||||
child: Padding(
|
||||
padding: const EdgeInsetsDirectional.only(start: 10, end: 10, bottom: 10),
|
||||
child: ListView.builder(
|
||||
controller: controller,
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
itemCount: state.getUserNews.length,
|
||||
itemBuilder: (context, index) {
|
||||
return UsernewsWidgets.buildNewsContainer(
|
||||
context: context,
|
||||
model: state.getUserNews[index],
|
||||
hasMore: state.hasMore,
|
||||
hasMoreNewsFetchError: state.hasMoreFetchError,
|
||||
index: index,
|
||||
totalCurrentNews: state.getUserNews.length,
|
||||
fetchMoreNews: fetchMoreNews);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
if (state is GetUserNewsFetchFailure) {
|
||||
return ErrorContainerWidget(errorMsg: (state.errorMessage.contains(ErrorMessageKeys.noInternet)) ? UiUtils.getTranslatedLabel(context, 'internetmsg') : state.errorMessage, onRetry: fetchNews);
|
||||
}
|
||||
return contentShimmer;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:news/cubits/GetUserDraftedNewsCubit.dart';
|
||||
import 'package:news/ui/screens/AddEditNews/Widgets/UserNewsWidgets.dart';
|
||||
import 'package:news/ui/widgets/errorContainerWidget.dart';
|
||||
import 'package:news/utils/ErrorMessageKeys.dart';
|
||||
import 'package:news/utils/uiUtils.dart';
|
||||
|
||||
class UserDrafterNewsTab extends StatelessWidget {
|
||||
final ScrollController controller;
|
||||
final Widget contentShimmer;
|
||||
final Function fetchDraftedNews;
|
||||
final Function fetchMoreDraftedNews;
|
||||
|
||||
UserDrafterNewsTab({super.key, required this.controller, required this.contentShimmer, required this.fetchDraftedNews, required this.fetchMoreDraftedNews});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<GetUserDraftedNewsCubit, GetUserDraftedNewsState>(builder: (context, state) {
|
||||
if (state is GetUserDraftedNewsFetchSuccess) {
|
||||
return Padding(
|
||||
padding: const EdgeInsetsDirectional.only(start: 10, end: 10, bottom: 10),
|
||||
child: RefreshIndicator(
|
||||
onRefresh: () async => fetchDraftedNews,
|
||||
child: ListView.builder(
|
||||
controller: controller,
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
itemCount: state.GetUserDraftedNews.length,
|
||||
itemBuilder: (context, index) {
|
||||
return UsernewsWidgets.buildNewsContainer(
|
||||
context: context,
|
||||
model: state.GetUserDraftedNews[index],
|
||||
hasMore: state.hasMore,
|
||||
hasMoreNewsFetchError: state.hasMoreFetchError,
|
||||
index: index,
|
||||
totalCurrentNews: state.GetUserDraftedNews.length,
|
||||
fetchMoreNews: fetchMoreDraftedNews);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
if (state is GetUserDraftedNewsFetchFailure) {
|
||||
return ErrorContainerWidget(
|
||||
errorMsg: (state.errorMessage.contains(ErrorMessageKeys.noInternet)) ? UiUtils.getTranslatedLabel(context, 'internetmsg') : state.errorMessage, onRetry: fetchDraftedNews);
|
||||
}
|
||||
return contentShimmer;
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user