elCaribe app - customization and branding

This commit is contained in:
2025-12-12 19:09:42 -04:00
parent 9e5d0d8ebf
commit ba7deac9f3
402 changed files with 31833 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View 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),
],
),
),
],
),
),
),
);
}
}

View 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),
),
),
)
],
);
}
}

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

View File

@@ -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)),
],
));
});
},
));
}
}

View 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) + "...";
}
}
}

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

View File

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

View File

@@ -0,0 +1,115 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:news/app/routes.dart';
import 'package:news/cubits/appLocalizationCubit.dart';
import 'package:news/cubits/Bookmark/bookmarkCubit.dart';
import 'package:news/data/models/NewsModel.dart';
import 'package:news/ui/widgets/customAppBar.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/ui/widgets/errorContainerWidget.dart';
import 'package:news/ui/widgets/newsCard.dart';
import 'package:news/ui/widgets/shimmerNewsList.dart';
import 'package:news/utils/ErrorMessageKeys.dart';
import 'package:news/utils/internetConnectivity.dart';
import 'package:news/utils/uiUtils.dart';
class BookmarkScreen extends StatefulWidget {
const BookmarkScreen({super.key});
@override
BookmarkScreenState createState() => BookmarkScreenState();
}
class BookmarkScreenState extends State<BookmarkScreen> {
late final ScrollController _controller = ScrollController()..addListener(hasMoreBookmarkScrollListener);
@override
void initState() {
super.initState();
getBookMark();
}
void getBookMark() async {
if (await InternetConnectivity.isNetworkAvailable()) {
context.read<BookmarkCubit>().getBookmark(langId: context.read<AppLocalizationCubit>().state.id);
}
}
void hasMoreBookmarkScrollListener() {
if (_controller.position.maxScrollExtent == _controller.offset) {
if (context.read<BookmarkCubit>().hasMoreBookmark()) {
context.read<BookmarkCubit>().getMoreBookmark(langId: context.read<AppLocalizationCubit>().state.id);
}
}
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(height: 45, isBackBtn: true, label: 'bookmarkLbl', horizontalPad: 15, isConvertText: true),
body: Padding(
padding: const EdgeInsetsDirectional.only(bottom: 10.0),
child: BlocBuilder<BookmarkCubit, BookmarkState>(
builder: (context, state) {
if (state is BookmarkFetchSuccess && state.bookmark.isNotEmpty) {
return Padding(
padding: const EdgeInsetsDirectional.only(start: 15.0, end: 15.0, top: 10.0, bottom: 10.0),
child: RefreshIndicator(
onRefresh: () async {
getBookMark();
},
child: ListView.builder(
controller: _controller,
physics: const AlwaysScrollableScrollPhysics(),
itemCount: state.bookmark.length,
itemBuilder: (context, index) {
return _buildBookmarkContainer(model: state.bookmark[index], hasMore: state.hasMore, hasMoreBookFetchError: state.hasMoreFetchError, index: index, totalCurrentBook: 6);
}),
),
);
} else if (state is BookmarkFetchFailure || ((state is! BookmarkFetchInProgress))) {
if (state is BookmarkFetchFailure) {
return ErrorContainerWidget(
errorMsg: (state.errorMessage.contains(ErrorMessageKeys.noInternet)) ? UiUtils.getTranslatedLabel(context, 'internetmsg') : state.errorMessage, onRetry: getBookMark);
} else {
return const Center(child: CustomTextLabel(text: 'bookmarkNotAvail', textAlign: TextAlign.center));
}
}
//default/Processing state
return Padding(padding: const EdgeInsets.only(bottom: 10.0, left: 10.0, right: 10.0), child: ShimmerNewsList(isNews: false));
},
)));
}
_buildBookmarkContainer({required NewsModel model, required int index, required int totalCurrentBook, required bool hasMoreBookFetchError, required bool hasMore}) {
if (index == totalCurrentBook - 1 && index != 0 && hasMore) {
if (hasMoreBookFetchError) {
return Center(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 8.0),
child: IconButton(
onPressed: () {
context.read<BookmarkCubit>().getMoreBookmark(langId: context.read<AppLocalizationCubit>().state.id);
},
icon: Icon(Icons.error, color: Theme.of(context).primaryColor))));
}
}
return Padding(
padding: const EdgeInsetsDirectional.only(top: 15.0),
child: NewsCard(
newsDetail: model,
showViews: true,
onTap: () async {
//Interstitial Ad here
UiUtils.showInterstitialAds(context: context);
Navigator.of(context).pushNamed(Routes.newsDetails, arguments: {"model": model, "isFromBreak": false, "fromShowMore": false});
}));
}
}

View File

@@ -0,0 +1,127 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:news/cubits/categoryCubit.dart';
import 'package:news/cubits/appLocalizationCubit.dart';
import 'package:news/data/models/CategoryModel.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/ui/widgets/errorContainerWidget.dart';
import 'package:news/ui/widgets/customAppBar.dart';
import 'package:news/ui/widgets/networkImage.dart';
import 'package:news/utils/ErrorMessageKeys.dart';
import 'package:news/utils/uiUtils.dart';
import 'package:news/app/routes.dart';
class CategoryScreen extends StatefulWidget {
const CategoryScreen({super.key});
@override
CategoryScreenState createState() => CategoryScreenState();
}
class CategoryScreenState extends State<CategoryScreen> {
late final ScrollController _categoryScrollController = ScrollController()..addListener(hasMoreCategoryScrollListener);
void getCategory() {
Future.delayed(Duration.zero, () {
context.read<CategoryCubit>().getCategory(langId: context.read<AppLocalizationCubit>().state.id);
});
}
@override
void initState() {
getCategory();
super.initState();
}
@override
void dispose() {
_categoryScrollController.dispose();
super.dispose();
}
void hasMoreCategoryScrollListener() {
if (_categoryScrollController.offset >= _categoryScrollController.position.maxScrollExtent && !_categoryScrollController.position.outOfRange) {
if (context.read<CategoryCubit>().hasMoreCategory()) {
context.read<CategoryCubit>().getMoreCategory(langId: context.read<AppLocalizationCubit>().state.id);
} else {}
}
}
Widget _buildCategory() {
return BlocBuilder<CategoryCubit, CategoryState>(
builder: (context, state) {
if (state is CategoryFetchSuccess) {
return RefreshIndicator(
onRefresh: () async {
getCategory();
},
child: GridView.count(
physics: const AlwaysScrollableScrollPhysics(),
scrollDirection: Axis.vertical,
padding: EdgeInsets.only(top: 15, bottom: MediaQuery.of(context).size.height / 10.0, left: 15, right: 15),
crossAxisCount: 2,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
childAspectRatio: 0.92,
shrinkWrap: true,
controller: _categoryScrollController,
children: List.generate(state.category.length, (index) {
return _buildCategoryContainer(
category: state.category[index], hasMore: state.hasMore, hasMoreCategoryFetchError: state.hasMoreFetchError, index: index, totalCurrentCategory: state.category.length);
}),
));
}
if (state is CategoryFetchFailure) {
return ErrorContainerWidget(
errorMsg: (state.errorMessage.contains(ErrorMessageKeys.noInternet)) ? UiUtils.getTranslatedLabel(context, 'internetmsg') : state.errorMessage, onRetry: getCategory);
}
return const SizedBox.shrink();
},
);
}
_buildCategoryContainer({required CategoryModel category, required int index, required int totalCurrentCategory, required bool hasMoreCategoryFetchError, required bool hasMore}) {
if (index == totalCurrentCategory - 1 && index != 0) {
if (hasMore) {
if (hasMoreCategoryFetchError) {
return const SizedBox.shrink();
} else {
return Center(child: Padding(padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 8.0), child: UiUtils.showCircularProgress(true, Theme.of(context).primaryColor)));
}
}
}
return GestureDetector(
onTap: () {
Navigator.of(context).pushNamed(Routes.subCat, arguments: {"catId": category.id, "catName": category.categoryName});
},
child: Card(
color: UiUtils.getColorScheme(context).surface,
child: Column(
spacing: 10,
mainAxisSize: MainAxisSize.min,
children: [
(category.image != null)
? Padding(
padding: const EdgeInsets.all(7.0),
child: ClipRRect(
borderRadius: BorderRadiusGeometry.circular(3), child: CustomNetworkImage(networkImageUrl: category.image!, height: 100, width: 140, isVideo: false, fit: BoxFit.cover)),
)
: const SizedBox.shrink(),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 5),
child: CustomTextLabel(
text: category.categoryName!,
textStyle: TextStyle(fontWeight: FontWeight.w600, fontSize: 16, color: UiUtils.getColorScheme(context).primaryContainer),
textAlign: TextAlign.center,
maxLines: 2)),
],
),
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(appBar: CustomAppBar(height: 45, isBackBtn: false, label: 'categoryLbl', isConvertText: true), body: _buildCategory());
}
}

View File

@@ -0,0 +1,407 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:news/ui/styles/colors.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:hive/hive.dart';
import 'package:location/location.dart' as loc;
import 'package:marqueer/marqueer.dart';
import 'package:news/app/routes.dart';
import 'package:news/cubits/Auth/authCubit.dart';
import 'package:news/cubits/Auth/registerTokenCubit.dart';
import 'package:news/cubits/Bookmark/bookmarkCubit.dart';
import 'package:news/cubits/LikeAndDislikeNews/LikeAndDislikeCubit.dart';
import 'package:news/cubits/appSystemSettingCubit.dart';
import 'package:news/cubits/breakingNewsCubit.dart';
import 'package:news/cubits/featureSectionCubit.dart';
import 'package:news/cubits/generalNewsCubit.dart';
import 'package:news/cubits/getUserDataByIdCubit.dart';
import 'package:news/cubits/appLocalizationCubit.dart';
import 'package:news/cubits/languageJsonCubit.dart';
import 'package:news/cubits/liveStreamCubit.dart';
import 'package:news/cubits/otherPagesCubit.dart';
import 'package:news/cubits/sectionByIdCubit.dart';
import 'package:news/cubits/settingCubit.dart';
import 'package:news/cubits/weatherCubit.dart';
import 'package:news/data/models/NewsModel.dart';
import 'package:news/data/models/authorModel.dart';
import 'package:news/data/repositories/SectionById/sectionByIdRepository.dart';
import 'package:news/data/repositories/Settings/settingsLocalDataRepository.dart';
import 'package:news/ui/screens/HomePage/Widgets/GeneralNewsRandomStyle.dart';
import 'package:news/ui/screens/HomePage/Widgets/LiveWithSearchView.dart';
import 'package:news/ui/screens/HomePage/Widgets/SectionShimmer.dart';
import 'package:news/ui/screens/HomePage/Widgets/WeatherData.dart';
import 'package:news/ui/screens/HomePage/Widgets/SectionStyle1.dart';
import 'package:news/ui/screens/HomePage/Widgets/SectionStyle2.dart';
import 'package:news/ui/screens/HomePage/Widgets/SectionStyle3.dart';
import 'package:news/ui/screens/HomePage/Widgets/SectionStyle4.dart';
import 'package:news/ui/screens/HomePage/Widgets/SectionStyle5.dart';
import 'package:news/ui/screens/HomePage/Widgets/SectionStyle6.dart';
import 'package:news/ui/screens/Profile/Widgets/customAlertDialog.dart';
import 'package:news/ui/widgets/SnackBarWidget.dart';
import 'package:news/ui/widgets/adSpaces.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/ui/widgets/errorContainerWidget.dart';
import 'package:news/utils/ErrorMessageKeys.dart';
import 'package:news/utils/hiveBoxKeys.dart';
import 'package:news/utils/strings.dart';
import 'package:news/utils/uiUtils.dart';
import 'package:news/data/models/AuthModel.dart';
import 'package:news/data/models/FeatureSectionModel.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
HomeScreenState createState() => HomeScreenState();
}
class HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
final GlobalKey<RefreshIndicatorState> _refreshIndicatorKey = GlobalKey<RefreshIndicatorState>();
late final ScrollController featuredSectionsScrollController = ScrollController()..addListener(hasMoreFeaturedSectionsScrollListener);
final loc.Location _location = loc.Location();
bool? _serviceEnabled;
loc.PermissionStatus? _permissionGranted;
double? lat;
double? lon;
bool updateList = false;
Set<String> get locationValue => SettingsLocalDataRepository().getLocationCityValues();
late final appConfig, authConfig;
String languageId = "14"; //set it as default language code
void getSections() {
Future.delayed(Duration.zero, () {
context.read<SectionCubit>().getSection(langId: languageId, latitude: locationValue.first, longitude: locationValue.last);
}).whenComplete(() => getGeneralNews());
}
void getLiveStreamData() {
Future.delayed(Duration.zero, () {
context.read<LiveStreamCubit>().getLiveStream(langId: languageId);
});
}
void getBookmark() {
Future.delayed(Duration.zero, () {
context.read<BookmarkCubit>().getBookmark(langId: languageId);
});
}
void getLikeNews() {
Future.delayed(Duration.zero, () {
context.read<LikeAndDisLikeCubit>().getLike(langId: languageId);
});
}
void getUserData() {
Future.delayed(Duration.zero, () {
context.read<GetUserByIdCubit>().getUserById();
});
}
checkForAppUpdate() {
WidgetsBinding.instance.addPostFrameCallback((_) async {
if (context.read<AppConfigurationCubit>().state is AppConfigurationFetchSuccess) {
if (context.read<AppConfigurationCubit>().isUpdateRequired()) {
openUpdateDialog();
}
}
});
}
openUpdateDialog() {
bool isForceUpdate = (context.read<AppConfigurationCubit>().getForceUpdateMode() != "" && context.read<AppConfigurationCubit>().getForceUpdateMode() == "1") ? true : false;
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return StatefulBuilder(builder: (BuildContext context, StateSetter setStater) {
return PopScope(
canPop: false,
child: CustomAlertDialog(
isForceAppUpdate: (isForceUpdate) ? true : false,
context: context,
yesButtonText: 'yesLbl',
yesButtonTextPostfix: '',
noButtonText: (isForceUpdate) ? 'exitLbl' : 'noLbl',
imageName: '',
titleWidget: CustomTextLabel(
text: (isForceUpdate) ? 'forceUpdateTitleLbl' : 'newVersionAvailableTitleLbl',
textStyle: Theme.of(context).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.w800, color: UiUtils.getColorScheme(context).primaryContainer)),
messageText: context.read<LanguageJsonCubit>().getTranslatedLabels((isForceUpdate) ? 'forceUpdateDescLbl' : 'newVersionAvailableDescLbl'),
onYESButtonPressed: () => UiUtils.gotoStores(context)),
);
});
});
}
showPermissionPopup() async {
loc.LocationData locationData;
_serviceEnabled = await _location.serviceEnabled();
if (!_serviceEnabled!) {
_serviceEnabled = await _location.requestService();
if (!_serviceEnabled!) {
return;
}
}
_permissionGranted = await _location.hasPermission();
if (_permissionGranted == loc.PermissionStatus.denied) {
SettingsLocalDataRepository().setLocationCityKeys(null, null);
_permissionGranted = await _location.requestPermission();
if (_permissionGranted != loc.PermissionStatus.granted) {
return;
}
}
locationData = await _location.getLocation();
setState(() {
lat = locationData.latitude;
lon = locationData.longitude;
});
getLocationPermission();
return (locationData);
}
getLocationPermission() async {
if (appConfig.getLocationWiseNewsMode() == "1") {
SettingsLocalDataRepository().setLocationCityKeys(lat, lon);
//update latitude,longitude - along with token
if (context.read<SettingsCubit>().getSettings().token != '') {
context.read<RegisterTokenCubit>().registerToken(fcmId: context.read<SettingsCubit>().getSettings().token, context: context);
context.read<SettingsCubit>().changeFcmToken(context.read<SettingsCubit>().getSettings().token);
}
} else {
SettingsLocalDataRepository().setLocationCityKeys(null, null);
}
if (appConfig.getWeatherMode() == "1") {
getWeatherData();
}
}
Future<void> getWeatherData() async {
if (lat != null && lon != null) {
context.read<WeatherCubit>().getWeatherDetails(langId: (Hive.box(settingsBoxKey).get(currentLanguageCodeKey)), lat: lat.toString(), lon: lon.toString());
}
}
void getBreakingNews() {
Future.delayed(Duration.zero, () {
context.read<BreakingNewsCubit>().getBreakingNews(langId: languageId);
});
}
void getGeneralNews() {
context.read<GeneralNewsCubit>().getGeneralNews(langId: languageId, latitude: locationValue.first, longitude: locationValue.last);
}
@override
void initState() {
super.initState();
appConfig = context.read<AppConfigurationCubit>();
authConfig = context.read<AuthCubit>();
languageId = context.read<AppLocalizationCubit>().state.id;
getSections();
if (appConfig.getWeatherMode() == "1" || appConfig.getLocationWiseNewsMode() == "1") showPermissionPopup();
if (authConfig.getUserId() != "0") {
getUserData();
}
getLiveStreamData();
if (appConfig.getBreakingNewsMode() == "1") getBreakingNews();
if (appConfig.getMaintenanceMode() == "1") Navigator.of(context).pushReplacementNamed(Routes.maintenance);
}
@override
void dispose() {
featuredSectionsScrollController.dispose();
super.dispose();
}
void hasMoreFeaturedSectionsScrollListener() {
if (featuredSectionsScrollController.position.atEdge) {
if (context.read<SectionCubit>().hasMoreSections()) {
context.read<SectionCubit>().getMoreSections(langId: languageId, latitude: locationValue.first, longitude: locationValue.last);
} else {}
}
}
Widget breakingNewsMarquee() {
return BlocBuilder<BreakingNewsCubit, BreakingNewsState>(builder: ((context, state) {
return (state is BreakingNewsFetchSuccess && state.breakingNews.isNotEmpty)
? Container(
margin: const EdgeInsets.only(top: 10),
color: primaryColor,
height: 32,
child: Marqueer.builder(
pps: 25.0,
restartAfterInteractionDuration: const Duration(seconds: 1),
separatorBuilder: (_, index) =>
Center(child: Text('', style: Theme.of(context).textTheme.titleSmall!.copyWith(color: UiUtils.getColorScheme(context).secondary, fontWeight: FontWeight.normal))),
itemBuilder: (context, index) {
var multiplier = index ~/ state.breakingNews.length;
var i = index;
if (multiplier > 0) {
i = index - (multiplier * state.breakingNews.length);
}
final item = state.breakingNews[i];
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 5),
child: CustomTextLabel(
text: item.title!, textStyle: Theme.of(context).textTheme.titleSmall!.copyWith(color: UiUtils.getColorScheme(context).secondary, fontWeight: FontWeight.normal)));
},
),
)
: const SizedBox.shrink();
}));
}
Widget getSectionList() {
return BlocBuilder<GeneralNewsCubit, GeneralNewsState>(builder: (context, newsState) {
return BlocBuilder<SectionCubit, SectionState>(builder: (context, sectionState) {
if (sectionState is SectionFetchSuccess) {
//if it has only one section and it doesn't have news in it then show No data found message
if (sectionState.section.length == 1) {
if (sectionState.section.first.newsType == "breaking_news") {
if (sectionState.section.first.breakNewsTotal == 0) {
return ErrorContainerWidget(errorMsg: ErrorMessageKeys.noDataMessage, onRetry: _refresh);
}
} else if (sectionState.section.first.newsTotal != null && sectionState.section.first.newsTotal! == 0) {
return ErrorContainerWidget(errorMsg: ErrorMessageKeys.noDataMessage, onRetry: _refresh);
}
}
return ListView.builder(
shrinkWrap: true,
padding: EdgeInsets.zero,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: ((context, index) {
FeatureSectionModel model = sectionState.section[index];
//check for more featured sections
if (index == sectionState.section.length - 1 && index != 0) {
if (sectionState.hasMore) {
if (sectionState.hasMoreFetchError) {
return const SizedBox.shrink();
} else {
return Center(child: Padding(padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 8.0), child: UiUtils.showCircularProgress(true, Theme.of(context).primaryColor)));
}
}
}
return sectionData(model: model);
}),
itemCount: sectionState.section.length);
}
if (sectionState is SectionFetchFailure) {
if (context.read<GeneralNewsCubit>().state is GeneralNewsFetchSuccess) {
return sectionData(newsModelList: (context.read<GeneralNewsCubit>().state as GeneralNewsFetchSuccess).generalNews);
} else {
return ErrorContainerWidget(
errorMsg: (sectionState.errorMessage.contains(ErrorMessageKeys.noInternet)) ? UiUtils.getTranslatedLabel(context, 'internetmsg') : sectionState.errorMessage, onRetry: _refresh);
}
}
return sectionShimmer(context); //state is SectionFetchInProgress || state is SectionInitial
});
});
}
Widget sectionData({FeatureSectionModel? model, List<NewsModel>? newsModelList}) {
return (model != null)
? Column(mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [
if (model.adSpaceDetails != null) AdSpaces(adsModel: model.adSpaceDetails!), //sponsored ads
if (model.styleApp == 'style_1') Style1Section(model: model),
if (model.styleApp == 'style_2') Style2Section(model: model),
if (model.styleApp == 'style_3') Style3Section(model: model),
if (model.styleApp == 'style_4') Style4Section(model: model),
if (model.styleApp == 'style_5') Style5Section(model: model),
if (model.styleApp == 'style_6') BlocProvider(create: (context) => SectionByIdCubit(SectionByIdRepository()), child: Style6Section(model: model))
])
: GeneralNewsRandomStyle(modelList: newsModelList!);
}
//refresh function to refresh page
Future<void> _refresh() async {
getSections();
getLocationPermission();
if (authConfig.getUserId() != "0") {
getUserData();
getBookmark();
getLikeNews();
}
getLiveStreamData();
getPages();
if (appConfig.getBreakingNewsMode() == "1") getBreakingNews();
if (appConfig.getMaintenanceMode() == "1") Navigator.of(context).pushReplacementNamed(Routes.maintenance);
if (appConfig.getWeatherMode() == "1") getWeatherData();
}
getPages() {
Future.delayed(Duration.zero, () {
context.read<OtherPageCubit>().getOtherPage(langId: languageId);
});
}
@override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
resizeToAvoidBottomInset: false,
body: RefreshIndicator(
key: _refreshIndicatorKey,
onRefresh: () => _refresh(),
child: BlocListener<GetUserByIdCubit, GetUserByIdState>(
bloc: context.read<GetUserByIdCubit>(),
listener: (context, state) {
if (state is GetUserByIdFetchSuccess) {
var data = state.result;
if (data[STATUS] == 0) {
showSnackBar(UiUtils.getTranslatedLabel(context, 'deactiveMsg'), context);
Future.delayed(const Duration(seconds: 2), () {
UiUtils.userLogOut(contxt: context);
});
} else {
authConfig.updateDetails(
authModel: AuthModel(
id: data[ID].toString(),
name: data[NAME],
status: data[STATUS].toString(),
mobile: data[MOBILE],
email: data[EMAIL],
type: data[TYPE],
profile: data[PROFILE],
role: data[ROLE].toString(),
jwtToken: data[TOKEN],
isAuthor: data[IS_AUTHOR],
authorDetails: (data[IS_AUTHOR] == 1 && data[AUTHOR] != null) ? Author.fromJson(data[AUTHOR]) : null));
}
}
},
child: SingleChildScrollView(
controller: featuredSectionsScrollController,
physics: ClampingScrollPhysics(), //To restrict scrolling on Refresh
child: Padding(
padding: const EdgeInsetsDirectional.only(start: 15.0, end: 15.0, bottom: 10.0),
child: Column(
children: [
const LiveWithSearchView(),
BlocBuilder<WeatherCubit, WeatherState>(builder: (context, state) {
if (state is WeatherFetchSuccess) {
return WeatherDataView(weatherData: state.weatherData);
}
return SizedBox.shrink();
}),
breakingNewsMarquee(),
getSectionList()
],
),
),
),
))),
);
}
}

View File

@@ -0,0 +1,46 @@
import 'package:flutter/material.dart';
import 'package:news/data/models/FeatureSectionModel.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/app/routes.dart';
import 'package:news/utils/uiUtils.dart';
Widget commonSectionTitle(FeatureSectionModel model, BuildContext context) {
return ListTile(
minVerticalPadding: 5,
contentPadding: EdgeInsets.zero,
title: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
child: CustomTextLabel(
text: model.title!,
textStyle: Theme.of(context).textTheme.titleMedium!.copyWith(color: UiUtils.getColorScheme(context).primaryContainer, fontWeight: FontWeight.bold),
softWrap: true,
maxLines: 1,
overflow: TextOverflow.ellipsis)),
GestureDetector(
onTap: () {
UiUtils.showInterstitialAds(context: context);
if ((model.newsType == 'news' || model.newsType == "user_choice") || model.videosType == 'news' && model.newsType != 'breaking_news') {
Navigator.of(context).pushNamed(Routes.sectionNews, arguments: {"sectionId": model.id!, "title": model.title!});
} else {
Navigator.of(context).pushNamed(Routes.sectionBreakNews, arguments: {"sectionId": model.id!, "title": model.title!});
}
},
child: CustomTextLabel(
text: 'viewMore',
textStyle: Theme.of(context).textTheme.titleSmall!.copyWith(decoration: TextDecoration.underline, fontWeight: FontWeight.bold, color: UiUtils.getColorScheme(context).outline)),
)
],
),
subtitle: (model.shortDescription != null)
? CustomTextLabel(
text: model.shortDescription!,
textStyle: Theme.of(context).textTheme.titleSmall!.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.6)),
softWrap: true,
maxLines: 3,
overflow: TextOverflow.ellipsis)
: SizedBox.shrink(),
);
}

View File

@@ -0,0 +1,165 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:news/app/routes.dart';
import 'package:news/cubits/appLocalizationCubit.dart';
import 'package:news/cubits/generalNewsCubit.dart';
import 'package:news/data/models/NewsModel.dart';
import 'package:news/data/repositories/Settings/settingsLocalDataRepository.dart';
import 'package:news/ui/styles/colors.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/ui/widgets/networkImage.dart';
import 'package:news/utils/uiUtils.dart';
class GeneralNewsRandomStyle extends StatefulWidget {
final List<NewsModel> modelList;
const GeneralNewsRandomStyle({super.key, required this.modelList});
@override
GeneralNewsRandomStyleState createState() => GeneralNewsRandomStyleState();
}
class GeneralNewsRandomStyleState extends State<GeneralNewsRandomStyle> {
late final ScrollController scrollController = ScrollController()..addListener(hasMoreGeneralNewsListener);
late int counter; //counter will handle unique index in both list & grid
late List<NewsModel> newsList;
@override
void initState() {
super.initState();
newsList = widget.modelList;
}
@override
void dispose() {
scrollController.removeListener(() {});
super.dispose();
}
void hasMoreGeneralNewsListener() {
if (scrollController.position.maxScrollExtent == scrollController.offset) {
if (context.read<GeneralNewsCubit>().hasMoreGeneralNews()) {
context.read<GeneralNewsCubit>().getMoreGeneralNews(
langId: context.read<AppLocalizationCubit>().state.id,
latitude: SettingsLocalDataRepository().getLocationCityValues().first,
longitude: SettingsLocalDataRepository().getLocationCityValues().last);
}
}
}
@override
Widget build(BuildContext context) {
counter = 0;
return Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ListView.separated(
controller: scrollController,
shrinkWrap: true,
padding: EdgeInsets.zero,
physics: const NeverScrollableScrollPhysics(),
itemCount: newsList.length,
separatorBuilder: (BuildContext context, int index) {
return (index.isOdd && counter + 1 < newsList.length) ? getGrid() : const SizedBox.shrink();
},
itemBuilder: (BuildContext context, int index) {
return (counter < newsList.length) ? listRow(counter++) : const SizedBox.shrink();
},
),
],
);
}
Widget getGrid() {
return SizedBox(
height: 200,
child: GridView.count(
crossAxisCount: 1,
scrollDirection: Axis.horizontal,
children: List.generate((counter % 3 == 0) ? 3 : 2, (index) {
return Padding(padding: const EdgeInsets.only(right: 15), child: listRow((counter < newsList.length) ? counter++ : counter));
})));
}
Widget listRow(int index) {
NewsModel newsModel = newsList[index];
return Padding(
padding: const EdgeInsets.only(top: 15),
child: GestureDetector(
onTap: () {
List<NewsModel> newList = [];
newList.addAll(newsList);
newList.removeAt(index);
Navigator.of(context).pushNamed(Routes.newsDetails, arguments: {"model": newsList[index], "newsList": newList, "isFromBreak": false, "fromShowMore": false});
},
child: Stack(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(15),
child: ShaderMask(
shaderCallback: (rect) =>
LinearGradient(begin: Alignment.center, end: Alignment.bottomCenter, colors: [Colors.transparent, darkSecondaryColor.withOpacity(0.9)]).createShader(rect),
blendMode: BlendMode.darken,
child: Container(
width: double.maxFinite,
height: MediaQuery.of(context).size.height / 3.3,
color: primaryColor.withValues(alpha: 0.15),
child: CustomNetworkImage(
networkImageUrl: newsModel.image!,
fit: BoxFit.cover,
width: double.maxFinite,
height: MediaQuery.of(context).size.height / 3.3,
isVideo: newsModel.type == 'videos' ? true : false),
))),
if (newsModel.type == 'videos')
Positioned.directional(
textDirection: Directionality.of(context),
top: MediaQuery.of(context).size.height * 0.12,
start: MediaQuery.of(context).size.width / 3,
end: MediaQuery.of(context).size.width / 3,
child: InkWell(
onTap: () {
List<NewsModel> allNewsList = List.from(newsList)..removeAt(index);
Navigator.of(context).pushNamed(Routes.newsVideo, arguments: {"from": 1, "model": newsModel, "otherVideos": allNewsList});
},
child: UiUtils.setPlayButton(context: context))),
Positioned.directional(
textDirection: Directionality.of(context),
bottom: 10,
start: 10,
end: 10,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
if (newsModel.categoryName != null)
ClipRRect(
borderRadius: BorderRadius.circular(8.0),
child: Container(
padding: const EdgeInsets.all(5),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
child: CustomTextLabel(
text: newsModel.categoryName!,
textStyle: Theme.of(context).textTheme.bodyLarge?.copyWith(color: secondaryColor.withOpacity(0.6)),
overflow: TextOverflow.ellipsis,
softWrap: true)),
),
),
Padding(
padding: const EdgeInsets.only(top: 8),
child: CustomTextLabel(
text: newsModel.title!,
textStyle: Theme.of(context).textTheme.titleMedium?.copyWith(color: secondaryColor, fontWeight: FontWeight.normal),
maxLines: 2,
overflow: TextOverflow.ellipsis,
softWrap: true)),
],
))
],
)),
);
}
}

View File

@@ -0,0 +1,145 @@
import 'package:flutter/material.dart';
import 'package:news/ui/screens/auth/Widgets/svgPictureWidget.dart';
import 'package:shimmer/shimmer.dart';
import 'package:news/app/routes.dart';
import 'package:news/utils/uiUtils.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:news/cubits/themeCubit.dart';
import 'package:news/ui/styles/appTheme.dart';
import 'package:news/cubits/liveStreamCubit.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/cubits/appSystemSettingCubit.dart';
class LiveWithSearchView extends StatefulWidget {
const LiveWithSearchView({super.key});
@override
LiveWithSearchState createState() => LiveWithSearchState();
}
class LiveWithSearchState extends State<LiveWithSearchView> {
Widget liveWithSearchView() {
return BlocBuilder<LiveStreamCubit, LiveStreamState>(
bloc: context.read<LiveStreamCubit>(),
builder: (context, state) {
if (state is LiveStreamFetchSuccess) {
return Padding(
padding: const EdgeInsets.only(top: 10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// Logo Caribe
Container(
decoration: BoxDecoration(shape: BoxShape.circle, color: UiUtils.getColorScheme(context).surface),
height: 60,
width: 60,
child: Center(
child: SvgPictureWidget(assetName: "logo_caribe", height: 40.0, width: 40.0),
)),
const SizedBox(width: 10),
// Search bar
Expanded(
child: InkWell(
child: Container(
alignment: Alignment.centerLeft,
height: 60,
decoration: BoxDecoration(borderRadius: BorderRadius.circular(30.0), color: UiUtils.getColorScheme(context).surface),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Padding(
padding: const EdgeInsetsDirectional.only(start: 10.0),
child: Icon(Icons.search_rounded, color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.7))),
Padding(
padding: const EdgeInsetsDirectional.only(start: 10.0),
child: CustomTextLabel(text: 'searchHomeNews', maxLines: 3, textStyle: TextStyle(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.7)))),
],
),
)),
onTap: () {
Navigator.of(context).pushNamed(Routes.search);
},
)),
if (state.liveStream.isNotEmpty && context.read<AppConfigurationCubit>().getLiveStreamMode() == "1")
Padding(
padding: const EdgeInsetsDirectional.only(start: 10.0),
child: InkWell(
child: Container(
decoration: BoxDecoration(shape: BoxShape.circle, color: UiUtils.getColorScheme(context).surface),
height: 60,
width: 60,
child: Center(
child: SvgPictureWidget(assetName: (context.read<ThemeCubit>().state.appTheme == AppTheme.Dark ? "live_news_dark" : "live_news"), height: 30.0, width: 54.0),
)),
onTap: () {
Navigator.of(context).pushNamed(Routes.live, arguments: {"liveNews": state.liveStream});
},
))
],
));
}
if (state is LiveStreamFetchFailure) {
return Padding(
padding: const EdgeInsets.only(top: 10),
child: Row(
children: [
// Logo Caribe
Container(
decoration: BoxDecoration(shape: BoxShape.circle, color: UiUtils.getColorScheme(context).surface),
height: 60,
width: 60,
child: Center(
child: SvgPictureWidget(assetName: "logo_caribe", height: 40.0, width: 40.0),
)),
const SizedBox(width: 10),
// Search bar
Expanded(
child: InkWell(
child: Container(
alignment: Alignment.centerLeft,
height: 60,
decoration: BoxDecoration(borderRadius: BorderRadius.circular(30.0), color: UiUtils.getColorScheme(context).surface),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: [
Padding(padding: const EdgeInsetsDirectional.only(start: 10.0), child: Icon(Icons.search_rounded, color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.7))),
Padding(
padding: const EdgeInsetsDirectional.only(start: 10.0),
child: CustomTextLabel(text: 'searchHomeNews', maxLines: 3, textStyle: TextStyle(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.7))),
),
],
),
)),
onTap: () {
Navigator.of(context).pushNamed(Routes.search);
},
),
),
],
),
);
}
return shimmerData(); //state is LiveStreamFetchInProgress || state is LiveStreamInitial
});
}
Widget shimmerData() {
return Shimmer.fromColors(
baseColor: Colors.grey.withOpacity(0.6),
highlightColor: Colors.grey,
child: Container(
height: 60,
margin: const EdgeInsets.only(top: 15),
width: double.maxFinite,
padding: const EdgeInsets.only(left: 10.0, right: 10.0),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(30), color: Colors.grey.withOpacity(0.6))));
}
@override
Widget build(BuildContext context) {
return liveWithSearchView();
}
}

View File

@@ -0,0 +1,29 @@
import 'package:flutter/material.dart';
import 'package:shimmer/shimmer.dart';
Widget sectionShimmer(BuildContext context) {
return Shimmer.fromColors(
baseColor: Colors.grey.withOpacity(0.6),
highlightColor: Colors.grey,
child: ListView(shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), padding: const EdgeInsetsDirectional.only(top: 10), children: [
Container(
height: 55,
width: double.maxFinite,
decoration: BoxDecoration(color: Colors.grey.withOpacity(0.6), borderRadius: BorderRadius.circular(10)),
),
ListView.builder(
padding: EdgeInsets.zero,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: 5,
itemBuilder: (context, index) {
return Container(
alignment: Alignment.center,
height: MediaQuery.of(context).size.height / 3.3,
width: MediaQuery.of(context).size.width,
margin: const EdgeInsets.only(top: 15),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(15), color: Colors.grey.withOpacity(0.6)),
);
}),
]));
}

View File

@@ -0,0 +1,321 @@
import 'dart:math';
import 'package:news/ui/screens/HomePage/Widgets/CommonSectionTitle.dart';
import 'package:flutter/material.dart';
import 'package:news/app/routes.dart';
import 'package:news/data/models/BreakingNewsModel.dart';
import 'package:news/data/models/FeatureSectionModel.dart';
import 'package:news/data/models/NewsModel.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/utils/constant.dart';
import 'package:news/utils/strings.dart';
import 'package:news/utils/uiUtils.dart';
import 'package:news/ui/styles/colors.dart';
import 'package:news/ui/widgets/networkImage.dart';
class Style1Section extends StatefulWidget {
final FeatureSectionModel model;
const Style1Section({super.key, required this.model});
@override
Style1SectionState createState() => Style1SectionState();
}
class Style1SectionState extends State<Style1Section> {
int? style1Sel;
PageController? _pageStyle1Controller = PageController();
int limit = limitOfStyle1;
int newsLength = 0;
int brNewsLength = 0;
@override
void dispose() {
_pageStyle1Controller!.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
newsLength = (widget.model.newsType == 'news' || widget.model.newsType == "user_choice") ? widget.model.news!.length : widget.model.videos!.length;
brNewsLength = widget.model.newsType == 'breaking_news' ? widget.model.breakNews!.length : widget.model.breakVideos!.length;
return style1Data(widget.model);
}
Widget style1Data(FeatureSectionModel model) {
if (model.breakVideos!.isNotEmpty || model.breakNews!.isNotEmpty || model.videos!.isNotEmpty || model.news!.isNotEmpty) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
commonSectionTitle(model, context),
if ((model.newsType == 'news' || model.videosType == "news" || model.newsType == "user_choice") &&
((model.newsType == 'news' || model.newsType == "user_choice") ? model.news!.isNotEmpty : model.videos!.isNotEmpty))
style1NewsData(model),
if ((model.newsType == 'breaking_news' || model.videosType == "breaking_news") && (model.newsType == 'breaking_news' ? model.breakNews!.isNotEmpty : model.breakVideos!.isNotEmpty))
style1BreakNewsData(model)
],
);
} else {
return const SizedBox.shrink();
}
}
Widget style1NewsData(FeatureSectionModel model) {
if ((model.newsType == 'news' || model.newsType == "user_choice") ? model.news!.length > 1 : model.videos!.length > 1) {
style1Sel ??= 1;
_pageStyle1Controller = PageController(initialPage: 1, viewportFraction: 0.87);
} else {
style1Sel = 0;
_pageStyle1Controller = PageController(initialPage: 0, viewportFraction: 1);
}
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: MediaQuery.of(context).size.height * 0.36,
width: double.maxFinite,
child: PageView.builder(
physics: const BouncingScrollPhysics(),
itemCount: min(newsLength, limit),
scrollDirection: Axis.horizontal,
pageSnapping: true,
controller: _pageStyle1Controller,
onPageChanged: (index) {
setState(() => style1Sel = index);
},
itemBuilder: (BuildContext context, int index) {
NewsModel data = (model.newsType == 'news' || model.newsType == "user_choice") ? model.news![index] : model.videos![index];
return InkWell(
child: Padding(
padding: EdgeInsetsDirectional.only(start: 7, end: 7, top: style1Sel == index ? 0 : MediaQuery.of(context).size.height * 0.027),
child: Stack(
children: <Widget>[
ClipRRect(
borderRadius: BorderRadius.circular(15),
child: CustomNetworkImage(
networkImageUrl: data.image!,
height: style1Sel == index ? MediaQuery.of(context).size.height / 4 : MediaQuery.of(context).size.height / 5,
width: double.maxFinite,
fit: BoxFit.cover,
isVideo: model.newsType == 'videos' ? true : false),
),
if (model.newsType == 'videos')
Positioned.directional(
textDirection: Directionality.of(context),
top: MediaQuery.of(context).size.height * 0.075,
start: MediaQuery.of(context).size.width / 3,
end: MediaQuery.of(context).size.width / 3,
child: InkWell(
onTap: () {
List<NewsModel> newsList = List.from(model.videos ?? [])..removeAt(index);
Navigator.of(context).pushNamed(Routes.newsVideo, arguments: {"from": 1, "model": data, "otherVideos": newsList});
},
child: UiUtils.setPlayButton(context: context)),
),
Positioned.directional(
textDirection: Directionality.of(context),
start: 8,
end: 8,
top: MediaQuery.of(context).size.height / 7,
child: Container(
alignment: Alignment.center,
height: MediaQuery.of(context).size.height / 5,
width: MediaQuery.of(context).size.width,
margin: const EdgeInsetsDirectional.all(10),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(15), color: UiUtils.getColorScheme(context).surface),
padding: const EdgeInsets.all(13),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (data.categoryName != null)
Container(
height: 20.0,
padding: const EdgeInsetsDirectional.only(start: 8.0, end: 8.0, top: 2.5),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(5), color: Theme.of(context).primaryColor),
child: CustomTextLabel(
text: data.categoryName!,
textAlign: TextAlign.center,
textStyle: Theme.of(context).textTheme.bodySmall?.copyWith(color: secondaryColor),
overflow: TextOverflow.ellipsis,
softWrap: true)),
Padding(
padding: const EdgeInsets.only(top: 15.0),
child: CustomTextLabel(
text: data.title!,
textStyle: Theme.of(context).textTheme.titleMedium!.copyWith(color: UiUtils.getColorScheme(context).primaryContainer, fontWeight: FontWeight.normal),
softWrap: true,
maxLines: 3,
overflow: TextOverflow.ellipsis)),
],
),
)),
],
),
),
onTap: () {
if (model.newsType == 'news' || model.newsType == "user_choice") {
//interstitial ads
UiUtils.showInterstitialAds(context: context);
List<NewsModel> newsList = [];
newsList.addAll(model.news!);
newsList.removeAt(index);
Navigator.of(context).pushNamed(Routes.newsDetails, arguments: {"model": data, "newsList": newsList, "isFromBreak": false, "fromShowMore": false});
}
},
);
},
),
),
style1Indicator(model, min(newsLength, limit))
],
);
}
Widget style1BreakNewsData(FeatureSectionModel model) {
if (model.newsType == 'breaking_news' ? model.breakNews!.length > 1 : model.breakVideos!.length > 1) {
style1Sel ??= 1;
_pageStyle1Controller = PageController(initialPage: 1, viewportFraction: 0.87);
} else {
style1Sel = 0;
_pageStyle1Controller = PageController(initialPage: 0, viewportFraction: 1);
}
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: MediaQuery.of(context).size.height * 0.36,
width: double.maxFinite,
child: PageView.builder(
physics: const BouncingScrollPhysics(),
itemCount: min(brNewsLength, limit),
scrollDirection: Axis.horizontal,
controller: _pageStyle1Controller,
reverse: false,
onPageChanged: (index) {
setState(() => style1Sel = index);
},
itemBuilder: (BuildContext context, int index) {
BreakingNewsModel data = model.newsType == 'breaking_news' ? model.breakNews![index] : model.breakVideos![index];
return Padding(
padding: EdgeInsetsDirectional.only(start: 7, end: 7, top: style1Sel == index ? 0 : MediaQuery.of(context).size.height * 0.027),
child: InkWell(
onTap: () {
if (model.newsType == 'breaking_news') {
//interstitial ads
UiUtils.showInterstitialAds(context: context);
List<BreakingNewsModel> breakList = [];
breakList.addAll(model.breakNews!);
breakList.removeAt(index);
Navigator.of(context).pushNamed(Routes.newsDetails, arguments: {"breakModel": data, "breakNewsList": breakList, "isFromBreak": true, "fromShowMore": false});
}
},
child: Stack(
children: <Widget>[
ClipRRect(
borderRadius: BorderRadius.circular(15),
child: CustomNetworkImage(
networkImageUrl: data.image!,
height: style1Sel == index ? MediaQuery.of(context).size.height / 4 : MediaQuery.of(context).size.height / 5,
width: double.maxFinite,
fit: BoxFit.cover,
isVideo: model.newsType == 'videos' ? true : false),
),
if (model.newsType == 'videos')
Positioned.directional(
textDirection: Directionality.of(context),
top: MediaQuery.of(context).size.height * 0.075,
start: MediaQuery.of(context).size.width / 3,
end: MediaQuery.of(context).size.width / 3,
child: InkWell(
onTap: () {
List<BreakingNewsModel> allBrNewsList = List.from(model.breakVideos ?? [])..removeAt(index);
Navigator.of(context).pushNamed(Routes.newsVideo, arguments: {"from": 3, "breakModel": data, "otherBreakingVideos": allBrNewsList});
},
child: UiUtils.setPlayButton(context: context)),
),
Positioned.directional(
textDirection: Directionality.of(context),
start: 8,
end: 8,
top: MediaQuery.of(context).size.height / 7,
child: Container(
alignment: Alignment.center,
height: MediaQuery.of(context).size.height / 5,
width: MediaQuery.of(context).size.width,
margin: const EdgeInsetsDirectional.all(10),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(15), color: UiUtils.getColorScheme(context).surface),
padding: const EdgeInsets.all(13),
child: CustomTextLabel(
text: data.title!,
textStyle: Theme.of(context).textTheme.titleMedium!.copyWith(color: UiUtils.getColorScheme(context).primaryContainer, fontWeight: FontWeight.normal),
softWrap: true,
maxLines: 4,
overflow: TextOverflow.ellipsis))),
],
),
),
);
},
),
),
style1Indicator(model, min(brNewsLength, limit))
],
);
}
Widget style1Indicator(FeatureSectionModel model, int len) {
return len <= 1
? const SizedBox.shrink()
: Align(
alignment: Alignment.center,
child: Padding(
padding: const EdgeInsets.only(top: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: map<Widget>(
(model.newsType == 'news' || model.newsType == "user_choice")
? model.news!
: (model.newsType == BREAKING_NEWS)
? model.breakNews!
: (model.newsType == 'videos' && (model.videosTotal! > 0 && model.videos!.isNotEmpty))
? model.videos!
: model.breakVideos!,
(index, url) {
return Container(
alignment: Alignment.center,
child: Padding(
padding: const EdgeInsetsDirectional.only(start: 5.0, end: 5.0),
child: Container(
height: 14.0,
width: 14.0,
decoration: BoxDecoration(color: Colors.transparent, shape: BoxShape.circle, border: Border.all(color: UiUtils.getColorScheme(context).primaryContainer)),
child: style1Sel == index
? Container(
margin: const EdgeInsets.all(2),
decoration: BoxDecoration(color: Theme.of(context).primaryColor, shape: BoxShape.circle),
)
: const SizedBox.shrink()),
),
);
},
),
)));
}
List<T> map<T>(List list, Function handler) {
List<T> result = [];
int mapLength = (widget.model.newsType == 'news' || widget.model.newsType == "user_choice") ? min(newsLength, limit) : min(brNewsLength, limit);
for (var i = 0; i < mapLength; i++) {
result.add(handler(i, list[i]));
}
return result;
}
}

View File

@@ -0,0 +1,170 @@
import 'dart:math';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:news/cubits/appSystemSettingCubit.dart';
import 'package:news/ui/screens/HomePage/Widgets/CommonSectionTitle.dart';
import 'package:news/app/routes.dart';
import 'package:news/data/models/BreakingNewsModel.dart';
import 'package:news/data/models/FeatureSectionModel.dart';
import 'package:news/data/models/NewsModel.dart';
import 'package:news/ui/styles/colors.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/ui/widgets/networkImage.dart';
import 'package:news/utils/constant.dart';
import 'package:news/utils/uiUtils.dart';
class Style2Section extends StatelessWidget {
final FeatureSectionModel model;
bool isNews = true;
Style2Section({super.key, required this.model});
@override
Widget build(BuildContext context) {
return style2Data(model, context);
}
Widget style2Data(FeatureSectionModel model, BuildContext context) {
if (model.breakVideos!.isNotEmpty || model.breakNews!.isNotEmpty || model.videos!.isNotEmpty || model.news!.isNotEmpty) {
if (model.newsType == 'news' || model.videosType == "news" || model.newsType == "user_choice") {
if ((model.newsType == 'news' || model.newsType == "user_choice") ? model.news!.isNotEmpty : model.videos!.isNotEmpty) {
isNews = true;
}
}
if (model.newsType == 'breaking_news' || model.videosType == "breaking_news") {
if (model.newsType == 'breaking_news' ? model.breakNews!.isNotEmpty : model.breakVideos!.isNotEmpty) {
isNews = false;
}
}
int limit = limitOfAllOtherStyle;
int newsLength = (model.newsType == 'news' || model.newsType == "user_choice") ? model.news!.length : model.videos!.length;
int brNewsLength = model.newsType == 'breaking_news' ? model.breakNews!.length : model.breakVideos!.length;
var totalCount = (isNews) ? min(newsLength, limit) : min(brNewsLength, limit);
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
commonSectionTitle(model, context),
ListView.builder(
padding: EdgeInsets.zero,
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: totalCount,
itemBuilder: (context, index) => (isNews)
? setStyle2(context: context, index: index, model: model, newsModel: (model.newsType == 'news' || model.newsType == "user_choice") ? model.news![index] : model.videos![index])
: setStyle2(context: context, index: index, model: model, breakingNewsModel: (model.newsType == 'breaking_news') ? model.breakNews![index] : model.breakVideos![index])),
],
);
} else {
return const SizedBox.shrink();
}
}
Widget setStyle2({required BuildContext context, required int index, required FeatureSectionModel model, NewsModel? newsModel, BreakingNewsModel? breakingNewsModel}) {
return Padding(
padding: EdgeInsets.only(top: index == 0 ? 0 : 15),
child: Column(
children: [
if (context.read<AppConfigurationCubit>().getInAppAdsMode() == "1" &&
(context.read<AppConfigurationCubit>().getAdsType() != "unity" || context.read<AppConfigurationCubit>().getIOSAdsType() != "unity"))
nativeAdsShow(context: context, index: index),
InkWell(
onTap: () {
//interstitial ads
UiUtils.showInterstitialAds(context: context);
if (model.newsType == 'news' || model.newsType == "user_choice") {
List<NewsModel> newsList = [];
newsList.addAll(model.news!);
newsList.removeAt(index);
Navigator.of(context).pushNamed(Routes.newsDetails, arguments: {"model": newsModel, "newsList": newsList, "isFromBreak": false, "fromShowMore": false});
} else if (model.newsType == 'breaking_news') {
List<BreakingNewsModel> breakList = [];
breakList.addAll(model.breakNews!);
breakList.removeAt(index);
Navigator.of(context).pushNamed(Routes.newsDetails, arguments: {"breakModel": breakingNewsModel, "breakNewsList": breakList, "isFromBreak": true, "fromShowMore": false});
}
},
child: Stack(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(15),
child: ShaderMask(
shaderCallback: (rect) =>
LinearGradient(begin: Alignment.center, end: Alignment.bottomCenter, colors: [Colors.transparent, darkSecondaryColor.withOpacity(0.9)]).createShader(rect),
blendMode: BlendMode.darken,
child: Container(
color: primaryColor.withAlpha(5),
width: double.maxFinite,
height: MediaQuery.of(context).size.height / 3.3,
child: CustomNetworkImage(
networkImageUrl: (newsModel != null) ? newsModel.image! : breakingNewsModel!.image!,
fit: BoxFit.cover,
width: double.maxFinite,
height: MediaQuery.of(context).size.height / 3.3,
isVideo: model.newsType == 'videos' ? true : false),
),
)),
if (model.newsType == 'videos')
Positioned.directional(
textDirection: Directionality.of(context),
top: MediaQuery.of(context).size.height * 0.12,
start: MediaQuery.of(context).size.width / 3,
end: MediaQuery.of(context).size.width / 3,
child: InkWell(
onTap: () {
List<NewsModel> newsList = [];
List<BreakingNewsModel> brNewsList = [];
if (model.breakVideos != null && model.breakVideos!.isNotEmpty) brNewsList = List.from(model.breakVideos ?? [])..removeAt(index);
if (model.videos != null && model.videos!.isNotEmpty) newsList = List.from(model.videos ?? [])..removeAt(index);
Navigator.of(context).pushNamed(Routes.newsVideo, arguments: {
"from": 1,
"model": (newsModel != null) ? newsModel : breakingNewsModel!,
if (newsList.isNotEmpty) "otherVideos": newsList,
if (brNewsList.isNotEmpty) "otherBreakingVideos": brNewsList
});
},
child: UiUtils.setPlayButton(context: context))),
Positioned.directional(
textDirection: Directionality.of(context),
bottom: 10,
start: 10,
end: 10,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
if (newsModel != null && newsModel.categoryName != null)
ClipRRect(
borderRadius: BorderRadius.circular(8.0),
child: Container(
padding: const EdgeInsets.all(5),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
child: CustomTextLabel(
text: newsModel.categoryName!,
textStyle: Theme.of(context).textTheme.bodyLarge?.copyWith(color: secondaryColor.withOpacity(0.6)),
overflow: TextOverflow.ellipsis,
softWrap: true)),
),
),
Padding(
padding: const EdgeInsets.only(top: 8),
child: CustomTextLabel(
text: (newsModel != null) ? newsModel.title! : breakingNewsModel!.title!,
textStyle: Theme.of(context).textTheme.titleMedium?.copyWith(color: secondaryColor, fontWeight: FontWeight.normal),
maxLines: 2,
overflow: TextOverflow.ellipsis,
softWrap: true)),
],
))
],
),
),
],
),
);
}
}

View File

@@ -0,0 +1,241 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:news/app/routes.dart';
import 'package:news/cubits/appSystemSettingCubit.dart';
import 'package:news/data/models/BreakingNewsModel.dart';
import 'package:news/data/models/FeatureSectionModel.dart';
import 'package:news/data/models/NewsModel.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/utils/constant.dart';
import 'package:news/utils/uiUtils.dart';
import 'package:news/ui/styles/colors.dart';
import 'package:news/ui/widgets/networkImage.dart';
import 'package:news/ui/screens/HomePage/Widgets/CommonSectionTitle.dart';
class Style3Section extends StatelessWidget {
final FeatureSectionModel model;
const Style3Section({super.key, required this.model});
@override
Widget build(BuildContext context) {
return style3Data(model, context);
}
Widget style3Data(FeatureSectionModel model, BuildContext context) {
int limit = limitOfAllOtherStyle;
int newsLength = (model.newsType == 'news' || model.newsType == "user_choice") ? model.news!.length : model.videos!.length;
int brNewsLength = model.newsType == 'breaking_news' ? model.breakNews!.length : model.breakVideos!.length;
if (model.breakVideos!.isNotEmpty || model.breakNews!.isNotEmpty || model.videos!.isNotEmpty || model.news!.isNotEmpty) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
commonSectionTitle(model, context),
if ((model.newsType == 'breaking_news' || model.videosType == "breaking_news") && (model.newsType == 'breaking_news' ? model.breakNews!.isNotEmpty : model.breakVideos!.isNotEmpty))
SizedBox(
height: MediaQuery.of(context).size.height * 0.34,
width: MediaQuery.of(context).size.width,
child: ListView.builder(
padding: EdgeInsets.zero,
shrinkWrap: true,
physics: const AlwaysScrollableScrollPhysics(),
itemCount: min(brNewsLength, limit),
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
BreakingNewsModel data = model.newsType == 'breaking_news' ? model.breakNews![index] : model.breakVideos![index];
return Row(
mainAxisSize: MainAxisSize.min,
children: [
if ((index != 0 && index % nativeAdsIndex == 0) &&
context.read<AppConfigurationCubit>().getInAppAdsMode() == "1" &&
(context.read<AppConfigurationCubit>().getAdsType() != "unity" || context.read<AppConfigurationCubit>().getIOSAdsType() != "unity"))
SizedBox(width: MediaQuery.of(context).size.width * 0.87, child: nativeAdsShow(context: context, index: index)),
InkWell(
child: SizedBox(
width: MediaQuery.of(context).size.width * 0.87,
child: Stack(
children: <Widget>[
Positioned.directional(
textDirection: Directionality.of(context),
start: 0,
end: 0,
top: MediaQuery.of(context).size.height / 15,
child: Container(
alignment: Alignment.center,
height: MediaQuery.of(context).size.height / 4,
margin: EdgeInsetsDirectional.only(start: index == 0 ? 0 : 10, end: 10, top: 10, bottom: 10),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(15), color: UiUtils.getColorScheme(context).surface),
padding: const EdgeInsets.all(14),
child: Padding(
padding: EdgeInsets.only(top: MediaQuery.of(context).size.height / 9),
child: CustomTextLabel(
text: data.title!,
textStyle: Theme.of(context).textTheme.titleMedium!.copyWith(color: UiUtils.getColorScheme(context).primaryContainer, fontWeight: FontWeight.normal),
softWrap: true,
maxLines: 2,
overflow: TextOverflow.ellipsis)),
)),
Positioned.directional(
textDirection: Directionality.of(context),
start: 30,
end: 30,
child: ClipRRect(
borderRadius: BorderRadius.circular(15),
child: CustomNetworkImage(
networkImageUrl: data.image!,
height: MediaQuery.of(context).size.height / 4.7,
width: double.maxFinite,
fit: BoxFit.cover,
isVideo: model.newsType == 'videos' ? true : false),
),
),
if (model.newsType == 'videos')
Positioned.directional(
textDirection: Directionality.of(context),
top: MediaQuery.of(context).size.height * 0.085,
start: MediaQuery.of(context).size.width / 3,
end: MediaQuery.of(context).size.width / 3,
child: InkWell(
onTap: () {
List<BreakingNewsModel> brNewsList = List.from(model.breakVideos ?? [])..removeAt(index);
Navigator.of(context).pushNamed(Routes.newsVideo, arguments: {"from": 3, "breakModel": data, "otherBreakingVideos": brNewsList});
},
child: UiUtils.setPlayButton(context: context)),
),
],
),
),
onTap: () {
if (model.newsType == 'breaking_news') {
//interstitial ads
UiUtils.showInterstitialAds(context: context);
List<BreakingNewsModel> breakList = [];
breakList.addAll(model.breakNews!);
breakList.removeAt(index);
Navigator.of(context).pushNamed(Routes.newsDetails, arguments: {"breakModel": data, "breakNewsList": breakList, "isFromBreak": true, "fromShowMore": false});
}
},
),
],
);
})),
if ((model.newsType == 'news' || model.videosType == "news" || model.newsType == "user_choice") &&
((model.newsType == 'news' || model.newsType == "user_choice") ? model.news!.isNotEmpty : model.videos!.isNotEmpty))
SizedBox(
height: MediaQuery.of(context).size.height * 0.34,
child: ListView.builder(
padding: EdgeInsets.zero,
physics: const AlwaysScrollableScrollPhysics(),
itemCount: min(newsLength, limit),
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
NewsModel data = (model.newsType == 'news' || model.newsType == "user_choice") ? model.news![index] : model.videos![index];
return Row(
mainAxisSize: MainAxisSize.min,
children: [
if ((index != 0 && index % nativeAdsIndex == 0) &&
context.read<AppConfigurationCubit>().getInAppAdsMode() == "1" &&
(context.read<AppConfigurationCubit>().getAdsType() != "unity" || context.read<AppConfigurationCubit>().getIOSAdsType() != "unity"))
SizedBox(width: MediaQuery.of(context).size.width * 0.87, child: nativeAdsShow(context: context, index: index)),
InkWell(
child: SizedBox(
width: MediaQuery.of(context).size.width * 0.87,
child: Stack(
children: <Widget>[
Positioned.directional(
textDirection: Directionality.of(context),
start: 0,
end: 0,
top: MediaQuery.of(context).size.height / 15,
child: Container(
alignment: Alignment.center,
height: MediaQuery.of(context).size.height / 3.8,
margin: EdgeInsetsDirectional.only(start: index == 0 ? 0 : 10, end: 10, top: 10, bottom: 10),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(15), color: UiUtils.getColorScheme(context).surface),
padding: const EdgeInsets.all(14),
child: Padding(
padding: EdgeInsets.only(top: MediaQuery.of(context).size.height / 8),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (data.categoryName != null)
Container(
height: 20.0,
padding: const EdgeInsetsDirectional.only(start: 8.0, end: 8.0, top: 2.5),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(5), color: Theme.of(context).primaryColor),
child: CustomTextLabel(
text: data.categoryName!,
textAlign: TextAlign.center,
textStyle: Theme.of(context).textTheme.bodyMedium?.copyWith(color: secondaryColor),
overflow: TextOverflow.ellipsis,
softWrap: true)),
Padding(
padding: const EdgeInsets.only(top: 10.0),
child: CustomTextLabel(
text: data.title!,
textStyle:
Theme.of(context).textTheme.titleMedium!.copyWith(color: UiUtils.getColorScheme(context).primaryContainer, fontWeight: FontWeight.normal),
softWrap: true,
maxLines: 2,
overflow: TextOverflow.ellipsis)),
],
),
),
)),
Positioned.directional(
textDirection: Directionality.of(context),
start: 30,
end: 30,
child: ClipRRect(
borderRadius: BorderRadius.circular(15),
child: CustomNetworkImage(
networkImageUrl: data.image!,
height: MediaQuery.of(context).size.height / 4.7,
width: MediaQuery.of(context).size.width,
fit: BoxFit.cover,
isVideo: model.newsType == 'videos' ? true : false),
),
),
if (model.newsType == 'videos')
Positioned.directional(
textDirection: Directionality.of(context),
top: MediaQuery.of(context).size.height * 0.085,
start: MediaQuery.of(context).size.width / 3,
end: MediaQuery.of(context).size.width / 3,
child: InkWell(
onTap: () {
List<NewsModel> allNewsList = List.from(model.videos ?? [])..removeAt(index);
Navigator.of(context).pushNamed(Routes.newsVideo, arguments: {"from": 1, "model": data, "otherVideos": allNewsList});
},
child: UiUtils.setPlayButton(context: context)),
),
],
),
),
onTap: () {
if (model.newsType == 'news' || model.newsType == "user_choice") {
//interstitial ads
UiUtils.showInterstitialAds(context: context);
List<NewsModel> newsList = [];
newsList.addAll(model.news!);
newsList.removeAt(index);
Navigator.of(context).pushNamed(Routes.newsDetails, arguments: {"model": data, "newsList": newsList, "isFromBreak": false, "fromShowMore": false});
}
},
),
],
);
})),
],
);
} else {
return const SizedBox.shrink();
}
}
}

View File

@@ -0,0 +1,189 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:news/ui/screens/HomePage/Widgets/CommonSectionTitle.dart';
import 'package:news/app/routes.dart';
import 'package:news/data/models/BreakingNewsModel.dart';
import 'package:news/data/models/FeatureSectionModel.dart';
import 'package:news/data/models/NewsModel.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/utils/constant.dart';
import 'package:news/utils/uiUtils.dart';
import 'package:news/ui/styles/colors.dart';
import 'package:news/ui/widgets/networkImage.dart';
class Style4Section extends StatelessWidget {
final FeatureSectionModel model;
const Style4Section({super.key, required this.model});
@override
Widget build(BuildContext context) {
return style4Data(model, context);
}
Widget style4Data(FeatureSectionModel model, BuildContext context) {
int limit = limitOfAllOtherStyle;
int newsLength = (model.newsType == 'news' || model.newsType == "user_choice") ? model.news!.length : model.videos!.length;
int brNewsLength = model.newsType == 'breaking_news' ? model.breakNews!.length : model.breakVideos!.length;
if (model.breakVideos!.isNotEmpty || model.breakNews!.isNotEmpty || model.videos!.isNotEmpty || model.news!.isNotEmpty) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
commonSectionTitle(model, context),
if ((model.newsType == 'news' || model.videosType == "news" || model.newsType == "user_choice") &&
((model.newsType == 'news' || model.newsType == "user_choice") ? model.news!.isNotEmpty : model.videos!.isNotEmpty))
Column(
children: [
setGridLayout(
context: context,
totalCount: min(newsLength, limit),
childWidget: (context, index) {
NewsModel data = (model.newsType == 'news' || model.newsType == "user_choice") ? model.news![index] : model.videos![index];
return InkWell(
child: Container(
padding: const EdgeInsets.all(7),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(10), color: UiUtils.getColorScheme(context).surface),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Stack(children: [
setNewsImage(context: context, imageURL: data.image!),
if (data.categoryName != null && data.categoryName != "")
Align(
alignment: Alignment.topLeft,
child: Container(
margin: const EdgeInsetsDirectional.only(start: 7.0, top: 7.0),
height: 18.0,
padding: const EdgeInsetsDirectional.only(start: 6.0, end: 6.0, top: 2.5),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(5), color: Theme.of(context).primaryColor),
child: CustomTextLabel(
text: data.categoryName!,
textAlign: TextAlign.center,
textStyle: Theme.of(context).textTheme.bodySmall?.copyWith(color: secondaryColor),
overflow: TextOverflow.ellipsis,
softWrap: true)),
),
if (model.newsType == 'videos')
Positioned.directional(
textDirection: Directionality.of(context),
top: MediaQuery.of(context).size.height * 0.065,
start: MediaQuery.of(context).size.width / 6,
end: MediaQuery.of(context).size.width / 6,
child: InkWell(
onTap: () {
List<NewsModel> allNewsList = List.from(model.videos ?? [])..removeAt(index);
Navigator.of(context).pushNamed(Routes.newsVideo, arguments: {"from": 1, "model": data, "otherVideos": allNewsList});
},
child: UiUtils.setPlayButton(context: context)),
),
]),
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: CustomTextLabel(
text: data.title!,
textStyle: Theme.of(context).textTheme.titleSmall!.copyWith(color: UiUtils.getColorScheme(context).primaryContainer),
softWrap: true,
maxLines: 3,
overflow: TextOverflow.ellipsis)),
],
),
),
onTap: () {
if (model.newsType == 'news' || model.newsType == "user_choice") {
//show interstitial ads
UiUtils.showInterstitialAds(context: context);
List<NewsModel> newsList = [];
newsList.addAll(model.news!);
newsList.removeAt(index);
Navigator.of(context).pushNamed(Routes.newsDetails, arguments: {"model": data, "newsList": newsList, "isFromBreak": false, "fromShowMore": false});
}
},
);
}),
],
),
if ((model.newsType == 'breaking_news' || model.videosType == "breaking_news") && (model.newsType == 'breaking_news' ? model.breakNews!.isNotEmpty : model.breakVideos!.isNotEmpty))
setGridLayout(
context: context,
totalCount: min(brNewsLength, limit),
childWidget: (context, index) {
BreakingNewsModel data = model.newsType == 'breaking_news' ? model.breakNews![index] : model.breakVideos![index];
return InkWell(
child: Container(
padding: const EdgeInsets.all(7),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(10), color: UiUtils.getColorScheme(context).surface),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Stack(children: [
setNewsImage(context: context, imageURL: data.image!),
if (model.newsType == 'videos')
Positioned.directional(
textDirection: Directionality.of(context),
top: MediaQuery.of(context).size.height * 0.065,
start: MediaQuery.of(context).size.width / 6,
end: MediaQuery.of(context).size.width / 6,
child: InkWell(
onTap: () {
List<BreakingNewsModel> brNewsList = List.from(model.breakVideos ?? [])..removeAt(index);
Navigator.of(context).pushNamed(Routes.newsVideo, arguments: {"from": 3, "breakModel": data, "otherBreakingVideos": brNewsList});
},
child: UiUtils.setPlayButton(context: context)),
),
]),
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: CustomTextLabel(
text: data.title!,
textStyle: Theme.of(context).textTheme.titleSmall!.copyWith(color: UiUtils.getColorScheme(context).primaryContainer),
softWrap: true,
maxLines: 3,
overflow: TextOverflow.ellipsis)),
],
),
),
onTap: () {
if (model.newsType == 'breaking_news') {
//interstitial ads
UiUtils.showInterstitialAds(context: context);
List<BreakingNewsModel> breakList = [];
breakList.addAll(model.breakNews!);
breakList.removeAt(index);
Navigator.of(context).pushNamed(Routes.newsDetails, arguments: {"breakModel": data, "breakNewsList": breakList, "isFromBreak": true, "fromShowMore": false});
}
},
);
})
],
);
} else {
return const SizedBox.shrink();
}
}
Widget setGridLayout({required BuildContext context, required int totalCount, required Widget? Function(BuildContext, int) childWidget}) {
return GridView.builder(
padding: EdgeInsets.zero,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(mainAxisExtent: MediaQuery.of(context).size.height * 0.27, crossAxisCount: 2, crossAxisSpacing: 10, mainAxisSpacing: 13),
shrinkWrap: true,
itemCount: totalCount,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: childWidget);
}
Widget setNewsImage({required BuildContext context, required String imageURL}) {
return ClipRRect(
borderRadius: BorderRadius.circular(10),
child: CustomNetworkImage(
networkImageUrl: imageURL,
height: MediaQuery.of(context).size.height * 0.175,
width: MediaQuery.of(context).size.width,
fit: BoxFit.cover,
isVideo: model.newsType == 'videos' ? true : false));
}
}

View File

@@ -0,0 +1,400 @@
import 'dart:math';
import 'package:news/ui/screens/HomePage/Widgets/CommonSectionTitle.dart';
import 'package:flutter/material.dart';
import 'package:news/app/routes.dart';
import 'package:news/data/models/BreakingNewsModel.dart';
import 'package:news/data/models/FeatureSectionModel.dart';
import 'package:news/data/models/NewsModel.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/utils/constant.dart';
import 'package:news/utils/uiUtils.dart';
import 'package:news/ui/styles/colors.dart';
import 'package:news/ui/widgets/networkImage.dart';
class Style5Section extends StatelessWidget {
final FeatureSectionModel model;
const Style5Section({super.key, required this.model});
@override
Widget build(BuildContext context) {
return style5Data(model, context);
}
Widget style5SingleNewsData(FeatureSectionModel model, BuildContext context) {
NewsModel data = (model.newsType == 'news' || model.newsType == "user_choice") ? model.news![0] : model.videos![0];
return InkWell(
child: Container(
height: MediaQuery.of(context).size.height * 0.28,
width: double.maxFinite,
decoration: BoxDecoration(borderRadius: BorderRadius.circular(15)),
child: Stack(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(15),
child: CustomNetworkImage(
networkImageUrl: data.image!, height: MediaQuery.of(context).size.height * 0.28, width: double.maxFinite, fit: BoxFit.cover, isVideo: model.newsType == 'videos' ? true : false),
),
Align(
alignment: Alignment.bottomLeft,
child: Padding(
padding: EdgeInsets.all(MediaQuery.of(context).size.height * 0.01),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (data.categoryName != null)
Container(
height: 20.0,
padding: const EdgeInsetsDirectional.only(start: 8.0, end: 8.0, top: 2.5),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(5), color: Theme.of(context).primaryColor),
child: CustomTextLabel(
text: data.categoryName!,
textAlign: TextAlign.left,
textStyle: Theme.of(context).textTheme.bodyMedium?.copyWith(color: secondaryColor),
overflow: TextOverflow.ellipsis,
softWrap: true)),
Padding(
padding: EdgeInsets.only(top: MediaQuery.of(context).size.height * 0.01),
child: CustomTextLabel(
text: data.title!,
textAlign: TextAlign.left,
textStyle: Theme.of(context).textTheme.titleMedium?.copyWith(color: secondaryColor, fontWeight: FontWeight.w700),
maxLines: 3,
overflow: TextOverflow.ellipsis,
softWrap: true)),
Padding(
padding: EdgeInsets.only(top: MediaQuery.of(context).size.height * 0.01),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.calendar_month, color: secondaryColor, size: 20),
Padding(
padding: const EdgeInsetsDirectional.only(start: 10),
child: CustomTextLabel(
text: UiUtils.convertToAgo(context, DateTime.parse(data.publishDate ?? data.date!), 0)!,
textAlign: TextAlign.left,
textStyle: Theme.of(context).textTheme.bodyMedium?.copyWith(color: secondaryColor, fontWeight: FontWeight.w600),
overflow: TextOverflow.ellipsis,
softWrap: true)),
],
),
),
if (model.newsType == 'videos')
InkWell(
child: Padding(padding: EdgeInsets.only(top: MediaQuery.of(context).size.height * 0.02), child: UiUtils.setPlayButton(context: context)),
onTap: () {
List<NewsModel> allNewsList = List.from(model.videos ?? [])..removeWhere((item) => item.id == data.id);
Navigator.of(context).pushNamed(Routes.newsVideo, arguments: {"from": 1, "model": data, "otherVideos": allNewsList});
},
),
],
),
),
),
],
),
),
onTap: () {
if (model.newsType == 'news' || model.newsType == "user_choice") {
//interstitial ads
UiUtils.showInterstitialAds(context: context);
List<NewsModel> newsList = [];
newsList.addAll(model.news!);
newsList.removeAt(0);
Navigator.of(context).pushNamed(Routes.newsDetails, arguments: {"model": data, "newsList": newsList, "isFromBreak": false, "fromShowMore": false});
}
},
);
}
Widget style5SingleBreakNewsData(FeatureSectionModel model, BuildContext context) {
BreakingNewsModel data = model.newsType == 'breaking_news' ? model.breakNews![0] : model.breakVideos![0];
return InkWell(
child: Container(
height: MediaQuery.of(context).size.height * 0.28,
width: double.maxFinite,
decoration: BoxDecoration(borderRadius: BorderRadius.circular(15)),
child: Stack(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(15),
child: ShaderMask(
shaderCallback: (rect) => LinearGradient(begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Colors.transparent, darkSecondaryColor.withOpacity(0.9)]).createShader(rect),
blendMode: BlendMode.darken,
child: CustomNetworkImage(
networkImageUrl: data.image!, height: MediaQuery.of(context).size.height * 0.28, width: double.maxFinite, fit: BoxFit.cover, isVideo: model.newsType == 'videos' ? true : false),
),
),
(model.newsType == 'videos')
? Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: EdgeInsets.all(MediaQuery.of(context).size.height * 0.01),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.only(top: MediaQuery.of(context).size.height * 0.02),
child: CustomTextLabel(
text: data.title!,
textAlign: TextAlign.left,
textStyle: Theme.of(context).textTheme.titleMedium?.copyWith(color: secondaryColor, fontWeight: FontWeight.w700),
maxLines: 3,
overflow: TextOverflow.ellipsis,
softWrap: true)),
InkWell(
child: Padding(padding: EdgeInsets.only(top: MediaQuery.of(context).size.height * 0.02), child: UiUtils.setPlayButton(context: context)),
onTap: () {
List<BreakingNewsModel> brNewsList = List.from(model.breakVideos ?? [])..removeWhere((item) => item.id == data.id);
Navigator.of(context).pushNamed(Routes.newsVideo, arguments: {"from": 3, "breakModel": data, "otherBreakingVideos": brNewsList});
},
),
],
)),
)
: Positioned(
bottom: 0,
left: 0,
right: 0,
child: Container(
padding: EdgeInsets.all(MediaQuery.of(context).size.height * 0.01),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(15)),
child: CustomTextLabel(
text: data.title!,
textAlign: TextAlign.left,
textStyle: Theme.of(context).textTheme.titleMedium?.copyWith(color: secondaryColor, fontWeight: FontWeight.w700),
maxLines: 3,
overflow: TextOverflow.ellipsis,
softWrap: true)),
),
],
),
),
onTap: () {
if (model.newsType == 'breaking_news') {
//show interstitial ads
UiUtils.showInterstitialAds(context: context);
List<BreakingNewsModel> breakList = [];
breakList.addAll(model.breakNews!);
breakList.removeAt(0);
Navigator.of(context).pushNamed(Routes.newsDetails, arguments: {"breakModel": data, "breakNewsList": breakList, "isFromBreak": true, "fromShowMore": false});
}
},
);
}
Widget style5Data(FeatureSectionModel model, BuildContext context) {
int limit = limitOfAllOtherStyle;
int newsLength = (model.newsType == 'news' || model.newsType == "user_choice") ? model.news!.length : model.videos!.length;
int brNewsLength = model.newsType == 'breaking_news' ? model.breakNews!.length : model.breakVideos!.length;
if (model.breakVideos!.isNotEmpty || model.breakNews!.isNotEmpty || model.videos!.isNotEmpty || model.news!.isNotEmpty) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
commonSectionTitle(model, context),
if ((model.newsType == 'news' || model.videosType == "news" || model.newsType == "user_choice") && (model.newsType == 'news' || model.newsType == "user_choice")
? model.news!.isNotEmpty
: model.videos!.isNotEmpty)
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
style5SingleNewsData(model, context),
if (newsLength > 1)
Padding(
padding: const EdgeInsets.only(top: 15.0),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: List.generate(min(newsLength, limit), (index) {
if (newsLength > index + 1) {
NewsModel data = (model.newsType == 'news' || model.newsType == "user_choice") ? model.news![index + 1] : model.videos![index + 1];
return InkWell(
child: SizedBox(
width: MediaQuery.of(context).size.width / 2.35,
child: Padding(
padding: EdgeInsetsDirectional.only(start: index == 0 ? 0 : 10.0),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Stack(children: [
ClipRRect(
borderRadius: BorderRadius.circular(10),
child: CustomNetworkImage(
networkImageUrl: data.image!,
height: MediaQuery.of(context).size.height * 0.15,
width: MediaQuery.of(context).size.width / 2.35,
fit: BoxFit.cover,
isVideo: model.newsType == 'videos' ? true : false),
),
if (data.categoryName != null)
Align(
alignment: Alignment.topLeft,
child: Container(
margin: const EdgeInsetsDirectional.only(start: 7.0, top: 7.0),
height: 18.0,
padding: const EdgeInsetsDirectional.only(start: 6.0, end: 6.0, top: 2.5),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(5), color: Theme.of(context).primaryColor),
child: CustomTextLabel(
text: data.categoryName!,
textAlign: TextAlign.left,
textStyle: Theme.of(context).textTheme.bodySmall?.copyWith(color: secondaryColor),
overflow: TextOverflow.ellipsis,
softWrap: true)),
),
if (model.newsType == 'videos')
Positioned.directional(
textDirection: Directionality.of(context),
top: MediaQuery.of(context).size.height * 0.058,
start: MediaQuery.of(context).size.width / 8.5,
end: MediaQuery.of(context).size.width / 8.5,
child: InkWell(
onTap: () {
List<NewsModel> allNewsList = List.from(model.videos ?? [])..removeAt(index);
Navigator.of(context).pushNamed(Routes.newsVideo, arguments: {"from": 1, "model": data, "otherVideos": allNewsList});
},
child: UiUtils.setPlayButton(context: context, heightVal: 28)),
),
]),
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: CustomTextLabel(
text: data.title!,
textStyle: Theme.of(context).textTheme.labelMedium!.copyWith(color: UiUtils.getColorScheme(context).primaryContainer),
softWrap: true,
maxLines: 2,
overflow: TextOverflow.ellipsis)),
Padding(
padding: const EdgeInsets.only(top: 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.calendar_month, color: UiUtils.getColorScheme(context).primaryContainer, size: 15),
Padding(
padding: const EdgeInsetsDirectional.only(start: 5),
child: CustomTextLabel(
text: UiUtils.convertToAgo(context, DateTime.parse(data.publishDate ?? data.date!), 0)!,
textAlign: TextAlign.left,
textStyle: Theme.of(context).textTheme.bodySmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer),
overflow: TextOverflow.ellipsis,
softWrap: true)),
],
),
),
],
),
),
),
onTap: () {
if (model.newsType == 'news' || model.newsType == "user_choice") {
//interstitial ads
UiUtils.showInterstitialAds(context: context);
List<NewsModel> newsList = [];
newsList.addAll(model.news!);
newsList.removeAt(index + 1);
Navigator.of(context).pushNamed(Routes.newsDetails, arguments: {"model": data, "newsList": newsList, "isFromBreak": false, "fromShowMore": false});
}
},
);
} else {
return const SizedBox.shrink();
}
})),
)),
],
),
if ((model.newsType == 'breaking_news' && model.breakNews?.isNotEmpty == true) || (model.videosType == 'breaking_news' && model.breakVideos?.isNotEmpty == true))
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
style5SingleBreakNewsData(model, context),
if (brNewsLength > 1)
Padding(
padding: const EdgeInsets.only(top: 15.0),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: List.generate(min(brNewsLength, limit), (index) {
if (brNewsLength > index + 1) {
BreakingNewsModel data = model.newsType == 'breaking_news' ? model.breakNews![index + 1] : model.breakVideos![index + 1];
return InkWell(
child: Container(
width: MediaQuery.of(context).size.width / 2.35,
padding: EdgeInsetsDirectional.only(start: index == 0 ? 0 : 10.0),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Stack(children: [
ClipRRect(
borderRadius: BorderRadius.circular(10),
child: CustomNetworkImage(
networkImageUrl: data.image!,
height: MediaQuery.of(context).size.height * 0.15,
width: MediaQuery.of(context).size.width / 2.35,
fit: BoxFit.cover,
isVideo: model.newsType == 'videos' ? true : false),
),
if (model.newsType == 'videos')
Positioned.directional(
textDirection: Directionality.of(context),
top: MediaQuery.of(context).size.height * 0.058,
start: MediaQuery.of(context).size.width / 8.5,
end: MediaQuery.of(context).size.width / 8.5,
child: InkWell(
onTap: () {
List<BreakingNewsModel> brNewsList = List.from(model.breakVideos ?? [])..removeAt(index);
Navigator.of(context).pushNamed(Routes.newsVideo, arguments: {"from": 3, "breakModel": data, "otherBreakingVideos": brNewsList});
},
child: UiUtils.setPlayButton(context: context, heightVal: 28)),
),
]),
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: CustomTextLabel(
text: data.title!,
textStyle: Theme.of(context).textTheme.titleSmall!.copyWith(color: UiUtils.getColorScheme(context).primaryContainer),
softWrap: true,
maxLines: 2,
overflow: TextOverflow.ellipsis)),
],
),
),
onTap: () {
if (model.newsType == 'breaking_news') {
//interstitial ads
UiUtils.showInterstitialAds(context: context);
List<BreakingNewsModel> breakList = [];
breakList.addAll(model.breakNews!);
breakList.removeAt(index + 1);
Navigator.of(context).pushNamed(Routes.newsDetails, arguments: {"breakModel": data, "breakNewsList": breakList, "isFromBreak": true, "fromShowMore": false});
}
},
);
} else {
return const SizedBox.shrink();
}
})),
)),
],
)
],
);
} else {
return const SizedBox.shrink();
}
}
}

View File

@@ -0,0 +1,216 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:news/app/routes.dart';
import 'package:news/cubits/appLocalizationCubit.dart';
import 'package:news/cubits/sectionByIdCubit.dart';
import 'package:news/data/models/BreakingNewsModel.dart';
import 'package:news/data/models/FeatureSectionModel.dart';
import 'package:news/data/models/NewsModel.dart';
import 'package:news/data/repositories/Settings/settingsLocalDataRepository.dart';
import 'package:news/utils/uiUtils.dart';
import 'package:news/ui/styles/colors.dart';
import 'package:news/ui/widgets/networkImage.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
class Style6Section extends StatefulWidget {
final FeatureSectionModel model;
const Style6Section({super.key, required this.model});
@override
Style6SectionState createState() => Style6SectionState();
}
class Style6SectionState extends State<Style6Section> {
int? style6Sel;
bool isNews = true;
late final ScrollController style6ScrollController = ScrollController()..addListener(hasMoreSectionScrollListener);
late BreakingNewsModel breakingNewsData;
late NewsModel newsData;
@override
void initState() {
super.initState();
getSectionDataById();
}
@override
void dispose() {
style6ScrollController.removeListener(() {});
super.dispose();
}
void hasMoreSectionScrollListener() {
if (style6ScrollController.position.maxScrollExtent == style6ScrollController.offset) {
if (context.read<SectionByIdCubit>().hasMoreSections() && !(context.read<SectionByIdCubit>().state is SectionByIdFetchInProgress)) {
context.read<SectionByIdCubit>().getMoreSectionById(langId: context.read<AppLocalizationCubit>().state.id, sectionId: widget.model.id!);
}
}
}
@override
Widget build(BuildContext context) {
return style6Data();
}
void getSectionDataById() {
Future.delayed(Duration.zero, () {
context.read<SectionByIdCubit>().getSectionById(
langId: context.read<AppLocalizationCubit>().state.id,
sectionId: widget.model.id!,
latitude: SettingsLocalDataRepository().getLocationCityValues().first,
longitude: SettingsLocalDataRepository().getLocationCityValues().last);
});
}
Widget style6Data() {
return BlocBuilder<SectionByIdCubit, SectionByIdState>(builder: (context, state) {
if (state is SectionByIdFetchSuccess) {
isNews = (state.type == 'news' || state.type == 'user_choice' || state.type == 'videos') && state.newsModel.isNotEmpty
? true
: (state.type == 'breaking_news' || state.type == 'videos') && state.breakNewsModel.isNotEmpty
? false
: isNews;
int totalCount = (isNews) ? state.newsModel.length : state.breakNewsModel.length;
return (state.breakNewsModel.isNotEmpty || state.newsModel.isNotEmpty)
? Padding(padding: const EdgeInsets.symmetric(vertical: 5), child: style6NewsDetails(newsData: state.newsModel, brNewsData: state.breakNewsModel, total: totalCount, type: state.type))
: SizedBox.shrink();
}
return SizedBox.shrink();
});
}
Widget style6NewsDetails({List<NewsModel>? newsData, List<BreakingNewsModel>? brNewsData, String? type, int total = 0}) {
return SizedBox(
height: MediaQuery.of(context).size.height / 2.8,
child: SingleChildScrollView(
controller: style6ScrollController,
scrollDirection: Axis.horizontal,
child: Row(
children: List.generate(total, (index) {
return SizedBox(
width: 180,
child: Padding(
padding: const EdgeInsets.all(5),
child: (isNews)
? setImageCard(index: index, total: total, type: type!, newsModel: newsData![index], allNewsList: newsData)
: setImageCard(index: index, total: total, type: type!, breakingNewsModel: brNewsData![index], breakingNewsList: brNewsData)));
}))));
}
Widget setImageCard(
{required int index,
required int total,
required String type,
NewsModel? newsModel,
BreakingNewsModel? breakingNewsModel,
List<NewsModel>? allNewsList,
List<BreakingNewsModel>? breakingNewsList}) {
return InkWell(
child: Stack(children: [
ClipRRect(
borderRadius: BorderRadius.circular(5),
child: ShaderMask(
shaderCallback: (bounds) {
return LinearGradient(begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Colors.transparent, darkSecondaryColor.withOpacity(0.9)]).createShader(bounds);
},
blendMode: BlendMode.darken,
child: Container(
color: primaryColor.withValues(alpha: 0.15),
width: MediaQuery.of(context).size.width / 1.9,
height: MediaQuery.of(context).size.height / 2.5,
child: CustomNetworkImage(
networkImageUrl: (newsModel != null) ? newsModel.image! : breakingNewsModel!.image!,
height: MediaQuery.of(context).size.height / 2.5,
width: MediaQuery.of(context).size.width / 1.9,
fit: BoxFit.cover,
isVideo: type == "videos"),
),
)),
(newsModel != null && newsModel.categoryName != null)
? Align(
alignment: Alignment.topLeft,
child: Container(
margin: const EdgeInsetsDirectional.only(start: 7.0, top: 7.0),
height: 20.0,
padding: const EdgeInsetsDirectional.only(start: 6.0, end: 6.0, top: 2.5),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(3), color: Theme.of(context).primaryColor),
child: CustomTextLabel(
text: newsModel.categoryName!,
textAlign: TextAlign.center,
textStyle: Theme.of(context).textTheme.bodySmall?.copyWith(color: secondaryColor),
overflow: TextOverflow.ellipsis,
softWrap: true)))
: const SizedBox.shrink(),
(type == "videos")
? Positioned.directional(
textDirection: Directionality.of(context),
top: MediaQuery.of(context).size.height * 0.13,
start: MediaQuery.of(context).size.width / 6,
end: MediaQuery.of(context).size.width / 6,
child: UiUtils.setPlayButton(context: context))
: const SizedBox.shrink(),
Positioned.directional(
textDirection: Directionality.of(context),
bottom: 5,
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
(newsModel != null)
? Padding(
padding: const EdgeInsets.symmetric(horizontal: 15),
child: CustomTextLabel(
text: UiUtils.convertToAgo(context, DateTime.parse(newsModel.publishDate ?? newsModel.date!), 3)!,
textStyle: Theme.of(context).textTheme.labelSmall!.copyWith(color: Colors.white)))
: const SizedBox.shrink(),
Container(
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 5),
width: MediaQuery.of(context).size.width * 0.50,
child: CustomTextLabel(
text: (newsModel != null) ? newsModel.title! : breakingNewsModel!.title!,
textStyle: Theme.of(context).textTheme.titleSmall!.copyWith(color: secondaryColor),
softWrap: true,
maxLines: 2,
overflow: TextOverflow.ellipsis))
]),
),
]),
onTap: () {
if (type == "videos") {
List<NewsModel> newsList = [];
List<BreakingNewsModel> brNewsList = [];
if (newsModel != null && allNewsList != null && allNewsList.isNotEmpty) {
newsList = List.from(allNewsList);
newsList.removeAt(index);
} else if (breakingNewsModel != null && breakingNewsList != null && breakingNewsList.isNotEmpty) {
brNewsList = List.from(breakingNewsList);
brNewsList.removeAt(index);
}
// List<NewsModel> newsList = List.from(allNewsList ?? [])..removeAt(index);
// List<BreakingNewsModel> breakNewsList = List.from(breakingNewsList ?? [])..removeAt(index);
Navigator.of(context).pushNamed(Routes.newsVideo, arguments: {
"from": (newsModel != null) ? 1 : 3,
if (newsModel != null) "model": newsModel,
if (breakingNewsModel != null) "breakModel": breakingNewsModel,
"otherBreakingVideos": brNewsList,
"otherVideos": newsList,
});
} else if (type == 'news' || type == "user_choice") {
//interstitial ads
UiUtils.showInterstitialAds(context: context);
if (allNewsList != null && allNewsList.isNotEmpty) {
List<NewsModel> newsList = List.from(allNewsList);
newsList.removeAt(index);
Navigator.of(context).pushNamed(Routes.newsDetails, arguments: {"model": newsModel, "newsList": newsList, "isFromBreak": false, "fromShowMore": false});
}
} else {
if (breakingNewsList != null && breakingNewsList.isNotEmpty) {
//interstitial ads
UiUtils.showInterstitialAds(context: context);
List<BreakingNewsModel> breakList = List.from(breakingNewsList);
breakList.removeAt(index);
Navigator.of(context).pushNamed(Routes.newsDetails, arguments: {"breakModel": breakingNewsModel, "breakNewsList": breakList, "isFromBreak": true, "fromShowMore": false});
}
}
},
);
}
}

View File

@@ -0,0 +1,140 @@
import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'package:intl/intl.dart';
import 'package:news/utils/hiveBoxKeys.dart';
import 'package:news/utils/uiUtils.dart';
import 'package:news/data/models/WeatherData.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
class WeatherDataView extends StatefulWidget {
final WeatherDetails weatherData;
const WeatherDataView({super.key, required this.weatherData});
@override
WeatherDataState createState() => WeatherDataState();
}
class WeatherDataState extends State<WeatherDataView> {
late Future<void> _future;
@override
void initState() {
super.initState();
_future = UiUtils.checkIfValidLocale(langCode: Hive.box(settingsBoxKey).get(currentLanguageCodeKey));
}
Widget weatherDataView() {
return FutureBuilder<void>(
future: _future,
builder: (context, snapshot) {
DateTime now = DateTime.now();
String day = DateFormat('EEEE').format(now);
WeatherDetails weatherData = widget.weatherData;
return Container(
margin: const EdgeInsetsDirectional.only(top: 15.0),
padding: const EdgeInsets.all(10.0),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(10.0), color: UiUtils.getColorScheme(context).surface),
height: MediaQuery.of(context).size.height * 0.14,
child: Row(
children: <Widget>[
Expanded(
flex: 3,
child: Column(
children: <Widget>[
CustomTextLabel(
text: 'weatherLbl',
textStyle: Theme.of(context).textTheme.titleSmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.8)),
maxLines: 1,
overflow: TextOverflow.ellipsis,
softWrap: true),
Row(
children: <Widget>[
Image.network("https:${weatherData.icon!}", width: 40.0, height: 40.0),
Padding(
padding: const EdgeInsetsDirectional.only(start: 7.0),
child: CustomTextLabel(
text: "${weatherData.tempC!.toString()}\u2103",
textStyle: Theme.of(context).textTheme.titleLarge?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.8)),
overflow: TextOverflow.ellipsis,
softWrap: true,
maxLines: 1))
],
)
],
),
),
const Spacer(),
Expanded(
flex: 4,
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
CustomTextLabel(
text: "${weatherData.name!},${weatherData.region!}",
textStyle: Theme.of(context).textTheme.titleSmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer, fontWeight: FontWeight.w600),
overflow: TextOverflow.ellipsis,
softWrap: true,
maxLines: 1),
CustomTextLabel(
text: weatherData.country!,
textStyle: Theme.of(context).textTheme.titleSmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer, fontWeight: FontWeight.w600),
overflow: TextOverflow.ellipsis,
softWrap: true,
maxLines: 1),
Padding(
padding: const EdgeInsetsDirectional.only(top: 3.0),
child: CustomTextLabel(
text: day,
textStyle: Theme.of(context).textTheme.bodySmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.8)),
overflow: TextOverflow.ellipsis,
softWrap: true,
maxLines: 1)),
Padding(
padding: const EdgeInsetsDirectional.only(top: 3.0),
child: CustomTextLabel(
text: weatherData.text!,
textStyle: Theme.of(context).textTheme.bodySmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.8)),
maxLines: 1,
overflow: TextOverflow.ellipsis,
softWrap: true)),
Expanded(
child: Padding(
padding: const EdgeInsetsDirectional.only(top: 3.0),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Icon(Icons.arrow_upward_outlined, size: 13.0, color: UiUtils.getColorScheme(context).primaryContainer),
CustomTextLabel(
text: "H:${weatherData.maxTempC!.toString()}\u2103",
textStyle: Theme.of(context).textTheme.bodySmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.8)),
maxLines: 1,
overflow: TextOverflow.ellipsis,
softWrap: true),
Padding(
padding: const EdgeInsetsDirectional.only(start: 8.0),
child: Icon(Icons.arrow_downward_outlined, size: 13.0, color: UiUtils.getColorScheme(context).primaryContainer)),
CustomTextLabel(
text: "L:${weatherData.minTempC!.toString()}\u2103",
textStyle: Theme.of(context).textTheme.bodySmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.8)),
maxLines: 1,
overflow: TextOverflow.ellipsis,
softWrap: true)
],
)),
)
],
),
)
],
));
},
);
}
@override
Widget build(BuildContext context) {
return weatherDataView();
}
}

View File

@@ -0,0 +1,125 @@
// ignore: must_be_immutable
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:news/utils/uiUtils.dart';
import 'package:photo_view/photo_view.dart';
class ImagePreview extends StatefulWidget {
final int index;
final List<String> imgList;
const ImagePreview({super.key, required this.index, required this.imgList});
@override
State<StatefulWidget> createState() => StatePreview();
static Route route(RouteSettings routeSettings) {
final arguments = routeSettings.arguments as Map<String, dynamic>;
return CupertinoPageRoute(builder: (_) => ImagePreview(index: arguments['index'], imgList: arguments['imgList']));
}
}
class StatePreview extends State<ImagePreview> {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
int? curPos;
@override
void initState() {
super.initState();
curPos = widget.index;
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
body: Stack(
children: <Widget>[
PageView.builder(
itemCount: widget.imgList.length,
controller: PageController(initialPage: curPos!),
onPageChanged: (index) async {
setState(() {
curPos = index;
});
},
itemBuilder: (BuildContext context, int index) {
return PhotoView(
backgroundDecoration: BoxDecoration(color: Theme.of(context).colorScheme.secondary),
initialScale: PhotoViewComputedScale.contained * 0.9,
minScale: PhotoViewComputedScale.contained * 0.9,
imageProvider: NetworkImage(widget.imgList[index]));
}),
Positioned.directional(
top: 35,
start: 20.0,
textDirection: Directionality.of(context),
child: InkWell(
onTap: () {
Navigator.of(context).pop();
},
child: ClipRRect(
borderRadius: BorderRadius.circular(22.0),
child: Container(
height: 35,
width: 35,
alignment: Alignment.center,
decoration: BoxDecoration(color: UiUtils.getColorScheme(context).primaryContainer, shape: BoxShape.circle),
child: Icon(
Icons.keyboard_backspace_rounded,
color: UiUtils.getColorScheme(context).surface,
))),
)),
Positioned(bottom: 10.0, left: 25.0, right: 25.0, child: SelectedPhoto(numberOfDots: widget.imgList.length, photoIndex: curPos!))
],
));
}
}
class SelectedPhoto extends StatelessWidget {
final int? numberOfDots;
final int? photoIndex;
const SelectedPhoto({super.key, this.numberOfDots, this.photoIndex});
Widget _inactivePhoto(BuildContext context) {
return Padding(
padding: const EdgeInsetsDirectional.only(start: 3.0, end: 3.0),
child: Container(height: 8.0, width: 8.0, decoration: BoxDecoration(color: Theme.of(context).primaryColor.withOpacity(0.4), borderRadius: BorderRadius.circular(4.0))),
);
}
Widget _activePhoto(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(left: 5.0, right: 5.0),
child: Container(
height: 10.0,
width: 10.0,
decoration:
BoxDecoration(color: Theme.of(context).primaryColor, borderRadius: BorderRadius.circular(10.0), boxShadow: const [BoxShadow(color: Colors.grey, spreadRadius: 0.0, blurRadius: 2.0)]),
),
);
}
List<Widget> _buildDots(BuildContext context) {
List<Widget> dots = [];
for (int i = 0; i < numberOfDots!; i++) {
dots.add(i == photoIndex ? _activePhoto(context) : _inactivePhoto(context));
}
return dots;
}
@override
Widget build(BuildContext context) {
return Center(
child: Row(mainAxisAlignment: MainAxisAlignment.center, children: _buildDots(context)),
);
}
}

View File

@@ -0,0 +1,79 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:news/ui/styles/colors.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/ui/widgets/networkImage.dart';
import 'package:news/ui/widgets/customAppBar.dart';
import 'package:news/app/routes.dart';
import 'package:news/data/models/LiveStreamingModel.dart';
class LiveStreaming extends StatefulWidget {
List<LiveStreamingModel> liveNews;
LiveStreaming({super.key, required this.liveNews});
@override
State<StatefulWidget> createState() => StateLive();
static Route route(RouteSettings routeSettings) {
final arguments = routeSettings.arguments as Map<String, dynamic>;
return CupertinoPageRoute(builder: (_) => LiveStreaming(liveNews: arguments['liveNews']));
}
}
class StateLive extends State<LiveStreaming> {
@override
void initState() {
super.initState();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(appBar: CustomAppBar(height: 45, isBackBtn: true, label: 'liveVideosLbl', horizontalPad: 15, isConvertText: true), body: mainListBuilder());
}
mainListBuilder() {
return Padding(
padding: const EdgeInsets.all(20),
child: ListView.separated(
itemBuilder: ((context, index) {
List<LiveStreamingModel> liveVideos = List.from(widget.liveNews)..removeAt(index);
return Padding(
padding: EdgeInsets.only(top: index == 0 ? 0 : 20),
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(10.0)),
child: InkWell(
onTap: () {
Navigator.of(context).pushNamed(Routes.newsVideo, arguments: {"from": 2, "liveModel": widget.liveNews[index], "otherLiveVideos": liveVideos});
},
child: Stack(
alignment: Alignment.center,
children: [
CustomNetworkImage(networkImageUrl: widget.liveNews[index].image!, height: 200, fit: BoxFit.cover, isVideo: true, width: double.infinity),
const CircleAvatar(radius: 30, backgroundColor: Colors.black45, child: Icon(Icons.play_arrow, size: 40, color: Colors.white)),
Positioned.directional(
textDirection: Directionality.of(context),
bottom: 10,
start: 20,
end: 20,
child: CustomTextLabel(
text: widget.liveNews[index].title!, textStyle: Theme.of(context).textTheme.titleSmall!.copyWith(color: secondaryColor), maxLines: 2, overflow: TextOverflow.ellipsis),
)
],
),
),
),
);
}),
separatorBuilder: (context, index) {
return const SizedBox(height: 3.0);
},
itemCount: widget.liveNews.length),
);
}
}

View File

@@ -0,0 +1,316 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:news/ui/widgets/errorContainerWidget.dart';
import 'package:news/utils/ErrorMessageKeys.dart';
import 'package:news/utils/uiUtils.dart';
import 'package:news/app/routes.dart';
import 'package:news/cubits/Auth/authCubit.dart';
import 'package:news/cubits/UserPreferences/setUserPreferenceCatCubit.dart';
import 'package:news/cubits/UserPreferences/userByCategoryCubit.dart';
import 'package:news/cubits/appLocalizationCubit.dart';
import 'package:news/cubits/categoryCubit.dart';
import 'package:news/data/models/CategoryModel.dart';
import 'package:news/ui/styles/colors.dart';
import 'package:news/ui/widgets/SnackBarWidget.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/ui/widgets/networkImage.dart';
import 'package:news/ui/widgets/customBackBtn.dart';
import 'package:news/ui/widgets/customTextBtn.dart';
class ManagePref extends StatefulWidget {
final int? from;
const ManagePref({super.key, this.from});
@override
State<StatefulWidget> createState() {
return StateManagePref();
}
static Route route(RouteSettings routeSettings) {
final arguments = routeSettings.arguments as Map<String, dynamic>;
return CupertinoPageRoute(builder: (_) => ManagePref(from: arguments['from']));
}
}
class StateManagePref extends State<ManagePref> with TickerProviderStateMixin {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
String catId = "";
List<String> selectedChoices = [];
String selCatId = "";
static int _count = 0;
List<bool> _checks = [];
late final ScrollController _categoryScrollController = ScrollController()..addListener(hasMoreCategoryScrollListener);
@override
void initState() {
super.initState();
getUserData();
}
void getUserData() {
Future.delayed(Duration.zero, () {
context.read<UserByCatCubit>().getUserById();
});
}
@override
void dispose() {
_categoryScrollController.dispose();
super.dispose();
}
void hasMoreCategoryScrollListener() {
if (_categoryScrollController.offset >= _categoryScrollController.position.maxScrollExtent && !_categoryScrollController.position.outOfRange) {
if (context.read<CategoryCubit>().hasMoreCategory()) {
context.read<CategoryCubit>().getMoreCategory(langId: context.read<AppLocalizationCubit>().state.id);
}
}
}
@override
Widget build(BuildContext context) {
return Scaffold(key: _scaffoldKey, appBar: setAppBar(), body: contentView());
}
setAppBar() {
return PreferredSize(
preferredSize: const Size(double.infinity, 70),
child: UiUtils.applyBoxShadow(
context: context,
child: AppBar(
leading: widget.from == 1 ? const CustomBackButton(horizontalPadding: 15) : const SizedBox.shrink(),
titleSpacing: 0.0,
centerTitle: false,
backgroundColor: Colors.transparent,
title: CustomTextLabel(
text: 'managePreferences',
textStyle: Theme.of(context).textTheme.titleLarge?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer, fontWeight: FontWeight.w600, letterSpacing: 0.5)),
actions: [skipBtn()],
),
));
}
skipBtn() {
if (widget.from != 1) {
return Align(
alignment: Alignment.topRight,
child: CustomTextButton(
onTap: () {
Navigator.of(context).pushReplacementNamed(Routes.home, arguments: false);
},
text: 'skip',
color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.7)));
} else {
return const SizedBox.shrink();
}
}
contentView() {
return BlocConsumer<UserByCatCubit, UserByCatState>(
bloc: context.read<UserByCatCubit>(),
listener: (context, state) {
if (state is UserByCatFetchSuccess) {
if (state.userByCat != null) {
for (int i = 0; i < state.userByCat.length; i++) {
catId = state.userByCat["category_id"];
}
setState(() {
selectedChoices = catId == "" ? catId.split('') : catId.split(',');
});
}
context.read<CategoryCubit>().getCategory(langId: context.read<AppLocalizationCubit>().state.id);
}
},
builder: (context, state) {
return BlocConsumer<CategoryCubit, CategoryState>(
bloc: context.read<CategoryCubit>(),
listener: (context, state) {
if (state is CategoryFetchSuccess) {
if ((state).category.isNotEmpty) {
setState(() {
_count = (state).category.length;
_checks = List.generate(_count, (i) => (selectedChoices.contains((state).category[i].id)) ? true : false);
});
}
}
},
builder: (context, state) {
if (state is CategoryFetchInProgress || state is CategoryInitial) {
return UiUtils.showCircularProgress(true, Theme.of(context).primaryColor);
}
if (state is CategoryFetchFailure) {
return ErrorContainerWidget(
errorMsg: (state.errorMessage.contains(ErrorMessageKeys.noInternet)) ? UiUtils.getTranslatedLabel(context, 'internetmsg') : state.errorMessage, onRetry: getUserData);
}
return SingleChildScrollView(
padding: const EdgeInsetsDirectional.only(start: 15.0, end: 15.0, bottom: 20.0),
controller: _categoryScrollController,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsetsDirectional.only(top: 25.0),
child: GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2, crossAxisSpacing: 20, mainAxisSpacing: 20),
shrinkWrap: true,
itemCount: (state as CategoryFetchSuccess).category.length,
physics: const ClampingScrollPhysics(),
itemBuilder: (context, index) {
return _buildCategoryContainer(
category: state.category[index],
hasMore: state.hasMore,
hasMoreCategoryFetchError: state.hasMoreFetchError,
index: index,
totalCurrentCategory: state.category.length);
})),
nxtBtn()
],
),
);
});
});
}
selectCatTxt() {
return Transform(
transform: Matrix4.translationValues(-50.0, 0.0, 0.0),
child: CustomTextLabel(
text: 'sel_pref_cat', textStyle: Theme.of(context).textTheme.titleLarge?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer, fontWeight: FontWeight.w100, letterSpacing: 0.5)),
);
}
_buildCategoryContainer({required CategoryModel category, required int index, required int totalCurrentCategory, required bool hasMoreCategoryFetchError, required bool hasMore}) {
if (index == totalCurrentCategory - 1 && index != 0) {
if (hasMore) {
if (hasMoreCategoryFetchError) {
return const SizedBox.shrink();
} else {
return Center(child: Padding(padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 8.0), child: UiUtils.showCircularProgress(true, Theme.of(context).primaryColor)));
}
}
}
return Container(
padding: EdgeInsets.zero,
decoration: const BoxDecoration(borderRadius: BorderRadius.all(Radius.circular(15))),
child: InkWell(
onTap: () {
_checks[index] = !_checks[index];
if (selectedChoices.contains(category.id)) {
selectedChoices.remove(category.id);
setState(() {});
} else {
selectedChoices.add(category.id!);
setState(() {});
}
if (selectedChoices.isEmpty) {
setState(() {
selectedChoices.add("0");
});
} else {
if (selectedChoices.contains("0")) {
selectedChoices = List.from(selectedChoices)..remove("0");
}
}
},
child: Stack(
fit: StackFit.expand,
children: [
ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(15.0)),
child: CustomNetworkImage(networkImageUrl: category.image!, fit: BoxFit.cover, isVideo: false, height: MediaQuery.of(context).size.height / 2.9, width: double.maxFinite)),
Positioned.directional(
//checkbox
textDirection: Directionality.of(context),
end: 10,
top: 10,
child: Container(
height: 20,
width: 20,
decoration:
BoxDecoration(borderRadius: const BorderRadius.all(Radius.circular(5)), color: selectedChoices.contains(category.id) ? Theme.of(context).primaryColor : secondaryColor),
child: selectedChoices.contains(category.id) ? const Icon(Icons.check_rounded, color: secondaryColor, size: 20) : null)),
Positioned.directional(
textDirection: Directionality.of(context),
bottom: 0,
start: 0,
end: 0,
child: Container(
decoration: BoxDecoration(
borderRadius: const BorderRadius.only(bottomLeft: Radius.circular(15.0), bottomRight: Radius.circular(15.0)),
gradient: LinearGradient(begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [darkSecondaryColor.withOpacity(0.03), darkSecondaryColor.withOpacity(0.85)])),
padding: EdgeInsets.zero,
margin: EdgeInsets.zero,
child: Padding(
padding: const EdgeInsetsDirectional.all(10),
child: CustomTextLabel(
text: category.categoryName!,
textStyle: const TextStyle(fontSize: 14, fontWeight: FontWeight.normal, color: Colors.white),
),
),
),
),
],
),
));
}
nxtBtn() {
return BlocConsumer<SetUserPrefCatCubit, SetUserPrefCatState>(
bloc: context.read<SetUserPrefCatCubit>(),
listener: (context, state) {
if (state is SetUserPrefCatFetchSuccess) {
showSnackBar(UiUtils.getTranslatedLabel(context, 'preferenceSave'), context);
if (widget.from == 2) {
Navigator.of(context).pushReplacementNamed(Routes.home, arguments: false);
} else {
Navigator.pop(context);
}
}
},
builder: (context, state) {
return Padding(
padding: const EdgeInsets.only(top: 30.0),
child: InkWell(
child: Container(
height: 45.0,
width: MediaQuery.of(context).size.width * 0.95,
alignment: Alignment.center,
decoration: BoxDecoration(color: Theme.of(context).primaryColor, borderRadius: BorderRadius.circular(15.0)),
child: (state is SetUserPrefCatFetchInProgress)
? UiUtils.showCircularProgress(true, secondaryColor)
: CustomTextLabel(
//@Start
text: (widget.from == 2) ? 'nxt' : 'saveLbl',
//from Settings
textStyle: Theme.of(context).textTheme.titleLarge?.copyWith(color: secondaryColor, fontWeight: FontWeight.w600, fontSize: 18),
),
),
onTap: () async {
if (context.read<AuthCubit>().getUserId() != "0") {
if (selectedChoices.isEmpty) {
//no preference selected
Navigator.of(context).pushNamedAndRemoveUntil(Routes.home, (Route<dynamic> route) => false);
return;
} else if (selectedChoices.length == 1) {
setState(() {
selCatId = selectedChoices.join();
});
} else {
setState(() {
selCatId = selectedChoices.join(',');
});
}
context.read<SetUserPrefCatCubit>().setUserPrefCat(catId: selCatId);
} else {
UiUtils.loginRequired(context);
}
}),
);
});
}
}

View File

@@ -0,0 +1,135 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:news/utils/constant.dart';
import 'package:news/utils/uiUtils.dart';
import 'package:news/utils/internetConnectivity.dart';
import 'package:unity_ads_plugin/unity_ads_plugin.dart';
import 'package:news/data/models/NewsModel.dart';
import 'package:news/ui/screens/NewsDetail/Widgets/InterstitialAds/googleInterstitialAds.dart';
import 'package:news/ui/screens/NewsDetail/Widgets/InterstitialAds/unityInterstitialAds.dart';
import 'package:news/ui/screens/NewsDetail/Widgets/RerwardAds/googleRewardAds.dart';
import 'package:news/ui/screens/NewsDetail/Widgets/RerwardAds/unityRewardAds.dart';
import 'package:news/ui/screens/NewsDetail/Widgets/NewsSubDetailsScreen.dart';
import 'package:news/cubits/appSystemSettingCubit.dart';
import 'package:news/data/models/BreakingNewsModel.dart';
class NewsDetailScreen extends StatefulWidget {
final NewsModel? model;
final List<NewsModel>? newsList;
final BreakingNewsModel? breakModel;
final List<BreakingNewsModel>? breakNewsList;
final bool isFromBreak;
final bool fromShowMore;
final String? slug;
const NewsDetailScreen({super.key, this.model, this.breakModel, this.breakNewsList, this.newsList, this.slug, required this.isFromBreak, required this.fromShowMore});
@override
NewsDetailsState createState() => NewsDetailsState();
static Route route(RouteSettings routeSettings) {
final arguments = routeSettings.arguments as Map<String, dynamic>;
return CupertinoPageRoute(
builder: (_) => NewsDetailScreen(
model: arguments['model'],
breakModel: arguments['breakModel'],
breakNewsList: arguments['breakNewsList'],
newsList: arguments['newsList'],
isFromBreak: arguments['isFromBreak'],
fromShowMore: arguments['fromShowMore'],
slug: arguments['slug']));
}
}
class NewsDetailsState extends State<NewsDetailScreen> {
final PageController pageController = PageController();
bool isScrollLocked = false;
void _setScrollLock(bool lock) {
setState(() {
isScrollLocked = lock;
});
}
@override
void initState() {
if (context.read<AppConfigurationCubit>().getInAppAdsMode() == "1") {
if (context.read<AppConfigurationCubit>().checkAdsType() == "google") {
createGoogleInterstitialAd(context);
createGoogleRewardedAd(context);
} else {
if (context.read<AppConfigurationCubit>().unityGameId() != null) {
UnityAds.init(
gameId: context.read<AppConfigurationCubit>().unityGameId()!,
testMode: true, //set it to False @Deployement
onComplete: () {
loadUnityInterAd(context.read<AppConfigurationCubit>().interstitialId()!);
loadUnityRewardAd(context.read<AppConfigurationCubit>().rewardId()!);
},
onFailed: (error, message) => debugPrint('Initialization Failed: $error $message'),
);
}
}
}
super.initState();
}
Widget showBreakingNews() {
return PageView.builder(
controller: pageController,
onPageChanged: (index) async {
if (await InternetConnectivity.isNetworkAvailable()) {
if (index % rewardAdsIndex == 0) showRewardAds();
if (index % interstitialAdsIndex == 0) UiUtils.showInterstitialAds(context: context);
}
},
itemCount: (widget.breakNewsList == null || widget.breakNewsList!.isEmpty) ? 1 : widget.breakNewsList!.length + 1,
itemBuilder: (context, index) {
return NewsSubDetails(
onLockScroll: (p0) {}, //no change for breaking news , No comments widget
breakModel: (index == 0) ? widget.breakModel : widget.breakNewsList![index - 1],
fromShowMore: widget.fromShowMore,
isFromBreak: widget.isFromBreak,
model: widget.model);
});
}
void showRewardAds() {
if (context.read<AppConfigurationCubit>().getInAppAdsMode() == "1") {
if (context.read<AppConfigurationCubit>().checkAdsType() == "google") {
showGoogleRewardedAd(context);
} else {
showUnityRewardAds(context.read<AppConfigurationCubit>().rewardId()!);
}
}
}
Widget showNews() {
return PageView.builder(
controller: pageController,
physics: isScrollLocked ? NeverScrollableScrollPhysics() : AlwaysScrollableScrollPhysics(),
onPageChanged: (index) async {
if (await InternetConnectivity.isNetworkAvailable()) {
if (index % rewardAdsIndex == 0) showRewardAds();
if (index % interstitialAdsIndex == 0) UiUtils.showInterstitialAds(context: context);
}
},
itemCount: (widget.newsList == null || widget.newsList!.isEmpty) ? 1 : widget.newsList!.length + 1,
itemBuilder: (context, index) {
return NewsSubDetails(
onLockScroll: _setScrollLock,
model: (index == 0) ? widget.model : widget.newsList![index - 1],
fromShowMore: widget.fromShowMore,
isFromBreak: widget.isFromBreak,
breakModel: widget.breakModel);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(backgroundColor: UiUtils.getColorScheme(context).secondary, body: widget.isFromBreak ? showBreakingNews() : showNews());
}
}

View File

@@ -0,0 +1,318 @@
import 'package:news/data/repositories/NewsComment/LikeAndDislikeComment/likeAndDislikeCommRepository.dart';
import 'package:news/ui/screens/NewsDetail/Widgets/delAndReportCom.dart';
import 'package:news/cubits/NewsComment/likeAndDislikeCommCubit.dart';
import 'package:news/cubits/NewsComment/setCommentCubit.dart';
import 'package:news/cubits/appLocalizationCubit.dart';
import 'package:news/data/models/CommentModel.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:news/cubits/Auth/authCubit.dart';
import 'package:news/cubits/commentNewsCubit.dart';
import 'package:news/utils/ErrorMessageKeys.dart';
import 'package:news/utils/uiUtils.dart';
import 'package:flutter/material.dart';
class CommentView extends StatefulWidget {
final String newsId;
final Function updateComFun;
final Function updateIsReplyFun;
const CommentView({super.key, required this.newsId, required this.updateComFun, required this.updateIsReplyFun});
@override
CommentViewState createState() => CommentViewState();
}
class CommentViewState extends State<CommentView> {
final TextEditingController _commentC = TextEditingController();
TextEditingController reportC = TextEditingController();
bool comBtnEnabled = false, isReply = false, isSending = false;
int? replyComIndex;
Widget commentsLengthView(int length) {
return Row(children: [
if (length > 0)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: Row(children: [
CustomTextLabel(
text: 'allLbl',
textStyle: Theme.of(context).textTheme.bodySmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.6), fontSize: 12.0, fontWeight: FontWeight.w600)),
CustomTextLabel(
text: " $length ",
textStyle: Theme.of(context).textTheme.bodySmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.6), fontSize: 12.0, fontWeight: FontWeight.w600)),
CustomTextLabel(
text: 'comsLbl',
textStyle: Theme.of(context).textTheme.bodySmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.6), fontSize: 12.0, fontWeight: FontWeight.w600)),
])),
const Spacer(),
Align(
alignment: Alignment.topRight,
child: InkWell(
child: const Icon(Icons.close_rounded),
onTap: () {
widget.updateComFun(false);
},
))
]);
}
Widget profileWithSendCom() {
return Padding(
padding: const EdgeInsetsDirectional.only(top: 5.0),
child: Row(children: [
Expanded(
flex: 1,
child: BlocBuilder<AuthCubit, AuthState>(builder: (context, state) {
if (state is Authenticated && context.read<AuthCubit>().getProfile() != "") {
return CircleAvatar(backgroundImage: NetworkImage(context.read<AuthCubit>().getProfile()));
} else {
return UiUtils.setFixedSizeboxForProfilePicture(childWidget: const Icon(Icons.account_circle, size: 35));
}
})),
BlocListener<SetCommentCubit, SetCommentState>(
bloc: context.read<SetCommentCubit>(),
listener: (context, state) {
if (state is SetCommentFetchSuccess) {
context.read<CommentNewsCubit>().commentUpdateList(state.setComment, state.total);
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus) {
currentFocus.unfocus();
}
_commentC.clear();
isSending = false;
setState(() {});
}
if (state is SetCommentFetchInProgress) {
setState(() => isSending = true);
}
},
child: Expanded(
flex: 7,
child: Padding(
padding: const EdgeInsetsDirectional.only(start: 18.0),
child: TextField(
controller: _commentC,
style: Theme.of(context).textTheme.titleSmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.7)),
onChanged: (String val) {
(_commentC.text.trim().isNotEmpty) ? setState(() => comBtnEnabled = true) : setState(() => comBtnEnabled = false);
},
keyboardType: TextInputType.multiline,
maxLines: null,
decoration: InputDecoration(
contentPadding: const EdgeInsets.only(top: 10.0, bottom: 2.0),
isDense: true,
suffixIconConstraints: BoxConstraints(maxHeight: (!isSending) ? 35 : 0, maxWidth: (!isSending) ? 30 : 0),
enabledBorder: UnderlineInputBorder(borderSide: BorderSide(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.5), width: 1.5)),
hintText: UiUtils.getTranslatedLabel(context, 'shareThoghtLbl'),
hintStyle: Theme.of(context).textTheme.titleSmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.7)),
suffixIcon: (_commentC.text.trim().isNotEmpty)
? (!isSending)
? IconButton(
icon: Icon(Icons.send, color: comBtnEnabled ? UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.8) : Colors.transparent, size: 20.0),
onPressed: () async {
(context.read<AuthCubit>().getUserId() != "0")
? context.read<SetCommentCubit>().setComment(parentId: "0", newsId: widget.newsId, message: _commentC.text)
: UiUtils.loginRequired(context);
})
: SizedBox(height: 12, width: 12, child: UiUtils.showCircularProgress(true, Theme.of(context).primaryColor))
: SizedBox.shrink())))))
]));
}
_buildCommContainer({required CommentModel model, required int index, required int totalCurrentComm, required bool hasMoreCommFetchError, required bool hasMore}) {
model = model;
if (index == totalCurrentComm - 1 && index <= 0) {
//check if hasMore
if (hasMore) {
if (hasMoreCommFetchError) {
return const SizedBox.shrink();
} else {
return Center(child: Padding(padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 8.0), child: UiUtils.showCircularProgress(true, Theme.of(context).primaryColor)));
}
}
}
return Builder(builder: (context) {
return Row(mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[
(model.profile != null && model.profile != "")
? UiUtils.setFixedSizeboxForProfilePicture(
childWidget: CircleAvatar(backgroundImage: (model.profile != null) ? NetworkImage(model.profile!) : NetworkImage(const Icon(Icons.account_circle, size: 35) as String), radius: 32))
: UiUtils.setFixedSizeboxForProfilePicture(childWidget: const Icon(Icons.account_circle, size: 35)),
Expanded(
child: Padding(
padding: const EdgeInsetsDirectional.only(start: 15.0),
child: Column(mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [
Row(children: [
CustomTextLabel(
text: model.name!, textStyle: Theme.of(context).textTheme.bodyMedium?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.7), fontSize: 13)),
Padding(padding: const EdgeInsetsDirectional.only(start: 10.0), child: Icon(Icons.circle, size: 4.0, color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.7))),
Padding(
padding: const EdgeInsetsDirectional.only(start: 10.0),
child: CustomTextLabel(
text: UiUtils.convertToAgo(context, DateTime.parse(model.date!), 1)!,
textStyle: Theme.of(context).textTheme.bodySmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.7), fontSize: 10),
))
]),
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: CustomTextLabel(
text: model.message!, textStyle: Theme.of(context).textTheme.titleSmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer, fontWeight: FontWeight.normal))),
BlocBuilder<LikeAndDislikeCommCubit, LikeAndDislikeCommState>(builder: (context, state) {
return Padding(
padding: const EdgeInsets.only(top: 15.0),
child: Row(
children: [
GestureDetector(
child: const Icon(Icons.thumb_up_off_alt_rounded),
onTap: () {
if (context.read<AuthCubit>().getUserId() != "0") {
context
.read<LikeAndDislikeCommCubit>()
.setLikeAndDislikeComm(langId: context.read<AppLocalizationCubit>().state.id, commId: model.id!, status: (model.like == "1") ? "0" : "1", fromLike: true);
} else {
UiUtils.loginRequired(context);
}
}),
model.totalLikes! != "0"
? Padding(
padding: const EdgeInsetsDirectional.only(start: 4.0),
child: CustomTextLabel(text: model.totalLikes!, textStyle: Theme.of(context).textTheme.titleSmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer)))
: const SizedBox(width: 12),
Padding(
padding: const EdgeInsetsDirectional.only(start: 35),
child: InkWell(
child: const Icon(Icons.thumb_down_alt_rounded),
onTap: () {
if (context.read<AuthCubit>().getUserId() != "0") {
context
.read<LikeAndDislikeCommCubit>()
.setLikeAndDislikeComm(langId: context.read<AppLocalizationCubit>().state.id, commId: model.id!, status: (model.dislike == "1") ? "0" : "2", fromLike: false);
} else {
UiUtils.loginRequired(context);
}
},
)),
model.totalDislikes! != "0"
? Padding(
padding: const EdgeInsetsDirectional.only(start: 4.0),
child:
CustomTextLabel(text: model.totalDislikes!, textStyle: Theme.of(context).textTheme.titleSmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer)))
: const SizedBox(width: 12),
Padding(
padding: const EdgeInsetsDirectional.only(start: 35),
child: InkWell(
child: const Icon(Icons.quickreply_rounded),
onTap: () {
widget.updateIsReplyFun(true, index);
setState(() {
isReply = true;
replyComIndex = index;
});
},
)),
model.replyComList!.isNotEmpty
? Padding(
padding: const EdgeInsetsDirectional.only(start: 5.0),
child: CustomTextLabel(
text: model.replyComList!.length.toString(),
textStyle: Theme.of(context).textTheme.titleSmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer)))
: const SizedBox.shrink(),
const Spacer(),
if (context.read<AuthCubit>().getUserId() != "0")
InkWell(
child: Icon(Icons.more_vert_outlined, color: UiUtils.getColorScheme(context).primaryContainer, size: 17),
onTap: () => delAndReportCom(index: index, newsId: widget.newsId, context: context, model: model, reportC: reportC, setState: setState))
],
),
);
}),
Padding(
padding: const EdgeInsets.only(top: 10.0),
child: InkWell(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: Row(children: [
CustomTextLabel(
text: model.replyComList!.isNotEmpty ? "${model.replyComList!.length} " : "",
textStyle: Theme.of(context).textTheme.bodySmall?.copyWith(color: Theme.of(context).primaryColor, fontSize: 12, fontWeight: FontWeight.w600)),
CustomTextLabel(
text: model.replyComList!.isNotEmpty ? ((model.replyComList!.length == 1) ? 'replyLbl' : 'repliesLbl') : "",
textStyle: Theme.of(context).textTheme.bodySmall?.copyWith(color: Theme.of(context).primaryColor, fontSize: 12, fontWeight: FontWeight.w600))
])),
onTap: () {
widget.updateIsReplyFun(true, index);
setState(() {
isReply = true;
replyComIndex = index;
});
}))
]))),
]);
});
}
Widget allComListView(CommentNewsFetchSuccess state) {
return BlocListener<LikeAndDislikeCommCubit, LikeAndDislikeCommState>(
listener: (context, likeDislikeState) {
if (likeDislikeState is LikeAndDislikeCommSuccess) {
final defaultIndex = state.commentNews.indexWhere((element) => element.id == likeDislikeState.comment.id);
if (defaultIndex != -1) {
state.commentNews[defaultIndex] = likeDislikeState.comment;
context.read<CommentNewsCubit>().emitSuccessState(state.commentNews);
}
}
},
child: Padding(
padding: const EdgeInsets.only(top: 20.0),
child: ListView.separated(
separatorBuilder: (BuildContext context, int index) => Divider(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.5)),
shrinkWrap: true,
primary: false,
padding: const EdgeInsets.only(top: 20.0),
physics: const NeverScrollableScrollPhysics(),
itemCount: state.commentNews.length,
itemBuilder: (context, index) {
return _buildCommContainer(
model: state.commentNews[index],
hasMore: state.hasMore,
hasMoreCommFetchError: (state).hasMoreFetchError,
index: index,
totalCurrentComm: (state.commentNews[index].replyComList!.length + state.commentNews.length),
);
})),
);
}
Widget commentView() {
return BlocBuilder<CommentNewsCubit, CommentNewsState>(builder: (context, state) {
if (state is CommentNewsFetchInProgress || state is CommentNewsInitial) {
return Center(child: UiUtils.showCircularProgress(true, UiUtils.getColorScheme(context).primaryContainer));
}
return Padding(
padding: const EdgeInsetsDirectional.only(top: 10.0, bottom: 10.0),
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
if (state is CommentNewsFetchSuccess) commentsLengthView((state).commentNews.length),
if (state is! CommentNewsFetchSuccess)
Row(children: [const Spacer(), Align(alignment: Alignment.topRight, child: InkWell(child: const Icon(Icons.close_rounded), onTap: () => widget.updateComFun(false)))]),
if ((state is CommentNewsFetchFailure && !state.errorMessage.contains(ErrorMessageKeys.noInternet) || state is CommentNewsFetchSuccess)) profileWithSendCom(),
if (state is CommentNewsFetchFailure)
SizedBox(
height: MediaQuery.of(context).size.height / 2,
child: Center(
child: CustomTextLabel(
text: (state.errorMessage.contains(ErrorMessageKeys.noInternet))
? UiUtils.getTranslatedLabel(context, 'internetmsg')
: (state.errorMessage == "No Data Found")
? UiUtils.getTranslatedLabel(context, 'noComments')
: state.errorMessage,
textAlign: TextAlign.center))),
if (state is CommentNewsFetchSuccess) allComListView(state)
]));
});
}
@override
Widget build(BuildContext context) {
return BlocProvider(create: (context) => LikeAndDislikeCommCubit(LikeAndDislikeCommRepository()), child: commentView());
}
}

View File

@@ -0,0 +1,98 @@
import 'package:flutter/material.dart';
import 'package:news/ui/styles/colors.dart';
import 'package:news/ui/widgets/networkImage.dart';
import '../../../../app/routes.dart';
import '../../../../data/models/BreakingNewsModel.dart';
import '../../../../data/models/NewsModel.dart';
class ImageView extends StatefulWidget {
final NewsModel? model;
final BreakingNewsModel? breakModel;
final bool isFromBreak;
const ImageView({super.key, this.model, this.breakModel, required this.isFromBreak});
@override
ImageViewState createState() => ImageViewState();
}
class ImageViewState extends State<ImageView> {
List<String> allImage = [];
int _curSlider = 0;
@override
void initState() {
allImage.clear();
if (!widget.isFromBreak) {
allImage.add(widget.model!.image!);
if (widget.model!.imageDataList!.isNotEmpty) {
for (int i = 0; i < widget.model!.imageDataList!.length; i++) {
allImage.add(widget.model!.imageDataList![i].otherImage!);
}
}
} else {
allImage.add(widget.breakModel!.image!);
}
super.initState();
}
Widget imageView() {
return SizedBox(
height: MediaQuery.of(context).size.height * 0.40,
width: double.maxFinite,
child: !widget.isFromBreak
? PageView.builder(
itemCount: allImage.length,
onPageChanged: (index) {
setState(() {
_curSlider = index;
});
},
itemBuilder: (BuildContext context, int index) {
return InkWell(
child: CustomNetworkImage(networkImageUrl: allImage[index], width: double.infinity, height: MediaQuery.of(context).size.height * 0.42, isVideo: false, fit: BoxFit.cover),
onTap: () {
Navigator.of(context).pushNamed(Routes.imagePreview, arguments: {"index": index, "imgList": allImage});
},
);
})
: CustomNetworkImage(networkImageUrl: widget.breakModel!.image!, width: double.infinity, height: MediaQuery.of(context).size.height * 0.42, isVideo: false, fit: BoxFit.cover));
}
List<T> map<T>(List list, Function handler) {
List<T> result = [];
for (var i = 0; i < list.length; i++) {
result.add(handler(i, list[i]));
}
return result;
}
Widget imageSliderDot() {
if (!widget.isFromBreak && allImage.length > 1) {
return Align(
alignment: Alignment.bottomCenter,
child: Container(
margin: EdgeInsets.only(top: MediaQuery.of(context).size.height / 2.6 - 32),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: map<Widget>(allImage, (index, url) {
return Container(
width: _curSlider == index ? 10.0 : 8.0,
height: _curSlider == index ? 10.0 : 8.0,
margin: const EdgeInsets.symmetric(horizontal: 1.0),
decoration: BoxDecoration(
shape: BoxShape.circle,
boxShadow: (_curSlider == index) ? const [BoxShadow(color: Colors.grey, spreadRadius: 0.0, blurRadius: 2.0)] : [],
color: _curSlider == index ? secondaryColor : secondaryColor.withOpacity((0.5))));
}))));
} else {
return const SizedBox.shrink();
}
}
@override
Widget build(BuildContext context) {
return Stack(children: [imageView(), imageSliderDot()]);
}
}

View File

@@ -0,0 +1,56 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'package:news/cubits/appSystemSettingCubit.dart';
const AdRequest request = AdRequest(
//static
keywords: <String>['foo', 'bar'],
contentUrl: 'http://foo.com/bar.html',
nonPersonalizedAds: true,
);
int maxFailedLoadAttempts = 3;
InterstitialAd? _interstitialAd;
int _numInterstitialLoadAttempts = 0;
void createGoogleInterstitialAd(BuildContext context) {
if (context.read<AppConfigurationCubit>().interstitialId() != "") {
InterstitialAd.load(
adUnitId: context.read<AppConfigurationCubit>().interstitialId()!,
request: request,
adLoadCallback: InterstitialAdLoadCallback(
onAdLoaded: (InterstitialAd ad) {
_interstitialAd = ad;
_numInterstitialLoadAttempts = 0;
_interstitialAd!.setImmersiveMode(true);
},
onAdFailedToLoad: (LoadAdError error) {
_numInterstitialLoadAttempts += 1;
_interstitialAd = null;
if (_numInterstitialLoadAttempts <= maxFailedLoadAttempts) {
createGoogleInterstitialAd(context);
}
},
));
}
}
void showGoogleInterstitialAd(BuildContext context) {
if (_interstitialAd == null) {
return;
}
_interstitialAd!.fullScreenContentCallback = FullScreenContentCallback(
onAdShowedFullScreenContent: (InterstitialAd ad) => debugPrint('ad onAdShowedFullScreenContent.'),
onAdDismissedFullScreenContent: (InterstitialAd ad) {
ad.dispose();
createGoogleInterstitialAd(context);
},
onAdFailedToShowFullScreenContent: (InterstitialAd ad, AdError error) {
ad.dispose();
createGoogleInterstitialAd(context);
},
);
_interstitialAd!.show();
_interstitialAd = null;
}

View File

@@ -0,0 +1,23 @@
import 'package:flutter/cupertino.dart';
import 'package:unity_ads_plugin/unity_ads_plugin.dart';
void loadUnityInterAd(String placementId) {
UnityAds.load(placementId: placementId, onComplete: (placementId) {}, onFailed: (placementId, error, message) {});
}
void showUnityInterstitialAds(String placementId) {
UnityAds.showVideoAd(
placementId: placementId,
onComplete: (placementId) {
loadUnityInterAd(placementId);
},
onFailed: (placementId, error, message) {
loadUnityInterAd(placementId);
},
onStart: (placementId) => debugPrint('Video Ad $placementId started'),
onClick: (placementId) => debugPrint('Video Ad $placementId click'),
onSkipped: (placementId) {
loadUnityInterAd(placementId);
},
);
}

View File

@@ -0,0 +1,417 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_tts/flutter_tts.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'package:news/app/routes.dart';
import 'package:news/cubits/adSpacesNewsDetailsCubit.dart';
import 'package:news/cubits/appLocalizationCubit.dart';
import 'package:news/cubits/commentNewsCubit.dart';
import 'package:news/cubits/relatedNewsCubit.dart';
import 'package:news/cubits/setNewsViewsCubit.dart';
import 'package:news/data/models/NewsModel.dart';
import 'package:news/ui/widgets/adSpaces.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/utils/uiUtils.dart';
import 'package:unity_ads_plugin/unity_ads_plugin.dart';
import 'package:news/cubits/appSystemSettingCubit.dart';
import 'package:news/data/models/BreakingNewsModel.dart';
import 'package:news/ui/screens/NewsDetail/Widgets/ImageView.dart';
import 'package:news/ui/screens/NewsDetail/Widgets/horizontalBtnList.dart';
import 'package:news/ui/screens/NewsDetail/Widgets/relatedNewsList.dart';
import 'package:news/ui/screens/NewsDetail/Widgets/setBannderAds.dart';
import 'package:news/ui/screens/NewsDetail/Widgets/tagView.dart';
import 'package:news/ui/screens/NewsDetail/Widgets/titleView.dart';
import 'package:news/ui/screens/NewsDetail/Widgets/videoBtn.dart';
import 'package:news/ui/screens/NewsDetail/Widgets/CommentView.dart';
import 'package:news/ui/screens/NewsDetail/Widgets/ReplyCommentView.dart';
import 'package:news/ui/screens/NewsDetail/Widgets/backBtn.dart';
import 'package:news/ui/screens/NewsDetail/Widgets/dateView.dart';
import 'package:news/ui/screens/NewsDetail/Widgets/descView.dart';
import 'package:news/ui/screens/NewsDetail/Widgets/likeBtn.dart';
class NewsSubDetails extends StatefulWidget {
final NewsModel? model;
final BreakingNewsModel? breakModel;
final bool fromShowMore;
final bool isFromBreak;
final Function(bool) onLockScroll;
const NewsSubDetails({super.key, this.model, this.breakModel, required this.fromShowMore, required this.isFromBreak, required this.onLockScroll});
@override
NewsSubDetailsState createState() => NewsSubDetailsState();
}
class NewsSubDetailsState extends State<NewsSubDetails> {
bool comEnabled = false;
bool isReply = false;
int? replyComIndex;
int fontValue = 15;
bool isPlaying = false;
double volume = 0.5;
double pitch = 1.0;
double rate = 0.5;
BannerAd? _bannerAd;
NewsModel? newsModel;
FlutterTts? _flutterTts;
bool _isScrollingUp = false;
late final ScrollController controller = ScrollController()..addListener(hasMoreCommScrollListener);
@override
void initState() {
super.initState();
newsModel = widget.model;
getComments();
getRelatedNews();
initializeTts();
setNewsViews(isBreakingNews: (widget.isFromBreak) ? true : false);
if (context.read<AppConfigurationCubit>().getInAppAdsMode() == "1") bannerAdsInitialized();
Future.delayed(Duration.zero, () {
context.read<AdSpacesNewsDetailsCubit>().getAdspaceForNewsDetails(langId: context.read<AppLocalizationCubit>().state.id);
});
}
setNewsViews({required bool isBreakingNews}) {
Future.delayed(Duration.zero, () {
context.read<SetNewsViewsCubit>().setNewsViews(newsId: isBreakingNews ? widget.breakModel!.id! : (newsModel!.newsId ?? newsModel!.id)!, isBreakingNews: isBreakingNews);
});
}
getComments() {
if (!widget.isFromBreak && context.read<AppConfigurationCubit>().getCommentsMode() == "1") {
Future.delayed(Duration.zero, () {
context.read<CommentNewsCubit>().getCommentNews(newsId: (newsModel!.newsId != null && newsModel!.newsId!.trim().isNotEmpty) ? newsModel!.newsId! : newsModel!.id!);
});
}
}
getRelatedNews() {
if (!widget.isFromBreak) {
Future.delayed(Duration.zero, () {
context.read<RelatedNewsCubit>().getRelatedNews(
langId: context.read<AppLocalizationCubit>().state.id,
catId: (newsModel!.categoryId == "0" || newsModel!.categoryId == '') ? newsModel!.categoryId : null,
subCatId: (newsModel!.subCatId != "0" || newsModel!.subCatId != '') ? newsModel!.subCatId : null);
});
}
}
@override
void dispose() {
_flutterTts!.stop();
controller.removeListener(hasMoreCommScrollListener);
controller.dispose();
super.dispose();
}
updateFontVal(int fontVal) {
setState(() => fontValue = fontVal);
}
initializeTts() {
_flutterTts = FlutterTts();
_flutterTts!.awaitSpeakCompletion(true);
_flutterTts!.setStartHandler(() async {
if (mounted) {
setState(() => isPlaying = true);
}
});
_flutterTts!.setCompletionHandler(() {
if (mounted) {
setState(() => isPlaying = false);
}
});
_flutterTts!.setErrorHandler((err) {
if (mounted) {
setState(() => isPlaying = false);
}
});
}
bannerAdsInitialized() {
if (context.read<AppConfigurationCubit>().checkAdsType() == "unity") {
UnityAds.init(
gameId: context.read<AppConfigurationCubit>().unityGameId()!,
testMode: true, //set it to false @Deployment
onComplete: () {},
onFailed: (error, message) {});
}
if (context.read<AppConfigurationCubit>().checkAdsType() == "google") {
_createBottomBannerAd();
}
}
void _createBottomBannerAd() {
if (context.read<AppConfigurationCubit>().bannerId() != "") {
_bannerAd = BannerAd(
adUnitId: context.read<AppConfigurationCubit>().bannerId()!,
request: const AdRequest(),
size: AdSize.banner,
listener: BannerAdListener(
onAdLoaded: (_) {},
onAdFailedToLoad: (ad, err) {
ad.dispose();
},
),
);
_bannerAd!.load();
}
}
speak(String description) async {
if (description.isNotEmpty) {
await _flutterTts!.setVolume(volume);
await _flutterTts!.setSpeechRate(rate);
await _flutterTts!.setPitch(pitch);
await _flutterTts!.setLanguage(() {
return context.read<AppLocalizationCubit>().state.languageCode;
}());
int length = description.length;
if (length < 4000) {
setState(() => isPlaying = true);
await _flutterTts!.speak(description);
_flutterTts!.setCompletionHandler(() {
setState(() {
_flutterTts!.stop();
isPlaying = false;
});
});
} else if (length < 8000) {
if (Platform.isAndroid) await _flutterTts!.setQueueMode(1);
String temp1 = description.substring(0, length ~/ 2);
await _flutterTts!.speak(temp1);
_flutterTts!.setCompletionHandler(() {
setState(() {
isPlaying = true;
});
});
String temp2 = description.substring(temp1.length, description.length);
await _flutterTts!.speak(temp2);
_flutterTts!.setCompletionHandler(() {
setState(() {
isPlaying = false;
});
});
} else if (length < 12000) {
if (Platform.isAndroid) await _flutterTts!.setQueueMode(2);
String temp1 = description.substring(0, 3999);
await _flutterTts!.speak(temp1);
_flutterTts!.setCompletionHandler(() {
setState(() {
isPlaying = true;
});
});
String temp2 = description.substring(temp1.length, 7999);
await _flutterTts!.speak(temp2);
_flutterTts!.setCompletionHandler(() {
setState(() {});
});
String temp3 = description.substring(temp2.length, description.length);
await _flutterTts!.speak(temp3);
_flutterTts!.setCompletionHandler(() {
setState(() {
isPlaying = false;
});
});
} else {
String temp = description.substring(0, description.length);
await _flutterTts!.speak(temp);
_flutterTts!.setCompletionHandler(() {
setState(() {
isPlaying = false;
});
});
}
}
}
stop() async {
var result = await _flutterTts!.stop();
if (result == 1) setState(() => isPlaying = false);
}
onBackPress(bool isTrue) {
(widget.fromShowMore == true) ? Navigator.of(context).popUntil((route) => route.isFirst) : Navigator.pop(context);
}
Widget showViews() {
return Row(mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.center, children: [
Icon(Icons.remove_red_eye_rounded, size: 17, color: UiUtils.getColorScheme(context).primaryContainer),
const SizedBox(width: 5),
Padding(
padding: const EdgeInsets.only(bottom: 2),
child: CustomTextLabel(
text: ((!widget.isFromBreak) ? newsModel!.totalViews : widget.breakModel!.totalViews) ?? '0',
textStyle: Theme.of(context).textTheme.bodySmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer, fontWeight: FontWeight.w600),
textAlign: TextAlign.center))
]);
}
Widget showAuthor() {
return Padding(
padding: const EdgeInsets.only(top: 5, left: 5, right: 5),
child: Container(
child: InkWell(
onTap: () {
Navigator.of(context).pushNamed(Routes.authorDetails, arguments: {"authorId": newsModel!.userAthorDetails!.id ?? "0"});
},
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
CircleAvatar(backgroundImage: NetworkImage(newsModel!.userAthorDetails!.profile!)),
SizedBox(width: 12),
Expanded(
child: CustomTextLabel(
text: newsModel!.userAthorDetails!.name ?? '',
textAlign: TextAlign.left,
textStyle: TextStyle(
color: UiUtils.getColorScheme(context).primaryContainer,
fontWeight: FontWeight.w600, // SemiBold
fontSize: 16,
),
),
)
],
))),
);
}
otherMainDetails() {
int readingTime = UiUtils.calculateReadingTime(widget.isFromBreak ? widget.breakModel!.desc! : newsModel!.desc!);
String minutesPostfix = (readingTime == 1) ? UiUtils.getTranslatedLabel(context, 'minute') : UiUtils.getTranslatedLabel(context, 'minutes');
return Padding(
padding: EdgeInsets.only(top: MediaQuery.of(context).size.height / 2.7),
child: Container(
padding: const EdgeInsetsDirectional.only(top: 20.0, start: 20.0, end: 20.0),
width: double.maxFinite,
decoration: BoxDecoration(borderRadius: const BorderRadius.only(topLeft: Radius.circular(25), topRight: Radius.circular(25)), color: UiUtils.getColorScheme(context).secondary),
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[
allRowBtn(
isFromBreak: widget.isFromBreak,
context: context,
breakModel: widget.isFromBreak ? widget.breakModel : null,
model: !widget.isFromBreak ? newsModel! : null,
fontVal: fontValue,
updateFont: updateFontVal,
isPlaying: isPlaying,
speak: speak,
stop: stop,
updateComEnabled: updateCommentshow),
if (newsModel!.authorDetails != null) showAuthor(),
BlocBuilder<AdSpacesNewsDetailsCubit, AdSpacesNewsDetailsState>(
builder: (context, state) {
return (state is AdSpacesNewsDetailsFetchSuccess && state.adSpaceTopData != null) ? AdSpaces(adsModel: state.adSpaceTopData!) : const SizedBox.shrink();
},
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (!widget.isFromBreak) tagView(model: newsModel!, context: context, isFromDetailsScreen: true),
if (!isReply && !comEnabled)
Padding(
padding: const EdgeInsetsDirectional.only(top: 8.0),
child: Row(mainAxisSize: MainAxisSize.min, children: [
if (!widget.isFromBreak) dateView(context, newsModel!.publishDate ?? newsModel!.date!),
if (!widget.isFromBreak) const SizedBox(width: 20),
showViews(),
const SizedBox(width: 20),
Icon(Icons.circle, size: 10, color: UiUtils.getColorScheme(context).primaryContainer),
const SizedBox(width: 10),
CustomTextLabel(
text: "$readingTime $minutesPostfix ${UiUtils.getTranslatedLabel(context, 'read')}",
textStyle:
Theme.of(context).textTheme.bodySmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.8), fontSize: 12.0, fontWeight: FontWeight.w400))
])),
if (!isReply && !comEnabled) titleView(title: widget.isFromBreak ? widget.breakModel!.title! : newsModel!.title!, context: context),
if (!isReply && !comEnabled) descView(desc: widget.isFromBreak ? widget.breakModel!.desc! : newsModel!.desc!, context: context, fontValue: fontValue.toDouble()),
],
),
if (!widget.isFromBreak && !isReply && comEnabled) CommentView(newsId: newsModel!.id!, updateComFun: updateCommentshow, updateIsReplyFun: updateComReply),
if (!widget.isFromBreak && isReply && comEnabled) ReplyCommentView(replyComIndex: replyComIndex!, replyComFun: updateComReply, newsId: newsModel!.id!),
BlocBuilder<AdSpacesNewsDetailsCubit, AdSpacesNewsDetailsState>(
builder: (context, state) {
return (state is AdSpacesNewsDetailsFetchSuccess && state.adSpaceBottomData != null)
? Padding(padding: const EdgeInsetsDirectional.only(bottom: 5), child: AdSpaces(adsModel: state.adSpaceBottomData!))
: const SizedBox.shrink();
},
),
if (!widget.isFromBreak && !isReply && !comEnabled && newsModel != null) RelatedNewsList(model: newsModel!),
]),
));
}
updateCommentshow(bool comEnabledUpdate) {
setState(() {
comEnabled = comEnabledUpdate;
widget.onLockScroll(comEnabledUpdate);
});
}
updateComReply(bool comReplyUpdate, int comIndex) {
setState(() {
isReply = comReplyUpdate;
replyComIndex = comIndex;
widget.onLockScroll(false);
});
}
void hasMoreCommScrollListener() {
if (!widget.isFromBreak && comEnabled && !isReply) {
if (controller.position.maxScrollExtent == controller.offset) {
if (context.read<CommentNewsCubit>().hasMoreCommentNews()) {
context.read<CommentNewsCubit>().getMoreCommentNews(newsId: (newsModel!.newsId != null && newsModel!.newsId!.trim().isNotEmpty) ? newsModel!.newsId! : newsModel!.id!);
} else {}
}
}
//for comments area
if (controller.position.userScrollDirection == ScrollDirection.forward) {
// User is scrolling up
if (!_isScrollingUp) {
setState(() {
_isScrollingUp = true;
});
}
} else if (controller.position.userScrollDirection == ScrollDirection.reverse) {
// User is scrolling down
if (_isScrollingUp) {
setState(() {
_isScrollingUp = false;
});
}
}
}
@override
Widget build(BuildContext context) {
return PopScope(
onPopInvoked: (bool isTrue) => onBackPress,
child: AnimatedPadding(
duration: Duration(milliseconds: 300),
padding: EdgeInsets.only(top: _isScrollingUp ? MediaQuery.of(context).viewPadding.top : 0),
child: Column(children: [
Expanded(
flex: (Platform.isAndroid) ? 12 : 9,
child: SingleChildScrollView(
controller: !widget.isFromBreak && comEnabled && !isReply ? controller : null,
child: Stack(children: <Widget>[
ImageView(isFromBreak: widget.isFromBreak, model: newsModel, breakModel: widget.breakModel),
backBtn(context, widget.fromShowMore),
videoBtn(context: context, isFromBreak: widget.isFromBreak, model: !widget.isFromBreak ? newsModel! : null, breakModel: widget.isFromBreak ? widget.breakModel! : null),
otherMainDetails(),
if (!widget.isFromBreak) likeBtn(context, newsModel!),
])),
),
if ((context.read<AppConfigurationCubit>().getInAppAdsMode() == "1") || _bannerAd != null) Flexible(child: setBannerAd(context, _bannerAd))
]),
),
);
}
}

View File

@@ -0,0 +1,324 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:news/cubits/appLocalizationCubit.dart';
import 'package:news/data/models/CommentModel.dart';
import 'package:news/ui/screens/NewsDetail/Widgets/delAndReportReplyComm.dart';
import 'package:news/ui/styles/colors.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/utils/uiUtils.dart';
import 'package:news/cubits/Auth/authCubit.dart';
import 'package:news/cubits/NewsComment/likeAndDislikeCommCubit.dart';
import 'package:news/cubits/NewsComment/setCommentCubit.dart';
import 'package:news/cubits/commentNewsCubit.dart';
import 'package:news/data/repositories/NewsComment/LikeAndDislikeComment/likeAndDislikeCommRepository.dart';
class ReplyCommentView extends StatefulWidget {
final int replyComIndex;
final Function replyComFun;
final String newsId;
const ReplyCommentView({super.key, required this.replyComIndex, required this.replyComFun, required this.newsId});
@override
ReplyCommentViewState createState() => ReplyCommentViewState();
}
class ReplyCommentViewState extends State<ReplyCommentView> {
bool isReply = false, replyComEnabled = false, isSending = false;
final TextEditingController _replyComC = TextEditingController();
TextEditingController reportC = TextEditingController();
Widget allReplyComView(CommentModel model) {
return Row(children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: Row(children: [
CustomTextLabel(
text: 'allLbl',
textStyle: Theme.of(context).textTheme.bodySmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.6), fontSize: 12.0, fontWeight: FontWeight.w600)),
CustomTextLabel(
text: " ${model.replyComList!.length} ",
textStyle: Theme.of(context).textTheme.bodySmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.6), fontSize: 12.0, fontWeight: FontWeight.w600)),
CustomTextLabel(
text: (model.replyComList!.length == 1) ? 'replyLbl' : 'repliesLbl',
textStyle: Theme.of(context).textTheme.bodySmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.6), fontSize: 12.0, fontWeight: FontWeight.w600)),
])),
const Spacer(),
Align(
alignment: Alignment.topRight,
child: InkWell(
child: const Icon(Icons.close),
onTap: () {
setState(() => isReply = false);
widget.replyComFun(false, widget.replyComIndex);
}))
]);
}
replyComProfileWithCom(CommentModel modelCom) {
return BlocBuilder<LikeAndDislikeCommCubit, LikeAndDislikeCommState>(
builder: (context, state) {
DateTime replyTime = DateTime.parse(modelCom.date!);
return Container(
decoration: BoxDecoration(border: Border.all(width: 2, color: borderColor)),
padding: const EdgeInsets.all(10),
child: Row(mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[
modelCom.profile != null && modelCom.profile != ""
? UiUtils.setFixedSizeboxForProfilePicture(childWidget: CircleAvatar(backgroundImage: NetworkImage(modelCom.profile!), radius: 32))
: UiUtils.setFixedSizeboxForProfilePicture(childWidget: const Icon(Icons.account_circle, size: 35)),
Expanded(
child: Padding(
padding: const EdgeInsetsDirectional.only(start: 15.0),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
CustomTextLabel(
text: modelCom.name!,
textStyle: Theme.of(context).textTheme.bodyMedium?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.7), fontSize: 13)),
Padding(
padding: const EdgeInsetsDirectional.only(start: 10.0),
child: Icon(Icons.circle, size: 4.0, color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.7))),
Padding(
padding: const EdgeInsetsDirectional.only(start: 10.0),
child: CustomTextLabel(
text: UiUtils.convertToAgo(context, replyTime, 1)!,
textStyle: Theme.of(context).textTheme.bodySmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.7), fontSize: 10)))
],
),
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: CustomTextLabel(
text: modelCom.message!,
textStyle: Theme.of(context).textTheme.titleSmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer, fontWeight: FontWeight.normal)),
),
],
))),
]));
},
);
}
replyComSendReplyView(CommentModel model) {
return context.read<AuthCubit>().getUserId() != "0"
? Padding(
padding: const EdgeInsetsDirectional.only(top: 10.0),
child: Row(
children: [
Expanded(
flex: 1,
child: context.read<AuthCubit>().getProfile() != ""
? UiUtils.setFixedSizeboxForProfilePicture(childWidget: CircleAvatar(backgroundImage: NetworkImage(context.read<AuthCubit>().getProfile()), radius: 32))
: UiUtils.setFixedSizeboxForProfilePicture(childWidget: const Icon(Icons.account_circle, size: 35))),
BlocListener<SetCommentCubit, SetCommentState>(
bloc: context.read<SetCommentCubit>(),
listener: (context, state) {
if (state is SetCommentFetchSuccess) {
context.read<CommentNewsCubit>().commentUpdateList(state.setComment, state.total);
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus) {
currentFocus.unfocus();
}
_replyComC.clear();
isSending = false;
setState(() {});
}
if (state is SetCommentFetchInProgress) {
setState(() => isSending = true);
}
},
child: Expanded(
flex: 7,
child: Padding(
padding: const EdgeInsetsDirectional.only(start: 18.0),
child: TextField(
controller: _replyComC,
style: Theme.of(context).textTheme.titleSmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.7)),
onChanged: (String val) {
if (_replyComC.text.trim().isNotEmpty) {
setState(() => replyComEnabled = true);
} else {
setState(() => replyComEnabled = false);
}
},
keyboardType: TextInputType.multiline,
maxLines: null,
decoration: InputDecoration(
contentPadding: const EdgeInsets.only(top: 10.0, bottom: 2.0),
isDense: true,
enabledBorder: UnderlineInputBorder(borderSide: BorderSide(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.5), width: 1.5)),
hintText: UiUtils.getTranslatedLabel(context, 'publicReply'),
hintStyle: Theme.of(context).textTheme.titleSmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.7)),
suffixIconConstraints: const BoxConstraints(maxHeight: 35, maxWidth: 30),
suffixIcon: (_replyComC.text.trim().isNotEmpty)
? (!isSending)
? IconButton(
icon: Icon(Icons.send, color: replyComEnabled ? UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.8) : Colors.transparent, size: 20.0),
onPressed: () async {
if (context.read<AuthCubit>().getUserId() != "0") {
context.read<SetCommentCubit>().setComment(parentId: model.id!, newsId: widget.newsId, message: _replyComC.text);
} else {
UiUtils.loginRequired(context);
}
},
)
: SizedBox(height: 12, width: 12, child: UiUtils.showCircularProgress(true, Theme.of(context).primaryColor))
: SizedBox.shrink()),
))))
],
))
: const SizedBox.shrink();
}
replyAllComListView(CommentModel model) {
return Padding(
padding: const EdgeInsets.only(top: 20.0),
child: SingleChildScrollView(
child: ListView.separated(
separatorBuilder: (BuildContext context, int index) => Divider(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.5)),
shrinkWrap: true,
reverse: true,
padding: const EdgeInsets.only(top: 20.0),
physics: const NeverScrollableScrollPhysics(),
itemCount: model.replyComList!.length,
itemBuilder: (context, index) {
DateTime time1 = DateTime.parse(model.replyComList![index].date!);
return Builder(builder: (context) {
return Row(mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[
model.replyComList![index].profile != null && model.replyComList![index].profile != ""
? UiUtils.setFixedSizeboxForProfilePicture(childWidget: CircleAvatar(backgroundImage: NetworkImage(model.replyComList![index].profile!), radius: 32))
: UiUtils.setFixedSizeboxForProfilePicture(childWidget: const Icon(Icons.account_circle, size: 35)),
Expanded(
child: Padding(
padding: const EdgeInsetsDirectional.only(start: 15.0),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
CustomTextLabel(
text: model.replyComList![index].name!,
textStyle: Theme.of(context).textTheme.bodyMedium?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.7), fontSize: 13)),
Padding(
padding: const EdgeInsetsDirectional.only(start: 10.0),
child: Icon(Icons.circle, size: 4.0, color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.7))),
Padding(
padding: const EdgeInsetsDirectional.only(start: 10.0),
child: CustomTextLabel(
text: UiUtils.convertToAgo(context, time1, 1)!,
textStyle: Theme.of(context).textTheme.bodySmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.7), fontSize: 10),
)),
],
),
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: CustomTextLabel(
text: model.replyComList![index].message!,
textStyle: Theme.of(context).textTheme.titleSmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer, fontWeight: FontWeight.normal),
),
),
BlocBuilder<LikeAndDislikeCommCubit, LikeAndDislikeCommState>(builder: (context, state) {
return Padding(
padding: const EdgeInsets.only(top: 15.0),
child: Row(
children: [
InkWell(
child: const Icon(Icons.thumb_up_off_alt_rounded),
onTap: () {
(context.read<AuthCubit>().getUserId() != "0")
? context.read<LikeAndDislikeCommCubit>().setLikeAndDislikeComm(
langId: context.read<AppLocalizationCubit>().state.id,
commId: model.replyComList![index].id!,
status: (model.replyComList![index].like == "1") ? "0" : "1",
fromLike: true)
: UiUtils.loginRequired(context);
},
),
model.replyComList![index].totalLikes! != "0"
? Padding(
padding: const EdgeInsetsDirectional.only(start: 4.0),
child: CustomTextLabel(
text: model.replyComList![index].totalLikes!,
textStyle: Theme.of(context).textTheme.titleSmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer)))
: const SizedBox(width: 12),
Padding(
padding: const EdgeInsetsDirectional.only(start: 35),
child: InkWell(
child: const Icon(Icons.thumb_down_alt_rounded),
onTap: () {
(context.read<AuthCubit>().getUserId() != "0")
? context.read<LikeAndDislikeCommCubit>().setLikeAndDislikeComm(
langId: context.read<AppLocalizationCubit>().state.id,
commId: model.replyComList![index].id!,
status: (model.replyComList![index].dislike == "1") ? "0" : "2",
fromLike: false)
: UiUtils.loginRequired(context);
},
)),
model.replyComList![index].totalDislikes! != "0"
? Padding(
padding: const EdgeInsetsDirectional.only(start: 4.0),
child: CustomTextLabel(
text: model.replyComList![index].totalDislikes!,
textStyle: Theme.of(context).textTheme.titleSmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer)))
: const SizedBox.shrink(),
const Spacer(),
if (context.read<AuthCubit>().getUserId() != "0")
InkWell(
child: Icon(Icons.more_vert_outlined, color: UiUtils.getColorScheme(context).primaryContainer, size: 17),
onTap: () => delAndReportReplyComm(model: model, context: context, reportC: reportC, newsId: widget.newsId, setState: setState, replyIndex: index))
],
),
);
})
],
))),
]);
});
})));
}
Widget replyCommentView() {
return BlocBuilder<CommentNewsCubit, CommentNewsState>(builder: (context, state) {
if (state is CommentNewsFetchSuccess && state.commentNews.isNotEmpty) {
return BlocListener<LikeAndDislikeCommCubit, LikeAndDislikeCommState>(
listener: (context, likeDislikeState) {
if (likeDislikeState is LikeAndDislikeCommSuccess) {
final defaultIndex = state.commentNews.indexWhere((element) => element.id == likeDislikeState.comment.id);
if (defaultIndex != -1) {
state.commentNews[defaultIndex].replyComList = likeDislikeState.comment.replyComList;
context.read<CommentNewsCubit>().emitSuccessState(state.commentNews);
}
}
},
child: Padding(
padding: const EdgeInsetsDirectional.only(top: 10.0, bottom: 10.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
allReplyComView(state.commentNews[widget.replyComIndex]),
replyComProfileWithCom(state.commentNews[widget.replyComIndex]),
replyComSendReplyView(state.commentNews[widget.replyComIndex]),
replyAllComListView(state.commentNews[widget.replyComIndex])
],
)),
);
}
if (state is CommentNewsFetchFailure) {
return Center(child: CustomTextLabel(text: state.errorMessage, textAlign: TextAlign.center));
}
//state is CommentNewsFetchInProgress || state is CommentNewsInitial
return const Padding(padding: EdgeInsets.only(bottom: 10.0, left: 10.0, right: 10.0), child: SizedBox.shrink());
});
}
@override
Widget build(BuildContext context) {
return BlocProvider(create: (context) => LikeAndDislikeCommCubit(LikeAndDislikeCommRepository()), child: replyCommentView());
}
}

View File

@@ -0,0 +1,58 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'package:news/cubits/appSystemSettingCubit.dart';
const AdRequest request = AdRequest(
//static
keywords: <String>['foo', 'bar'],
contentUrl: 'http://foo.com/bar.html',
nonPersonalizedAds: true,
);
RewardedAd? rewardedAd;
int _numRewardedLoadAttempts = 0;
int maxFailedLoadAttempts = 3;
void createGoogleRewardedAd(BuildContext context) {
if (context.read<AppConfigurationCubit>().rewardId() != "") {
RewardedAd.load(
adUnitId: context.read<AppConfigurationCubit>().rewardId()!,
request: request,
rewardedAdLoadCallback: RewardedAdLoadCallback(
onAdLoaded: (RewardedAd ad) {
rewardedAd = ad;
_numRewardedLoadAttempts = 0;
},
onAdFailedToLoad: (LoadAdError error) {
rewardedAd = null;
_numRewardedLoadAttempts += 1;
if (_numRewardedLoadAttempts <= maxFailedLoadAttempts) {
createGoogleRewardedAd(context);
}
},
));
}
}
void showGoogleRewardedAd(BuildContext context) {
if (context.read<AppConfigurationCubit>().rewardId() != "") {
if (rewardedAd == null) {
return;
}
rewardedAd!.fullScreenContentCallback = FullScreenContentCallback(
onAdShowedFullScreenContent: (RewardedAd ad) => debugPrint('ad onAdShowedFullScreenContent.'),
onAdDismissedFullScreenContent: (RewardedAd ad) {
ad.dispose();
createGoogleRewardedAd(context);
},
onAdFailedToShowFullScreenContent: (RewardedAd ad, AdError error) {
ad.dispose();
createGoogleRewardedAd(context);
},
);
rewardedAd!.setImmersiveMode(true);
rewardedAd!.show(onUserEarnedReward: (AdWithoutView ad, RewardItem reward) {});
rewardedAd = null;
}
}

View File

@@ -0,0 +1,23 @@
import 'package:flutter/foundation.dart';
import 'package:unity_ads_plugin/unity_ads_plugin.dart';
void loadUnityRewardAd(String placementId) {
UnityAds.load(placementId: placementId, onComplete: (placementId) {}, onFailed: (placementId, error, message) {});
}
void showUnityRewardAds(String placementId) {
UnityAds.showVideoAd(
placementId: placementId,
onComplete: (placementId) {
loadUnityRewardAd(placementId);
},
onFailed: (placementId, error, message) {
loadUnityRewardAd(placementId);
},
onStart: (placementId) => debugPrint('Video Ad $placementId started'),
onClick: (placementId) => debugPrint('Video Ad $placementId click'),
onSkipped: (placementId) {
loadUnityRewardAd(placementId);
},
);
}

View File

@@ -0,0 +1,121 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:news/cubits/relatedNewsCubit.dart';
import 'package:news/cubits/appLocalizationCubit.dart';
import 'package:news/data/models/NewsModel.dart';
import 'package:news/data/repositories/Settings/settingsLocalDataRepository.dart';
import 'package:news/ui/widgets/NewsItem.dart';
import 'package:news/ui/widgets/errorContainerWidget.dart';
import 'package:news/ui/widgets/shimmerNewsList.dart';
import 'package:news/ui/widgets/customAppBar.dart';
import 'package:news/utils/ErrorMessageKeys.dart';
import 'package:news/utils/uiUtils.dart';
class ShowMoreNewsList extends StatefulWidget {
final NewsModel model;
const ShowMoreNewsList({super.key, required this.model});
@override
ShowMoreNewsListState createState() => ShowMoreNewsListState();
static Route route(RouteSettings routeSettings) {
final arguments = routeSettings.arguments as Map<String, dynamic>;
return CupertinoPageRoute(builder: (_) => ShowMoreNewsList(model: arguments['model']));
}
}
class ShowMoreNewsListState extends State<ShowMoreNewsList> {
late final ScrollController controller = ScrollController()..addListener(hasMoreRelatedNewsScrollListener);
Set<String> get locationValue => SettingsLocalDataRepository().getLocationCityValues();
void hasMoreRelatedNewsScrollListener() {
if (controller.position.maxScrollExtent == controller.offset) {
if (context.read<RelatedNewsCubit>().hasMoreRelatedNews()) {
context.read<RelatedNewsCubit>().getMoreRelatedNews(
langId: context.read<AppLocalizationCubit>().state.id,
catId: widget.model.subCatId == "0" || widget.model.subCatId == '' ? widget.model.categoryId : null,
subCatId: widget.model.subCatId != "0" || widget.model.subCatId != '' ? widget.model.subCatId : null,
latitude: locationValue.first,
longitude: locationValue.last);
} else {}
}
}
_buildRelatedNewsContainer(
{required NewsModel model, required int index, required int totalCurrentRelatedNews, required bool hasMoreRelatedNewsFetchError, required bool hasMore, required List<NewsModel> newsList}) {
if (index == totalCurrentRelatedNews - 1 && index != 0) {
if (hasMore) {
if (hasMoreRelatedNewsFetchError) {
return Center(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 8.0),
child: IconButton(
onPressed: () {
context.read<RelatedNewsCubit>().getMoreRelatedNews(
langId: context.read<AppLocalizationCubit>().state.id,
catId: widget.model.subCatId == "0" || widget.model.subCatId == '' ? widget.model.categoryId : null,
subCatId: widget.model.subCatId != "0" || widget.model.subCatId != '' ? widget.model.subCatId : null,
latitude: locationValue.first,
longitude: locationValue.last);
},
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 NewsItem(model: model, index: index, newslist: newsList, fromShowMore: true);
}
void refreshNewsList() {
context.read<RelatedNewsCubit>().getRelatedNews(
langId: context.read<AppLocalizationCubit>().state.id,
catId: widget.model.subCatId == "0" || widget.model.subCatId == '' ? widget.model.categoryId : null,
subCatId: widget.model.subCatId != "0" || widget.model.subCatId != '' ? widget.model.subCatId : null);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(height: 45, isBackBtn: true, label: 'relatedNews', horizontalPad: 15, isConvertText: true),
body: BlocBuilder<RelatedNewsCubit, RelatedNewsState>(
builder: (context, state) {
if (state is RelatedNewsFetchSuccess) {
return RefreshIndicator(
onRefresh: () async {
refreshNewsList();
},
child: ListView.builder(
controller: controller,
physics: const AlwaysScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: state.relatedNews.length,
itemBuilder: (context, index) {
return _buildRelatedNewsContainer(
model: state.relatedNews[index],
hasMore: state.hasMore,
hasMoreRelatedNewsFetchError: state.hasMoreFetchError,
index: index,
totalCurrentRelatedNews: state.relatedNews.length,
newsList: state.relatedNews);
}),
);
}
if (state is RelatedNewsFetchFailure) {
return ErrorContainerWidget(
errorMsg: (state.errorMessage.contains(ErrorMessageKeys.noInternet)) ? UiUtils.getTranslatedLabel(context, 'internetmsg') : state.errorMessage, onRetry: refreshNewsList);
}
//state is RelatedNewsInitial || state is RelatedNewsFetchInProgress
return Padding(padding: const EdgeInsets.only(bottom: 10.0, left: 10.0, right: 10.0), child: ShimmerNewsList(isNews: true));
},
),
);
}
}

View File

@@ -0,0 +1,24 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:news/ui/styles/colors.dart';
Widget backBtn(BuildContext context, bool fromShowMore) {
return Positioned.directional(
textDirection: Directionality.of(context),
top: 40,
start: 20.0,
child: InkWell(
child: ClipRRect(
borderRadius: BorderRadius.circular(52.0),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
child: Container(
alignment: Alignment.center,
height: 39,
width: 39,
decoration: const BoxDecoration(color: secondaryColor, shape: BoxShape.circle),
child: const Icon(Icons.keyboard_backspace_rounded, color: darkSecondaryColor)))),
onTap: () {
(fromShowMore == true) ? Navigator.of(context).popUntil((route) => route.isFirst) : Navigator.pop(context);
}));
}

View File

@@ -0,0 +1,15 @@
import 'package:flutter/material.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/utils/uiUtils.dart';
Widget dateView(BuildContext context, String date) {
return Row(
children: [
const Icon(Icons.access_time_filled_rounded, size: 15),
const SizedBox(width: 3),
CustomTextLabel(
text: UiUtils.convertToAgo(context, DateTime.parse(date), 0)!,
textStyle: Theme.of(context).textTheme.bodySmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.8), fontSize: 12.0, fontWeight: FontWeight.w600))
],
);
}

View File

@@ -0,0 +1,131 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:news/cubits/Auth/authCubit.dart';
import 'package:news/cubits/NewsComment/deleteCommentCubit.dart';
import 'package:news/cubits/NewsComment/flagCommentCubit.dart';
import 'package:news/cubits/commentNewsCubit.dart';
import 'package:news/data/models/CommentModel.dart';
import 'package:news/ui/screens/auth/Widgets/svgPictureWidget.dart';
import 'package:news/ui/widgets/customTextBtn.dart';
import 'package:news/utils/uiUtils.dart';
import 'package:news/ui/widgets/SnackBarWidget.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
delAndReportCom(
{required int index,
required CommentModel model,
required BuildContext context,
required TextEditingController reportC,
required String newsId,
required StateSetter setState,
Function? isReplyUpdate}) {
showDialog(
context: context,
barrierDismissible: true,
builder: (BuildContext context) {
return AlertDialog(
contentPadding: const EdgeInsets.all(20),
elevation: 2.0,
backgroundColor: UiUtils.getColorScheme(context).surface,
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(15.0))),
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
if (context.read<AuthCubit>().getUserId() == model.userId!)
InkWell(
onTap: () async {
if (context.read<AuthCubit>().getUserId() != "0") {
context.read<DeleteCommCubit>().setDeleteComm(commId: model.id!);
}
},
splashColor: Colors.transparent,
highlightColor: Colors.transparent,
child: Row(
children: <Widget>[
CustomTextLabel(
text: 'deleteTxt',
textStyle: Theme.of(context).textTheme.titleMedium?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.7), fontWeight: FontWeight.bold)),
const Spacer(),
BlocConsumer<DeleteCommCubit, DeleteCommState>(
bloc: context.read<DeleteCommCubit>(),
listener: (context, state) {
if (state is DeleteCommSuccess) {
context.read<CommentNewsCubit>().deleteComment(index);
showSnackBar(state.message, context);
if (isReplyUpdate != null) {
isReplyUpdate(false, index);
}
Navigator.pop(context);
}
},
builder: (context, state) {
return SvgPictureWidget(assetName: "delete_icon", assetColor: ColorFilter.mode(UiUtils.getColorScheme(context).primaryContainer, BlendMode.srcIn), height: 20, width: 20);
}),
],
),
),
if (context.read<AuthCubit>().getUserId() != model.userId!)
Padding(
padding: const EdgeInsets.only(top: 15),
child: Row(children: <Widget>[
CustomTextLabel(
text: 'reportTxt',
textStyle: Theme.of(context).textTheme.titleMedium?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.7), fontWeight: FontWeight.bold)),
const Spacer(),
SvgPictureWidget(assetName: "flag_icon", assetColor: ColorFilter.mode(UiUtils.getColorScheme(context).primaryContainer, BlendMode.srcIn), height: 20, width: 20)
])),
if (context.read<AuthCubit>().getUserId() != model.userId!)
Padding(
padding: const EdgeInsets.only(top: 5),
child: TextField(
controller: reportC,
keyboardType: TextInputType.multiline,
maxLines: null,
style: Theme.of(context).textTheme.bodySmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.6)),
decoration: InputDecoration(
enabledBorder: OutlineInputBorder(borderSide: BorderSide(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.8), width: 0.5)),
focusedBorder: OutlineInputBorder(borderSide: BorderSide(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.8), width: 0.5)),
))),
if (context.read<AuthCubit>().getUserId() != model.userId!)
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
CustomTextButton(
onTap: () => Navigator.pop(context),
textWidget: CustomTextLabel(
text: 'cancelBtn',
textStyle: Theme.of(context).textTheme.titleMedium?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.7), fontWeight: FontWeight.bold))),
BlocConsumer<SetFlagCubit, SetFlagState>(
bloc: context.read<SetFlagCubit>(),
listener: (context, state) {
if (state is SetFlagFetchSuccess) {
setState(() => reportC.text = "");
showSnackBar(state.message, context);
Navigator.pop(context);
}
},
builder: (context, state) {
return CustomTextButton(
onTap: () {
if (context.read<AuthCubit>().getUserId() != "0") {
if (reportC.text.trim().isNotEmpty) {
context.read<SetFlagCubit>().setFlag(commId: model.id!, newsId: newsId, message: reportC.text);
} else {
showSnackBar(UiUtils.getTranslatedLabel(context, 'firstFillData'), context);
}
} else {
UiUtils.loginRequired(context);
}
},
textWidget: CustomTextLabel(
text: 'submitBtn',
textStyle:
Theme.of(context).textTheme.titleMedium?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.7), fontWeight: FontWeight.bold)));
})
],
),
],
)));
});
}

View File

@@ -0,0 +1,130 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:news/data/models/CommentModel.dart';
import 'package:news/ui/screens/auth/Widgets/svgPictureWidget.dart';
import 'package:news/ui/widgets/customTextBtn.dart';
import 'package:news/utils/uiUtils.dart';
import 'package:news/cubits/Auth/authCubit.dart';
import 'package:news/cubits/NewsComment/deleteCommentCubit.dart';
import 'package:news/cubits/NewsComment/flagCommentCubit.dart';
import 'package:news/cubits/commentNewsCubit.dart';
import 'package:news/ui/widgets/SnackBarWidget.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
delAndReportReplyComm(
{required CommentModel model, required BuildContext context, required TextEditingController reportC, required String newsId, required StateSetter setState, required int replyIndex}) {
showDialog(
context: context,
barrierDismissible: true,
builder: (BuildContext context) {
return AlertDialog(
contentPadding: const EdgeInsets.all(20),
elevation: 2.0,
backgroundColor: UiUtils.getColorScheme(context).surface,
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(15.0))),
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
if (context.read<AuthCubit>().getUserId() == model.replyComList![replyIndex].userId!)
InkWell(
splashColor: Colors.transparent,
highlightColor: Colors.transparent,
onTap: () async {
if (context.read<AuthCubit>().getUserId() != "0") {
context.read<DeleteCommCubit>().setDeleteComm(commId: model.replyComList![replyIndex].id!);
}
},
child: Row(
children: <Widget>[
CustomTextLabel(
text: 'deleteTxt',
textStyle: Theme.of(context).textTheme.titleMedium?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.7), fontWeight: FontWeight.bold)),
const Spacer(),
BlocConsumer<DeleteCommCubit, DeleteCommState>(
bloc: context.read<DeleteCommCubit>(),
listener: (context, state) {
if (state is DeleteCommSuccess) {
context.read<CommentNewsCubit>().deleteCommentReply(model.id!, replyIndex);
showSnackBar(state.message, context);
Navigator.pop(context);
}
},
builder: (context, state) {
return SvgPictureWidget(assetName: "delete_icon", assetColor: ColorFilter.mode(UiUtils.getColorScheme(context).primaryContainer, BlendMode.srcIn), height: 20, width: 20);
}),
],
),
),
if (context.read<AuthCubit>().getUserId() != model.replyComList![replyIndex].userId!)
Padding(
padding: const EdgeInsets.only(top: 15),
child: Row(
children: <Widget>[
CustomTextLabel(
text: 'reportTxt',
textStyle: Theme.of(context).textTheme.titleMedium?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.7), fontWeight: FontWeight.bold)),
const Spacer(),
SvgPictureWidget(assetName: "flag_icon", assetColor: ColorFilter.mode(UiUtils.getColorScheme(context).primaryContainer, BlendMode.srcIn), height: 20, width: 20)
],
)),
if (context.read<AuthCubit>().getUserId() != model.replyComList![replyIndex].userId!)
Padding(
padding: const EdgeInsets.only(top: 5),
child: TextField(
controller: reportC,
keyboardType: TextInputType.multiline,
maxLines: null,
style: Theme.of(context).textTheme.bodySmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.6)),
decoration: InputDecoration(
enabledBorder: OutlineInputBorder(borderSide: BorderSide(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.8), width: 0.5)),
focusedBorder: OutlineInputBorder(borderSide: BorderSide(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.8), width: 0.5)),
),
)),
if (context.read<AuthCubit>().getUserId() != model.replyComList![replyIndex].userId!)
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
CustomTextButton(
onTap: () {
Navigator.pop(context);
},
textWidget: CustomTextLabel(
text: 'cancelBtn',
textStyle: Theme.of(context).textTheme.titleMedium?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.7), fontWeight: FontWeight.bold))),
BlocConsumer<SetFlagCubit, SetFlagState>(
bloc: context.read<SetFlagCubit>(),
listener: (context, state) {
if (state is SetFlagFetchSuccess) {
setState(() {
reportC.text = "";
});
showSnackBar(state.message, context);
Navigator.pop(context);
}
},
builder: (context, state) {
return CustomTextButton(
onTap: () {
if (context.read<AuthCubit>().getUserId() != "0") {
if (reportC.text.trim().isNotEmpty) {
context.read<SetFlagCubit>().setFlag(commId: model.replyComList![replyIndex].id!, newsId: newsId, message: reportC.text);
} else {
showSnackBar(UiUtils.getTranslatedLabel(context, 'firstFillData'), context);
}
} else {
UiUtils.loginRequired(context);
}
},
textWidget: CustomTextLabel(
text: 'submitBtn',
textStyle:
Theme.of(context).textTheme.titleMedium?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.7), fontWeight: FontWeight.bold)));
})
],
),
],
)));
});
}

View File

@@ -0,0 +1,41 @@
import 'package:flutter/material.dart';
import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/utils/uiUtils.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:news/ui/screens/NewsDetailsVideo.dart';
Widget descView({required String desc, required double fontValue, required BuildContext context}) {
return Padding(
padding: const EdgeInsets.only(top: 5.0),
child: HtmlWidget(
desc,
onTapUrl: (String? url) async {
if (await canLaunchUrl(Uri.parse(url!))) {
await launchUrl(Uri.parse(url));
return true;
} else {
throw 'Could not launch $url';
}
},
onErrorBuilder: (context, element, error) => CustomTextLabel(text: '$element error: $error'),
onLoadingBuilder: (context, element, loadingProgress) => UiUtils.showCircularProgress(true, Theme.of(context).primaryColor),
renderMode: RenderMode.column,
// set the default styling for text
textStyle: TextStyle(fontSize: fontValue.toDouble()),
customWidgetBuilder: (element) {
if ((element.toString() == "<html iframe>") || (element.toString() == "<html video>")) {
return FittedBox(
fit: BoxFit.fill,
child: Container(
height: 220,
width: MediaQuery.of(context).size.width,
color: Colors.transparent,
child: (element.toString() == "<html iframe>") ? NewsDetailsVideo(src: element.attributes["src"], type: "1") : NewsDetailsVideo(type: "2", src: element.outerHtml)),
);
}
return null;
},
));
}

View File

@@ -0,0 +1,288 @@
import 'package:flutter/material.dart';
import 'package:html/parser.dart' show parse;
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:news/cubits/Auth/authCubit.dart';
import 'package:news/cubits/Bookmark/UpdateBookmarkCubit.dart';
import 'package:news/cubits/Bookmark/bookmarkCubit.dart';
import 'package:news/cubits/appSystemSettingCubit.dart';
import 'package:news/data/models/BreakingNewsModel.dart';
import 'package:news/data/models/NewsModel.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/ui/widgets/SnackBarWidget.dart';
import 'package:news/utils/internetConnectivity.dart';
import 'package:news/utils/uiUtils.dart';
Widget allRowBtn(
{required bool isFromBreak,
required BuildContext context,
BreakingNewsModel? breakModel,
NewsModel? model,
required int fontVal,
required Function updateFont,
required bool isPlaying,
required Function speak,
required Function stop,
required Function updateComEnabled}) {
return !isFromBreak
? Padding(
padding: const EdgeInsetsDirectional.only(end: 85),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(children: [
if (context.read<AppConfigurationCubit>().getCommentsMode() == "1")
InkWell(
child: Column(children: [
const Icon(Icons.insert_comment_rounded),
Padding(
padding: const EdgeInsetsDirectional.only(top: 4.0),
child: CustomTextLabel(
text: 'comLbl',
maxLines: 3,
textAlign: TextAlign.center,
textStyle: Theme.of(context).textTheme.bodySmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.8), fontSize: 9.0)))
]),
onTap: () {
if (model!.isCommentEnabled != null && model.isCommentEnabled == 0) {
//comments disabled by Admin
showSnackBar(UiUtils.getTranslatedLabel(context, "disabledCommentsMsg"), context);
} else {
updateComEnabled(true);
}
}),
InkWell(
child: setShareBtn(context),
onTap: () async {
if (await InternetConnectivity.isNetworkAvailable()) {
UiUtils.shareNews(context: context, slug: model!.slug!, title: model.title!, isNews: true, isVideo: false, videoId: "", isBreakingNews: false);
} else {
showSnackBar(UiUtils.getTranslatedLabel(context, 'internetmsg'), context);
}
},
),
BlocBuilder<BookmarkCubit, BookmarkState>(
bloc: context.read<BookmarkCubit>(),
builder: (context, bookmarkState) {
bool isBookmark = context.read<BookmarkCubit>().isNewsBookmark(model!.newsId!);
return BlocConsumer<UpdateBookmarkStatusCubit, UpdateBookmarkStatusState>(
bloc: context.read<UpdateBookmarkStatusCubit>(),
listener: ((context, state) {
if (state is UpdateBookmarkStatusSuccess) {
if (state.wasBookmarkNewsProcess) {
context.read<BookmarkCubit>().addBookmarkNews(state.news);
} else {
context.read<BookmarkCubit>().removeBookmarkNews(state.news);
}
}
isBookmark = context.read<BookmarkCubit>().isNewsBookmark(model.newsId!);
}),
builder: (context, state) {
return InkWell(
onTap: () {
if (context.read<AuthCubit>().getUserId() != "0") {
if (state is UpdateBookmarkStatusInProgress) {
return;
}
context.read<UpdateBookmarkStatusCubit>().setBookmarkNews(news: model, status: (isBookmark) ? "0" : "1");
} else {
UiUtils.loginRequired(context);
}
},
child: SizedBox(
width: MediaQuery.of(context).size.width * 0.13,
child: Column(children: [
state is UpdateBookmarkStatusInProgress
? SizedBox(height: 24, width: 24, child: UiUtils.showCircularProgress(true, Theme.of(context).primaryColor))
: Icon(isBookmark ? Icons.bookmark_added_rounded : Icons.bookmark_add_outlined),
Padding(
padding: const EdgeInsetsDirectional.only(top: 4.0),
child: CustomTextLabel(
text: 'saveLbl',
maxLines: 2,
textAlign: TextAlign.center,
textStyle: Theme.of(context).textTheme.bodySmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.8), fontSize: 9.0)))
]),
));
});
}),
InkWell(
child: SizedBox(
width: MediaQuery.of(context).size.width * 0.13,
child: Center(
child: Column(
children: [
const Icon(Icons.text_fields_rounded),
Padding(
padding: const EdgeInsetsDirectional.only(top: 4.0),
child: CustomTextLabel(
text: 'txtSizeLbl',
maxLines: 3,
textAlign: TextAlign.center,
textStyle: Theme.of(context).textTheme.bodySmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.8), fontSize: 9.0)))
],
),
),
),
onTap: () {
changeFontSizeSheet(context, fontVal, updateFont);
},
),
InkWell(
child: setSpeakBtn(context, isPlaying),
onTap: () {
if (isPlaying) {
stop();
} else {
final document = parse("${model!.title}\n${model.desc}"); //Speak Title along with Description
String parsedString = parse(document.body!.text).documentElement!.text;
speak(parsedString);
}
})
]),
))
: Row(children: [
InkWell(
child: setTextSize(context),
onTap: () {
changeFontSizeSheet(context, fontVal, updateFont);
},
),
Padding(
padding: const EdgeInsetsDirectional.only(start: 8.0),
child: InkWell(
child: setSpeakBtn(context, isPlaying),
onTap: () {
if (isPlaying) {
stop();
} else {
final document = parse("${breakModel!.title}\n${breakModel.desc}"); //Speak Title along with Description
String parsedString = parse(document.body!.text).documentElement!.text;
speak(parsedString);
}
})),
Padding(
padding: const EdgeInsetsDirectional.only(start: 8.0),
child: InkWell(
child: setShareBtn(context),
onTap: () async {
if (await InternetConnectivity.isNetworkAvailable()) {
UiUtils.shareNews(context: context, slug: breakModel!.slug!, title: breakModel.title!, isVideo: false, videoId: "", isBreakingNews: true, isNews: false);
} else {
showSnackBar(UiUtils.getTranslatedLabel(context, 'internetmsg'), context);
}
}))
]);
}
changeFontSizeSheet(BuildContext context, int fontValue, Function updateFun) {
showModalBottomSheet<dynamic>(
context: context,
elevation: 5.0,
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.only(topLeft: Radius.circular(50), topRight: Radius.circular(50))),
builder: (BuildContext context) {
return StatefulBuilder(builder: (BuildContext context, setStater) {
return Container(
padding: const EdgeInsetsDirectional.only(bottom: 20.0, top: 5.0, start: 20.0, end: 20.0),
decoration: const BoxDecoration(borderRadius: BorderRadius.only(topLeft: Radius.circular(50), topRight: Radius.circular(50))),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(vertical: 30),
child: Row(mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[
const Icon(Icons.text_fields_rounded),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 15),
child: CustomTextLabel(
text: 'txtSizeLbl',
textStyle: Theme.of(context).textTheme.titleMedium?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer),
)),
CustomTextLabel(text: "( $fontValue )", textStyle: Theme.of(context).textTheme.titleMedium?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer)),
])),
SliderTheme(
data: SliderTheme.of(context).copyWith(
activeTrackColor: Colors.red[700],
inactiveTrackColor: Colors.red[100],
trackShape: const RoundedRectSliderTrackShape(),
trackHeight: 4.0,
thumbShape: const RoundSliderThumbShape(enabledThumbRadius: 8.0),
thumbColor: Colors.redAccent,
overlayColor: Colors.red.withAlpha(32),
overlayShape: const RoundSliderOverlayShape(overlayRadius: 28.0),
tickMarkShape: const RoundSliderTickMarkShape(),
activeTickMarkColor: Colors.red[700],
inactiveTickMarkColor: Colors.red[100],
valueIndicatorShape: const PaddleSliderValueIndicatorShape(),
valueIndicatorColor: Colors.redAccent,
valueIndicatorTextStyle: const TextStyle(color: Colors.white)),
child: Slider(
label: '$fontValue',
value: fontValue.toDouble(),
activeColor: Theme.of(context).primaryColor,
min: 15,
max: 40,
divisions: 10,
onChanged: (value) {
setStater(() {
fontValue = value.round();
updateFun(value.round());
});
}))
],
));
});
});
}
setShareBtn(BuildContext context) {
return SizedBox(
width: MediaQuery.of(context).size.width * 0.13,
child: Column(
children: [
const Icon(Icons.share_rounded),
Padding(
padding: const EdgeInsetsDirectional.only(top: 4.0),
child: CustomTextLabel(
text: 'shareLbl',
maxLines: 3,
textAlign: TextAlign.center,
textStyle: Theme.of(context).textTheme.bodySmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.8), fontSize: 9.0),
))
],
),
);
}
setSpeakBtn(BuildContext context, bool isPlaying) {
return SizedBox(
width: MediaQuery.of(context).size.width * 0.13,
child: Column(
children: [
Icon(Icons.speaker_phone_rounded, color: isPlaying ? Theme.of(context).primaryColor : UiUtils.getColorScheme(context).primaryContainer),
Padding(
padding: const EdgeInsetsDirectional.only(top: 4.0),
child: CustomTextLabel(
text: 'speakLoudLbl',
maxLines: 3,
textAlign: TextAlign.center,
textStyle: Theme.of(context)
.textTheme
.bodySmall
?.copyWith(color: isPlaying ? Theme.of(context).primaryColor : UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.8), fontSize: 9.0)))
],
),
);
}
setTextSize(BuildContext context) {
return Column(
children: [
const Icon(Icons.text_fields_rounded),
Padding(
padding: const EdgeInsetsDirectional.only(top: 4.0),
child: CustomTextLabel(
text: 'txtSizeLbl', maxLines: 2, textStyle: Theme.of(context).textTheme.bodySmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.8), fontSize: 9.0)))
],
);
}

View File

@@ -0,0 +1,89 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:news/cubits/Auth/authCubit.dart';
import 'package:news/cubits/LikeAndDislikeNews/LikeAndDislikeCubit.dart';
import 'package:news/cubits/LikeAndDislikeNews/updateLikeAndDislikeCubit.dart';
import 'package:news/cubits/appLocalizationCubit.dart';
import 'package:news/data/models/NewsModel.dart';
import 'package:news/ui/styles/colors.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/utils/uiUtils.dart';
Widget likeBtn(BuildContext context, NewsModel model) {
bool isLike = context.read<LikeAndDisLikeCubit>().isNewsLikeAndDisLike(model.newsId!);
return BlocConsumer<LikeAndDisLikeCubit, LikeAndDisLikeState>(
bloc: context.read<LikeAndDisLikeCubit>(),
listener: ((context, state) {
if (state is LikeAndDisLikeFetchSuccess) {
isLike = context.read<LikeAndDisLikeCubit>().isNewsLikeAndDisLike(model.newsId!);
}
}),
builder: (context, likeAndDislikeState) {
return BlocConsumer<UpdateLikeAndDisLikeStatusCubit, UpdateLikeAndDisLikeStatusState>(
bloc: context.read<UpdateLikeAndDisLikeStatusCubit>(),
listener: ((context, state) {
if (state is UpdateLikeAndDisLikeStatusSuccess) {
context.read<LikeAndDisLikeCubit>().getLike(langId: context.read<AppLocalizationCubit>().state.id);
model.totalLikes = (!isLike)
? (int.parse(model.totalLikes.toString()) + 1).toString()
: (model.totalLikes!.isNotEmpty)
? (int.parse(model.totalLikes.toString()) - 1).toString()
: "0";
}
}),
builder: (context, state) {
isLike = context.read<LikeAndDisLikeCubit>().isNewsLikeAndDisLike(model.newsId!);
return Positioned.directional(
textDirection: Directionality.of(context),
top: MediaQuery.of(context).size.height / 2.90,
end: MediaQuery.of(context).size.width / 10.8,
child: Column(
children: [
InkWell(
splashColor: Colors.transparent,
onTap: () {
if (context.read<AuthCubit>().getUserId() != "0") {
if (state is UpdateLikeAndDisLikeStatusInProgress) {
return;
}
context.read<UpdateLikeAndDisLikeStatusCubit>().setLikeAndDisLikeNews(news: model, status: (isLike) ? "0" : "1");
} else {
UiUtils.loginRequired(context);
}
},
child: ClipRRect(
borderRadius: BorderRadius.circular(52.0),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
child: Container(
alignment: Alignment.center,
height: 39,
width: 39,
decoration: BoxDecoration(boxShadow: [
BoxShadow(blurRadius: 6, offset: const Offset(5.0, 5.0), color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.4), spreadRadius: 0),
], color: secondaryColor, shape: BoxShape.circle),
child: (state is UpdateLikeAndDisLikeStatusInProgress)
? SizedBox(height: 20, width: 20, child: UiUtils.showCircularProgress(true, Theme.of(context).primaryColor))
: isLike
? const Icon(Icons.thumb_up_alt, color: darkSecondaryColor)
: const Icon(Icons.thumb_up_off_alt, color: darkSecondaryColor),
))),
),
SizedBox(
width: MediaQuery.of(context).size.width / 7.5,
child: Padding(
padding: const EdgeInsetsDirectional.only(top: 5.0),
child: CustomTextLabel(
text: (int.tryParse(model.totalLikes!)! > 0) ? "${model.totalLikes!} ${UiUtils.getTranslatedLabel(context, 'likeLbl')}" : "",
maxLines: 2,
textStyle: Theme.of(context).textTheme.bodySmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.7)),
),
),
)
],
));
});
});
}

View File

@@ -0,0 +1,166 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:news/cubits/relatedNewsCubit.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/ui/widgets/networkImage.dart';
import 'package:news/utils/uiUtils.dart';
import 'package:news/app/routes.dart';
import 'package:news/data/models/NewsModel.dart';
import 'package:news/ui/styles/colors.dart';
class RelatedNewsList extends StatefulWidget {
final NewsModel model;
const RelatedNewsList({super.key, required this.model});
@override
RelatedNewsListState createState() => RelatedNewsListState();
}
class RelatedNewsListState extends State<RelatedNewsList> {
int sliderIndex = 0;
List<NewsModel> relatedList = [];
Widget getRelatedList() {
return BlocConsumer<RelatedNewsCubit, RelatedNewsState>(
bloc: context.read<RelatedNewsCubit>(),
listener: (context, state) {
if (state is RelatedNewsFetchSuccess) {
setState(() {
relatedList.clear();
relatedList.addAll(state.relatedNews);
relatedList.removeWhere((element) => (element.id == widget.model.id!) || (element.id == widget.model.newsId!));
});
}
},
builder: (context, state) {
if (state is RelatedNewsFetchSuccess && relatedList.isNotEmpty) {
return Padding(
padding: const EdgeInsetsDirectional.only(top: 15.0, bottom: 15.0),
child: Column(children: [
Align(
alignment: Alignment.topLeft,
child: Padding(
padding: const EdgeInsetsDirectional.only(bottom: 15.0),
child: CustomTextLabel(
text: 'relatedNews',
textStyle: Theme.of(context).textTheme.titleMedium?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer, fontWeight: FontWeight.w600)))),
showRelatedNews(relatedList)
]));
}
return const SizedBox.shrink(); //state is RelatedNewsFetchInProgress || state is RelatedNewsInitial || state is RelatedNewsFetchFailure
});
}
Widget relatedNewsData(NewsModel model, List<NewsModel> newsList) {
return Stack(children: [
ClipRRect(
borderRadius: const BorderRadius.only(topLeft: Radius.circular(15.0), topRight: Radius.circular(15.0)),
child: ShaderMask(
shaderCallback: (rect) => const LinearGradient(begin: Alignment.center, end: Alignment.bottomCenter, colors: [Colors.transparent, secondaryColor]).createShader(rect),
blendMode: BlendMode.darken,
child: GestureDetector(
child: CustomNetworkImage(networkImageUrl: model.image!, width: double.maxFinite, height: MediaQuery.of(context).size.height / 2.9, isVideo: false, fit: BoxFit.cover),
onTap: () {
List<NewsModel> addNewsList = [];
addNewsList.addAll(newsList);
addNewsList.remove(model);
UiUtils.showInterstitialAds(context: context);
Navigator.of(context).pushNamed(Routes.newsDetails, arguments: {"model": model, "newsList": addNewsList, "isFromBreak": false, "fromShowMore": false});
}))),
Align(
alignment: Alignment.bottomCenter,
child: Container(
padding: EdgeInsetsDirectional.only(bottom: MediaQuery.of(context).size.height / 18.9, start: MediaQuery.of(context).size.width / 20.0, end: 5.0),
width: MediaQuery.of(context).size.width,
child: CustomTextLabel(
text: model.title!,
textStyle: Theme.of(context).textTheme.titleSmall?.copyWith(color: secondaryColor, fontWeight: FontWeight.normal, fontSize: 12.5, height: 1.0),
maxLines: 1,
softWrap: true,
overflow: TextOverflow.ellipsis)))
]);
}
List<T> map<T>(List list, Function handler) {
List<T> result = [];
for (var i = 0; i < list.length; i++) {
result.add(handler(i, list[i]));
}
return result;
}
CustomTextLabel setCoverageText(BuildContext context) {
return CustomTextLabel(
text: 'viewFullCoverage',
textAlign: TextAlign.center,
textStyle: Theme.of(context).textTheme.titleMedium?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.9), fontWeight: FontWeight.w600),
);
}
Icon setCoverageIcon(BuildContext context) {
return Icon(Icons.image, color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.9));
}
Widget showRelatedNews(List<NewsModel> relatedNews) {
return Column(children: [
Stack(children: [
SizedBox(
height: MediaQuery.of(context).size.height / 2.9,
width: double.infinity,
child: PageView.builder(
itemCount: relatedNews.length,
scrollDirection: Axis.horizontal,
physics: const AlwaysScrollableScrollPhysics(),
onPageChanged: (index) {
setState(() {
sliderIndex = index;
});
},
itemBuilder: (BuildContext context, int index) {
return relatedNewsData(relatedNews[index], relatedNews);
})),
Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding: EdgeInsetsDirectional.only(top: MediaQuery.of(context).size.height / 3.3),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: map<Widget>(relatedNews, (index, url) {
return AnimatedContainer(
duration: const Duration(milliseconds: 500),
width: sliderIndex == index ? MediaQuery.of(context).size.width / 15.0 : MediaQuery.of(context).size.width / 15.0,
height: 5.0,
margin: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 2.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5.0),
color: sliderIndex == index ? Theme.of(context).primaryColor : darkBackgroundColor.withOpacity(0.5),
));
}))))
]),
Container(
height: 38.0,
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: const BorderRadius.only(bottomLeft: Radius.circular(15.0), bottomRight: Radius.circular(15.0)),
color: UiUtils.getColorScheme(context).surface,
boxShadow: [BoxShadow(color: borderColor.withOpacity(0.4), offset: const Offset(0.0, 2.0), blurRadius: 6.0, spreadRadius: 0)]),
child: ElevatedButton.icon(
icon: setCoverageIcon(context),
label: setCoverageText(context),
onPressed: () {
Navigator.of(context).pushNamed(Routes.showMoreRelatedNews, arguments: {"model": widget.model});
},
style: ElevatedButton.styleFrom(foregroundColor: Theme.of(context).primaryColor, backgroundColor: Colors.transparent, shadowColor: Colors.transparent),
))
]);
}
@override
Widget build(BuildContext context) {
return getRelatedList();
}
}

View File

@@ -0,0 +1,27 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'package:unity_ads_plugin/unity_ads_plugin.dart' as unity;
import 'package:news/cubits/appSystemSettingCubit.dart';
setBannerAd(BuildContext context, BannerAd? bannerAd) {
if (context.read<AppConfigurationCubit>().bannerId() != "") {
switch (context.read<AppConfigurationCubit>().checkAdsType()) {
case "google":
return Padding(
padding: const EdgeInsetsDirectional.only(start: 5.0, end: 5.0),
child: SizedBox(width: double.maxFinite, height: bannerAd!.size.height.toDouble(), child: AdWidget(ad: bannerAd)),
);
case "unity":
return unity.UnityBannerAd(
placementId: context.read<AppConfigurationCubit>().bannerId()!,
onLoad: (placementId) => debugPrint('Banner loaded: $placementId'),
onClick: (placementId) => debugPrint('Banner clicked: $placementId'),
onFailed: (placementId, error, message) => debugPrint('Banner Ad $placementId failed: $error $message'),
);
default:
return const SizedBox.shrink();
}
}
}

View File

@@ -0,0 +1,63 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:news/data/models/NewsModel.dart';
import 'package:news/app/routes.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/utils/uiUtils.dart';
Widget tagView({required NewsModel model, required BuildContext context, bool? isFromDetailsScreen = false}) {
List<String> tagList = [];
if (model.tagName! != "") {
final tagName = model.tagName!;
tagList = tagName.split(',');
}
List<String> tagId = [];
if (model.tagId != null && model.tagId! != "") {
tagId = model.tagId!.split(",");
}
return model.tagName! != ""
? Padding(
padding: const EdgeInsetsDirectional.only(top: 15.0),
child: SizedBox(
height: 20.0,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: List.generate(tagList.length, (index) {
return Padding(
padding: EdgeInsetsDirectional.only(start: index == 0 ? 0 : 7),
child: InkWell(
child: ClipRRect(
borderRadius: BorderRadius.circular(3.0),
child: (isFromDetailsScreen != null && isFromDetailsScreen)
? tagsContainer(context: context, tagList: tagList, index: index)
: BackdropFilter(filter: ImageFilter.blur(sigmaX: 30, sigmaY: 30), child: tagsContainer(context: context, tagList: tagList, index: index))),
onTap: () {
Navigator.of(context).pushNamed(Routes.tagScreen, arguments: {"tagId": tagId[index], "tagName": tagList[index]});
},
));
}),
),
)))
: const SizedBox.shrink();
}
Widget tagsContainer({required BuildContext context, required List<String> tagList, required int index}) {
return Container(
height: 20.0,
width: 65,
alignment: Alignment.center,
padding: const EdgeInsetsDirectional.only(start: 3.0, end: 3.0),
decoration: BoxDecoration(
borderRadius: const BorderRadius.only(topLeft: Radius.circular(10.0), bottomRight: Radius.circular(10.0)), color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.7)),
child: CustomTextLabel(
text: tagList[index],
textStyle: Theme.of(context).textTheme.bodyMedium?.copyWith(color: UiUtils.getColorScheme(context).secondary, fontSize: 11),
overflow: TextOverflow.ellipsis,
softWrap: true));
}

View File

@@ -0,0 +1,9 @@
import 'package:flutter/material.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/utils/uiUtils.dart';
Widget titleView({required String title, required BuildContext context}) {
return Padding(
padding: const EdgeInsetsDirectional.only(top: 6.0),
child: CustomTextLabel(text: title, textStyle: Theme.of(context).textTheme.titleMedium?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer, fontWeight: FontWeight.w600)));
}

View File

@@ -0,0 +1,24 @@
import 'package:flutter/material.dart';
import 'package:news/data/models/BreakingNewsModel.dart';
import 'package:news/data/models/NewsModel.dart';
import 'package:news/ui/styles/colors.dart';
import 'package:news/app/routes.dart';
Widget videoBtn({required BuildContext context, required bool isFromBreak, NewsModel? model, BreakingNewsModel? breakModel}) {
if ((breakModel != null && breakModel.contentValue != "") || model != null && model.contentValue != "") {
return Positioned.directional(
textDirection: Directionality.of(context),
top: 35,
end: 20.0,
child: InkWell(
child:
Container(height: 39, width: 39, decoration: const BoxDecoration(color: secondaryColor, shape: BoxShape.circle), child: const Icon(Icons.play_arrow_rounded, color: darkSecondaryColor)),
onTap: () {
Navigator.of(context)
.pushNamed(Routes.newsVideo, arguments: (!isFromBreak) ? {"from": 1, "model": model} : {"from": 3, "breakModel": breakModel, "otherVideos": [], "otherBreakingVideos": []});
},
));
} else {
return const SizedBox.shrink();
}
}

View File

@@ -0,0 +1,75 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:news/utils/internetConnectivity.dart';
class NewsDetailsVideo extends StatefulWidget {
String? src;
String type;
NewsDetailsVideo({super.key, this.src, required this.type});
@override
State<StatefulWidget> createState() => StateNewsDetailsVideo();
}
class StateNewsDetailsVideo extends State<NewsDetailsVideo> {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
bool _isNetworkAvail = true;
var iframe;
@override
void initState() {
super.initState();
checkNetwork();
if ((widget.type == "1") || (widget.type == "3")) {
iframe = '''
<html>
<iframe src="${widget.src!}" width="100%" height="100%" allowfullscreen="allowfullscreen" frame-options="sameorigin"></iframe>
</html>
''';
} else {
iframe = '''
<html>
<video controls="controls" width="100%" height="100%">
<source src="${widget.src!}"></video>
</html>
''';
}
}
checkNetwork() async {
if (await InternetConnectivity.isNetworkAvailable()) {
setState(() {
_isNetworkAvail = true;
});
} else {
setState(() {
_isNetworkAvail = false;
});
}
}
@override
void dispose() {
// set screen back to portrait mode
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
super.dispose();
}
@override
Widget build(BuildContext context) {
// set screen to landscape mode bydefault
return SafeArea(child: Scaffold(key: _scaffoldKey, body: _isNetworkAvail ? viewVideo() : const SizedBox.shrink()));
}
//news video link set
viewVideo() {
WebUri frm;
frm = WebUri.uri(Uri.dataFromString(iframe, mimeType: 'text/html'));
return Center(
child: InAppWebView(initialUrlRequest: URLRequest(url: frm)),
);
}
}

View File

@@ -0,0 +1,185 @@
import 'package:flick_video_player/flick_video_player.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:video_player/video_player.dart';
import 'package:youtube_parser/youtube_parser.dart';
import 'package:youtube_player_flutter/youtube_player_flutter.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:news/utils/uiUtils.dart';
import 'package:news/utils/internetConnectivity.dart';
import 'package:news/ui/screens/NewsDetailsVideo.dart';
import 'package:news/data/models/LiveStreamingModel.dart';
import 'package:news/data/models/NewsModel.dart';
import 'package:news/data/models/BreakingNewsModel.dart';
class NewsVideo extends StatefulWidget {
int from;
LiveStreamingModel? liveModel;
NewsModel? model;
BreakingNewsModel? breakModel;
NewsVideo({super.key, this.model, required this.from, this.liveModel, this.breakModel});
@override
State<StatefulWidget> createState() => StateVideo();
static Route route(RouteSettings routeSettings) {
final arguments = routeSettings.arguments as Map<String, dynamic>;
return CupertinoPageRoute(builder: (_) => NewsVideo(from: arguments['from'], liveModel: arguments['liveModel'], model: arguments['model'], breakModel: arguments['breakModel']));
}
}
class StateVideo extends State<NewsVideo> {
FlickManager? flickManager;
YoutubePlayerController? _yc;
bool _isNetworkAvail = true;
VideoPlayerController? _controller;
@override
void initState() {
super.initState();
checkNetwork();
initialisePlayer();
}
initialisePlayer() {
switch (widget.from) {
case 1:
if (widget.model!.contentValue != "" || widget.model!.contentValue != null) {
if (widget.model!.contentType == "video_upload") {
_controller = VideoPlayerController.networkUrl(Uri.parse(widget.model!.contentValue!));
flickManager = FlickManager(videoPlayerController: _controller!, autoPlay: true);
} else if (widget.model!.contentType == "video_youtube") {
_yc = YoutubePlayerController(initialVideoId: YoutubePlayer.convertUrlToId(widget.model!.contentValue!) ?? "", flags: const YoutubePlayerFlags(autoPlay: true));
}
}
break;
case 2:
if (widget.liveModel!.type == "url_youtube") {
_yc = YoutubePlayerController(initialVideoId: getIdFromUrl(widget.liveModel!.url!) ?? "", flags: const YoutubePlayerFlags(autoPlay: true, isLive: true));
}
break;
default:
if (widget.breakModel!.contentValue != "" || widget.breakModel!.contentValue != null) {
if (widget.breakModel!.contentType == "video_upload") {
_controller = VideoPlayerController.networkUrl(Uri.parse(widget.breakModel!.contentValue!));
flickManager = FlickManager(videoPlayerController: _controller!, autoPlay: true);
} else if (widget.breakModel!.contentType == "video_youtube") {
_yc = YoutubePlayerController(initialVideoId: YoutubePlayer.convertUrlToId(widget.breakModel!.contentValue!) ?? "", flags: const YoutubePlayerFlags(autoPlay: true));
}
}
}
}
checkNetwork() async {
if (await InternetConnectivity.isNetworkAvailable()) {
setState(() => _isNetworkAvail = true);
} else {
setState(() => _isNetworkAvail = false);
}
}
@override
void dispose() {
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: SystemUiOverlay.values);
if (_controller != null && _controller!.value.isPlaying) _controller!.pause();
switch (widget.from) {
case 1:
if (widget.model!.contentType == "video_upload") {
Future.delayed(const Duration(milliseconds: 10)).then((value) {
flickManager!.flickControlManager!.exitFullscreen();
flickManager!.dispose();
_controller!.dispose();
_controller = null;
flickManager = null;
});
} else if (widget.model!.contentType == "video_youtube") {
_yc!.dispose();
}
break;
case 2:
if (widget.liveModel!.type == "url_youtube") {
_yc!.dispose();
}
break;
default:
if (widget.breakModel!.contentType == "video_upload") {
_controller = null;
flickManager!.dispose();
} else if (widget.breakModel!.contentType == "video_youtube") {
_yc!.dispose();
}
}
Future.delayed(const Duration(milliseconds: 20)).then((value) {
super.dispose();
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
extendBodyBehindAppBar: true, //to show Landscape video fullscreen
appBar: AppBar(
backgroundColor: Colors.transparent,
leading: InkWell(
onTap: () {
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
Navigator.of(context).pop();
},
splashColor: Colors.transparent,
highlightColor: Colors.transparent,
child: Padding(
padding: const EdgeInsetsDirectional.only(start: 20),
child: ClipRRect(
borderRadius: BorderRadius.circular(22.0),
child: Container(
alignment: Alignment.center,
decoration: BoxDecoration(color: UiUtils.getColorScheme(context).primaryContainer, shape: BoxShape.circle),
child: Icon(Icons.keyboard_backspace_rounded, color: UiUtils.getColorScheme(context).surface)))),
)),
body: PopScope(
canPop: true,
onPopInvoked: (val) async {
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
if (widget.from == 1 && (widget.model!.contentType == "video_upload")) {
_controller!.pause();
Future.delayed(const Duration(milliseconds: 10)).then((value) {
flickManager!.flickControlManager!.exitFullscreen();
flickManager!.dispose();
_controller!.dispose();
});
}
},
child: Padding(
padding: const EdgeInsetsDirectional.only(start: 15.0, end: 15.0, bottom: 5.0),
child: _isNetworkAvail
? Container(alignment: Alignment.center, decoration: BoxDecoration(borderRadius: BorderRadius.circular(10.0)), child: viewVideo())
: const Center(child: CustomTextLabel(text: 'internetmsg'))),
));
}
viewVideo() {
return widget.from == 1
? widget.model!.contentType == "video_upload"
? FlickVideoPlayer(flickManager: flickManager!, flickVideoWithControlsFullscreen: const FlickVideoWithControls(videoFit: BoxFit.fitWidth))
: widget.model!.contentType == "video_youtube"
? YoutubePlayer(controller: _yc!, showVideoProgressIndicator: true, progressIndicatorColor: Theme.of(context).primaryColor)
: widget.model!.contentType == "video_other"
? Center(child: NewsDetailsVideo(src: widget.model!.contentValue, type: "3"))
: const SizedBox.shrink()
: widget.from == 2
? widget.liveModel!.type == "url_youtube"
? YoutubePlayer(controller: _yc!, showVideoProgressIndicator: true, progressIndicatorColor: Theme.of(context).primaryColor)
: Center(child: NewsDetailsVideo(src: widget.liveModel!.url, type: "3"))
: widget.breakModel!.contentType == "video_upload"
? FlickVideoPlayer(flickManager: flickManager!)
: widget.breakModel!.contentType == "video_youtube"
? YoutubePlayer(controller: _yc!, showVideoProgressIndicator: true, progressIndicatorColor: Theme.of(context).primaryColor)
: widget.breakModel!.contentType == "video_other"
? Center(child: NewsDetailsVideo(src: widget.breakModel!.contentValue, type: "3"))
: const SizedBox.shrink();
}
}

View File

@@ -0,0 +1,55 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
import 'package:news/ui/widgets/customAppBar.dart';
import 'package:url_launcher/url_launcher.dart';
class PrivacyPolicy extends StatefulWidget {
final String? title;
final String? from;
final String? desc;
const PrivacyPolicy({super.key, this.title, this.from, this.desc});
@override
State<StatefulWidget> createState() {
return StatePrivacy();
}
static Route route(RouteSettings routeSettings) {
final arguments = routeSettings.arguments as Map<String, dynamic>;
return CupertinoPageRoute(
builder: (_) => PrivacyPolicy(
from: arguments['from'],
title: arguments['title'],
desc: arguments['desc'],
));
}
}
class StatePrivacy extends State<PrivacyPolicy> with TickerProviderStateMixin {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(height: 45, isBackBtn: true, label: widget.title!, horizontalPad: 15, isConvertText: false),
body: SingleChildScrollView(
padding: const EdgeInsetsDirectional.only(start: 20.0, end: 20.0, top: 10.0),
child: HtmlWidget(
widget.desc!,
onTapUrl: (String? url) async {
if (await canLaunchUrl(Uri.parse(url!))) {
await launchUrl(Uri.parse(url));
return true;
} else {
throw 'Could not launch $url';
}
},
),
));
}
}

View File

@@ -0,0 +1,555 @@
import 'dart:io';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:in_app_review/in_app_review.dart';
import 'package:news/app/app.dart';
import 'package:news/app/routes.dart';
import 'package:news/cubits/Auth/authCubit.dart';
import 'package:news/cubits/Auth/deleteUserCubit.dart';
import 'package:news/cubits/Auth/registerTokenCubit.dart';
import 'package:news/cubits/Author/authorCubit.dart';
import 'package:news/cubits/appLocalizationCubit.dart';
import 'package:news/cubits/appSystemSettingCubit.dart';
import 'package:news/cubits/languageJsonCubit.dart';
import 'package:news/cubits/otherPagesCubit.dart';
import 'package:news/cubits/settingCubit.dart';
import 'package:news/data/repositories/Auth/authLocalDataSource.dart';
import 'package:news/ui/screens/Profile/Widgets/customAlertDialog.dart';
import 'package:news/ui/styles/colors.dart';
import 'package:news/ui/widgets/customAppBar.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/utils/constant.dart';
import 'package:news/utils/internetConnectivity.dart';
import 'package:news/utils/uiUtils.dart';
import 'package:share_plus/share_plus.dart';
import 'package:news/cubits/themeCubit.dart';
import 'package:news/ui/styles/appTheme.dart';
import 'package:news/ui/widgets/SnackBarWidget.dart';
class ProfileScreen extends StatefulWidget {
const ProfileScreen({super.key});
@override
ProfileScreenState createState() => ProfileScreenState();
static Route route(RouteSettings routeSettings) {
return CupertinoPageRoute(builder: (_) => const ProfileScreen());
}
}
class ProfileScreenState extends State<ProfileScreen> {
File? image;
String? name, mobile, email, profile;
TextEditingController? nameC, monoC, emailC = TextEditingController();
AuthLocalDataSource authLocalDataSource = AuthLocalDataSource();
bool isEditMono = false, isEditEmail = false, isAuthor = false;
final FirebaseAuth _auth = FirebaseAuth.instance;
final InAppReview _inAppReview = InAppReview.instance;
late AuthorStatus authorStatus;
@override
void initState() {
getOtherPagesData();
super.initState();
}
getOtherPagesData() {
Future.delayed(Duration.zero, () {
context.read<OtherPageCubit>().getOtherPage(langId: context.read<AppLocalizationCubit>().state.id);
});
}
Widget pagesBuild() {
return BlocBuilder<OtherPageCubit, OtherPageState>(builder: (context, state) {
if (state is OtherPageFetchSuccess) {
return ScrollConfiguration(
behavior: GlobalScrollBehavior(),
child: ListView.builder(
shrinkWrap: true,
padding: EdgeInsets.zero,
physics: const NeverScrollableScrollPhysics(),
itemCount: state.otherPage.length,
itemBuilder: ((context, index) =>
setDrawerItem(state.otherPage[index].title!, Icons.info_rounded, false, true, false, 8, image: state.otherPage[index].image!, desc: state.otherPage[index].pageContent))),
);
} else {
//state is OtherPageFetchInProgress || state is OtherPageInitial || state is OtherPageFetchFailure
return const SizedBox.shrink();
}
});
}
switchTheme(bool value) async {
if (value) {
if (await InternetConnectivity.isNetworkAvailable()) {
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.light);
context.read<ThemeCubit>().changeTheme(AppTheme.Dark);
UiUtils.setUIOverlayStyle(appTheme: AppTheme.Dark);
//for non-appbar screens
} else {
showSnackBar(UiUtils.getTranslatedLabel(context, 'internetmsg'), context);
}
} else {
if (await InternetConnectivity.isNetworkAvailable()) {
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.dark);
context.read<ThemeCubit>().changeTheme(AppTheme.Light);
UiUtils.setUIOverlayStyle(appTheme: AppTheme.Light);
//for non-appbar screens
} else {
showSnackBar(UiUtils.getTranslatedLabel(context, 'internetmsg'), context);
}
}
}
bool getTheme() {
return (context.read<ThemeCubit>().state.appTheme == AppTheme.Dark) ? true : false;
}
bool getNotification() {
if (context.read<SettingsCubit>().state.settingsModel!.notification == true) {
return true;
} else {
return false;
}
}
switchNotification(bool value) {
if (!value) {
FirebaseMessaging.instance.deleteToken().then((value) async {
context.read<RegisterTokenCubit>().registerToken(fcmId: '', context: context);
});
} else {
FirebaseMessaging.instance.getToken().then((value) async {
context.read<RegisterTokenCubit>().registerToken(fcmId: value!, context: context);
context.read<SettingsCubit>().changeFcmToken(value);
});
}
context.read<SettingsCubit>().changeNotification(value);
setState(() {});
}
//set drawer item list
Widget setDrawerItem(String title, IconData? icon, bool isTrailing, bool isNavigate, bool isSwitch, int id, {String? image, String? desc, bool isDeleteAcc = false}) {
return ListTile(
dense: true,
contentPadding: EdgeInsets.zero,
leading: Container(
height: 30,
width: 30,
padding: EdgeInsets.all(3),
decoration: BoxDecoration(shape: BoxShape.rectangle, borderRadius: BorderRadius.circular(7), color: borderColor.withOpacity(0.2)),
child: (image != null && image != "")
? ClipRRect(
borderRadius: BorderRadius.circular(7),
child: Image.network(
image,
width: 20,
height: 20,
color: (image.contains("png")) ? UiUtils.getColorScheme(context).primaryContainer : null,
errorBuilder: (context, error, stackTrace) {
return Icon(icon);
},
),
)
: Icon(icon, size: 20)),
iconColor: (isDeleteAcc) ? (context.read<ThemeCubit>().state.appTheme == AppTheme.Dark ? darkIconColor : iconColor) : UiUtils.getColorScheme(context).primaryContainer,
trailing: (isTrailing)
? SizedBox(
height: 45,
width: 55,
child: FittedBox(
fit: BoxFit.fill,
child: Switch.adaptive(
onChanged: (id == 0) ? switchTheme : switchNotification,
value: (id == 0) ? getTheme() : getNotification(),
activeColor: Theme.of(context).primaryColor,
activeTrackColor: Theme.of(context).primaryColor,
inactiveThumbColor: Colors.grey,
inactiveTrackColor: Colors.grey)))
: const SizedBox.shrink(),
title: CustomTextLabel(
text: title,
textStyle: Theme.of(context)
.textTheme
.titleMedium
?.copyWith(color: (isDeleteAcc) ? (context.read<ThemeCubit>().state.appTheme == AppTheme.Dark ? darkIconColor : iconColor) : UiUtils.getColorScheme(context).primaryContainer)),
onTap: () {
if (isNavigate) {
switch (id) {
case 2:
Navigator.of(context).pushNamed(Routes.languageList, arguments: {"from": "profile"});
break;
case 3:
Navigator.of(context).pushNamed(Routes.bookmark);
break;
case 5:
Navigator.of(context).pushNamed(Routes.addNews, arguments: {"isEdit": false, "from": "profile"});
break;
case 6:
Navigator.of(context).pushNamed(Routes.manageUserNews);
break;
case 7:
Navigator.of(context).pushNamed(Routes.managePref, arguments: {"from": 1});
break;
case 8:
Navigator.of(context).pushNamed(Routes.privacy, arguments: {"from": "setting", "title": title, "desc": desc});
break;
case 9:
_openStoreListing();
break;
case 10:
String str = "$appName\n\n${context.read<AppConfigurationCubit>().getShareAppText()}\n\n${context.read<AppConfigurationCubit>().getAndroidAppLink()}\n";
bool isIOSAppLive = context.read<AppConfigurationCubit>().getiOSAppLink()?.trim().isNotEmpty ?? false;
if (isIOSAppLive) str += "\n\n${context.read<AppConfigurationCubit>().getiOSAppLink()}";
Share.share(str, sharePositionOrigin: Rect.fromLTWH(0, 0, MediaQuery.of(context).size.width, MediaQuery.of(context).size.height / 2));
break;
case 12:
deleteAccount();
break;
case 13:
Navigator.of(context).pushNamed(Routes.editUserProfile, arguments: {'from': 'profile'});
default:
break;
}
}
},
);
}
logOutDialog() async {
await showDialog(
context: context,
builder: (BuildContext context) {
return StatefulBuilder(builder: (BuildContext context, StateSetter setStater) {
return CustomAlertDialog(
isForceAppUpdate: false,
context: context,
yesButtonText: 'yesLbl',
yesButtonTextPostfix: 'logoutLbl',
noButtonText: 'noLbl',
imageName: 'logout',
titleWidget: CustomTextLabel(
text: 'logoutLbl', textStyle: Theme.of(this.context).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.w500, color: UiUtils.getColorScheme(context).primaryContainer)),
messageText: 'logoutTxt',
onYESButtonPressed: () async {
UiUtils.userLogOut(contxt: context);
});
});
});
}
//set Delete dialogue
deleteAccount() async {
await showDialog(
context: context,
builder: (BuildContext context) {
return StatefulBuilder(builder: (BuildContext context, StateSetter setStater) {
return CustomAlertDialog(
context: context,
isForceAppUpdate: false,
yesButtonText: (_auth.currentUser != null) ? 'yesLbl' : 'logoutLbl',
yesButtonTextPostfix: (_auth.currentUser != null) ? 'deleteTxt' : '',
noButtonText: (_auth.currentUser != null) ? 'noLbl' : 'cancelBtn',
imageName: 'deleteAccount',
titleWidget: (_auth.currentUser != null)
? CustomTextLabel(
text: 'deleteAcc', textStyle: Theme.of(context).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.w800, color: UiUtils.getColorScheme(context).primaryContainer))
: CustomTextLabel(
text: 'deleteAlertTitle', textStyle: Theme.of(context).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.w800, color: UiUtils.getColorScheme(context).primaryContainer)),
messageText: (_auth.currentUser != null) ? 'deleteConfirm' : 'deleteRelogin',
onYESButtonPressed: () async {
(_auth.currentUser != null) ? proceedToDeleteProfile() : askToLoginAgain();
});
});
});
}
askToLoginAgain() {
showSnackBar(UiUtils.getTranslatedLabel(context, 'loginReqMsg'), context);
Navigator.of(context).pushNamedAndRemoveUntil(Routes.login, (route) => false);
}
proceedToDeleteProfile() async {
//delete user from firebase
try {
await _auth.currentUser!.delete().then((value) {
//delete user prefs from App-local
context.read<DeleteUserCubit>().deleteUser().then((value) {
showSnackBar(value["message"], context);
for (int i = 0; i < AuthProviders.values.length; i++) {
if (AuthProviders.values[i].name == context.read<AuthCubit>().getType()) {
context.read<AuthCubit>().signOut(AuthProviders.values[i]).then((value) {
Navigator.of(context).pushNamedAndRemoveUntil(Routes.login, (route) => false);
});
}
}
});
});
} on FirebaseAuthException catch (error) {
if (error.code == "requires-recent-login") {
for (int i = 0; i < AuthProviders.values.length; i++) {
if (AuthProviders.values[i].name == context.read<AuthCubit>().getType()) {
context.read<AuthCubit>().signOut(AuthProviders.values[i]).then((value) {
Navigator.of(context).pushNamedAndRemoveUntil(Routes.login, (route) => false);
});
}
}
} else {
throw showSnackBar('${error.message}', context);
}
} catch (e) {}
}
Future<void> _openStoreListing() => _inAppReview.openStoreListing(appStoreId: context.read<AppConfigurationCubit>().getAppstoreId(), microsoftStoreId: 'microsoftStoreId');
Widget setHeader() {
return BlocBuilder<AuthCubit, AuthState>(builder: (context, authState) {
if (authState is Authenticated && context.read<AuthCubit>().getUserId() != "0") {
isAuthor = (authState.authModel.isAuthor == 1);
authorStatus = ((authState.authModel.authorDetails != null) ? authState.authModel.authorDetails?.status : AuthorStatus.rejected)!;
return Padding(
padding: const EdgeInsetsDirectional.only(start: 15.0, end: 15.0, top: 15.0),
child: Container(
padding: const EdgeInsetsDirectional.only(start: 20.0, end: 20.0, top: 10),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(15.0), color: Theme.of(context).colorScheme.surface),
child: Row(crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: <Widget>[
CircleAvatar(
radius: 34,
backgroundColor: Colors.transparent,
child: ClipOval(
clipBehavior: Clip.antiAliasWithSaveLayer,
child: (authState.authModel.profile != null && authState.authModel.profile.toString().trim().isNotEmpty)
? Image.network(
authState.authModel.profile!,
fit: BoxFit.fill,
width: 80,
height: 80,
filterQuality: FilterQuality.high,
errorBuilder: (context, error, stackTrace) {
return const Icon(Icons.person);
},
)
: Icon(Icons.person, color: UiUtils.getColorScheme(context).primaryContainer)),
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.6,
child: Padding(
padding: const EdgeInsetsDirectional.only(start: 5),
child: Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
if (authState.authModel.name != null && authState.authModel.name != "")
CustomTextLabel(
text: authState.authModel.name!,
maxLines: 2,
overflow: TextOverflow.ellipsis,
textStyle: Theme.of(context).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w800, color: UiUtils.getColorScheme(context).primaryContainer)),
const SizedBox(height: 3),
if (authState.authModel.mobile != null && authState.authModel.mobile!.trim().isNotEmpty)
CustomTextLabel(
text: authState.authModel.mobile!, textStyle: Theme.of(context).textTheme.titleSmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.7))),
const SizedBox(height: 3),
if (authState.authModel.email != null && authState.authModel.email != "")
CustomTextLabel(
text: authState.authModel.email!,
maxLines: 2,
overflow: TextOverflow.ellipsis,
textStyle: Theme.of(context).textTheme.titleSmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.7))),
BlocConsumer<AuthorCubit, AuthorState>(
listener: (context, state) {
if (state is AuthorRequestSent) showSnackBar(state.responseMessage, context);
},
builder: (context, state) {
return buildAuthorButton(context, state);
},
)
],
),
),
),
]),
),
);
} else {
return Row(mainAxisAlignment: MainAxisAlignment.spaceAround, children: <Widget>[
//For Guest User
Container(
margin: const EdgeInsets.all(15),
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(shape: BoxShape.circle, border: Border.all(color: UiUtils.getColorScheme(context).primaryContainer)),
alignment: Alignment.center,
child: Icon(Icons.person, size: 25.0, color: UiUtils.getColorScheme(context).primaryContainer)),
Expanded(child: setGuestText())
]);
}
});
}
Widget buildAuthorButton(BuildContext context, AuthorState state) {
//check if user is author already
if (isAuthor || authorStatus == AuthorStatus.approved || state is AuthorApproved) {
return IntrinsicWidth(
child: Container(
margin: EdgeInsets.symmetric(vertical: 10),
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(color: backgroundColor, borderRadius: BorderRadius.circular(5), border: Border.all(color: authorApprovedColor)),
child: Row(
children: [
Icon(Icons.verified_outlined, size: 18, color: authorApprovedColor),
SizedBox(width: 6),
const CustomTextLabel(text: 'authorLbl', textStyle: TextStyle(color: authorApprovedColor, fontWeight: FontWeight.w600)),
],
),
),
);
}
if (authorStatus == AuthorStatus.pending || state is AuthorRequestSent) {
return IntrinsicWidth(
child: Container(
margin: EdgeInsets.symmetric(vertical: 10),
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(color: backgroundColor, borderRadius: BorderRadius.circular(8), border: Border.all(color: authorReviewColor)),
child: Row(
mainAxisSize: MainAxisSize.min,
children: const [
Icon(Icons.info_outline, size: 18, color: authorReviewColor),
SizedBox(width: 6),
CustomTextLabel(text: 'authorReviewPendingLbl', textStyle: TextStyle(color: authorReviewColor, fontWeight: FontWeight.w500)),
],
),
),
);
}
if (authorStatus == AuthorStatus.rejected || state is AuthorInitial) {
return InkWell(
onTap: () {
//redirect to update profile screen
Navigator.of(context).pushNamed(Routes.editUserProfile, arguments: {'from': 'becomeAuthor'});
},
child: Container(
margin: EdgeInsets.symmetric(vertical: 10),
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(color: backgroundColor, borderRadius: BorderRadius.circular(5), border: Border.all(color: authorRequestColor)),
child: const CustomTextLabel(text: 'becomeAuthorLbl', textStyle: TextStyle(color: authorRequestColor, fontWeight: FontWeight.w500))),
);
}
return const SizedBox.shrink();
}
Widget setGuestText() {
return BlocBuilder<LanguageJsonCubit, LanguageJsonState>(
builder: (context, langState) {
return RichText(
text: TextSpan(
text: UiUtils.getTranslatedLabel(context, 'plzLbl'),
style: Theme.of(context).textTheme.titleSmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer, overflow: TextOverflow.ellipsis),
children: <TextSpan>[
TextSpan(
text: " ${UiUtils.getTranslatedLabel(context, 'loginBtn')} ",
style: Theme.of(context).textTheme.titleMedium?.copyWith(color: Theme.of(context).primaryColor, fontWeight: FontWeight.w600, overflow: TextOverflow.ellipsis),
recognizer: TapGestureRecognizer()
..onTap = () {
Future.delayed(const Duration(milliseconds: 500), () {
setState(() {
Navigator.of(context).pushReplacementNamed(Routes.login);
});
});
}),
TextSpan(
text: "${UiUtils.getTranslatedLabel(context, 'firstAccLbl')} ", style: Theme.of(context).textTheme.titleSmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer)),
TextSpan(
text: UiUtils.getTranslatedLabel(context, 'allFunLbl'),
style: Theme.of(context).textTheme.titleSmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer, overflow: TextOverflow.ellipsis))
],
),
);
},
);
}
Widget setBody() {
return Padding(
padding: const EdgeInsetsDirectional.only(start: 15.0, end: 15.0, top: 15.0),
child: Container(
padding: const EdgeInsetsDirectional.only(start: 20.0, end: 20.0),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(15.0), color: Theme.of(context).colorScheme.surface),
child: ScrollConfiguration(
behavior: GlobalScrollBehavior(),
child: BlocBuilder<AuthCubit, AuthState>(
builder: (context, state) {
return ListView(
padding: const EdgeInsetsDirectional.only(top: 10.0),
shrinkWrap: true,
physics: const BouncingScrollPhysics(),
children: <Widget>[
if (context.read<AuthCubit>().getUserId() != "0") setDrawerItem('editProfile', Icons.edit_outlined, false, true, false, 13),
setDrawerItem('darkModeLbl', Icons.swap_horizontal_circle, true, false, true, 0),
setDrawerItem('notificationLbl', Icons.notifications_rounded, true, false, true, 1),
setDrawerItem('changeLang', Icons.g_translate_rounded, false, true, false, 2),
Divider(thickness: 2),
if (context.read<AuthCubit>().getUserId() != "0") setDrawerItem('bookmarkLbl', Icons.bookmarks_rounded, false, true, false, 3),
// if (context.read<AuthCubit>().getUserId() != "0") setDrawerItem('notificationLbl', Icons.notifications, false, true, false, 4),
if (context.read<AuthCubit>().getUserId() != "0" && isAuthor)
// context.read<AuthCubit>().getRole() != "0")
setDrawerItem('createNewsLbl', Icons.add_box_rounded, false, true, false, 5),
if (context.read<AuthCubit>().getUserId() != "0" && isAuthor)
// context.read<AuthCubit>().getRole() != "0")
setDrawerItem('manageNewsLbl', Icons.edit_document, false, true, false, 6),
if (context.read<AuthCubit>().getUserId() != "0") setDrawerItem('managePreferences', Icons.thumbs_up_down_rounded, false, true, false, 7),
if (context.read<AuthCubit>().getUserId() != "0") Divider(thickness: 2),
pagesBuild(),
setDrawerItem('rateUs', Icons.stars_sharp, false, true, false, 9),
setDrawerItem('shareApp', Icons.share_rounded, false, true, false, 10),
if (context.read<AuthCubit>().getUserId() != "0") setDrawerItem('deleteAcc', Icons.delete_forever_rounded, false, true, false, 12, isDeleteAcc: true),
],
);
},
),
)),
);
}
@override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: CustomAppBar(
height: 45,
isBackBtn: false,
label: 'myProfile',
isConvertText: true,
actionWidget: [if (context.read<AuthCubit>().getUserId() != "0") logoutButton()],
),
body: Stack(
children: [
SingleChildScrollView(
padding: const EdgeInsetsDirectional.only(top: 15.0, bottom: 10.0),
child: Column(mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: <Widget>[setHeader(), setBody()])),
],
)));
}
Widget logoutButton() {
return GestureDetector(
onTap: () => logOutDialog(),
child: Container(
margin: EdgeInsetsDirectional.only(end: 15, top: 12, bottom: 12),
height: 30,
width: 30,
padding: EdgeInsets.all(3),
decoration: BoxDecoration(shape: BoxShape.rectangle, borderRadius: BorderRadius.circular(7), color: Colors.transparent, border: Border.all(width: 2, color: borderColor.withOpacity(0.3))),
child: Icon(Icons.power_settings_new_rounded, color: UiUtils.getColorScheme(context).primaryContainer, size: 20)),
);
}
}

View File

@@ -0,0 +1,81 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:news/ui/screens/auth/Widgets/svgPictureWidget.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/utils/uiUtils.dart';
class CustomAlertDialog extends StatelessWidget {
final BuildContext context;
final String yesButtonText;
final String yesButtonTextPostfix;
final String noButtonText;
final String imageName;
final Widget titleWidget;
final String messageText;
final Function() onYESButtonPressed;
final bool isForceAppUpdate;
const CustomAlertDialog(
{super.key,
required this.context,
required this.yesButtonText,
required this.yesButtonTextPostfix,
required this.noButtonText,
required this.imageName,
required this.titleWidget,
required this.messageText,
required this.onYESButtonPressed,
required this.isForceAppUpdate});
@override
Widget build(BuildContext context) {
return AlertDialog(
contentPadding: const EdgeInsets.all(20),
backgroundColor: UiUtils.getColorScheme(context).surface,
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(12.0))),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SvgPictureWidget(assetName: imageName),
const SizedBox(height: 15),
titleWidget,
const SizedBox(height: 5),
CustomTextLabel(text: messageText, textAlign: TextAlign.center, textStyle: Theme.of(this.context).textTheme.titleSmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer)),
],
),
actionsAlignment: MainAxisAlignment.spaceAround,
actionsOverflowButtonSpacing: 15,
actions: <Widget>[
MaterialButton(
minWidth: MediaQuery.of(context).size.width / 3.5,
elevation: 0.0,
highlightColor: Colors.transparent,
color: Colors.transparent,
splashColor: Colors.transparent,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4), side: BorderSide(color: UiUtils.getColorScheme(context).primaryContainer)),
onPressed: () => (isForceAppUpdate) ? exit(0) : Navigator.of(context).pop(false),
child: CustomTextLabel(
text: noButtonText, textStyle: Theme.of(this.context).textTheme.titleSmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer, fontWeight: FontWeight.w500)),
),
MaterialButton(
elevation: 0.0,
color: UiUtils.getColorScheme(context).primaryContainer,
splashColor: Colors.transparent,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)),
onPressed: onYESButtonPressed,
child: RichText(
text: TextSpan(
text: UiUtils.getTranslatedLabel(context, yesButtonText),
style: Theme.of(this.context).textTheme.titleSmall?.copyWith(color: UiUtils.getColorScheme(context).surface, fontWeight: FontWeight.w500),
children: [
const TextSpan(text: " , "),
TextSpan(
text: UiUtils.getTranslatedLabel(context, yesButtonTextPostfix),
style: Theme.of(this.context).textTheme.titleSmall?.copyWith(color: UiUtils.getColorScheme(context).surface, fontWeight: FontWeight.w500))
]),
)),
],
);
}
}

View File

@@ -0,0 +1,401 @@
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:image_picker/image_picker.dart';
import 'package:news/app/routes.dart';
import 'package:news/cubits/Auth/authCubit.dart';
import 'package:news/cubits/Auth/updateUserCubit.dart';
import 'package:news/cubits/Author/authorCubit.dart';
import 'package:news/cubits/languageCubit.dart';
import 'package:news/data/repositories/Auth/authLocalDataSource.dart';
import 'package:news/ui/styles/colors.dart';
import 'package:news/ui/widgets/SnackBarWidget.dart';
import 'package:news/ui/widgets/customAppBar.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/utils/uiUtils.dart';
import 'package:news/utils/validators.dart';
class UserProfileScreen extends StatefulWidget {
final String from;
const UserProfileScreen({super.key, required this.from});
@override
State createState() => UserProfileScreenState();
static Route route(RouteSettings routeSettings) {
Map arguments = routeSettings.arguments as Map;
return CupertinoPageRoute(builder: (_) => UserProfileScreen(from: arguments['from'] as String));
}
}
class UserProfileScreenState extends State<UserProfileScreen> {
final _formKey = GlobalKey<FormState>();
AuthLocalDataSource authLocalDataSource = AuthLocalDataSource();
dynamic size;
FocusNode nameFocus = FocusNode();
FocusNode mobNoFocus = FocusNode();
FocusNode emailFocus = FocusNode();
FocusNode crntFocus = FocusNode();
FocusNode authorBioFocus = FocusNode();
FocusNode telegramLinkFocus = FocusNode();
FocusNode facebookLinkFocus = FocusNode();
FocusNode linkedInLinkFocus = FocusNode();
FocusNode whatsappLinkFocus = FocusNode();
TextEditingController? nameC = TextEditingController(), monoC = TextEditingController(), emailC = TextEditingController();
TextEditingController? authorBioC = TextEditingController();
TextEditingController? telegramLinkC = TextEditingController();
TextEditingController? facebookLinkC = TextEditingController();
TextEditingController? whatsappLinkC = TextEditingController();
TextEditingController? linkedInLinkC = TextEditingController();
String? profile, mobile;
bool isEditMono = false, isEditEmail = false, isSaving = false, isThisUserAnAuthor = false;
String? updateValue;
File? image;
@override
void initState() {
isThisUserAnAuthor = context.read<AuthCubit>().isAuthor();
setControllers();
if (widget.from == "login") getLanguageList();
super.initState();
}
setControllers() {
nameC = TextEditingController(text: authLocalDataSource.getName());
emailC = TextEditingController(text: authLocalDataSource.getEmail());
monoC = TextEditingController(text: authLocalDataSource.getMobile());
profile = context.read<AuthCubit>().getProfile();
if (isThisUserAnAuthor) {
authorBioC = TextEditingController(text: (authLocalDataSource.getAuthorBio().isEmpty) ? context.read<AuthCubit>().getAuthorBio() : authLocalDataSource.getAuthorBio());
whatsappLinkC =
TextEditingController(text: (authLocalDataSource.getAuthorWhatsappLink()!.isEmpty) ? context.read<AuthCubit>().getAuthorWhatsappLink() : authLocalDataSource.getAuthorWhatsappLink());
telegramLinkC =
TextEditingController(text: (authLocalDataSource.getAuthorTelegramLink()!.isEmpty) ? context.read<AuthCubit>().getAuthorTelegramLink() : authLocalDataSource.getAuthorTelegramLink());
linkedInLinkC =
TextEditingController(text: (authLocalDataSource.getAuthorLinkedInLink()!.isEmpty) ? context.read<AuthCubit>().getAuthorLinkedInLink() : authLocalDataSource.getAuthorLinkedInLink());
facebookLinkC =
TextEditingController(text: (authLocalDataSource.getAuthorFacebookLink()!.isEmpty) ? context.read<AuthCubit>().getAuthorFacebookLink() : authLocalDataSource.getAuthorFacebookLink());
}
}
getLanguageList() {
Future.delayed(Duration.zero, () {
context.read<LanguageCubit>().getLanguage();
});
}
@override
Widget build(BuildContext context) {
size = MediaQuery.of(context).size;
return Scaffold(appBar: CustomAppBar(height: 45, isBackBtn: (widget.from == "login") ? false : true, label: 'editProfile', isConvertText: true), body: buildProfileFields());
}
onBackPress(bool isTrue) {
(widget.from == "login") ? Navigator.of(context).popUntil((route) => route.isFirst) : Navigator.pop(context);
}
setPendingStatusControllerValues() {
authorBioC?.clear();
whatsappLinkC?.clear();
telegramLinkC?.clear();
linkedInLinkC?.clear();
facebookLinkC?.clear();
}
buildProfileFields() {
return PopScope(
canPop: (widget.from != "login") ? true : false,
onPopInvoked: (bool isTrue) => onBackPress,
child: BlocConsumer<UpdateUserCubit, UpdateUserState>(
listener: (context, state) {
//show snackbar incase of success & failure both
if (state is UpdateUserFetchSuccess && state.updatedUser != null) {
showSnackBar(UiUtils.getTranslatedLabel(context, 'profileUpdateMsg'), context);
isSaving = false;
setState(() {});
if (widget.from == "becomeAuthor") {
//call author request API here
context.read<AuthorCubit>().requestToBecomeAuthor();
setPendingStatusControllerValues();
}
if (widget.from == "login") {
if (context.read<LanguageCubit>().langList().length > 1) {
Navigator.of(context).pushNamedAndRemoveUntil(Routes.languageList, (route) => false, arguments: {"from": "firstLogin"});
} else {
Navigator.of(context).pushNamedAndRemoveUntil(Routes.managePref, (route) => false, arguments: {"from": 2});
}
} else {
Navigator.pop(context);
}
}
if (state is UpdateUserFetchFailure) {
isSaving = false;
showSnackBar(state.errorMessage, context);
}
},
builder: (context, state) {
return SingleChildScrollView(
child: Form(
key: _formKey,
child: Column(children: [
profileWidget(),
setTextField(
validatorMethod: (value) => Validators.nameValidation(value!, context),
focusNode: nameFocus,
nextFocus: (context.read<AuthCubit>().getType() != loginMbl) ? mobNoFocus : emailFocus,
textInputAction: TextInputAction.next,
keyboardType: TextInputType.name,
controller: nameC,
hintlbl: UiUtils.getTranslatedLabel(context, 'nameLbl')),
SizedBox(height: size.height * 0.03),
setMobileNumber(),
SizedBox(height: size.height * 0.03),
setTextField(
validatorMethod: (value) => value!.trim().isEmpty ? null : Validators.emailValidation(value, context),
focusNode: emailFocus,
nextFocus: isThisUserAnAuthor ? authorBioFocus : null,
textInputAction: isThisUserAnAuthor ? TextInputAction.next : TextInputAction.done,
keyboardType: TextInputType.emailAddress,
controller: emailC,
isenable: (context.read<AuthCubit>().getType() == loginMbl) ? true : false,
hintlbl: UiUtils.getTranslatedLabel(context, 'emailLbl')),
SizedBox(height: size.height * 0.05),
setAuthorBio(),
SizedBox(height: size.height * 0.05),
Container(
alignment: AlignmentGeometry.centerLeft,
padding: EdgeInsetsDirectional.only(start: 20),
child: CustomTextLabel(
text: 'socialMediaLinksLbl',
textStyle: Theme.of(this.context).textTheme.titleMedium?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer, fontWeight: FontWeight.w700))),
SizedBox(height: size.height * 0.03),
setSocialMediaLink(linkController: telegramLinkC, currFocus: telegramLinkFocus, nextFocus: whatsappLinkFocus, typeForHintText: 'Telegram'),
SizedBox(height: size.height * 0.03),
setSocialMediaLink(linkController: whatsappLinkC, currFocus: whatsappLinkFocus, nextFocus: facebookLinkFocus, typeForHintText: 'Whatsapp'),
SizedBox(height: size.height * 0.03),
setSocialMediaLink(linkController: facebookLinkC, currFocus: facebookLinkFocus, nextFocus: linkedInLinkFocus, typeForHintText: 'Facebook'),
SizedBox(height: size.height * 0.03),
setSocialMediaLink(linkController: linkedInLinkC, currFocus: linkedInLinkFocus, nextFocus: null, typeForHintText: 'LinkedIn'),
SizedBox(height: size.height * 0.03),
submitBtn(context)
])));
},
),
);
}
Widget setAuthorBio() {
return Container(
width: double.maxFinite,
child: setTextField(
hintlbl: UiUtils.getTranslatedLabel(context, 'addYourBioHintLbl'),
controller: authorBioC,
focusNode: authorBioFocus,
nextFocus: telegramLinkFocus,
textInputAction: TextInputAction.newline,
maxLines: 2));
}
Widget setSocialMediaLink({required TextEditingController? linkController, required FocusNode currFocus, required FocusNode? nextFocus, required String typeForHintText}) {
return setTextField(
hintlbl: getAddLinkHint(context, typeForHintText),
controller: linkController,
focusNode: currFocus,
nextFocus: nextFocus,
textInputAction: TextInputAction.next,
keyboardType: TextInputType.url);
}
String getAddLinkHint(BuildContext context, String type) {
final template = UiUtils.getTranslatedLabel(context, 'addLinkHereHintLbl');
return template.replaceAll("{type}", type);
}
Widget setMobileNumber() {
return SizedBox(
height: 60,
child: setTextField(
validatorMethod: (value) => value!.trim().isEmpty ? null : Validators.mobValidation(value, context),
keyboardType: TextInputType.phone,
hintlbl: UiUtils.getTranslatedLabel(context, 'mobileLbl'),
textInputAction: TextInputAction.next,
controller: monoC,
focusNode: mobNoFocus,
nextFocus: isThisUserAnAuthor ? authorBioFocus : emailFocus,
isenable: (context.read<AuthCubit>().getType() != loginMbl) ? true : false));
}
//set image camera
getFromCamera() async {
try {
XFile? pickedFile = await ImagePicker().pickImage(source: ImageSource.camera);
if (pickedFile != null) {
Navigator.of(context).pop(); //pop dialog
setState(() {
image = File(pickedFile.path);
profile = image!.path;
});
}
} catch (e) {}
}
// set image gallery
_getFromGallery() async {
XFile? pickedFile = await ImagePicker().pickImage(source: ImageSource.gallery, maxWidth: 1800, maxHeight: 1800);
if (pickedFile != null) {
setState(() {
image = File(pickedFile.path);
profile = image!.path;
Navigator.of(context).pop(); //pop dialog
});
}
}
profileWidget() {
return Container(padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 15), child: Center(child: profileImgWidget()));
}
Widget profilePictureWidget() {
final fallbackIcon = Container(
padding: EdgeInsets.all(20),
decoration: BoxDecoration(shape: BoxShape.circle, border: BoxBorder.all(color: UiUtils.getColorScheme(context).primaryContainer)),
child: Icon(Icons.person, color: UiUtils.getColorScheme(context).primaryContainer));
if (image != null) {
return Image.file(image!, fit: BoxFit.fill, width: 75, height: 75);
}
if (profile == null || profile!.trim().isEmpty) {
return fallbackIcon;
}
return Image.network(profile!,
fit: BoxFit.fill, width: 85, errorBuilder: (_, __, ___) => fallbackIcon, loadingBuilder: (_, child, loadingProgress) => loadingProgress == null ? child : fallbackIcon);
}
profileImgWidget() {
return GestureDetector(
onTap: () => UiUtils().showUploadImageBottomsheet(context: context, onCamera: getFromCamera, onGallery: _getFromGallery),
child: Stack(
alignment: Alignment.center,
children: [
CircleAvatar(
radius: 46,
backgroundColor: Colors.transparent,
child: CircleAvatar(radius: 44, backgroundColor: Colors.transparent, child: ClipOval(clipBehavior: Clip.antiAliasWithSaveLayer, child: profilePictureWidget()))),
Positioned(
right: 0,
bottom: 0,
child: Container(
padding: const EdgeInsets.all(5),
decoration:
BoxDecoration(color: UiUtils.getColorScheme(context).primaryContainer, shape: BoxShape.circle, border: Border.all(width: 2, color: UiUtils.getColorScheme(context).secondary)),
child: Icon(Icons.camera_alt_outlined, color: Theme.of(context).colorScheme.secondary, size: 20)))
],
),
);
}
submitBtn(BuildContext context) {
return Container(
alignment: Alignment.center,
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: ElevatedButton(
style: ElevatedButton.styleFrom(backgroundColor: Theme.of(context).primaryColor, shadowColor: Colors.transparent, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5.0))),
child: Container(
height: 45.0,
width: MediaQuery.of(context).size.width * 0.8,
alignment: Alignment.center,
child: (isSaving)
? UiUtils.showCircularProgress(true, UiUtils.getColorScheme(context).primaryContainer)
: CustomTextLabel(text: 'saveLbl', textStyle: Theme.of(this.context).textTheme.titleMedium?.copyWith(color: secondaryColor, fontWeight: FontWeight.w500, letterSpacing: 0.6)),
),
onPressed: () async {
validateData();
}));
}
validateData() async {
if (_formKey.currentState!.validate()) {
profileupdateprocess();
} else {}
}
profileupdateprocess() async {
isSaving = true;
//in case of Clearing Existing mobile number -> set mobile to blank, so it can be passed to replace existing value of mobile number as NULL mobile number won't be passed to APi
mobile = monoC!.text.trim();
if (mobile == null && context.read<AuthCubit>().getType() != loginMbl && context.read<AuthCubit>().getMobile().isNotEmpty) {
mobile = " ";
}
try {
context.read<UpdateUserCubit>().setUpdateUser(
context: context,
name: nameC!.text,
email: emailC!.text,
mobile: mobile,
filePath: (image != null) ? image!.path : "",
authorBio: authorBioC?.text,
whatsappLink: whatsappLinkC?.text,
facebookLink: facebookLinkC?.text,
telegramLink: telegramLinkC?.text,
linkedInLink: linkedInLinkC?.text);
} catch (e) {
showSnackBar(e.toString(), context);
}
}
setTextField(
{String? Function(String?)? validatorMethod,
FocusNode? focusNode,
FocusNode? nextFocus,
TextInputAction? textInputAction,
TextInputType? keyboardType,
TextEditingController? controller,
String? hintlbl,
bool isenable = true,
int? maxLines}) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: TextFormField(
enabled: isenable,
decoration: InputDecoration(
hintText: hintlbl,
hintStyle: Theme.of(context).textTheme.titleMedium?.copyWith(color: UiUtils.getColorScheme(context).outline.withOpacity(0.2)),
filled: true,
fillColor: Theme.of(context).colorScheme.surface,
contentPadding: const EdgeInsets.symmetric(horizontal: 17, vertical: 17),
focusedBorder: OutlineInputBorder(borderSide: BorderSide(color: UiUtils.getColorScheme(context).outline.withOpacity(0.7)), borderRadius: BorderRadius.circular(5.0)),
enabledBorder: OutlineInputBorder(borderSide: BorderSide.none, borderRadius: BorderRadius.circular(5.0)),
disabledBorder: OutlineInputBorder(borderSide: BorderSide.none, borderRadius: BorderRadius.circular(5.0)),
),
validator: validatorMethod,
inputFormatters: [(focusNode == mobNoFocus) ? FilteringTextInputFormatter.digitsOnly : FilteringTextInputFormatter.singleLineFormatter],
focusNode: focusNode,
textInputAction: textInputAction,
onEditingComplete: () {
crntFocus = FocusNode();
(nextFocus != null) ? fieldFocusChange(context, focusNode!, nextFocus) : focusNode?.unfocus();
},
onChanged: (String value) {
setState(() => crntFocus = focusNode!);
},
onTapOutside: (val) => FocusScope.of(context).unfocus(),
maxLines: (maxLines ?? 1),
textAlignVertical: TextAlignVertical.center,
style: Theme.of(context).textTheme.titleMedium!.copyWith(color: (isenable) ? UiUtils.getColorScheme(context).primaryContainer : borderColor),
keyboardType: keyboardType,
controller: controller));
}
fieldFocusChange(BuildContext context, FocusNode currentFocus, FocusNode nextFocus) {
currentFocus.unfocus();
FocusScope.of(context).requestFocus(nextFocus);
}
}

View File

@@ -0,0 +1,189 @@
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:news/cubits/getUserDataByIdCubit.dart';
import 'package:news/data/repositories/Settings/settingsLocalDataRepository.dart';
import 'package:news/ui/screens/dashBoard/dashBoardScreen.dart';
import 'package:news/utils/constant.dart';
import 'package:news/utils/strings.dart';
import 'package:path_provider/path_provider.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart';
import 'dart:io';
import 'package:news/cubits/NewsByIdCubit.dart';
import 'package:news/cubits/appLocalizationCubit.dart';
import 'package:news/app/app.dart';
import 'package:news/app/routes.dart';
import 'package:news/utils/uiUtils.dart';
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
FirebaseMessaging messaging = FirebaseMessaging.instance;
SettingsLocalDataRepository settingsRepo = SettingsLocalDataRepository();
backgroundMessage(NotificationResponse notificationResponse) {
//for notification only
}
@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {}
void redirectToNewsDetailsScreen(RemoteMessage message, BuildContext context) async {
var data = message.data;
if (data[TYPE] == "default" || data[TYPE] == "category") {
var payload = data[NEWS_ID];
var lanId = data[LANGUAGE_ID] ?? "14";
if (lanId == context.read<AppLocalizationCubit>().state.id) {
//show only if Current language is Same as Notification Language
if (payload == null) {
Navigator.push(context, MaterialPageRoute(builder: (context) => const MyApp()));
} else {
context.read<NewsByIdCubit>().getNewsById(newsId: payload, langId: context.read<AppLocalizationCubit>().state.id).then((value) {
UiUtils.rootNavigatorKey.currentState!.pushNamed(Routes.newsDetails, arguments: {"model": value[0], "isFromBreak": false, "fromShowMore": false});
}).catchError((e) {});
}
}
}
}
class PushNotificationService {
late BuildContext context;
PushNotificationService({required this.context});
Future initialise() async {
messaging.getToken();
const AndroidInitializationSettings initializationSettingsAndroid = AndroidInitializationSettings('notification_icon');
const DarwinInitializationSettings initializationSettingsIOS = DarwinInitializationSettings(requestAlertPermission: true, requestBadgePermission: false, requestSoundPermission: true);
Future<dynamic> notificationHandler(RemoteMessage message) async {
if (settingsRepo.getNotification()) {
var data = message.data;
var notif = message.notification;
if (data.isNotEmpty) {
var title = (data[TITLE] != null) ? data[TITLE].toString() : appName;
var body = (data[MESSAGE] != null) ? data[MESSAGE].toString() : "";
var image = data[IMAGE];
var payload = data[NEWS_ID];
String lanId = (data[LANGUAGE_ID] != null) ? data[LANGUAGE_ID].toString() : "1"; //1 = ENGLISH bydefault
(payload == null) ? payload = "" : payload = payload;
if ((data[TYPE] == "default" || data[TYPE] == "category" || data[TYPE] == "comment" || data[TYPE] == "comment_like" || data[TYPE] == "newlyadded")) {
if (lanId == context.read<AppLocalizationCubit>().state.id) {
//show only if Current language is Same as Notification Language
(image != null && image != "") ? generateImageNotification(title, body, image, payload) : generateSimpleNotification(title, body, payload);
}
} else if ((data[TYPE] == "author_approved") || (data[TYPE] == "author_rejected")) {
generateSimpleNotification(title, body, payload);
Future.delayed(Duration.zero, () {
context.read<GetUserByIdCubit>().getUserById(); // update Author status
});
}
} else if (notif != null) {
//Direct Firebase Notification
RemoteNotification notification = notif;
String title = notif.title.toString();
String msg = notif.body.toString();
String iosImg = (notification.apple != null && notification.apple!.imageUrl != null) ? notification.apple!.imageUrl! : "";
String androidImg = (notification.android != null && notification.android!.imageUrl != null) ? notification.android!.imageUrl! : "";
if (title != '' && msg != '') {
if (Platform.isIOS) {
(iosImg != "") ? generateImageNotification(title, msg, notification.apple!.imageUrl!, '') : generateSimpleNotification(title, msg, '');
}
if (Platform.isAndroid) {
(androidImg != "") ? generateImageNotification(title, msg, notification.android!.imageUrl!, '') : generateSimpleNotification(title, msg, '');
}
}
}
}
}
//for android 13 - notification permission
flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>()?.requestNotificationsPermission();
InitializationSettings initializationSettings = const InitializationSettings(android: initializationSettingsAndroid, iOS: initializationSettingsIOS);
await flutterLocalNotificationsPlugin.initialize(
initializationSettings,
onDidReceiveNotificationResponse: (NotificationResponse notificationResponse) {
switch (notificationResponse.notificationResponseType) {
case NotificationResponseType.selectedNotification:
selectNotificationPayload(notificationResponse.payload!);
break;
case NotificationResponseType.selectedNotificationAction:
break;
}
},
onDidReceiveBackgroundNotificationResponse: backgroundMessage,
);
messaging.getInitialMessage().then((RemoteMessage? message) async {
if (message != null && message.data.isNotEmpty) {
isNotificationReceivedInbg = true;
notificationNewsId = message.data[NEWS_ID];
saleNotification = message.data['sale'] ?? '';
}
});
_startForegroundService();
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
FirebaseMessaging.onMessage.listen((RemoteMessage message) async {
await notificationHandler(message);
});
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
redirectToNewsDetailsScreen(message, context);
});
}
Future<void> _startForegroundService() async {
const AndroidNotificationDetails androidNotificationDetails =
AndroidNotificationDetails('YOUR_PACKAGE_NAME_HERE', 'news', channelDescription: 'your channel description', importance: Importance.max, priority: Priority.high, ticker: 'ticker');
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>()
?.startForegroundService(1, 'plain title', 'plain body', notificationDetails: androidNotificationDetails, payload: '');
}
selectNotificationPayload(String? payload) async {
if (payload != null && payload.isNotEmpty && payload != "0") {
context.read<NewsByIdCubit>().getNewsById(newsId: payload, langId: context.read<AppLocalizationCubit>().state.id).then((value) {
UiUtils.rootNavigatorKey.currentState!.pushNamed(Routes.newsDetails, arguments: {"model": value[0], "isFromBreak": false, "fromShowMore": false});
});
}
}
}
Future<String> _downloadAndSaveImage(String url, String fileName) async {
if (url.isNotEmpty && url != "null") {
final Directory directory = await getApplicationDocumentsDirectory();
final String filePath = '${directory.path}/$fileName';
final Response response = await get(Uri.parse(url));
final File file = File(filePath);
await file.writeAsBytes(response.bodyBytes);
return filePath;
} else {
return "";
}
}
Future<void> generateImageNotification(String title, String msg, String image, String type) async {
var largeIconPath = await _downloadAndSaveImage(image, Platform.isAndroid ? 'largeIcon' : 'largeIcon.png');
var bigPicturePath = await _downloadAndSaveImage(image, Platform.isAndroid ? 'bigPicture' : 'bigPicture.png');
var bigPictureStyleInformation =
BigPictureStyleInformation(FilePathAndroidBitmap(bigPicturePath), hideExpandedLargeIcon: true, contentTitle: title, htmlFormatContentTitle: true, summaryText: msg, htmlFormatSummaryText: true);
var androidPlatformChannelSpecifics = AndroidNotificationDetails('big text channel id', 'big text channel name',
channelDescription: 'big text channel description', largeIcon: FilePathAndroidBitmap(largeIconPath), styleInformation: bigPictureStyleInformation);
final DarwinNotificationDetails darwinNotificationDetails = DarwinNotificationDetails(
categoryIdentifier: "", presentAlert: true, presentSound: true, attachments: <DarwinNotificationAttachment>[DarwinNotificationAttachment(bigPicturePath, hideThumbnail: false)]);
var platformChannelSpecifics = NotificationDetails(android: androidPlatformChannelSpecifics, iOS: darwinNotificationDetails);
if (msg != "") await flutterLocalNotificationsPlugin.show(1, title, msg, platformChannelSpecifics, payload: type);
}
Future<void> generateSimpleNotification(String title, String msg, String type) async {
var androidPlatformChannelSpecifics = const AndroidNotificationDetails(
'YOUR_PACKAGE_NAME_HERE', //your package name
'news',
importance: Importance.max,
priority: Priority.high,
ticker: 'ticker');
DarwinNotificationDetails darwinNotificationDetails = const DarwinNotificationDetails(categoryIdentifier: "", presentAlert: true, presentSound: true);
var platformChannelSpecifics = NotificationDetails(android: androidPlatformChannelSpecifics, iOS: darwinNotificationDetails);
if (msg.isNotEmpty) await flutterLocalNotificationsPlugin.show(1, title, msg, platformChannelSpecifics, payload: type);
}

View File

@@ -0,0 +1,124 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:news/ui/styles/colors.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/ui/widgets/errorContainerWidget.dart';
import 'package:news/ui/widgets/networkImage.dart';
import 'package:news/utils/ErrorMessageKeys.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:xml2json/xml2json.dart';
import 'dart:convert';
class RSSFeedDetailsScreen extends StatefulWidget {
String feedUrl;
RSSFeedDetailsScreen({Key? key, required this.feedUrl}) : super(key: key);
@override
_RSSFeedDetailsScreenState createState() => _RSSFeedDetailsScreenState();
static Route route(RouteSettings routeSettings) {
final arguments = routeSettings.arguments as Map<String, dynamic>;
return CupertinoPageRoute(builder: (_) => RSSFeedDetailsScreen(feedUrl: arguments['feedUrl']));
}
}
class _RSSFeedDetailsScreenState extends State<RSSFeedDetailsScreen> {
List<dynamic> _feedItems = [];
bool isFeedLoaded = false;
@override
void initState() {
super.initState();
fetchFeed();
}
Widget buildFeedItem(Map<String, dynamic> item) {
return GestureDetector(
onTap: () => _launchURL(item['link'] ?? ''),
child: Card(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
margin: EdgeInsets.all(8.0),
child: Padding(
padding: EdgeInsets.all(16.0),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
((item.containsKey('image') && item['image'] != null) || (item.containsKey('url') && item['image']['url'] != null))
? Padding(
padding: const EdgeInsetsDirectional.only(end: 10.0),
child: CustomNetworkImage(
networkImageUrl: (item['image'] != null) ? item['image'] : item['image']['url'],
height: MediaQuery.of(context).size.height * 0.13,
width: MediaQuery.of(context).size.width * 0.23,
fit: BoxFit.cover),
)
: SizedBox.shrink(),
Expanded(
flex: 1,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CustomTextLabel(text: item['title'] ?? 'No Title', maxLines: 2, textStyle: TextStyle(fontWeight: FontWeight.bold, fontSize: 16)),
SizedBox(height: 8),
CustomTextLabel(text: item['description'] ?? 'No Description', maxLines: 3, textStyle: TextStyle(color: borderColor)),
SizedBox(height: 8),
CustomTextLabel(text: item['pubDate'] ?? 'Unknown Date', textStyle: TextStyle(fontSize: 12, color: Colors.grey[500]))
],
),
),
],
),
),
),
);
}
Future<void> _launchURL(String url) async {
if (await canLaunch(url)) {
await launch(url);
} else {
throw 'Could not launch $url';
}
}
Future<void> fetchFeed() async {
final url = widget.feedUrl;
try {
final response = await http.get(Uri.parse(url));
if (response.statusCode == 200) {
final xml2json = Xml2Json();
xml2json.parse(response.body);
final jsonString = xml2json.toParker();
final jsonData = jsonDecode(jsonString);
_feedItems = jsonData['rss']['channel']['item'] ?? [];
} else {
throw Exception('Failed to load feed');
}
} catch (e) {}
isFeedLoaded = true;
setState(() {});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: RefreshIndicator(
onRefresh: fetchFeed,
child: !isFeedLoaded
? Center(child: CircularProgressIndicator())
: _feedItems.isEmpty
? ErrorContainerWidget(errorMsg: ErrorMessageKeys.noDataMessage, onRetry: fetchFeed)
: ListView.builder(
itemCount: _feedItems.length,
itemBuilder: (context, index) {
return buildFeedItem(_feedItems[index]);
},
)),
);
}
}

View File

@@ -0,0 +1,335 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:news/app/routes.dart';
import 'package:news/cubits/appLocalizationCubit.dart';
import 'package:news/cubits/categoryCubit.dart';
import 'package:news/cubits/rssFeedCubit.dart';
import 'package:news/data/models/CategoryModel.dart';
import 'package:news/data/models/RSSFeedModel.dart';
import 'package:news/ui/screens/AddEditNews/Widgets/customBottomsheet.dart';
import 'package:news/ui/screens/auth/Widgets/svgPictureWidget.dart';
import 'package:news/ui/styles/colors.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/ui/widgets/errorContainerWidget.dart';
import 'package:news/utils/ErrorMessageKeys.dart';
import 'package:news/utils/internetConnectivity.dart';
import 'package:news/utils/uiUtils.dart';
class RSSFeedScreen extends StatefulWidget {
@override
RSSFeedScreenState createState() => RSSFeedScreenState();
}
class RSSFeedScreenState extends State<RSSFeedScreen> {
final Set<String> selectedTopics = {};
final Random random = Random();
late final ScrollController _controller = ScrollController()..addListener(hasMoreRssFeedScrollListener);
String? catSel = "", subCatSel = "", subCatSelId, catSelId;
int? catIndex, subCatIndex;
bool isFilter = true, showSubcat = false;
@override
void initState() {
super.initState();
getRSSFeed();
}
setAppBar() {
return PreferredSize(
preferredSize: const Size(double.infinity, 52),
child: UiUtils.applyBoxShadow(
context: context,
child: Padding(
padding: EdgeInsetsDirectional.only(top: MediaQuery.of(context).padding.top + 10.0, start: 25, end: 25, bottom: 15),
child: Row(children: [
CustomTextLabel(
text: 'rssFeed',
textStyle: Theme.of(context).textTheme.titleLarge?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer, fontWeight: FontWeight.w600, letterSpacing: 0.5),
),
Spacer(),
GestureDetector(child: Icon(Icons.filter_list_rounded), onTap: () => setCategorySubcategoryFilter(context))
]),
)));
}
setCategorySubcategoryFilter(BuildContext context) {
showModalBottomSheet<dynamic>(
context: context,
elevation: 5.0,
isScrollControlled: true,
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.only(topLeft: Radius.circular(50), topRight: Radius.circular(50))),
builder: (BuildContext context) {
return StatefulBuilder(builder: (context, setBottomSheetState) {
return Container(
height: MediaQuery.of(context).size.height * 0.35,
padding: const EdgeInsets.only(top: 10.0, bottom: 5, left: 10, right: 10),
child: Column(children: [
titleWithDropdownButton(),
const SizedBox(height: 5),
(isFilter) ? setCategoryFilter(setBottomSheetState) : SizedBox.shrink(),
(isFilter) ? setSubCategoryFilter(setBottomSheetState) : SizedBox.shrink(),
]));
});
});
}
void getRSSFeed() async {
if (await InternetConnectivity.isNetworkAvailable()) {
Future.delayed(Duration.zero, () {
context.read<RSSFeedCubit>().getRSSFeed(langId: context.read<AppLocalizationCubit>().state.id, categoryId: catSelId, subCategoryId: subCatSelId);
});
}
}
void hasMoreRssFeedScrollListener() {
if (_controller.position.maxScrollExtent == _controller.offset) {
if (context.read<RSSFeedCubit>().hasMoreRSSFeed()) {
context.read<RSSFeedCubit>().getMoreRSSFeed(langId: context.read<AppLocalizationCubit>().state.id);
}
}
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
Widget titleWithDropdownButton() {
return InkWell(
onTap: () {
isFilter = !isFilter;
setState(() {});
},
child: Container(
width: double.maxFinite,
alignment: Alignment.topLeft,
padding: const EdgeInsets.symmetric(horizontal: 25, vertical: 15),
child: Row(
children: [
CustomTextLabel(text: 'FilterBy', textStyle: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.bold, color: UiUtils.getColorScheme(context).primaryContainer)),
Spacer(),
if (catSelId != null || subCatSelId != null) (isFilter) ? clearFilterButton() : SizedBox.shrink(),
],
),
));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: setAppBar(),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: BlocBuilder<RSSFeedCubit, RSSFeedState>(
builder: (context, state) {
if (state is RSSFeedFetchSuccess) {
return RefreshIndicator(
onRefresh: () async {
getRSSFeed();
},
child: ListView.builder(
padding: EdgeInsets.only(top: 15),
itemCount: state.RSSFeed.length,
controller: _controller,
itemBuilder: (context, index) {
return Container(
alignment: Alignment.center,
margin: EdgeInsets.symmetric(vertical: 5),
child: buildRSSFeedItem(state.RSSFeed[index], index, state.RSSFeed.length, state.hasMoreFetchError, state.hasMore));
}),
);
}
if (state is RSSFeedFetchFailure) {
return ErrorContainerWidget(
errorMsg: (state.errorMessage.contains(ErrorMessageKeys.noInternet)) ? UiUtils.getTranslatedLabel(context, 'internetmsg') : state.errorMessage,
onRetry: getRSSFeed); //Add category & subcategory id if selected
}
if (state is RSSFeedFetchInProgress || state is RSSFeedInitial) {
return Center(child: UiUtils.showCircularProgress(true, Theme.of(context).primaryColor));
}
return SizedBox.shrink();
},
),
),
);
}
Widget buildRSSFeedItem(RSSFeedModel feed, int index, int totalCurrentFeeds, bool hasMoreFeedsFetchError, bool hasMore) {
if (index == totalCurrentFeeds - 1 && index != 0) {
if (hasMore) {
if (hasMoreFeedsFetchError) {
return const SizedBox.shrink();
} else {
return Center(child: Padding(padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 8.0), child: UiUtils.showCircularProgress(true, Theme.of(context).primaryColor)));
}
}
}
return GestureDetector(
onTap: () async {
Navigator.of(context).pushNamed(Routes.rssFeedDetails, arguments: {"feedUrl": feed.feedUrl});
},
child: Container(
height: MediaQuery.of(context).size.height * 0.08,
decoration: BoxDecoration(borderRadius: BorderRadius.all(Radius.circular(10)), border: Border.all(color: borderColor.withAlpha((0.3 * 255).round()))),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(crossAxisAlignment: CrossAxisAlignment.start, children: [
Center(child: SvgPictureWidget(assetName: 'rss_feed', height: 20, width: 10, fit: BoxFit.fill)),
const SizedBox(width: 12),
Expanded(
child: Align(
alignment: Alignment.centerLeft,
child: Text(feed.feedName!,
maxLines: 3,
overflow: TextOverflow.ellipsis,
style: TextStyle(color: UiUtils.getColorScheme(context).primaryContainer, fontWeight: FontWeight.bold, fontSize: 16),
textAlign: TextAlign.start),
),
)
]),
),
),
);
}
Widget setCategoryFilter(void Function(void Function()) setStater) {
return BlocConsumer<CategoryCubit, CategoryState>(listener: (context, state) {
if (catSel != "") catIndex = context.read<CategoryCubit>().getCategoryIndex(categoryName: catSel!);
}, builder: (context, state) {
if (state is CategoryFetchSuccess) {
return Padding(
padding: EdgeInsets.symmetric(vertical: 5),
child: InkWell(
onTap: () => showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (BuildContext context) {
return CustomBottomsheet(
context: context,
titleTxt: 'selCatLbl',
language_id: context.read<AppLocalizationCubit>().state.id,
listLength: context.read<CategoryCubit>().getCatList().length,
listViewChild: (context, index) {
return catListItem(index, context.read<CategoryCubit>().getCatList(), setStater);
});
}),
child: UiUtils.setRowWithContainer(
context: context,
firstChild: CustomTextLabel(
text: (catSel == "" || catSel == null) ? 'catLbl' : catSel!,
textStyle: Theme.of(context)
.textTheme
.titleMedium!
.copyWith(color: catSel == "" ? UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.6) : UiUtils.getColorScheme(context).primaryContainer)),
isContentTypeUpload: false),
),
);
}
return SizedBox.shrink();
});
}
Widget catListItem(int index, List<CategoryModel> catList, void Function(void Function()) setStater) {
return Padding(
padding: EdgeInsets.only(top: 10.0),
child: InkWell(
onTap: () {
setState(() {
subCatSel = "";
subCatSelId = null;
catSel = catList[index].categoryName!;
catIndex = index;
catSelId = catList[index].id!;
showSubcat = true;
});
setStater(() {});
getRSSFeed();
Navigator.pop(context);
},
child: UiUtils.setBottomsheetContainer(entryId: catSelId ?? "0", listItem: catList[index].categoryName!, compareTo: catList[index].id!, context: context),
),
);
}
Widget subCatListItem(int index, List<CategoryModel> catList, void Function(void Function()) setStater) {
return Padding(
padding: EdgeInsets.only(top: 10.0),
child: InkWell(
onTap: () {
setState(() {
subCatSel = catList[catIndex!].subData![index].subCatName!;
subCatSelId = catList[catIndex!].subData![index].id!;
});
setStater(() {});
getRSSFeed();
Navigator.pop(context);
},
child: UiUtils.setBottomsheetContainer(
entryId: subCatSelId ?? "0", listItem: catList[catIndex!].subData![index].subCatName!, compareTo: catList[catIndex!].subData![index].id!, context: context)),
);
}
Widget setSubCategoryFilter(void Function(void Function()) setStater) {
if ((catSel != "" && catSel != null) &&
(catIndex != null) &&
(!catIndex!.isNegative && context.read<CategoryCubit>().getCatList().isNotEmpty) &&
(context.read<CategoryCubit>().getCatList()[catIndex!].subData!.isNotEmpty)) {
return BlocBuilder<CategoryCubit, CategoryState>(
builder: (context, state) {
return Padding(
padding: EdgeInsets.symmetric(vertical: 5),
child: InkWell(
onTap: () => showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (BuildContext context) {
return CustomBottomsheet(
context: context,
titleTxt: 'selSubCatLbl',
language_id: context.read<AppLocalizationCubit>().state.id,
listLength: context.read<CategoryCubit>().getCatList()[catIndex!].subData!.length,
listViewChild: (context, index) => subCatListItem(index, context.read<CategoryCubit>().getCatList(), setStater));
}),
child: UiUtils.setRowWithContainer(
context: context,
firstChild: CustomTextLabel(
text: (subCatSel == "" || subCatSel == null) ? 'subcatLbl' : subCatSel!,
textStyle: Theme.of(context)
.textTheme
.titleMedium!
.copyWith(color: subCatSel == "" ? UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.6) : UiUtils.getColorScheme(context).primaryContainer)),
isContentTypeUpload: false),
),
);
},
);
} else {
return const SizedBox.shrink();
}
}
Widget clearFilterButton() {
return Center(
child: TextButton(
onPressed: () {
setState(() {
catSelId = null;
catSel = null;
subCatSelId = null;
subCatSel = null;
showSubcat = false;
});
getRSSFeed(); //call API without Ids
Navigator.of(context).pop();
},
child: CustomTextLabel(
text: 'clearFilter',
textStyle: TextStyle(decoration: TextDecoration.underline, decorationThickness: 2),
)));
}
}

View File

@@ -0,0 +1,488 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:news/app/routes.dart';
import 'package:news/cubits/appLocalizationCubit.dart';
import 'package:news/cubits/appSystemSettingCubit.dart';
import 'package:news/cubits/categoryCubit.dart';
import 'package:news/cubits/tagCubit.dart';
import 'package:news/data/repositories/Settings/settingsLocalDataRepository.dart';
import 'package:news/data/models/NewsModel.dart';
import 'package:news/data/models/CategoryModel.dart';
import 'package:news/data/models/TagModel.dart';
import 'package:news/ui/screens/auth/Widgets/svgPictureWidget.dart';
import 'package:news/ui/screens/filter/FilterBottomSheet.dart';
import 'package:news/ui/screens/filter/widgets/duration_filter_widget.dart';
import 'package:news/ui/styles/colors.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/ui/widgets/networkImage.dart';
import 'package:news/ui/widgets/SnackBarWidget.dart';
import 'package:news/ui/widgets/customBackBtn.dart';
import 'package:news/utils/constant.dart';
import 'package:news/utils/hiveBoxKeys.dart';
import 'package:news/utils/api.dart';
import 'package:news/utils/strings.dart';
import 'package:news/utils/uiUtils.dart';
import 'package:news/utils/internetConnectivity.dart';
import 'dart:async';
class Search extends StatefulWidget {
const Search({super.key});
@override
SearchState createState() => SearchState();
}
bool buildResult = false; //used in 2 classes here _SearchState & _SuggestionList
class SearchState extends State<Search> with TickerProviderStateMixin {
final TextEditingController _controller = TextEditingController();
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
int pos = 0;
List<NewsModel> searchList = [];
final List<TextEditingController> _controllerList = [];
bool isNetworkAvail = true;
String query = "";
int notificationoffset = 0;
ScrollController? notificationcontroller;
bool notificationisloadmore = true, notificationisgettingdata = false, notificationisnodata = false;
// Filter related variables
bool isFilterApplied = false;
List<CategoryModel> filterSelectedCategories = [];
List<TagModel> filterSelectedTags = [];
DateTime? filterSelectedDate;
DurationFilter? filterDurationFilter;
Timer? _debounce;
List<NewsModel> history = [];
List<String> hisList = [];
// Apply filters to search results
void applyFilters() {
notificationoffset = 0;
getSearchNews(isFilter: true);
}
@override
void initState() {
super.initState();
searchList.clear();
// Load categories and tags if not already loaded
context.read<CategoryCubit>().loadIfFailed(langId: context.read<AppLocalizationCubit>().state.id);
context.read<TagCubit>().loadIfFailed(langId: context.read<AppLocalizationCubit>().state.id);
notificationoffset = 0;
notificationcontroller = ScrollController(keepScrollOffset: true);
notificationcontroller!.addListener(_searchScrollListener);
_controller.addListener(() {
if (_controller.text.isEmpty) {
if (mounted) {
setState(() {
query = "";
});
}
} else {
query = _controller.text.trim();
notificationoffset = 0;
notificationisnodata = false;
buildResult = false;
if (query.isNotEmpty) {
if (_debounce?.isActive ?? false) _debounce!.cancel();
_debounce = Timer(const Duration(milliseconds: 500), () {
notificationisloadmore = true;
notificationoffset = 0;
getSearchNews();
});
}
}
});
}
_searchScrollListener() {
if (notificationcontroller!.offset >= notificationcontroller!.position.maxScrollExtent && !notificationcontroller!.position.outOfRange) {
if (mounted) {
setState(() {
getSearchNews();
});
}
}
}
Future<List<String>> getHistory() async {
hisList = UiUtils.getDynamicListValue(historyListKey);
return hisList;
}
@override
void dispose() {
notificationcontroller!.dispose();
_controller.dispose();
for (int i = 0; i < _controllerList.length; i++) {
_controllerList[i].dispose();
}
super.dispose();
}
PreferredSizeWidget appbar() {
return AppBar(
leading: const CustomBackButton(horizontalPadding: 15),
backgroundColor: Theme.of(context).canvasColor,
title: TextField(
controller: _controller,
autofocus: true,
decoration: InputDecoration(
contentPadding: const EdgeInsets.fromLTRB(0, 15.0, 0, 15.0),
hintText: UiUtils.getTranslatedLabel(context, 'search'),
hintStyle: TextStyle(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.7)),
enabledBorder: UnderlineInputBorder(borderSide: BorderSide(color: UiUtils.getColorScheme(context).primaryContainer)),
focusedBorder: UnderlineInputBorder(borderSide: BorderSide(color: UiUtils.getColorScheme(context).primaryContainer)),
fillColor: secondaryColor)),
titleSpacing: 0,
actions: [
IconButton(
onPressed: () {
_controller.text = '';
},
icon: Icon(Icons.close, color: UiUtils.getColorScheme(context).primaryContainer)),
IconButton(
padding: EdgeInsetsDirectional.only(end: 7),
onPressed: () async {
FocusManager.instance.rootScope.unfocus();
final NewsFilterData? result = await showFilterBottomSheet(
context: context,
isCategoryModeON: (context.read<AppConfigurationCubit>().getCategoryMode() == "1"),
initialFilters: NewsFilterData(
selectedCategories: filterSelectedCategories,
selectedTags: filterSelectedTags,
selectedDate: filterSelectedDate,
durationFilter: filterDurationFilter,
),
);
if (result != null) {
setState(() {
buildResult = true;
isFilterApplied = true;
filterSelectedCategories = result.selectedCategories;
filterSelectedTags = result.selectedTags;
filterSelectedDate = result.selectedDate;
filterDurationFilter = result.durationFilter;
applyFilters();
});
} else if (result == null) {
setState(() {
buildResult = false;
isFilterApplied = false;
filterSelectedCategories = [];
filterSelectedTags = [];
filterSelectedDate = null;
filterDurationFilter = null;
});
}
},
icon: Icon(
Icons.filter_list,
color: UiUtils.getColorScheme(context).primaryContainer,
))
],
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: appbar(),
body: _showContent(),
);
}
Widget listItem(int index) {
if (_controllerList.length < index + 1) {
_controllerList.add(TextEditingController());
}
return Padding(
padding: const EdgeInsetsDirectional.only(bottom: 7.0),
child: ListTile(
title: CustomTextLabel(
text: searchList[index].title!,
textStyle: Theme.of(context).textTheme.titleSmall!.copyWith(color: UiUtils.getColorScheme(context).primaryContainer, fontWeight: FontWeight.bold),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
leading: ClipRRect(
borderRadius: BorderRadius.circular(7.0), child: CustomNetworkImage(networkImageUrl: searchList[index].image ?? "", height: 80, width: 80, fit: BoxFit.cover, isVideo: false)),
onTap: () async {
FocusScope.of(context).requestFocus(FocusNode());
List<NewsModel> addNewsList = [];
addNewsList.addAll(searchList);
addNewsList.removeAt(index);
UiUtils.showInterstitialAds(context: context);
Navigator.of(context).pushNamed(Routes.newsDetails, arguments: {"model": searchList[index], "newsList": addNewsList, "isFromBreak": false, "fromShowMore": false});
}));
}
Future getSearchNews({bool? isFilter}) async {
if (await InternetConnectivity.isNetworkAvailable()) {
try {
String latitude = SettingsLocalDataRepository().getLocationCityValues().first;
String longitude = SettingsLocalDataRepository().getLocationCityValues().last;
if (notificationisloadmore || (isFilter ?? false)) {
if (mounted) {
setState(() {
notificationisloadmore = false;
notificationisgettingdata = true;
if (notificationoffset == 0) {
searchList = [];
}
});
}
var parameter = {
if (query.isNotEmpty) SEARCH: query.trim(),
LIMIT: "20",
OFFSET: notificationoffset.toString(),
LANGUAGE_ID: context.read<AppLocalizationCubit>().state.id,
};
if (latitude != "null" && longitude != "null") {
parameter[LATITUDE] = latitude;
parameter[LONGITUDE] = longitude;
}
// Apply filters if present
if (filterSelectedCategories.isNotEmpty) {
parameter[CATEGORY_ID] = filterSelectedCategories.map((cat) => cat.id).join(',');
}
if (filterSelectedTags.isNotEmpty) {
parameter[TAG_ID] = filterSelectedTags.map((tag) => tag.id).join(',');
}
if (filterSelectedDate != null) {
parameter[DATE] = "${filterSelectedDate!.year}-${filterSelectedDate!.month.toString().padLeft(2, '0')}-${filterSelectedDate!.day.toString().padLeft(2, '0')}";
}
if (filterDurationFilter != null) {
///To map will be called so automatic it will add the parm of selected type
parameter.addAll(filterDurationFilter!.toMap());
}
final result = await Api.sendApiRequest(body: parameter, url: Api.getNewsApi);
bool error = result["error"];
int totalResult = result[TOTAL];
notificationisgettingdata = false;
if (notificationoffset == 0) {
if (error == false && totalResult > 0) {
notificationisnodata = false;
} else {
notificationisnodata = true;
}
}
if (error == false && totalResult > 0) {
if (mounted) {
Future.delayed(
Duration.zero,
() => setState(() {
List mainlist = result[DATA];
if (mainlist.isNotEmpty) {
List<NewsModel> items = [];
List<NewsModel> allItems = [];
items.addAll(mainlist.map((data) => NewsModel.fromJson(data)).toList());
allItems.addAll(items);
if (notificationoffset == 0 && (isFilter ?? !buildResult)) {
NewsModel element = NewsModel(title: '${UiUtils.getTranslatedLabel(context, 'searchForLbl')} "$query"', image: "", history: false);
searchList.insert(0, element);
for (int i = 0; i < history.length; i++) {
if (history[i].title == query) {
searchList.insert(0, history[i]);
}
}
}
for (NewsModel item in items) {
searchList.where((i) => i.id == item.id).map((obj) {
allItems.remove(item);
return obj;
}).toList();
}
searchList.addAll(allItems);
notificationisloadmore = false;
notificationoffset = notificationoffset + limitOfAPIData;
} else {
notificationisloadmore = false;
}
}));
}
} else {
notificationisloadmore = false;
setState(() {});
}
}
} on TimeoutException catch (_) {
showSnackBar(UiUtils.getTranslatedLabel(context, 'somethingMSg'), context);
setState(() {
notificationisloadmore = false;
});
} catch (e) {
setState(() {
notificationisnodata = true;
notificationisloadmore = false;
});
}
} else {
setState(() {
isNetworkAvail = false;
});
}
}
clearAll() {
setState(() {
query = _controller.text;
notificationoffset = 0;
notificationisloadmore = true;
searchList.clear();
});
}
_showContent() {
if (!isFilterApplied && _controller.text == "") {
return FutureBuilder<List<String>>(
future: getHistory(),
builder: (BuildContext context, AsyncSnapshot<List<String>> snapshot) {
if (snapshot.connectionState == ConnectionState.done && snapshot.hasData) {
final List<String> entities = snapshot.data!;
final List<NewsModel> itemList = [];
for (int i = 0; i < entities.length; i++) {
NewsModel item = NewsModel.history(entities[i]);
itemList.add(item);
}
history.clear();
history.addAll(itemList);
return SingleChildScrollView(
padding: const EdgeInsetsDirectional.only(top: 15.0),
child: Column(
children: [
_SuggestionList(
textController: _controller,
suggestions: itemList,
notificationController: notificationcontroller,
getProduct: getSearchNews,
clearAll: clearAll,
),
],
),
);
} else {
return const Column();
}
});
} else if (buildResult) {
return notificationisnodata
? const Center(child: CustomTextLabel(text: 'noNews'))
: Padding(
padding: const EdgeInsetsDirectional.only(top: 15.0),
child: Column(
children: <Widget>[
Expanded(
child: ListView.builder(
padding: const EdgeInsetsDirectional.only(bottom: 5, start: 10, end: 10, top: 12),
controller: notificationcontroller,
physics: const AlwaysScrollableScrollPhysics(),
itemCount: searchList.length,
itemBuilder: (context, index) {
NewsModel? item;
try {
item = searchList.isEmpty ? null : searchList[index];
if (notificationisloadmore && index == (searchList.length - 1) && notificationcontroller!.position.pixels <= 0) {
getSearchNews();
}
} on Exception catch (_) {}
return item == null ? const SizedBox.shrink() : listItem(index);
}),
),
notificationisgettingdata ? const Padding(padding: EdgeInsetsDirectional.only(top: 5, bottom: 5), child: CircularProgressIndicator()) : const SizedBox.shrink()
],
));
}
return notificationisnodata
? const Center(child: CustomTextLabel(text: 'noNews'))
: Padding(
padding: const EdgeInsetsDirectional.only(top: 15.0),
child: Column(
children: <Widget>[
Expanded(child: _SuggestionList(textController: _controller, suggestions: searchList, notificationController: notificationcontroller, getProduct: getSearchNews, clearAll: clearAll)),
notificationisgettingdata ? const Padding(padding: EdgeInsetsDirectional.only(top: 5, bottom: 5), child: CircularProgressIndicator()) : const SizedBox.shrink()
],
));
}
}
class _SuggestionList extends StatelessWidget {
const _SuggestionList({this.suggestions, this.textController, this.notificationController, this.getProduct, this.clearAll});
final List<NewsModel>? suggestions;
final TextEditingController? textController;
final notificationController;
final Function? getProduct, clearAll;
@override
Widget build(BuildContext context) {
return ListView.separated(
itemCount: suggestions!.length,
shrinkWrap: true,
controller: notificationController,
separatorBuilder: (BuildContext context, int index) => const Divider(),
itemBuilder: (BuildContext context, int i) {
final NewsModel suggestion = suggestions![i];
return ListTile(
title: CustomTextLabel(
text: suggestion.title!,
textStyle: Theme.of(context).textTheme.titleSmall!.copyWith(color: UiUtils.getColorScheme(context).primaryContainer, fontWeight: FontWeight.bold),
maxLines: 2,
overflow: TextOverflow.ellipsis),
leading: textController!.text.toString().trim().isEmpty || suggestion.history!
? const Icon(Icons.history)
: ClipRRect(
borderRadius: BorderRadius.circular(7.0),
child: suggestion.image == ''
? Image.asset(UiUtils.getPlaceholderPngPath(), height: 80, width: 80)
: CustomNetworkImage(networkImageUrl: suggestion.image!, height: 80, width: 80, fit: BoxFit.cover, isVideo: false)),
trailing: SvgPictureWidget(
assetName: "searchbar_arrow",
height: 11,
width: 11,
fit: BoxFit.contain,
assetColor: ColorFilter.mode(UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.7), BlendMode.srcIn)),
onTap: () async {
if (suggestion.title!.startsWith('${UiUtils.getTranslatedLabel(context, 'searchForLbl')} ')) {
UiUtils.setDynamicListValue(historyListKey, textController!.text.toString().trim());
buildResult = true;
clearAll!();
getProduct!();
} else if (suggestion.history!) {
clearAll!();
buildResult = true;
textController!.text = suggestion.title!;
textController!.selection = TextSelection.fromPosition(TextPosition(offset: textController!.text.length));
} else {
UiUtils.setDynamicListValue(historyListKey, textController!.text.trim());
buildResult = false;
UiUtils.showInterstitialAds(context: context);
Navigator.of(context).pushNamed(Routes.newsDetails, arguments: {"model": suggestion, "isFromBreak": false, "fromShowMore": false});
}
});
},
);
}
}

View File

@@ -0,0 +1,104 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:news/data/repositories/Settings/settingsLocalDataRepository.dart';
import 'package:news/ui/widgets/breakingVideoItem.dart';
import 'package:news/ui/widgets/errorContainerWidget.dart';
import 'package:news/ui/widgets/shimmerNewsList.dart';
import 'package:news/utils/ErrorMessageKeys.dart';
import 'package:news/utils/uiUtils.dart';
import 'package:news/cubits/appLocalizationCubit.dart';
import 'package:news/cubits/sectionByIdCubit.dart';
import 'package:news/data/models/BreakingNewsModel.dart';
import 'package:news/ui/widgets/breakingNewsItem.dart';
import 'package:news/ui/widgets/customAppBar.dart';
class SectionMoreBreakingNewsList extends StatefulWidget {
final String sectionId;
final String title;
const SectionMoreBreakingNewsList({super.key, required this.sectionId, required this.title});
@override
State<StatefulWidget> createState() {
return _SectionBreakingNewsState();
}
static Route route(RouteSettings routeSettings) {
final arguments = routeSettings.arguments as Map<String, dynamic>;
return CupertinoPageRoute(builder: (_) => SectionMoreBreakingNewsList(sectionId: arguments['sectionId'], title: arguments['title']));
}
}
class _SectionBreakingNewsState extends State<SectionMoreBreakingNewsList> {
late final ScrollController controller = ScrollController()..addListener(hasMoreSectionScrollListener);
Set<String> get locationValue => SettingsLocalDataRepository().getLocationCityValues();
@override
void initState() {
getSectionByData();
super.initState();
}
void getSectionByData() {
Future.delayed(Duration.zero, () {
context.read<SectionByIdCubit>().getSectionById(langId: context.read<AppLocalizationCubit>().state.id, sectionId: widget.sectionId, latitude: locationValue.first, longitude: locationValue.last);
});
}
void hasMoreSectionScrollListener() {
if (controller.position.maxScrollExtent == controller.offset) {
if (context.read<SectionByIdCubit>().hasMoreSections() && !(context.read<SectionByIdCubit>().state is SectionByIdFetchInProgress)) {
context.read<SectionByIdCubit>().getMoreSectionById(langId: context.read<AppLocalizationCubit>().state.id, sectionId: widget.sectionId);
}
}
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
_buildSectionBreakingNewsContainer({required BreakingNewsModel model, required String type, required int index, required List<BreakingNewsModel> newsList}) {
return type == 'breaking_news' ? BreakNewsItem(model: model, index: index, breakNewsList: newsList) : BreakVideoItem(model: model);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(height: 45, isBackBtn: true, label: widget.title, horizontalPad: 15, isConvertText: false),
body: BlocBuilder<SectionByIdCubit, SectionByIdState>(
builder: (context, state) {
if (state is SectionByIdFetchSuccess) {
return Padding(
padding: const EdgeInsetsDirectional.all(10.0),
child: RefreshIndicator(
onRefresh: () async {
context
.read<SectionByIdCubit>()
.getSectionById(sectionId: widget.sectionId, langId: context.read<AppLocalizationCubit>().state.id, latitude: locationValue.first, longitude: locationValue.last);
},
child: ListView.builder(
controller: controller,
physics: const AlwaysScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: state.breakNewsModel.length,
itemBuilder: (context, index) {
return _buildSectionBreakingNewsContainer(
model: (state).breakNewsModel[index], type: (state).type, index: index, newsList: (state).type == 'breaking_news' ? state.breakNewsModel : []);
}),
),
);
}
if (state is SectionByIdFetchFailure) {
return ErrorContainerWidget(
errorMsg: (state.errorMessage.contains(ErrorMessageKeys.noInternet)) ? UiUtils.getTranslatedLabel(context, 'internetmsg') : state.errorMessage, onRetry: getSectionByData);
}
//state is SectionByIdFetchInProgress || state is SectionByIdInitial
return ShimmerNewsList(isNews: false);
},
),
);
}
}

View File

@@ -0,0 +1,104 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:news/cubits/appLocalizationCubit.dart';
import 'package:news/cubits/sectionByIdCubit.dart';
import 'package:news/data/models/NewsModel.dart';
import 'package:news/data/repositories/Settings/settingsLocalDataRepository.dart';
import 'package:news/ui/widgets/NewsItem.dart';
import 'package:news/ui/widgets/customAppBar.dart';
import 'package:news/ui/widgets/errorContainerWidget.dart';
import 'package:news/utils/ErrorMessageKeys.dart';
import 'package:news/utils/uiUtils.dart';
import 'package:news/ui/widgets/shimmerNewsList.dart';
import 'package:news/ui/widgets/videoItem.dart';
class SectionMoreNewsList extends StatefulWidget {
final String sectionId;
final String title;
const SectionMoreNewsList({super.key, required this.sectionId, required this.title});
@override
State<StatefulWidget> createState() {
return _SectionNewsState();
}
static Route route(RouteSettings routeSettings) {
final arguments = routeSettings.arguments as Map<String, dynamic>;
return CupertinoPageRoute(builder: (_) => SectionMoreNewsList(sectionId: arguments['sectionId'], title: arguments['title']));
}
}
class _SectionNewsState extends State<SectionMoreNewsList> {
late final ScrollController controller = ScrollController()..addListener(hasMoreSectionScrollListener);
Set<String> get locationValue => SettingsLocalDataRepository().getLocationCityValues();
@override
void initState() {
getSectionByData();
super.initState();
}
void getSectionByData() {
Future.delayed(Duration.zero, () {
context.read<SectionByIdCubit>().getSectionById(langId: context.read<AppLocalizationCubit>().state.id, sectionId: widget.sectionId, latitude: locationValue.first, longitude: locationValue.last);
});
}
void hasMoreSectionScrollListener() {
if (controller.position.maxScrollExtent == controller.offset) {
if (context.read<SectionByIdCubit>().hasMoreSections() && !(context.read<SectionByIdCubit>().state is SectionByIdFetchInProgress)) {
context.read<SectionByIdCubit>().getMoreSectionById(langId: context.read<AppLocalizationCubit>().state.id, sectionId: widget.sectionId);
}
}
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
_buildSectionNewsContainer({required NewsModel model, required String type, required int index, required List<NewsModel> newsList}) {
return (type == 'news' || type == 'user_choice') ? NewsItem(model: model, index: index, newslist: newsList, fromShowMore: false) : VideoItem(model: model);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(height: 45, isBackBtn: true, label: widget.title, horizontalPad: 15, isConvertText: false),
body: BlocBuilder<SectionByIdCubit, SectionByIdState>(
builder: (context, state) {
if (state is SectionByIdFetchSuccess) {
return Padding(
padding: const EdgeInsetsDirectional.symmetric(vertical: 10),
child: RefreshIndicator(
onRefresh: () async {
context
.read<SectionByIdCubit>()
.getSectionById(sectionId: widget.sectionId, langId: context.read<AppLocalizationCubit>().state.id, latitude: locationValue.first, longitude: locationValue.last);
},
child: ListView.builder(
controller: controller,
physics: const AlwaysScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: state.newsModel.length,
itemBuilder: (context, index) {
return _buildSectionNewsContainer(
model: (state).newsModel[index], type: (state).type, index: index, newsList: ((state).type == 'news' || state.type == 'user_choice') ? state.newsModel : []);
}),
),
);
}
if (state is SectionByIdFetchFailure) {
return ErrorContainerWidget(
errorMsg: (state.errorMessage.contains(ErrorMessageKeys.noInternet)) ? UiUtils.getTranslatedLabel(context, 'internetmsg') : state.errorMessage, onRetry: getSectionByData);
}
//state is SectionByIdFetchInProgress || state is SectionByIdInitial
return ShimmerNewsList(isNews: true);
},
),
);
}
}

View File

@@ -0,0 +1,138 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:news/ui/styles/colors.dart';
import 'package:news/ui/widgets/customBackBtn.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/ui/widgets/shimmerNewsList.dart';
import 'package:news/ui/screens/SubCategory/Widgets/SubCatNewsList.dart';
import 'package:news/ui/screens/SubCategory/Widgets/categoryShimmer.dart';
import 'package:news/utils/uiUtils.dart';
import 'package:news/cubits/subCategoryCubit.dart';
import 'package:news/cubits/appLocalizationCubit.dart';
import 'package:news/cubits/appSystemSettingCubit.dart';
import 'package:news/cubits/subCatNewsCubit.dart';
import 'package:news/data/repositories/SubCatNews/subCatRepository.dart';
class SubCategoryScreen extends StatefulWidget {
final String catId;
final String catName;
const SubCategoryScreen({super.key, required this.catId, required this.catName});
@override
SubCategoryScreenState createState() => SubCategoryScreenState();
static Route route(RouteSettings routeSettings) {
final arguments = routeSettings.arguments as Map<String, dynamic>;
return CupertinoPageRoute(builder: (_) => SubCategoryScreen(catId: arguments['catId'], catName: arguments['catName']));
}
}
class SubCategoryScreenState extends State<SubCategoryScreen> with TickerProviderStateMixin {
final List<Map<String, dynamic>> _tabs = [];
TabController? _tc;
bool isSubcatExist = true;
@override
void initState() {
getSubCatData();
super.initState();
}
@override
void dispose() {
if (_tc != null) {
_tc!.dispose();
}
super.dispose();
}
Future getSubCatData() async {
Future.delayed(Duration.zero, () {
context.read<SubCategoryCubit>().getSubCategory(context: context, langId: context.read<AppLocalizationCubit>().state.id, catId: widget.catId);
});
}
Widget subcatData() {
return BlocConsumer<SubCategoryCubit, SubCategoryState>(
bloc: context.read<SubCategoryCubit>(),
listener: (context, state) {
if (state is SubCategoryFetchSuccess) {
setState(() {
for (int i = 0; i < state.subCategory.length; i++) {
_tabs.add({'text': state.subCategory[i].subCatName, 'subCatId': state.subCategory[i].id});
}
_tc = TabController(vsync: this, length: state.subCategory.length)..addListener(() {});
});
}
if (state is SubCategoryFetchFailure) {
isSubcatExist = false;
setState(() {});
}
},
builder: (context, state) {
if (state is SubCategoryFetchSuccess) {
return DefaultTabController(
length: state.subCategory.length,
child: _tabs.isNotEmpty
? TabBar(
controller: _tc,
labelColor: secondaryColor,
indicatorColor: Colors.transparent,
isScrollable: true,
padding: const EdgeInsets.only(top: 10, bottom: 5, left: 3, right: 3),
physics: const AlwaysScrollableScrollPhysics(),
unselectedLabelColor: UiUtils.getColorScheme(context).primaryContainer,
indicator: BoxDecoration(borderRadius: BorderRadius.circular(5.0), color: UiUtils.getColorScheme(context).secondaryContainer),
tabs: _tabs
.map((tab) => AnimatedContainer(
height: 30, duration: const Duration(milliseconds: 600), padding: const EdgeInsetsDirectional.only(top: 5.0, bottom: 5.0), child: Tab(text: tab['text'])))
.toList())
: subcatShimmer());
}
if (state is SubCategoryFetchFailure) {
return const SizedBox.shrink();
}
//state is SubCategoryFetchInProgress || state is SubCategoryInitial
return subcatShimmer();
});
}
setAppBar() {
return PreferredSize(
preferredSize: Size(double.infinity, (isSubcatExist) ? 80 : 40),
child: AppBar(
leading: const CustomBackButton(horizontalPadding: 15),
titleSpacing: 0.0,
centerTitle: false,
backgroundColor: Colors.transparent,
title: CustomTextLabel(
text: widget.catName,
textStyle: Theme.of(context).textTheme.titleLarge?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer, fontWeight: FontWeight.w600, letterSpacing: 0.5)),
bottom: PreferredSize(preferredSize: Size(MediaQuery.of(context).size.width, 30), child: subcatData()),
));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: setAppBar(),
body: BlocBuilder<SubCategoryCubit, SubCategoryState>(builder: (context, state) {
if (state is SubCategoryFetchFailure || context.read<AppConfigurationCubit>().getSubCatMode() != "1") {
return BlocProvider<SubCatNewsCubit>(create: (_) => SubCatNewsCubit(SubCatNewsRepository()), child: SubCatNewsList(from: 1, catId: widget.catId));
}
if (state is SubCategoryFetchSuccess && _tc != null && _tc!.length > 0) {
return TabBarView(
controller: _tc,
children: List<Widget>.generate(_tc!.length, (int index) {
return BlocProvider<SubCatNewsCubit>(
create: (_) => SubCatNewsCubit(SubCatNewsRepository()),
child: SubCatNewsList(from: index == 0 ? 1 : 2, subCatId: index == 0 ? null : _tabs[index]['subCatId'], catId: widget.catId));
}));
}
return ShimmerNewsList(isNews: true);
}),
);
}
}

View File

@@ -0,0 +1,197 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:news/cubits/subCatNewsCubit.dart';
import 'package:news/cubits/surveyQuestionCubit.dart';
import 'package:news/data/repositories/Settings/settingsLocalDataRepository.dart';
import 'package:news/ui/widgets/NewsItem.dart';
import 'package:news/ui/screens/SubCategory/Widgets/showSurveyQuestion.dart';
import 'package:news/ui/screens/SubCategory/Widgets/showSurveyResult.dart';
import 'package:news/ui/widgets/errorContainerWidget.dart';
import 'package:news/ui/widgets/shimmerNewsList.dart';
import 'package:news/utils/ErrorMessageKeys.dart';
import 'package:news/utils/constant.dart';
import 'package:news/cubits/Auth/authCubit.dart';
import 'package:news/cubits/appLocalizationCubit.dart';
import 'package:news/data/models/NewsModel.dart';
import 'package:news/utils/uiUtils.dart';
class SubCatNewsList extends StatefulWidget {
final int from;
final String catId;
final String? subCatId;
const SubCatNewsList({super.key, required this.from, required this.catId, this.subCatId});
@override
SubCatNewsListState createState() => SubCatNewsListState();
static Route route(RouteSettings routeSettings) {
final arguments = routeSettings.arguments as Map<String, dynamic>;
return CupertinoPageRoute(builder: (_) => SubCatNewsList(from: arguments['from'], catId: arguments['catId'], subCatId: arguments['subCatId']));
}
}
class SubCatNewsListState extends State<SubCatNewsList> with AutomaticKeepAliveClientMixin {
List<NewsModel> combineList = [];
int totalSurveyQue = 0;
Set<String> get locationValue => SettingsLocalDataRepository().getLocationCityValues();
late final ScrollController controller = ScrollController()..addListener(hasMoreNotiScrollListener);
@override
void initState() {
getSubCatNewsData();
super.initState();
}
@override
void dispose() {
totalSurveyQue = 0;
controller.dispose();
super.dispose();
}
Future getSubCatNewsData() async {
Future.delayed(Duration.zero, () {
if (context.read<AuthCubit>().getUserId() != "0") {
context.read<SurveyQuestionCubit>().getSurveyQuestion(langId: context.read<AppLocalizationCubit>().state.id).whenComplete(() {
getSubcategoryNews();
});
} else {
getSubcategoryNews();
}
});
}
void hasMoreNotiScrollListener() {
if (controller.position.maxScrollExtent == controller.offset) {
if (context.read<SubCatNewsCubit>().hasMoreSubCatNews()) {
context.read<SubCatNewsCubit>().getMoreSubCatNews(
langId: context.read<AppLocalizationCubit>().state.id,
subCatId: widget.from == 2 ? widget.subCatId : null,
catId: widget.from == 1 ? widget.catId : null,
latitude: locationValue.first,
longitude: locationValue.last);
} else {}
}
}
updateCombineList(NewsModel model, int index) {
setState(() {
combineList[index] = model;
});
}
Widget getNewsList() {
return BlocConsumer<SubCatNewsCubit, SubCatNewsState>(
bloc: context.read<SubCatNewsCubit>(),
listener: (context, state) {
if (state is SubCatNewsFetchSuccess) {
combineList.clear();
int cur = 0;
for (int i = 0; i < (state).subCatNews.length; i++) {
if (i != 0 && i % surveyShow == 0) {
if (context.read<SurveyQuestionCubit>().surveyList().isNotEmpty && context.read<SurveyQuestionCubit>().surveyList().length > cur) {
combineList.add(context.read<SurveyQuestionCubit>().surveyList()[cur]);
cur++;
}
}
combineList.add((state).subCatNews[i]);
}
}
},
builder: (context, stateSubCat) {
if (stateSubCat is SubCatNewsFetchSuccess) {
return combineNewsList(stateSubCat, stateSubCat.subCatNews);
}
if (stateSubCat is SubCatNewsFetchFailure) {
return ErrorContainerWidget(
errorMsg: (stateSubCat.errorMessage.contains(ErrorMessageKeys.noInternet)) ? UiUtils.getTranslatedLabel(context, 'internetmsg') : stateSubCat.errorMessage, onRetry: getSubCatNewsData);
}
//stateSubCat is SubCatNewsFetchInProgress || stateSubCat is SubCatNewsInitial
return ShimmerNewsList(isNews: true);
});
}
setTotalSurveyQueCount() {
for (var element in combineList) {
if (element.type == "survey") totalSurveyQue += 1;
}
}
Widget combineNewsList(SubCatNewsFetchSuccess state, List<NewsModel> newsList) {
setTotalSurveyQueCount();
return RefreshIndicator(
onRefresh: () async {
combineList.clear();
if (context.read<AuthCubit>().getUserId() != "0") {
context.read<SurveyQuestionCubit>().getSurveyQuestion(langId: context.read<AppLocalizationCubit>().state.id).whenComplete(() {
getSubcategoryNews();
});
} else {
getSubcategoryNews();
}
setTotalSurveyQueCount();
setState(() {});
},
child: ListView.builder(
padding: const EdgeInsetsDirectional.symmetric(vertical: 15),
physics: const AlwaysScrollableScrollPhysics(),
controller: controller,
itemCount: combineList.length,
itemBuilder: (context, index) {
return _buildNewsContainer(
model: combineList[index], hasMore: state.hasMore, hasMoreNewsFetchError: state.hasMoreFetchError, index: index, totalCurrentNews: combineList.length, newsList: newsList);
}),
);
}
void getSubcategoryNews() {
context.read<SubCatNewsCubit>().getSubCatNews(
langId: context.read<AppLocalizationCubit>().state.id,
subCatId: widget.from == 2 ? widget.subCatId : null,
catId: widget.from == 1 ? widget.catId : null,
latitude: locationValue.first,
longitude: locationValue.last);
}
_buildNewsContainer({required NewsModel model, required int index, required int totalCurrentNews, required bool hasMoreNewsFetchError, required bool hasMore, required List<NewsModel> newsList}) {
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: () {
context.read<SubCatNewsCubit>().getMoreSubCatNews(
langId: context.read<AppLocalizationCubit>().state.id,
subCatId: widget.from == 2 ? widget.subCatId : null,
catId: widget.from == 1 ? widget.catId : null,
latitude: locationValue.first,
longitude: locationValue.last);
},
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 model.type == "survey"
? model.from == 2
? showSurveyQueResult(model, context, true)
: ShowSurveyQue(model: model, index: index, surveyId: context.read<SurveyQuestionCubit>().getSurveyQuestionIndex(questionTitle: model.question!), updateList: updateCombineList)
: NewsItem(model: model, index: (index <= totalSurveyQue) ? index : (index - totalSurveyQue), newslist: newsList, fromShowMore: false);
}
@override
Widget build(BuildContext context) {
super.build(context);
return getNewsList();
}
@override
bool get wantKeepAlive => true;
}

View File

@@ -0,0 +1,22 @@
import 'package:flutter/material.dart';
import 'package:shimmer/shimmer.dart';
subcatShimmer() {
return Shimmer.fromColors(
baseColor: Colors.grey.withOpacity(0.4),
highlightColor: Colors.grey.withOpacity(0.4),
child: SingleChildScrollView(
padding: const EdgeInsets.only(top: 10),
scrollDirection: Axis.horizontal,
child: Row(
children: [0, 1, 2, 3, 4, 5, 6]
.map((i) => Padding(
padding: const EdgeInsetsDirectional.only(start: 15, top: 0),
child: Container(
decoration: BoxDecoration(borderRadius: BorderRadius.circular(5.0), color: const Color.fromARGB(255, 59, 49, 49)),
height: 32.0,
width: 70.0,
)))
.toList()),
));
}

View File

@@ -0,0 +1,134 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:news/cubits/getSurveyAnswerCubit.dart';
import 'package:news/cubits/setSurveyAnswerCubit.dart';
import 'package:news/cubits/surveyQuestionCubit.dart';
import 'package:news/ui/styles/colors.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/utils/uiUtils.dart';
import 'package:news/cubits/appLocalizationCubit.dart';
import 'package:news/data/models/NewsModel.dart';
import 'package:news/ui/widgets/SnackBarWidget.dart';
class ShowSurveyQue extends StatefulWidget {
final NewsModel model;
final int index;
final String surveyId;
final bool isPaddingRequired;
final Function(NewsModel, int) updateList;
const ShowSurveyQue({super.key, required this.model, required this.index, required this.surveyId, required this.updateList, this.isPaddingRequired = true});
@override
ShowSurveyQueState createState() => ShowSurveyQueState();
}
class ShowSurveyQueState extends State<ShowSurveyQue> {
String? optSel;
@override
Widget build(BuildContext context) {
return showSurveyQue();
}
Widget showSurveyQue() {
return BlocConsumer<SetSurveyAnsCubit, SetSurveyAnsState>(
bloc: context.read<SetSurveyAnsCubit>(),
listener: (context, state) {
if (state is SetSurveyAnsInitial || state is SetSurveyAnsFetchInProgress) {
Center(child: UiUtils.showCircularProgress(true, Theme.of(context).primaryColor));
}
if (state is SetSurveyAnsFetchSuccess) {
context.read<SurveyQuestionCubit>().removeQuestion(widget.surveyId);
context.read<GetSurveyAnsCubit>().getSurveyAns(langId: context.read<AppLocalizationCubit>().state.id);
}
},
builder: (context, state) {
return BlocConsumer<GetSurveyAnsCubit, GetSurveyAnsState>(
bloc: context.read<GetSurveyAnsCubit>(),
listener: (context, state) {
if (state is GetSurveyAnsInitial || state is GetSurveyAnsFetchInProgress) {
Center(child: UiUtils.showCircularProgress(true, Theme.of(context).primaryColor));
}
if (state is GetSurveyAnsFetchSuccess) {
for (var element in state.getSurveyAns) {
if (element.id == widget.model.id) {
NewsModel newModel = element;
newModel.from = 2;
widget.updateList(newModel, widget.index);
}
}
}
},
builder: (context, state) {
return Padding(
padding: EdgeInsets.only(top: 15.0, left: (widget.isPaddingRequired) ? 15 : 0, right: (widget.isPaddingRequired) ? 15 : 0),
child: Container(
decoration: BoxDecoration(borderRadius: BorderRadius.circular(10.0), color: UiUtils.getColorScheme(context).surface),
padding: const EdgeInsets.all(10.0),
child: Column(
children: [
CustomTextLabel(
text: widget.model.question!, textStyle: Theme.of(context).textTheme.titleLarge?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer, height: 1.0)),
Padding(
padding: const EdgeInsetsDirectional.only(top: 15.0, start: 7.0, end: 7.0),
child: ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.vertical,
physics: const NeverScrollableScrollPhysics(),
itemCount: widget.model.optionDataList!.length,
itemBuilder: (context, j) {
return Padding(
padding: const EdgeInsets.only(bottom: 10.0),
child: InkWell(
child: Container(
height: 50,
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10.0),
color: optSel == widget.model.optionDataList![j].id ? Theme.of(context).primaryColor.withOpacity(0.1) : UiUtils.getColorScheme(context).secondary),
child: CustomTextLabel(
text: widget.model.optionDataList![j].options!,
textStyle: Theme.of(context).textTheme.titleSmall?.copyWith(
color: optSel == widget.model.optionDataList![j].id ? Theme.of(context).primaryColor : UiUtils.getColorScheme(context).primaryContainer,
height: 1.0),
textAlign: TextAlign.center)),
onTap: () {
setState(() {
optSel = widget.model.optionDataList![j].id;
});
},
),
);
})),
Padding(
padding: const EdgeInsets.only(top: 15.0),
child: InkWell(
child: Container(
height: 40.0,
width: MediaQuery.of(context).size.width * 0.35,
alignment: Alignment.center,
decoration: BoxDecoration(color: secondaryColor, borderRadius: BorderRadius.circular(7.0)),
child: CustomTextLabel(
text: 'submitBtn',
textStyle: Theme.of(context).textTheme.titleMedium?.copyWith(color: Theme.of(context).primaryColor, fontWeight: FontWeight.w600, letterSpacing: 0.6),
),
),
onTap: () async {
if (optSel != null) {
//get survey id from survey question
String currentIndex = context.read<SurveyQuestionCubit>().getSurveyQuestionIndex(questionTitle: widget.model.question!);
context.read<SetSurveyAnsCubit>().setSurveyAns(optId: optSel!, queId: currentIndex);
await Future.delayed(Duration(seconds: 2));
} else {
showSnackBar(UiUtils.getTranslatedLabel(context, 'optSel'), context);
}
}),
)
],
)));
});
});
}
}

View File

@@ -0,0 +1,39 @@
import 'package:flutter/material.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/utils/uiUtils.dart';
import 'package:percent_indicator/linear_percent_indicator.dart';
import 'package:news/data/models/NewsModel.dart';
showSurveyQueResult(NewsModel model, BuildContext context, bool isPaddingRequired) {
return Padding(
padding: EdgeInsets.only(top: 15.0, left: (isPaddingRequired) ? 15 : 0, right: (isPaddingRequired) ? 15 : 0),
child: Container(
decoration: BoxDecoration(borderRadius: BorderRadius.circular(10.0), color: UiUtils.getColorScheme(context).surface),
padding: const EdgeInsets.all(10.0),
child: Column(
children: [
CustomTextLabel(text: model.question!, textStyle: Theme.of(context).textTheme.titleLarge?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer, height: 1.0)),
Padding(
padding: const EdgeInsetsDirectional.only(top: 15.0, start: 7.0, end: 7.0),
child: ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.vertical,
physics: const NeverScrollableScrollPhysics(),
itemCount: model.optionDataList!.length,
itemBuilder: (context, j) {
return Padding(
padding: const EdgeInsetsDirectional.only(bottom: 10.0, start: 15.0, end: 15.0),
child: LinearPercentIndicator(
animation: true,
animationDuration: 1000,
lineHeight: 40.0,
percent: model.optionDataList![j].percentage! / 100,
center: CustomTextLabel(text: "${(model.optionDataList![j].percentage!).toStringAsFixed(2)}%", textStyle: Theme.of(context).textTheme.titleSmall),
barRadius: const Radius.circular(16),
progressColor: Theme.of(context).primaryColor,
isRTL: false,
padding: const EdgeInsets.only(top: 10.0, bottom: 10.0)));
})),
],
)));
}

View File

@@ -0,0 +1,281 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:news/ui/widgets/customAppBar.dart';
import 'package:news/ui/widgets/SnackBarWidget.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/ui/widgets/shimmerNewsList.dart';
import 'package:news/ui/widgets/errorContainerWidget.dart';
import 'package:news/ui/widgets/networkImage.dart';
import 'package:news/utils/ErrorMessageKeys.dart';
import 'package:news/utils/internetConnectivity.dart';
import 'package:news/utils/uiUtils.dart';
import 'package:news/app/routes.dart';
import 'package:news/cubits/Auth/authCubit.dart';
import 'package:news/cubits/tagNewsCubit.dart';
import 'package:news/cubits/appLocalizationCubit.dart';
import 'package:news/cubits/Bookmark/bookmarkCubit.dart';
import 'package:news/cubits/Bookmark/UpdateBookmarkCubit.dart';
import 'package:news/cubits/LikeAndDislikeNews/LikeAndDislikeCubit.dart';
import 'package:news/cubits/LikeAndDislikeNews/updateLikeAndDislikeCubit.dart';
import 'package:news/data/models/NewsModel.dart';
import 'package:news/data/repositories/Bookmark/bookmarkRepository.dart';
import 'package:news/data/repositories/Settings/settingsLocalDataRepository.dart';
import 'package:news/data/repositories/LikeAndDisLikeNews/LikeAndDisLikeNewsRepository.dart';
class NewsTag extends StatefulWidget {
final String tagId;
final String tagName;
const NewsTag({super.key, required this.tagId, required this.tagName});
@override
NewsTagState createState() => NewsTagState();
static Route route(RouteSettings routeSettings) {
final arguments = routeSettings.arguments as Map<String, dynamic>;
return CupertinoPageRoute(builder: (_) => NewsTag(tagId: arguments['tagId'], tagName: arguments['tagName']));
}
}
class NewsTagState extends State<NewsTag> {
@override
void initState() {
super.initState();
getTagWiseNews();
}
getTagWiseNews() {
Future.delayed(Duration.zero, () {
context.read<TagNewsCubit>().getTagNews(
tagId: widget.tagId,
langId: context.read<AppLocalizationCubit>().state.id,
latitude: SettingsLocalDataRepository().getLocationCityValues().first,
longitude: SettingsLocalDataRepository().getLocationCityValues().last);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(appBar: CustomAppBar(isConvertText: false, height: 45, isBackBtn: true, label: widget.tagName, horizontalPad: 15), body: viewContent());
}
newsItem(NewsModel model, int index) {
List<String> tagList = [];
List<String> tagId = [];
DateTime time1 = DateTime.parse(model.publishDate ?? model.date!);
if (model.tagName! != "") {
final tagName = model.tagName!;
tagList = tagName.split(',');
}
if (model.tagId! != "") tagId = model.tagId!.split(",");
return Builder(builder: (context) {
bool isLike = context.read<LikeAndDisLikeCubit>().isNewsLikeAndDisLike(model.id!);
return Padding(
padding: EdgeInsetsDirectional.only(top: index == 0 ? 0 : MediaQuery.of(context).size.height / 25.0),
child: Column(children: <Widget>[
InkWell(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
SizedBox(
height: MediaQuery.of(context).size.height / 4.2,
child: Stack(
alignment: Alignment.bottomLeft,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(10.0),
child: CustomNetworkImage(networkImageUrl: model.image!, width: double.maxFinite, height: MediaQuery.of(context).size.height / 4.2, isVideo: false, fit: BoxFit.cover)),
if (model.tagName! != "")
Container(
margin: const EdgeInsets.only(bottom: 5.0, left: 5.0, right: 5.0),
child: SizedBox(
height: 16,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: tagList.length,
itemBuilder: (context, index) {
return Padding(
padding: EdgeInsetsDirectional.only(start: index == 0 ? 0 : 5.5),
child: InkWell(
child: Container(
height: 20.0,
width: 65,
alignment: Alignment.center,
padding: const EdgeInsetsDirectional.only(start: 3.0, end: 3.0, top: 1.0, bottom: 1.0),
decoration: BoxDecoration(
borderRadius: const BorderRadius.only(bottomLeft: Radius.circular(10.0), topRight: Radius.circular(10.0)),
color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.85),
),
child: CustomTextLabel(
text: tagList[index],
textStyle: Theme.of(context).textTheme.bodyMedium?.copyWith(color: UiUtils.getColorScheme(context).secondary, fontSize: 9.5),
overflow: TextOverflow.ellipsis,
softWrap: true)),
onTap: () async {
Navigator.of(context).pushNamed(Routes.tagScreen, arguments: {"tagId": tagId[index], "tagName": tagList[index]});
},
));
})),
)
],
),
),
Container(
padding: const EdgeInsetsDirectional.only(top: 4.0, start: 5.0, end: 5.0),
child: CustomTextLabel(
text: model.title!,
textStyle: Theme.of(context).textTheme.titleSmall?.copyWith(color: Theme.of(context).colorScheme.primaryContainer.withOpacity(0.9)),
maxLines: 2,
softWrap: true,
overflow: TextOverflow.ellipsis),
),
Row(
children: [
Expanded(
child: Padding(
padding: const EdgeInsetsDirectional.only(top: 4.0, start: 5.0, end: 5.0),
child: CustomTextLabel(
text: UiUtils.convertToAgo(context, time1, 0)!,
textStyle: Theme.of(context).textTheme.bodySmall!.copyWith(color: Theme.of(context).colorScheme.primaryContainer.withOpacity(0.6)))),
),
Row(
children: [
Padding(
padding: const EdgeInsetsDirectional.only(start: 13.0),
child: InkWell(
child: const Icon(Icons.share_rounded),
onTap: () async {
if (await InternetConnectivity.isNetworkAvailable()) {
UiUtils.shareNews(context: context, slug: model.slug!, title: model.title!, isVideo: false, videoId: "", isBreakingNews: false, isNews: true);
} else {
showSnackBar(UiUtils.getTranslatedLabel(context, 'internetmsg'), context);
}
},
),
),
SizedBox(width: MediaQuery.of(context).size.width / 99.0),
BlocProvider(
create: (context) => UpdateBookmarkStatusCubit(BookmarkRepository()),
child: BlocBuilder<BookmarkCubit, BookmarkState>(
bloc: context.read<BookmarkCubit>(),
builder: (context, bookmarkState) {
bool isBookmark = context.read<BookmarkCubit>().isNewsBookmark(model.id!);
return BlocConsumer<UpdateBookmarkStatusCubit, UpdateBookmarkStatusState>(
bloc: context.read<UpdateBookmarkStatusCubit>(),
listener: ((context, state) {
if (state is UpdateBookmarkStatusSuccess) {
if (state.wasBookmarkNewsProcess) {
context.read<BookmarkCubit>().addBookmarkNews(state.news);
} else {
context.read<BookmarkCubit>().removeBookmarkNews(state.news);
}
}
}),
builder: (context, state) {
return InkWell(
onTap: () {
if (context.read<AuthCubit>().getUserId() != "0") {
if (state is UpdateBookmarkStatusInProgress) {
return;
}
context.read<UpdateBookmarkStatusCubit>().setBookmarkNews(news: model, status: (isBookmark) ? "0" : "1");
} else {
UiUtils.loginRequired(context);
}
},
child: Padding(
padding: EdgeInsetsDirectional.zero,
child: InkWell(
child: state is UpdateBookmarkStatusInProgress
? SizedBox(height: 15, width: 15, child: UiUtils.showCircularProgress(true, Theme.of(context).primaryColor))
: Icon(isBookmark ? Icons.bookmark_added_rounded : Icons.bookmark_add_outlined))));
});
}),
),
SizedBox(width: MediaQuery.of(context).size.width / 99.0),
BlocProvider(
create: (context) => UpdateLikeAndDisLikeStatusCubit(LikeAndDisLikeRepository()),
child: BlocConsumer<LikeAndDisLikeCubit, LikeAndDisLikeState>(
bloc: context.read<LikeAndDisLikeCubit>(),
listener: ((context, state) {
isLike = context.read<LikeAndDisLikeCubit>().isNewsLikeAndDisLike(model.id!);
}),
builder: (context, likeAndDislikeState) {
return BlocConsumer<UpdateLikeAndDisLikeStatusCubit, UpdateLikeAndDisLikeStatusState>(
bloc: context.read<UpdateLikeAndDisLikeStatusCubit>(),
listener: ((context, state) {
if (state is UpdateLikeAndDisLikeStatusSuccess) {
context.read<LikeAndDisLikeCubit>().getLike(langId: context.read<AppLocalizationCubit>().state.id);
state.news.totalLikes = (!isLike)
? (int.parse(state.news.totalLikes.toString()) + 1).toString()
: (state.news.totalLikes!.isNotEmpty)
? (int.parse(state.news.totalLikes.toString()) - 1).toString()
: "0";
}
}),
builder: (context, state) {
isLike = context.read<LikeAndDisLikeCubit>().isNewsLikeAndDisLike(model.id!);
return InkWell(
splashColor: Colors.transparent,
onTap: () {
if (context.read<AuthCubit>().getUserId() != "0") {
if (state is UpdateLikeAndDisLikeStatusInProgress) {
return;
}
context.read<UpdateLikeAndDisLikeStatusCubit>().setLikeAndDisLikeNews(news: model, status: (isLike) ? "0" : "1");
} else {
UiUtils.loginRequired(context);
}
},
child: (state is UpdateLikeAndDisLikeStatusInProgress)
? SizedBox(height: 20, width: 20, child: UiUtils.showCircularProgress(true, Theme.of(context).primaryColor))
: isLike
? const Icon(Icons.thumb_up_alt)
: const Icon(Icons.thumb_up_off_alt));
});
})),
],
),
],
),
],
),
onTap: () {
if (index % 3 == 0) UiUtils.showInterstitialAds(context: context);
Navigator.of(context).pushNamed(Routes.newsDetails, arguments: {"model": model, "isFromBreak": false, "fromShowMore": false});
},
),
]));
});
}
viewContent() {
return BlocBuilder<TagNewsCubit, TagNewsState>(builder: (context, state) {
if (state is TagNewsFetchSuccess) {
return Padding(
padding: const EdgeInsetsDirectional.only(top: 10.0, bottom: 10.0, start: 13.0, end: 13.0),
child: ListView.builder(
itemCount: state.tagNews.length,
shrinkWrap: true,
padding: const EdgeInsets.only(bottom: 20),
physics: const AlwaysScrollableScrollPhysics(),
itemBuilder: ((context, index) {
return newsItem((state).tagNews[index], index);
})));
}
if (state is TagNewsFetchFailure) {
return ErrorContainerWidget(
errorMsg: (state.errorMessage.contains(ErrorMessageKeys.noInternet)) ? UiUtils.getTranslatedLabel(context, 'internetmsg') : state.errorMessage, onRetry: getTagWiseNews);
}
return ShimmerNewsList(isNews: true);
});
}
}

View File

@@ -0,0 +1,229 @@
import 'package:news/data/repositories/Settings/settingsLocalDataRepository.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:news/app/routes.dart';
import 'package:news/cubits/appLocalizationCubit.dart';
import 'package:news/cubits/appSystemSettingCubit.dart';
import 'package:news/cubits/videosCubit.dart';
import 'package:news/data/models/NewsModel.dart';
import 'package:news/ui/styles/colors.dart';
import 'package:news/ui/widgets/customAppBar.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/ui/widgets/errorContainerWidget.dart';
import 'package:news/ui/widgets/networkImage.dart';
import 'package:news/ui/widgets/videoItem.dart';
import 'package:news/utils/ErrorMessageKeys.dart';
import 'package:news/utils/constant.dart';
import 'package:news/utils/uiUtils.dart';
class VideoScreen extends StatefulWidget {
const VideoScreen({super.key});
@override
VideoScreenState createState() => VideoScreenState();
static Route<dynamic> route(RouteSettings routeSettings) {
return CupertinoPageRoute(builder: (_) => const VideoScreen());
}
}
class VideoScreenState extends State<VideoScreen> {
late final PageController _videoScrollController = PageController()..addListener(hasMoreVideoScrollListener);
int currentIndex = 0;
int totalItems = 0;
String? initializedVideoId;
late String latitude, longitude;
VideoViewType? videoViewType;
void getVideos() {
Future.delayed(Duration.zero, () {
context.read<VideoCubit>().getVideo(langId: context.read<AppLocalizationCubit>().state.id, latitude: latitude, longitude: longitude);
});
}
@override
void initState() {
videoViewType = context.read<AppConfigurationCubit>().getVideoTypePreference();
setLatitudeLongitude();
getVideos();
super.initState();
}
@override
void dispose() {
_videoScrollController.dispose();
super.dispose();
}
void setLatitudeLongitude() {
latitude = SettingsLocalDataRepository().getLocationCityValues().first;
longitude = SettingsLocalDataRepository().getLocationCityValues().last;
}
void hasMoreVideoScrollListener() {
if (_videoScrollController.offset >= _videoScrollController.position.maxScrollExtent && !_videoScrollController.position.outOfRange) {
if (context.read<VideoCubit>().hasMoreVideo()) {
context.read<VideoCubit>().getMoreVideo(langId: context.read<AppLocalizationCubit>().state.id, latitude: latitude, longitude: longitude);
} else {}
}
}
@override
Widget build(BuildContext context) {
return Scaffold(appBar: CustomAppBar(height: 44, isBackBtn: false, label: 'videosLbl', isConvertText: true), body: _buildVideos(videoViewType ?? VideoViewType.normal));
}
Widget _buildVideos(VideoViewType type) {
return BlocBuilder<VideoCubit, VideoState>(builder: (context, state) {
if (state is VideoFetchSuccess) {
totalItems = state.video.length;
if (type == VideoViewType.page) {
return RefreshIndicator(
onRefresh: () async {
getVideos();
},
child: PageView.builder(
controller: _videoScrollController,
scrollDirection: Axis.vertical,
physics: PageScrollPhysics(),
itemCount: totalItems,
itemBuilder: (context, index) {
return _buildVideoContainer(
video: state.video[index], hasMore: state.hasMore, hasMoreVideoFetchError: state.hasMoreFetchError, index: index, totalCurrentVideo: state.video.length);
}));
} else {
return Padding(
padding: const EdgeInsets.only(top: 15.0),
child: ListView.separated(
controller: _videoScrollController,
itemBuilder: (context, index) {
return _buildHorizontalViewContainer(videosList: state.video, video: state.video[index], index: index, totalCurrentVideo: state.video.length);
},
separatorBuilder: (context, index) {
return SizedBox(height: 16);
},
itemCount: state.video.length),
);
}
}
if (state is VideoFetchFailure) {
return ErrorContainerWidget(errorMsg: (state.errorMessage.contains(ErrorMessageKeys.noInternet)) ? UiUtils.getTranslatedLabel(context, 'internetmsg') : state.errorMessage, onRetry: getVideos);
}
return SizedBox.shrink();
});
}
_buildVideoContainer({required NewsModel video, required int index, required int totalCurrentVideo, required bool hasMoreVideoFetchError, required bool hasMore}) {
if (index == totalCurrentVideo - 1 && index != 0) {
if (hasMore) {
if (hasMoreVideoFetchError) {
return Center(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 8.0),
child: IconButton(
onPressed: () {
context.read<VideoCubit>().getMoreVideo(
langId: context.read<AppLocalizationCubit>().state.id,
latitude: SettingsLocalDataRepository().getLocationCityValues().first,
longitude: SettingsLocalDataRepository().getLocationCityValues().last);
},
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 VideoItem(model: video);
}
Widget _buildHorizontalViewContainer({required List<NewsModel> videosList, required NewsModel video, required int index, required int totalCurrentVideo}) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: VideoNewsCard(
video: video,
videosList: videosList,
onInitializeVideo: () {
initializedVideoId = video.id;
setState(() {});
},
),
);
}
}
class VideoNewsCard extends StatefulWidget {
final NewsModel video;
final List<NewsModel> videosList;
const VideoNewsCard({super.key, required this.videosList, required this.video, required this.onInitializeVideo});
final void Function() onInitializeVideo;
@override
VideoNewsCardState createState() => VideoNewsCardState();
}
class VideoNewsCardState extends State<VideoNewsCard> {
@override
Widget build(BuildContext context) {
return Container(
clipBehavior: Clip.antiAlias,
decoration: BoxDecoration(color: UiUtils.getColorScheme(context).surface, borderRadius: BorderRadius.circular(8)),
child: GestureDetector(
onTap: () {
List<NewsModel> videosList = List.from(widget.videosList)..removeWhere((x) => x.id == widget.video.id);
Navigator.of(context).pushNamed(Routes.newsVideo, arguments: {"from": 1, "model": widget.video, "otherVideos": videosList});
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 8,
children: [
Container(height: 192, color: borderColor, child: _buildThumbnail()),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 8,
children: [
CustomTextLabel(text: widget.video.title!, textStyle: TextStyle(fontWeight: FontWeight.bold)),
if (widget.video.date != null && widget.video.date!.isNotEmpty)
Row(
spacing: 8,
children: [Icon(Icons.calendar_month_rounded), CustomTextLabel(text: UiUtils.formatDate(widget.video.date ?? ''))],
),
],
),
)
],
),
),
);
}
Widget _buildThumbnail() {
return Container(
child: Stack(
fit: StackFit.expand,
children: [
ShaderMask(
shaderCallback: (rect) =>
LinearGradient(begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [darkSecondaryColor.withOpacity(0.6), darkSecondaryColor.withOpacity(0.6)]).createShader(rect),
blendMode: BlendMode.darken,
child: Container(
color: primaryColor.withAlpha(5),
width: double.maxFinite,
height: MediaQuery.of(context).size.height / 3.3,
child: CustomNetworkImage(width: double.maxFinite, networkImageUrl: widget.video.image ?? ''),
)),
Center(
child: Icon(Icons.play_circle_outline_rounded, size: 50, color: backgroundColor),
)
],
),
);
}
}

View File

@@ -0,0 +1,93 @@
import 'package:flutter/material.dart';
import 'package:news/app/routes.dart';
import 'package:news/data/models/BreakingNewsModel.dart';
import 'package:news/data/models/LiveStreamingModel.dart';
import 'package:news/data/models/NewsModel.dart';
import 'package:news/ui/styles/colors.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/ui/widgets/networkImage.dart';
import 'package:news/utils/uiUtils.dart';
class OtherVideosCard extends StatelessWidget {
final NewsModel? model;
final BreakingNewsModel? brModel;
final LiveStreamingModel? liveModel;
OtherVideosCard({super.key, this.model, this.brModel, this.liveModel});
@override
Widget build(BuildContext context) {
bool isLive = liveModel != null;
return InkWell(
splashColor: Colors.transparent,
onTap: () {
bool isNews = (model != null) ? true : false;
bool isBreakingNews = (brModel != null) ? true : false;
int fromVal = (isNews) ? 1 : (isBreakingNews ? 2 : 3); // VAL 1 = news, 2 = liveNews , 3 = breakingNews
// and pass current model accordingly.Do not pass other videos - set it from newsVideo screen only
Map<String, dynamic> videoArguments = {"from": fromVal};
if (fromVal == 1) {
videoArguments.addAll({"model": model});
} else if (fromVal == 2) {
videoArguments.addAll({"brModel": brModel});
} else {
videoArguments.addAll({"liveModel": liveModel});
}
Navigator.pushReplacementNamed(context, Routes.newsVideo, arguments: videoArguments);
},
child: Container(
margin: const EdgeInsets.symmetric(vertical: 6),
decoration: BoxDecoration(color: UiUtils.getColorScheme(context).surface, borderRadius: BorderRadius.circular(12), border: Border.all(color: dividerColor.withAlpha(70))),
padding: const EdgeInsets.all(8),
child: Row(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(6),
child: CustomNetworkImage(
networkImageUrl: (isLive)
? liveModel!.image!
: (model != null)
? model!.image!
: brModel!.image!,
height: 60,
width: 90,
fit: BoxFit.cover)),
const SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
(!isLive && (model != null && model!.categoryName != null && model!.categoryName!.isNotEmpty))
? Container(
height: 25.0,
width: 65,
alignment: Alignment.center,
margin: const EdgeInsetsDirectional.only(start: 2.0, end: 2.0, top: 1.0, bottom: 1.0),
decoration: BoxDecoration(
color: UiUtils.getColorScheme(context).onPrimary.withAlpha(40),
border: Border.all(color: UiUtils.getColorScheme(context).secondary.withOpacity(0.85)),
borderRadius: BorderRadius.circular(5)),
child: CustomTextLabel(
text: model!.categoryName ?? "",
textStyle: Theme.of(context).textTheme.bodyMedium?.copyWith(color: UiUtils.getColorScheme(context).onPrimary, fontWeight: FontWeight.bold, fontSize: 12),
overflow: TextOverflow.ellipsis,
softWrap: true))
: SizedBox.shrink(),
SizedBox(height: 4),
CustomTextLabel(
text: (isLive)
? liveModel!.title!
: (model != null)
? model!.title ?? ""
: brModel!.title ?? "",
textStyle: TextStyle(color: UiUtils.getColorScheme(context).onPrimary),
maxLines: 2,
overflow: TextOverflow.ellipsis)
],
),
),
],
),
),
);
}
}

View File

@@ -0,0 +1,271 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:intl/intl.dart';
import 'package:news/ui/styles/colors.dart';
import 'package:news/app/routes.dart';
import 'package:news/cubits/Auth/authCubit.dart';
import 'package:news/cubits/Bookmark/UpdateBookmarkCubit.dart';
import 'package:news/cubits/Bookmark/bookmarkCubit.dart';
import 'package:news/cubits/LikeAndDislikeNews/LikeAndDislikeCubit.dart';
import 'package:news/cubits/LikeAndDislikeNews/updateLikeAndDislikeCubit.dart';
import 'package:news/cubits/appLocalizationCubit.dart';
import 'package:news/data/models/BreakingNewsModel.dart';
import 'package:news/data/models/LiveStreamingModel.dart';
import 'package:news/data/models/NewsModel.dart';
import 'package:news/data/repositories/Bookmark/bookmarkRepository.dart';
import 'package:news/data/repositories/LikeAndDisLikeNews/LikeAndDisLikeNewsRepository.dart';
import 'package:news/ui/widgets/SnackBarWidget.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/ui/widgets/videoPlayContainer.dart';
import 'package:news/utils/internetConnectivity.dart';
import 'package:news/utils/strings.dart';
import 'package:news/utils/uiUtils.dart';
class VideoCard extends StatefulWidget {
final NewsModel? model;
final BreakingNewsModel? brModel;
final LiveStreamingModel? liveModel;
VideoCard({super.key, this.model, this.liveModel, this.brModel});
@override
State<StatefulWidget> createState() => VideoCardState();
}
class VideoCardState extends State<VideoCard> {
late NewsModel? model;
late BreakingNewsModel? brModel;
late LiveStreamingModel? liveModel;
bool isLiveVideo = false, isBreakingVideo = false;
List<String>? tagList = [];
List<String>? tagId = [];
String formattedDate = "", contentType = "", contentValue = "", titleTxt = "";
@override
void initState() {
super.initState();
model = widget.model;
brModel = widget.brModel;
liveModel = widget.liveModel;
isLiveVideo = (liveModel != null);
isBreakingVideo = (brModel != null);
setFormattedDate();
setTitle();
setContentValueAndContentType();
setTags();
}
void setTitle() {
titleTxt = (isLiveVideo)
? liveModel?.title ?? ""
: (isBreakingVideo)
? brModel?.title ?? ""
: model?.title ?? "";
}
void setTags() {
if (model != null && model?.tagName != null && (model!.sourceType != null && model!.sourceType != BREAKING_NEWS)) {
if (model?.tagId != null && model!.tagId!.isNotEmpty) {
tagId = model?.tagId?.split(",");
}
if (model!.tagName!.isNotEmpty) {
final tagName = model?.tagName!;
tagList = tagName?.split(',');
}
}
}
void setFormattedDate() {
String dateVal = (isLiveVideo) ? liveModel!.updatedDate ?? "" : (model?.publishDate ?? model?.date ?? "");
if (dateVal.isNotEmpty) {
DateTime parsedDate = DateFormat("yyyy-MM-dd").parse(dateVal);
formattedDate = DateFormat("MMM dd, yyyy").format(parsedDate);
}
}
void setContentValueAndContentType() {
contentType = (isLiveVideo) ? liveModel?.type ?? "" : ((model != null) ? model?.contentType ?? "" : brModel!.contentType ?? "");
contentValue = (isLiveVideo)
? liveModel?.url ?? ""
: (model != null)
? model?.contentValue ?? ""
: brModel!.contentValue ?? "";
}
@override
void dispose() {
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
super.dispose();
}
Widget likeButton() {
bool isLike = context.read<LikeAndDisLikeCubit>().isNewsLikeAndDisLike(widget.model?.newsId ?? "0");
return BlocProvider(
create: (context) => UpdateLikeAndDisLikeStatusCubit(LikeAndDisLikeRepository()),
child: BlocConsumer<LikeAndDisLikeCubit, LikeAndDisLikeState>(
bloc: context.read<LikeAndDisLikeCubit>(),
listener: ((context, state) {
if (state is LikeAndDisLikeFetchSuccess) {
isLike = context.read<LikeAndDisLikeCubit>().isNewsLikeAndDisLike(model?.newsId ?? "0");
} else {
isLike = false; //in case of failue - no other likes found
}
}),
builder: (context, likeAndDislikeState) {
return BlocConsumer<UpdateLikeAndDisLikeStatusCubit, UpdateLikeAndDisLikeStatusState>(
bloc: context.read<UpdateLikeAndDisLikeStatusCubit>(),
listener: ((context, state) {
if (state is UpdateLikeAndDisLikeStatusSuccess) {
context.read<LikeAndDisLikeCubit>().getLike(langId: context.read<AppLocalizationCubit>().state.id);
}
}),
builder: (context, state) {
return InkWell(
splashColor: Colors.transparent,
onTap: () {
if (context.read<AuthCubit>().getUserId() != "0") {
if (state is UpdateLikeAndDisLikeStatusInProgress) {
return;
}
context.read<UpdateLikeAndDisLikeStatusCubit>().setLikeAndDisLikeNews(news: model ?? NewsModel(), status: (isLike) ? "0" : "1");
} else {
UiUtils.loginRequired(context);
}
},
child: designButtons(
childWidget: (state is UpdateLikeAndDisLikeStatusInProgress)
? SizedBox(height: 15, width: 15, child: UiUtils.showCircularProgress(true, Theme.of(context).primaryColor))
: ((isLike)
? Icon(Icons.thumb_up_alt, size: 25, color: UiUtils.getColorScheme(context).onPrimary)
: Icon(Icons.thumb_up_off_alt, size: 25, color: UiUtils.getColorScheme(context).onPrimary))));
});
}));
}
@override
Widget build(BuildContext context) {
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
return Container(
decoration: BoxDecoration(color: UiUtils.getColorScheme(context).surface, borderRadius: BorderRadius.circular(12)),
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(10),
child: AspectRatio(aspectRatio: 16 / 9, child: VideoPlayContainer(contentType: contentType, contentValue: contentValue)),
),
const SizedBox(height: 10),
(model != null && model!.sourceType != BREAKING_NEWS)
? Wrap(
spacing: 8,
children: tagList!
.map((tag) => tag.trim())
.where((tag) => tag.isNotEmpty)
.map(
(tag) => InkWell(
onTap: () async {
Navigator.of(context).pushNamed(Routes.tagScreen, arguments: {"tagId": tagId, "tagName": tagList});
},
child: Container(
height: 25.0,
width: 65,
alignment: Alignment.center,
padding: const EdgeInsetsDirectional.only(start: 3.0, end: 3.0, top: 1.0, bottom: 1.0),
decoration: BoxDecoration(borderRadius: const BorderRadius.all(Radius.circular(15)), color: UiUtils.getColorScheme(context).secondary.withOpacity(0.85)),
child: CustomTextLabel(
text: tag,
textStyle: Theme.of(context).textTheme.bodyMedium?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer, fontSize: 12),
overflow: TextOverflow.ellipsis,
softWrap: true)),
),
)
.toList(),
)
: SizedBox.shrink(),
const SizedBox(height: 10),
CustomTextLabel(text: titleTxt, textStyle: TextStyle(color: UiUtils.getColorScheme(context).onPrimary)),
const SizedBox(height: 10),
Divider(),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
(formattedDate.isNotEmpty)
? Row(
children: [
Icon(Icons.calendar_month_rounded, size: 20),
SizedBox(width: 4),
CustomTextLabel(text: formattedDate, textStyle: TextStyle(fontSize: 12, color: UiUtils.getColorScheme(context).onPrimary)),
],
)
: SizedBox.shrink(),
Row(
children: [
if ((model?.sourceType != null && model?.newsId != null || model?.id != null) || (model?.sourceType == NEWS || model?.sourceType == VIDEOS))
Row(children: [
InkWell(
onTap: () async {
(await InternetConnectivity.isNetworkAvailable())
? UiUtils.shareNews(
context: context, slug: model?.slug ?? "", title: model?.title ?? "", isVideo: true, videoId: model?.id ?? "0", isBreakingNews: false, isNews: false)
: showSnackBar(UiUtils.getTranslatedLabel(context, 'internetmsg'), context);
},
splashColor: Colors.transparent,
child: designButtons(childWidget: Icon(Icons.share_rounded, color: UiUtils.getColorScheme(context).onPrimary))),
const SizedBox(height: 15),
BlocProvider(
create: (context) => UpdateBookmarkStatusCubit(BookmarkRepository()),
child: BlocBuilder<BookmarkCubit, BookmarkState>(
bloc: context.read<BookmarkCubit>(),
builder: (context, bookmarkState) {
bool isBookmark = context.read<BookmarkCubit>().isNewsBookmark(model?.id ?? "0");
return BlocConsumer<UpdateBookmarkStatusCubit, UpdateBookmarkStatusState>(
bloc: context.read<UpdateBookmarkStatusCubit>(),
listener: ((context, state) {
if (state is UpdateBookmarkStatusSuccess) {
(state.wasBookmarkNewsProcess) ? context.read<BookmarkCubit>().addBookmarkNews(state.news) : context.read<BookmarkCubit>().removeBookmarkNews(state.news);
setState(() {});
}
}),
builder: (context, state) {
return InkWell(
onTap: () {
if (context.read<AuthCubit>().getUserId() != "0") {
if (state is UpdateBookmarkStatusInProgress) return;
context.read<UpdateBookmarkStatusCubit>().setBookmarkNews(news: model!, status: (isBookmark) ? "0" : "1");
} else {
UiUtils.loginRequired(context);
}
},
child: state is UpdateBookmarkStatusInProgress
? SizedBox(height: 15, width: 15, child: UiUtils.showCircularProgress(true, Theme.of(context).primaryColor))
: designButtons(
childWidget: Icon(isBookmark ? Icons.bookmark_added_rounded : Icons.bookmark_add_outlined, color: UiUtils.getColorScheme(context).onPrimary)));
});
}),
),
const SizedBox(height: 15),
likeButton()
]),
],
),
],
),
Divider(),
],
),
);
}
Widget designButtons({required Widget childWidget}) {
return Container(
height: 30,
width: 30,
margin: EdgeInsets.symmetric(horizontal: 5),
padding: EdgeInsets.all(3),
decoration: BoxDecoration(shape: BoxShape.rectangle, borderRadius: BorderRadius.circular(7), color: borderColor.withOpacity(0.2)),
child: childWidget);
}
}

View File

@@ -0,0 +1,104 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:news/data/models/BreakingNewsModel.dart';
import 'package:news/data/models/LiveStreamingModel.dart';
import 'package:news/data/models/NewsModel.dart';
import 'package:news/ui/screens/Videos/Widgets/otherVideosCard.dart';
import 'package:news/ui/screens/Videos/Widgets/videoCard.dart';
import 'package:news/ui/widgets/customAppBar.dart';
import 'package:news/utils/uiUtils.dart';
class VideoDetailsScreen extends StatefulWidget {
int from;
LiveStreamingModel? liveModel;
NewsModel? model;
BreakingNewsModel? breakModel;
List<NewsModel>? otherVideos;
List<BreakingNewsModel>? otherBreakingVideos;
List<LiveStreamingModel>? otherLiveVideos;
VideoDetailsScreen({super.key, this.model, required this.from, this.liveModel, this.breakModel, required this.otherVideos, this.otherLiveVideos, this.otherBreakingVideos});
@override
State<StatefulWidget> createState() => VideoDetailsState();
static Route route(RouteSettings routeSettings) {
final arguments = routeSettings.arguments as Map<String, dynamic>;
return CupertinoPageRoute(
builder: (_) => VideoDetailsScreen(
from: arguments['from'],
liveModel: arguments['liveModel'],
model: arguments['model'],
breakModel: arguments['breakModel'],
otherVideos: arguments['otherVideos'],
otherLiveVideos: arguments['otherLiveVideos'],
otherBreakingVideos: arguments['otherBreakingVideos']));
}
}
class VideoDetailsState extends State<VideoDetailsScreen> {
bool isLiveVideo = false, isBreakingNewsVideo = false, isNewsVideo = true;
//FROM VAL 1 = news, 2 = liveNews , 3 = breakingNews
@override
void initState() {
isLiveVideo = (widget.from == 2);
isBreakingNewsVideo = (widget.from == 3);
isNewsVideo = !(isLiveVideo || isBreakingNewsVideo);
super.initState();
}
@override
void dispose() {
// set screen back to portrait mode
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
super.dispose();
}
@override
Widget build(BuildContext context) {
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
return Scaffold(
backgroundColor: UiUtils.getColorScheme(context).surface,
appBar: CustomAppBar(height: 45, isBackBtn: true, label: 'videosLbl', isConvertText: true),
body: SingleChildScrollView(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
VideoCard(
model: (widget.model != null) ? widget.model : null, brModel: (widget.breakModel != null) ? widget.breakModel : null, liveModel: (widget.liveModel != null) ? widget.liveModel : null),
const SizedBox(height: 16),
((isLiveVideo && widget.otherLiveVideos != null && widget.otherLiveVideos!.isNotEmpty) ||
((widget.otherVideos != null && widget.otherVideos!.isNotEmpty) || (widget.otherBreakingVideos != null && widget.otherBreakingVideos!.isNotEmpty)))
? Text(
UiUtils.getTranslatedLabel(context, 'recentVidLbl'),
style: TextStyle(color: UiUtils.getColorScheme(context).onPrimary, fontWeight: FontWeight.bold, fontSize: 18),
)
: SizedBox.shrink(),
const SizedBox(height: 8),
((isLiveVideo && widget.otherLiveVideos != null && widget.otherLiveVideos!.isNotEmpty) ||
((widget.otherVideos != null && widget.otherVideos!.isNotEmpty) || (widget.otherBreakingVideos != null && widget.otherBreakingVideos!.isNotEmpty)))
? ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: (isLiveVideo)
? widget.otherLiveVideos!.length
: (widget.otherVideos != null && widget.otherVideos!.isNotEmpty)
? widget.otherVideos!.length
: widget.otherBreakingVideos!.length,
itemBuilder: (context, index) => OtherVideosCard(
brModel: (widget.otherBreakingVideos != null && widget.otherBreakingVideos!.isNotEmpty) ? widget.otherBreakingVideos![index] : null,
model: (widget.otherVideos != null && widget.otherVideos!.isNotEmpty) ? widget.otherVideos![index] : null,
liveModel: (widget.otherLiveVideos != null && widget.otherLiveVideos!.isNotEmpty) ? widget.otherLiveVideos![index] : null),
)
: SizedBox.shrink(),
],
),
),
);
}
}

View File

@@ -0,0 +1,137 @@
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:news/ui/screens/auth/Widgets/svgPictureWidget.dart';
import 'package:news/ui/widgets/customBackBtn.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/utils/internetConnectivity.dart';
import 'package:news/utils/uiUtils.dart';
import 'package:news/ui/widgets/SnackBarWidget.dart';
import 'package:news/ui/screens/auth/Widgets/setEmail.dart';
import 'package:news/ui/screens/auth/Widgets/setLoginAndSignUpBtn.dart';
class ForgotPassword extends StatefulWidget {
const ForgotPassword({super.key});
@override
FrgtPswdState createState() => FrgtPswdState();
}
class FrgtPswdState extends State<ForgotPassword> {
TextEditingController emailC = TextEditingController();
final FirebaseAuth _auth = FirebaseAuth.instance;
final GlobalKey<FormState> _formkey = GlobalKey<FormState>();
int durationInMiliSeconds = 2500;
@override
void dispose() {
emailC.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(body: screenContent());
}
Widget backBtn() {
return const Padding(padding: EdgeInsets.only(top: 20.0, left: 10.0), child: CustomBackButton());
}
Widget forgotIcon() {
return Container(
padding: const EdgeInsets.all(20.0),
child: Center(
child: SvgPictureWidget(
assetName: "forgot",
width: 120,
height: 120,
fit: BoxFit.fill,
assetColor: ColorFilter.mode(UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.7), BlendMode.srcIn),
),
),
);
}
Widget forgotPassLbl() {
return Center(
child: CustomTextLabel(
text: 'forgotPassLbl',
maxLines: 3,
textStyle: Theme.of(context).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w600, fontSize: 22, color: UiUtils.getColorScheme(context).primaryContainer),
),
);
}
Widget forgotPassHead() {
return Padding(
padding: const EdgeInsetsDirectional.only(top: 20.0),
child: CustomTextLabel(
text: 'frgtPassHead',
maxLines: 3,
textStyle: Theme.of(context).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w500, fontSize: 16, color: UiUtils.getColorScheme(context).primaryContainer),
));
}
Widget forgotPassSubHead() {
return Padding(
padding: const EdgeInsetsDirectional.only(top: 30.0),
child: CustomTextLabel(
text: 'forgotPassSub',
maxLines: 3,
textStyle: Theme.of(context).textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.normal, fontSize: 14.0, color: UiUtils.getColorScheme(context).primaryContainer),
));
}
Widget emailTextCtrl() {
return SetEmail(emailC: emailC, email: emailC.text, topPad: 25);
}
Widget submitBtn() {
return SetLoginAndSignUpBtn(
onTap: () async {
FocusScope.of(context).unfocus(); //dismiss keyboard
if (await InternetConnectivity.isNetworkAvailable()) {
Future.delayed(const Duration(seconds: 1)).then((_) async {
if (emailC.text.isEmpty) {
showSnackBar(UiUtils.getTranslatedLabel(context, 'emailValid'), context, durationInMiliSeconds: durationInMiliSeconds);
} else {
try {
await _auth.sendPasswordResetEmail(email: emailC.text.trim());
final form = _formkey.currentState;
form!.save();
if (form.validate()) {
showSnackBar(UiUtils.getTranslatedLabel(context, 'passReset'), context, durationInMiliSeconds: durationInMiliSeconds);
Navigator.pop(context);
}
} on FirebaseAuthException catch (e) {
if (e.code == "user-not-found") {
showSnackBar(UiUtils.getTranslatedLabel(context, 'userNotFound'), context, durationInMiliSeconds: durationInMiliSeconds);
} else {
showSnackBar(e.message!, context, durationInMiliSeconds: durationInMiliSeconds);
}
}
}
});
} else {
showSnackBar(UiUtils.getTranslatedLabel(context, 'internetmsg'), context, durationInMiliSeconds: durationInMiliSeconds);
}
},
text: 'submitBtn',
topPad: 30);
}
Widget screenContent() {
return Container(
padding: const EdgeInsetsDirectional.all(20.0),
child: SingleChildScrollView(
child: Form(
key: _formkey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: [backBtn(), const SizedBox(height: 50), forgotIcon(), forgotPassLbl(), forgotPassHead(), forgotPassSubHead(), emailTextCtrl(), submitBtn()],
),
)));
}
}

View File

@@ -0,0 +1,263 @@
import 'package:country_code_picker/country_code_picker.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:news/cubits/appSystemSettingCubit.dart';
import 'package:news/ui/styles/colors.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/ui/widgets/SnackBarWidget.dart';
import 'package:news/app/routes.dart';
import 'package:news/utils/uiUtils.dart';
import 'package:news/utils/internetConnectivity.dart';
import 'package:news/utils/validators.dart';
class RequestOtp extends StatefulWidget {
const RequestOtp({super.key});
@override
RequestOtpState createState() => RequestOtpState();
}
class RequestOtpState extends State<RequestOtp> {
TextEditingController phoneC = TextEditingController();
String? phone, conCode;
final GlobalKey<FormState> _formkey = GlobalKey<FormState>();
bool isLoading = false;
CountryCode? code;
String? verificationId;
String errorMessage = '';
final FirebaseAuth _auth = FirebaseAuth.instance;
int forceResendingToken = 0;
@override
void initState() {
super.initState();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
SafeArea(
child: showContent(),
),
UiUtils.showCircularProgress(isLoading, Theme.of(context).primaryColor)
],
));
}
//show form content
showContent() {
return Container(
padding: const EdgeInsetsDirectional.all(20.0),
child: SingleChildScrollView(
child: Form(
key: _formkey,
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[
Align(
//backButton
alignment: Alignment.topLeft,
child: InkWell(onTap: () => Navigator.of(context).pop(), splashColor: Colors.transparent, child: const Icon(Icons.keyboard_backspace_rounded))),
const SizedBox(height: 50),
otpVerifySet(),
enterMblSet(),
receiveDigitSet(),
setCodeWithMono(),
reqOtpBtn()
]))),
);
}
otpVerifySet() {
return CustomTextLabel(
text: 'loginLbl',
textStyle: Theme.of(context).textTheme.headlineSmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer, fontWeight: FontWeight.w800, letterSpacing: 0.5),
textAlign: TextAlign.center);
}
enterMblSet() {
return Padding(
padding: const EdgeInsets.only(top: 35.0),
child: CustomTextLabel(
text: 'enterMblLbl',
textStyle: Theme.of(context).textTheme.bodyLarge?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer, fontWeight: FontWeight.w500),
),
);
}
receiveDigitSet() {
return Container(
padding: const EdgeInsets.only(top: 20.0),
child: CustomTextLabel(
text: 'receiveDigitLbl',
textStyle: Theme.of(context).textTheme.bodyMedium?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.8), fontSize: 14),
textAlign: TextAlign.left),
);
}
setCodeWithMono() {
return Padding(
padding: const EdgeInsets.only(top: 30.0),
child: Container(
decoration: BoxDecoration(borderRadius: BorderRadius.circular(10.0), color: Theme.of(context).colorScheme.surface),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[setCountryCode(), setMono()],
)));
}
setCountryCode() {
double width = MediaQuery.of(context).size.width;
double height = MediaQuery.of(context).size.height;
return SizedBox(
height: 45,
child: CountryCodePicker(
boxDecoration: BoxDecoration(color: Theme.of(context).colorScheme.surface),
searchDecoration: InputDecoration(hintStyle: TextStyle(color: UiUtils.getColorScheme(context).primaryContainer), fillColor: UiUtils.getColorScheme(context).primaryContainer),
initialSelection: context.read<AppConfigurationCubit>().getCountryCode(),
dialogSize: Size(width - 50, height - 50),
builder: (CountryCode? code) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsetsDirectional.only(top: 10.0, bottom: 10.0, start: 20.0, end: 4.0),
child:
ClipRRect(borderRadius: BorderRadius.circular(3.0), child: Image.asset(code!.flagUri.toString(), package: 'country_code_picker', height: 30, width: 30, fit: BoxFit.cover))),
Container(
//CountryCode
width: 55.0,
height: 55.0,
alignment: Alignment.center,
child: CustomTextLabel(
text: code.dialCode.toString(),
textStyle: Theme.of(context).textTheme.titleMedium?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.7)),
overflow: TextOverflow.ellipsis,
softWrap: true)),
],
);
},
onChanged: (CountryCode countryCode) {
conCode = countryCode.dialCode;
},
onInit: (CountryCode? code) {
conCode = code?.dialCode;
}));
}
setMono() {
return Expanded(
child: Padding(
padding: const EdgeInsetsDirectional.only(top: 5.0, bottom: 15.0),
child: Container(
height: 40,
width: MediaQuery.of(context).size.width * 0.57,
alignment: Alignment.center,
child: TextFormField(
keyboardType: const TextInputType.numberWithOptions(signed: true, decimal: true),
controller: phoneC,
style: Theme.of(context).textTheme.titleMedium?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.7)),
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
validator: (val) => Validators.mobValidation(val!, context),
onSaved: (String? value) => phone = value,
decoration: InputDecoration(
hintText: UiUtils.getTranslatedLabel(context, 'enterMblLbl'),
hintStyle: Theme.of(context).textTheme.titleSmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.5)),
enabledBorder: InputBorder.none,
focusedBorder: InputBorder.none,
),
)),
),
);
}
Future<void> verifyPhone(BuildContext context) async {
try {
await _auth.verifyPhoneNumber(
phoneNumber: (conCode!.contains("+")) ? "$conCode${phoneC.text.trim()}" : "+$conCode${phoneC.text.trim()}",
verificationCompleted: (AuthCredential phoneAuthCredential) {
showSnackBar(phoneAuthCredential.toString(), context);
},
verificationFailed: (FirebaseAuthException exception) {
setState(() => isLoading = false);
if (exception.code == "invalid-phone-number") {
//invalidPhoneNumber
showSnackBar(UiUtils.getTranslatedLabel(context, 'invalidPhoneNumber'), context);
} else {
showSnackBar('${exception.message}', context);
}
},
forceResendingToken: forceResendingToken,
codeAutoRetrievalTimeout: (String verId) => verificationId = verId,
codeSent: processCodeSent(),
//smsOTPSent
timeout: const Duration(seconds: 60));
} on FirebaseAuthException catch (authError) {
setState(() => isLoading = false);
showSnackBar(authError.message!, context);
} on FirebaseException catch (e) {
setState(() => isLoading = false);
showSnackBar(e.toString(), context);
} catch (e) {
setState(() => isLoading = false);
showSnackBar(e.toString(), context);
}
}
processCodeSent() {
try {
smsOTPSent(String? verId, [int? forceCodeResend]) async {
if (forceCodeResend != null) forceResendingToken = forceCodeResend;
verificationId = verId;
setState(() => isLoading = false);
showSnackBar(UiUtils.getTranslatedLabel(context, 'codeSent'), context);
await Navigator.of(context).pushNamed(Routes.verifyOtp, arguments: {"verifyId": verificationId, "countryCode": conCode, "mono": phoneC.text.trim()});
}
return smsOTPSent;
} catch (e) {}
}
reqOtpBtn() {
return Container(
alignment: Alignment.center,
padding: const EdgeInsets.only(top: 60.0),
child: InkWell(
child: Container(
height: 45.0,
width: MediaQuery.of(context).size.width * 0.9,
alignment: Alignment.center,
decoration: BoxDecoration(color: Theme.of(context).primaryColor, borderRadius: BorderRadius.circular(7.0)),
child:
CustomTextLabel(text: 'reqOtpLbl', textStyle: Theme.of(context).textTheme.titleLarge?.copyWith(color: secondaryColor, fontWeight: FontWeight.w600, letterSpacing: 0.5, fontSize: 16))),
onTap: () async {
FocusScope.of(context).unfocus(); //dismiss keyboard
if (validateAndSave()) {
if (await InternetConnectivity.isNetworkAvailable()) {
setState(() => isLoading = true);
verifyPhone(context);
} else {
showSnackBar(UiUtils.getTranslatedLabel(context, 'internetmsg'), context);
}
}
},
),
);
}
//check validation of form data
bool validateAndSave() {
final form = _formkey.currentState;
form!.save();
return (form.validate()) ? true : false;
}
}

View File

@@ -0,0 +1,268 @@
import 'dart:async';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:news/cubits/Auth/socialSignUpCubit.dart';
import 'package:news/ui/styles/colors.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/utils/uiUtils.dart';
import 'package:sms_autofill/sms_autofill.dart';
import 'package:news/cubits/Auth/authCubit.dart';
import 'package:news/utils/internetConnectivity.dart';
import 'package:news/ui/widgets/SnackBarWidget.dart';
class VerifyOtp extends StatefulWidget {
String? verifyId, countryCode, mono;
VerifyOtp({super.key, this.verifyId, this.countryCode, this.mono});
@override
VerifyOtpState createState() => VerifyOtpState();
static Route route(RouteSettings routeSettings) {
final arguments = routeSettings.arguments as Map<String, dynamic>;
return CupertinoPageRoute(
builder: (_) => VerifyOtp(
verifyId: arguments['verifyId'],
countryCode: arguments['countryCode'],
mono: arguments['mono'],
));
}
}
class VerifyOtpState extends State<VerifyOtp> {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
String? otp;
final GlobalKey<FormState> _formkey = GlobalKey<FormState>();
int secondsRemaining = 60;
bool enableResend = false;
Timer? timer;
final FirebaseAuth _auth = FirebaseAuth.instance;
String? verifId;
@override
void initState() {
super.initState();
timer = Timer.periodic(const Duration(seconds: 1), (_) {
if (secondsRemaining != 0) {
setState(() {
secondsRemaining--;
});
} else {
setState(() {
enableResend = true;
});
}
});
}
@override
void dispose() {
timer?.cancel();
super.dispose();
}
void _resendCode() {
otp = "";
_onVerifyCode();
setState(() {
secondsRemaining = 60;
enableResend = false;
});
}
void _onVerifyCode() async {
verificationCompleted(AuthCredential phoneAuthCredential) {
_auth.signInWithCredential(phoneAuthCredential).then((UserCredential value) {
if (value.user != null) {
showSnackBar(UiUtils.getTranslatedLabel(context, 'otpMsg'), context);
} else {
showSnackBar(UiUtils.getTranslatedLabel(context, 'otpError'), context);
}
}).catchError((error) {
showSnackBar(error.toString(), context);
});
}
verificationFailed(FirebaseAuthException authException) {
if (authException.code == 'invalidVerificationCode') {
showSnackBar(UiUtils.getTranslatedLabel(context, 'invalidVerificationCode'), context);
} else {
showSnackBar(authException.message.toString(), context);
}
}
codeSent(String? verificationId, [int? forceResendingToken]) async {
verifId = verificationId;
if (mounted) {
setState(() {
verifId = verificationId;
});
}
}
codeAutoRetrievalTimeout(String verificationId) {
verifId = verificationId;
setState(() {
verifId = verificationId;
});
showSnackBar(UiUtils.getTranslatedLabel(context, 'otpTimeoutLbl'), context);
}
await _auth.verifyPhoneNumber(
phoneNumber: "+${widget.countryCode}${widget.mono}",
timeout: const Duration(seconds: 60),
verificationCompleted: verificationCompleted,
verificationFailed: verificationFailed,
codeSent: codeSent,
codeAutoRetrievalTimeout: codeAutoRetrievalTimeout);
}
@override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
body: BlocConsumer<SocialSignUpCubit, SocialSignUpState>(
bloc: context.read<SocialSignUpCubit>(),
listener: (context, state) async {
if (state is SocialSignUpFailure) {
showSnackBar(state.errorMessage, context);
}
},
builder: (context, state) {
return Stack(
children: <Widget>[SafeArea(child: showContent()), if (state is SocialSignUpProgress) Center(child: UiUtils.showCircularProgress(true, Theme.of(context).primaryColor))],
);
}));
}
//show form content
showContent() {
return Container(
padding: const EdgeInsetsDirectional.all(20.0),
child: SingleChildScrollView(
child: Form(
key: _formkey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[const SizedBox(height: 50), otpVerifySet(), otpSentSet(), mblSet(), otpFillSet(), buildTimer(), submitBtn(), if (secondsRemaining == 0) showResendOTPButton()]))),
);
}
otpVerifySet() {
return CustomTextLabel(
text: 'otpVerifyLbl',
textStyle: Theme.of(context).textTheme.headlineSmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer, fontWeight: FontWeight.w800, letterSpacing: 0.5),
textAlign: TextAlign.left);
}
otpSentSet() {
return Padding(
padding: const EdgeInsets.only(top: 25.0),
child: CustomTextLabel(text: 'otpSentLbl', textStyle: Theme.of(context).textTheme.titleMedium?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer, fontWeight: FontWeight.w500)),
);
}
mblSet() {
return Padding(
padding: const EdgeInsets.only(top: 10.0),
child: CustomTextLabel(
text: "${widget.countryCode} ${widget.mono}", textStyle: Theme.of(context).textTheme.titleSmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.8))),
);
}
otpFillSet() {
return Container(
alignment: Alignment.center,
padding: const EdgeInsets.only(top: 30.0),
child: PinFieldAutoFill(
decoration: BoxLooseDecoration(
strokeColorBuilder: PinListenColorBuilder(Theme.of(context).colorScheme.surface, Theme.of(context).colorScheme.surface),
bgColorBuilder: PinListenColorBuilder(Theme.of(context).colorScheme.surface, Theme.of(context).colorScheme.surface),
gapSpace: 7.0),
currentCode: otp,
codeLength: 6,
keyboardType: const TextInputType.numberWithOptions(signed: true, decimal: true),
onCodeChanged: (String? code) {
otp = code;
},
onCodeSubmitted: (String code) {
otp = code;
}));
}
showResendOTPButton() {
return Container(
padding: const EdgeInsets.only(top: 20.0),
alignment: Alignment.center,
child: Text.rich(
TextSpan(
text: "${UiUtils.getTranslatedLabel(context, 'didntGetCode')} ",
style: TextStyle(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.8), fontWeight: FontWeight.bold, letterSpacing: 0.5),
children: [
TextSpan(
text: UiUtils.getTranslatedLabel(context, 'resendLbl'),
style: TextStyle(color: UiUtils.getColorScheme(context).secondaryContainer, fontWeight: FontWeight.bold, letterSpacing: 0.5, decoration: TextDecoration.underline),
recognizer: TapGestureRecognizer()..onTap = enableResend ? _resendCode : null,
),
],
),
textAlign: TextAlign.center,
));
}
buildTimer() {
return Container(
alignment: AlignmentDirectional.bottomStart,
padding: const EdgeInsets.only(top: 30.0),
child: secondsRemaining != 0
? Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
CustomTextLabel(text: 'resendCodeLbl', textStyle: Theme.of(context).textTheme.titleSmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.8))),
CustomTextLabel(text: ' 00:${secondsRemaining.toString().padLeft(2, '0')}', textStyle: Theme.of(context).textTheme.titleSmall?.copyWith(color: Theme.of(context).primaryColor)),
],
)
: SizedBox.shrink(),
);
}
submitBtn() {
return Container(
padding: const EdgeInsets.only(top: 45.0),
child: InkWell(
child: Container(
height: 45.0,
width: MediaQuery.of(context).size.width * 0.9,
alignment: Alignment.center,
decoration: BoxDecoration(color: Theme.of(context).primaryColor, borderRadius: BorderRadius.circular(7.0)),
child:
CustomTextLabel(text: 'submitBtn', textStyle: Theme.of(context).textTheme.titleLarge?.copyWith(color: secondaryColor, fontWeight: FontWeight.w600, fontSize: 16, letterSpacing: 0.6)),
),
onTap: () async {
if (otp!.trim().isEmpty) return showSnackBar(UiUtils.getTranslatedLabel(context, 'enterOtpTxt'), context);
FocusScope.of(context).unfocus(); //dismiss keyboard
if (validateAndSave()) {
if (await InternetConnectivity.isNetworkAvailable()) {
context.read<SocialSignUpCubit>().socialSignUpUser(authProvider: AuthProviders.mobile, verifiedId: widget.verifyId, otp: otp, context: context);
} else {
showSnackBar(UiUtils.getTranslatedLabel(context, 'internetmsg'), context);
}
}
}));
}
//check validation of form data
bool validateAndSave() {
final form = _formkey.currentState;
form!.save();
if (form.validate()) {
return true;
}
return false;
}
}

View File

@@ -0,0 +1,35 @@
import 'package:flutter/material.dart';
import 'package:news/ui/screens/auth/Widgets/svgPictureWidget.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/utils/uiUtils.dart';
class BottomCommButton extends StatelessWidget {
final Function onTap;
final String img;
final Color? btnColor;
final String btnCaption;
const BottomCommButton({super.key, required this.onTap, required this.img, this.btnColor, required this.btnCaption});
@override
Widget build(BuildContext context) {
String textLbl = "${UiUtils.getTranslatedLabel(context, 'continueWith')} ${UiUtils.getTranslatedLabel(context, btnCaption)}";
return InkWell(
splashColor: Colors.transparent,
child: Container(
height: 45.0,
width: MediaQuery.of(context).size.width * 0.9,
alignment: Alignment.center,
decoration: BoxDecoration(color: UiUtils.getColorScheme(context).surface, borderRadius: BorderRadius.circular(7.0)),
padding: const EdgeInsets.all(9.0),
margin: EdgeInsets.symmetric(vertical: 10),
child: Wrap(
spacing: 15,
children: [
SvgPictureWidget(assetName: img, width: 20, height: 20, fit: BoxFit.contain, assetColor: btnColor != null ? ColorFilter.mode(btnColor!, BlendMode.srcIn) : null),
CustomTextLabel(text: textLbl, textStyle: TextStyle(fontSize: 14, fontWeight: FontWeight.w600))
],
)),
onTap: () => onTap());
}
}

View File

@@ -0,0 +1,6 @@
import 'package:flutter/cupertino.dart';
fieldFocusChange(BuildContext context, FocusNode currentFocus, FocusNode nextFocus) {
currentFocus.unfocus();
FocusScope.of(context).requestFocus(nextFocus);
}

View File

@@ -0,0 +1,77 @@
import 'package:flutter/material.dart';
import 'package:news/utils/uiUtils.dart';
class SetConfirmPass extends StatefulWidget {
final FocusNode currFocus;
final TextEditingController confPassC;
late String confPass;
late String pass;
SetConfirmPass({super.key, required this.currFocus, required this.confPassC, required this.confPass, required this.pass});
@override
State<StatefulWidget> createState() {
return _SetConfPassState();
}
}
class _SetConfPassState extends State<SetConfirmPass> {
bool isObscure = true;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(top: 20),
child: TextFormField(
focusNode: widget.currFocus,
textInputAction: TextInputAction.done,
controller: widget.confPassC,
obscureText: isObscure,
style: Theme.of(context).textTheme.titleMedium?.copyWith(
color: UiUtils.getColorScheme(context).primaryContainer,
),
validator: (value) {
if (value!.isEmpty) {
return UiUtils.getTranslatedLabel(context, 'confPassRequired');
}
if (value.trim() != widget.pass.trim()) {
return UiUtils.getTranslatedLabel(context, 'confPassNotMatch');
} else {
return null;
}
},
onChanged: (String value) {
widget.confPass = value;
},
decoration: InputDecoration(
hintText: UiUtils.getTranslatedLabel(context, 'confpassLbl'),
hintStyle: Theme.of(context).textTheme.titleMedium?.copyWith(
color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.5),
),
suffixIcon: Padding(
padding: const EdgeInsetsDirectional.only(end: 12.0),
child: IconButton(
icon: isObscure
? Icon(Icons.visibility_rounded, size: 20, color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.6))
: Icon(Icons.visibility_off_rounded, size: 20, color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.6)),
splashColor: Colors.transparent,
onPressed: () {
setState(() => isObscure = !isObscure);
},
)),
filled: true,
fillColor: Theme.of(context).colorScheme.surface,
contentPadding: const EdgeInsets.symmetric(horizontal: 25, vertical: 17),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: UiUtils.getColorScheme(context).outline.withOpacity(0.7)),
borderRadius: BorderRadius.circular(10.0),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.circular(10.0),
),
),
),
);
}
}

View File

@@ -0,0 +1,25 @@
import 'package:flutter/material.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/utils/uiUtils.dart';
class SetDividerOR extends StatelessWidget {
const SetDividerOR({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
Color color = UiUtils.getColorScheme(context).outline.withOpacity(0.9);
return Padding(
padding: const EdgeInsetsDirectional.only(top: 30.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(child: Divider(indent: 10, endIndent: 10, color: color)),
CustomTextLabel(
text: 'orLbl',
textStyle: Theme.of(context).textTheme.titleMedium?.merge(TextStyle(color: color, fontSize: 12.0)),
),
Expanded(child: Divider(indent: 10, endIndent: 10, color: color)),
],
));
}
}

View File

@@ -0,0 +1,47 @@
import 'package:flutter/material.dart';
import 'package:news/utils/uiUtils.dart';
import 'package:news/utils/validators.dart';
import 'fieldFocusChange.dart';
class SetEmail extends StatelessWidget {
final FocusNode? currFocus;
final FocusNode? nextFocus;
final TextEditingController emailC;
late String email;
final double topPad;
SetEmail({super.key, this.currFocus, this.nextFocus, required this.emailC, required this.email, required this.topPad});
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.only(top: topPad),
child: TextFormField(
focusNode: currFocus,
textInputAction: TextInputAction.next,
controller: emailC,
style: Theme.of(context).textTheme.titleMedium?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer),
validator: (val) => Validators.emailValidation(val!, context),
onFieldSubmitted: (v) {
if (currFocus != null || nextFocus != null) fieldFocusChange(context, currFocus!, nextFocus!);
},
decoration: InputDecoration(
hintText: UiUtils.getTranslatedLabel(context, 'emailLbl'),
hintStyle: Theme.of(context).textTheme.titleMedium?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.5)),
filled: true,
fillColor: Theme.of(context).colorScheme.surface,
contentPadding: const EdgeInsets.symmetric(horizontal: 25, vertical: 17),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: UiUtils.getColorScheme(context).outline.withOpacity(0.7)),
borderRadius: BorderRadius.circular(10.0),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.circular(10.0),
),
),
),
);
}
}

View File

@@ -0,0 +1,16 @@
import 'package:flutter/material.dart';
import 'package:news/ui/widgets/customTextBtn.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/utils/uiUtils.dart';
import 'package:news/app/routes.dart';
setForgotPass(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(top: 5.0),
child: Align(
alignment: Alignment.topRight,
child: CustomTextButton(
onTap: () => Navigator.of(context).pushNamed(Routes.forgotPass),
buttonStyle: ButtonStyle(overlayColor: WidgetStateProperty.all(Colors.transparent), foregroundColor: WidgetStateProperty.all(UiUtils.getColorScheme(context).outline.withOpacity(0.7))),
textWidget: const CustomTextLabel(text: 'forgotPassLbl'))));
}

View File

@@ -0,0 +1,31 @@
import 'package:flutter/material.dart';
import 'package:news/ui/styles/colors.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
class SetLoginAndSignUpBtn extends StatelessWidget {
final Function onTap;
final String text;
final double topPad;
const SetLoginAndSignUpBtn({super.key, required this.onTap, required this.text, required this.topPad});
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.only(top: topPad),
child: InkWell(
splashColor: Colors.transparent,
child: Container(
height: 45.0,
width: MediaQuery.of(context).size.width * 0.9,
alignment: Alignment.center,
decoration: BoxDecoration(color: Theme.of(context).primaryColor, borderRadius: BorderRadius.circular(7.0)),
child: CustomTextLabel(
text: text,
textStyle: Theme.of(context).textTheme.titleLarge?.copyWith(color: secondaryColor, fontWeight: FontWeight.w600, fontSize: 16, letterSpacing: 0.6),
),
),
onTap: () => onTap()),
);
}
}

View File

@@ -0,0 +1,49 @@
import 'package:flutter/material.dart';
import 'package:news/utils/uiUtils.dart';
import 'package:news/utils/validators.dart';
import 'package:news/ui/screens/auth/Widgets/fieldFocusChange.dart';
class SetName extends StatelessWidget {
final FocusNode currFocus;
final FocusNode nextFocus;
final TextEditingController nameC;
late String name;
SetName({super.key, required this.currFocus, required this.nextFocus, required this.nameC, required this.name});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(top: 40),
child: TextFormField(
focusNode: currFocus,
textInputAction: TextInputAction.next,
controller: nameC,
style: Theme.of(context).textTheme.titleMedium?.copyWith(
color: UiUtils.getColorScheme(context).primaryContainer,
),
validator: (val) => Validators.nameValidation(val!, context),
onFieldSubmitted: (v) {
fieldFocusChange(context, currFocus, nextFocus);
},
decoration: InputDecoration(
hintText: UiUtils.getTranslatedLabel(context, 'nameLbl'),
hintStyle: Theme.of(context).textTheme.titleMedium?.copyWith(
color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.5),
),
filled: true,
fillColor: Theme.of(context).colorScheme.surface,
contentPadding: const EdgeInsets.symmetric(horizontal: 25, vertical: 17),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: UiUtils.getColorScheme(context).outline.withOpacity(0.7)),
borderRadius: BorderRadius.circular(10.0),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.circular(10.0),
),
),
),
);
}
}

View File

@@ -0,0 +1,81 @@
import 'package:flutter/material.dart';
import 'package:news/utils/uiUtils.dart';
import 'package:news/utils/validators.dart';
import 'fieldFocusChange.dart';
class SetPassword extends StatefulWidget {
final FocusNode currFocus;
final FocusNode? nextFocus;
final TextEditingController passC;
late String pass;
final double topPad;
final bool isLogin;
SetPassword({super.key, required this.currFocus, this.nextFocus, required this.passC, required this.pass, required this.topPad, required this.isLogin});
@override
State<StatefulWidget> createState() {
return _SetPassState();
}
}
class _SetPassState extends State<SetPassword> {
bool isObscure = true;
@override
Widget build(BuildContext context) {
return StatefulBuilder(builder: (context, StateSetter setStater) {
return Padding(
padding: EdgeInsets.only(top: widget.topPad),
child: TextFormField(
focusNode: widget.currFocus,
textInputAction: widget.isLogin ? TextInputAction.done : TextInputAction.next,
controller: widget.passC,
obscureText: isObscure,
style: Theme.of(context).textTheme.titleMedium?.copyWith(
color: UiUtils.getColorScheme(context).primaryContainer,
),
validator: (val) => Validators.passValidation(val!, context),
onFieldSubmitted: (v) {
if (!widget.isLogin) {
fieldFocusChange(context, widget.currFocus, widget.nextFocus!);
}
},
onChanged: (String value) {
widget.pass = value;
setStater(() {});
},
decoration: InputDecoration(
hintText: UiUtils.getTranslatedLabel(context, 'passLbl'),
hintStyle: Theme.of(context).textTheme.titleMedium?.copyWith(
color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.5),
),
suffixIcon: Padding(
padding: const EdgeInsetsDirectional.only(end: 12.0),
child: IconButton(
icon: isObscure
? Icon(Icons.visibility_rounded, size: 20, color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.6))
: Icon(Icons.visibility_off_rounded, size: 20, color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.6)),
splashColor: Colors.transparent,
onPressed: () {
setState(() => isObscure = !isObscure);
},
)),
filled: true,
fillColor: Theme.of(context).colorScheme.surface,
contentPadding: const EdgeInsets.symmetric(horizontal: 25, vertical: 17),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: UiUtils.getColorScheme(context).outline.withOpacity(0.7)),
borderRadius: BorderRadius.circular(10.0),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.circular(10.0),
),
),
),
);
});
}
}

View File

@@ -0,0 +1,40 @@
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:news/app/routes.dart';
import 'package:news/cubits/privacyTermsCubit.dart';
import 'package:news/utils/uiUtils.dart';
setTermPolicyTxt(BuildContext context, PrivacyTermsFetchSuccess state) {
return Container(
alignment: AlignmentDirectional.bottomCenter,
padding: const EdgeInsets.only(top: 20.0),
child: RichText(
textAlign: TextAlign.center,
text: TextSpan(children: [
TextSpan(
text: "${UiUtils.getTranslatedLabel(context, 'agreeTermPolicyLbl')}\n",
style: Theme.of(context).textTheme.bodyLarge?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.7), overflow: TextOverflow.ellipsis),
),
TextSpan(
text: UiUtils.getTranslatedLabel(context, 'termLbl'),
style: Theme.of(context).textTheme.bodyLarge?.copyWith(color: Theme.of(context).primaryColor, decoration: TextDecoration.underline, overflow: TextOverflow.ellipsis),
recognizer: TapGestureRecognizer()
..onTap = (() {
Navigator.of(context).pushNamed(Routes.privacy, arguments: {"from": "login", "title": state.termsPolicy.title, "desc": state.termsPolicy.pageContent});
}),
),
TextSpan(
text: "\t${UiUtils.getTranslatedLabel(context, 'andLbl')}\t",
style: Theme.of(context).textTheme.bodyLarge?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.7), overflow: TextOverflow.ellipsis),
),
TextSpan(
text: UiUtils.getTranslatedLabel(context, 'priPolicy'),
style: Theme.of(context).textTheme.bodyLarge?.copyWith(color: Theme.of(context).primaryColor, decoration: TextDecoration.underline, overflow: TextOverflow.ellipsis),
recognizer: TapGestureRecognizer()
..onTap = (() {
Navigator.of(context).pushNamed(Routes.privacy, arguments: {"from": "login", "title": state.privacyPolicy.title, "desc": state.privacyPolicy.pageContent});
}),
),
]),
));
}

View File

@@ -0,0 +1,18 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:news/utils/uiUtils.dart';
class SvgPictureWidget extends StatelessWidget {
String assetName;
ColorFilter? assetColor;
double? height, width;
BoxFit? fit;
SvgPictureWidget({Key? key, required this.assetName, this.assetColor, this.height, this.width, this.fit}) : super(key: key);
@override
Widget build(BuildContext context) {
return SvgPicture.asset(
placeholderBuilder: (_) => Center(child: CircularProgressIndicator()), UiUtils.getSvgImagePath(assetName), colorFilter: assetColor, height: height, width: width, fit: fit ?? BoxFit.fill);
}
}

View File

@@ -0,0 +1,455 @@
import 'dart:io';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:news/cubits/Auth/authCubit.dart';
import 'package:news/cubits/Auth/registerTokenCubit.dart';
import 'package:news/cubits/appLocalizationCubit.dart';
import 'package:news/cubits/appSystemSettingCubit.dart';
import 'package:news/cubits/privacyTermsCubit.dart';
import 'package:news/cubits/settingCubit.dart';
import 'package:news/ui/screens/auth/Widgets/bottomComBtn.dart';
import 'package:news/ui/screens/auth/Widgets/fieldFocusChange.dart';
import 'package:news/ui/screens/auth/Widgets/setConfimPass.dart';
import 'package:news/ui/screens/auth/Widgets/setDivider.dart';
import 'package:news/ui/screens/auth/Widgets/setEmail.dart';
import 'package:news/ui/screens/auth/Widgets/setForgotPass.dart';
import 'package:news/ui/screens/auth/Widgets/setLoginAndSignUpBtn.dart';
import 'package:news/ui/screens/auth/Widgets/setName.dart';
import 'package:news/ui/screens/auth/Widgets/setPassword.dart';
import 'package:news/ui/screens/auth/Widgets/setTermPolicy.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/utils/constant.dart';
import 'package:news/utils/internetConnectivity.dart';
import 'package:news/utils/uiUtils.dart';
import 'package:news/app/routes.dart';
import 'package:news/cubits/Auth/socialSignUpCubit.dart';
import 'package:news/utils/validators.dart';
class LoginScreen extends StatefulWidget {
final bool? isFromApp;
const LoginScreen({super.key, this.isFromApp});
@override
LoginScreenState createState() => LoginScreenState();
static Route route(RouteSettings routeSettings) {
if (routeSettings.arguments == null) {
return CupertinoPageRoute(builder: (_) => const LoginScreen());
} else {
final arguments = routeSettings.arguments as Map<String, dynamic>;
return CupertinoPageRoute(builder: (_) => LoginScreen(isFromApp: arguments['isFromApp'] ?? false));
}
}
}
class LoginScreenState extends State<LoginScreen> with TickerProviderStateMixin {
final GlobalKey<FormState> _formkey = GlobalKey<FormState>();
TabController? _tabController;
FocusNode emailFocus = FocusNode();
FocusNode passFocus = FocusNode();
FocusNode nameFocus = FocusNode();
FocusNode emailSFocus = FocusNode();
FocusNode passSFocus = FocusNode();
FocusNode confPassFocus = FocusNode();
TextEditingController? emailC, passC, sEmailC, sPassC, sNameC, sConfPassC;
String? name, email, pass, mobile, profile, confPass;
bool isPolicyAvailable = false;
bool isObscure = true; //setPassword widget
@override
void initState() {
Future.delayed(Duration.zero, () {
context.read<PrivacyTermsCubit>().getPrivacyTerms(langId: context.read<AppLocalizationCubit>().state.id);
});
_tabController = TabController(length: 2, vsync: this, initialIndex: 0);
assignAllTextController();
_tabController!.addListener(() {
FocusScope.of(context).unfocus(); //dismiss keyboard
clearLoginTextFields();
clearSignUpTextFields();
});
super.initState();
}
assignAllTextController() {
emailC = TextEditingController();
passC = TextEditingController();
sEmailC = TextEditingController();
sPassC = TextEditingController();
sNameC = TextEditingController();
sConfPassC = TextEditingController();
}
clearSignUpTextFields() {
setState(() {
sNameC!.clear();
sEmailC!.clear();
sPassC!.clear();
sConfPassC!.clear();
});
}
clearLoginTextFields() {
setState(() {
emailC!.clear();
passC!.clear();
});
}
disposeAllTextController() {
emailC!.dispose();
passC!.dispose();
sEmailC!.dispose();
sPassC!.dispose();
sNameC!.dispose();
sConfPassC!.dispose();
}
@override
void dispose() {
_tabController?.dispose();
disposeAllTextController();
super.dispose();
}
showContent() {
return BlocConsumer<SocialSignUpCubit, SocialSignUpState>(
bloc: context.read<SocialSignUpCubit>(),
listener: (context, state) async {
if (state is SocialSignUpFailure) {
showSnackBar(state.errorMessage, context);
}
if (state is SocialSignUpSuccess) {
context.read<AuthCubit>().checkAuthStatus();
if (state.authModel.status == "0") {
showSnackBar(UiUtils.getTranslatedLabel(context, 'deactiveMsg'), context);
} else {
FirebaseMessaging.instance.getToken().then((token) async {
if (token != null) {
context.read<RegisterTokenCubit>().registerToken(fcmId: token, context: context);
if (token != context.read<SettingsCubit>().getSettings().token) {
context.read<SettingsCubit>().changeFcmToken(token);
}
if (state.authModel.isFirstLogin != null && state.authModel.isFirstLogin!.isNotEmpty && state.authModel.isFirstLogin == "0" && state.authModel.type != loginApple) {
Navigator.of(context).pushNamedAndRemoveUntil(Routes.editUserProfile, (route) => false, arguments: {"from": "login"});
} else if (widget.isFromApp == true) {
Navigator.pop(context);
} else {
Navigator.pushNamedAndRemoveUntil(context, Routes.home, (route) => false);
}
}
});
}
}
},
builder: (context, state) {
return Form(
key: _formkey,
child: Stack(
children: [
Container(
padding: const EdgeInsetsDirectional.only(top: 30.0, bottom: 5.0, start: 20.0, end: 20.0),
width: MediaQuery.of(context).size.width,
child: Column(crossAxisAlignment: CrossAxisAlignment.center, children: <Widget>[skipBtn(), showTabs(), showTabBarView()]),
),
if (state is SocialSignUpProgress) UiUtils.showCircularProgress(true, Theme.of(context).primaryColor),
],
));
});
}
skipBtn() {
return Align(
alignment: AlignmentDirectional.topEnd,
child: CustomTextButton(
onTap: () {
Navigator.of(context).pushReplacementNamed(Routes.home, arguments: false);
},
text: UiUtils.getTranslatedLabel(context, 'skip'),
color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.7),
));
}
showTabs() {
return Align(
alignment: Alignment.centerLeft,
child: DefaultTabController(
length: 2,
child: Container(
padding: const EdgeInsetsDirectional.only(start: 10.0),
child: TabBar(
overlayColor: WidgetStateProperty.all(Colors.transparent),
controller: _tabController,
labelStyle: Theme.of(context).textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600, letterSpacing: 0.5),
labelPadding: EdgeInsets.zero,
labelColor: backgroundColor,
unselectedLabelColor: UiUtils.getColorScheme(context).primaryContainer,
indicator: BoxDecoration(borderRadius: BorderRadius.circular(50), color: UiUtils.getColorScheme(context).secondaryContainer),
tabs: [Tab(text: UiUtils.getTranslatedLabel(context, 'signInTab')), Tab(text: UiUtils.getTranslatedLabel(context, 'signupBtn'))])),
));
}
bool validateAndSave() {
final form = _formkey.currentState;
form!.save();
if (!isPolicyAvailable) {
showSnackBar(UiUtils.getTranslatedLabel(context, 'addTCFirst'), context);
return false;
}
return form.validate();
}
Widget setPassword({required FocusNode currFocus, FocusNode? nextFocus, required TextEditingController passC, required String pass, required double topPad, required bool isLogin}) {
return Padding(
padding: EdgeInsets.only(top: topPad),
child: TextFormField(
focusNode: currFocus,
textInputAction: isLogin ? TextInputAction.done : TextInputAction.next,
controller: passC,
obscureText: isObscure,
style: Theme.of(context).textTheme.titleMedium?.copyWith(
color: UiUtils.getColorScheme(context).primaryContainer,
),
validator: (val) => Validators.passValidation(val!, context),
onFieldSubmitted: (v) {
if (!isLogin) {
fieldFocusChange(context, currFocus, nextFocus!);
}
},
onChanged: (String value) {
pass = value;
setState(() {});
},
decoration: InputDecoration(
hintText: UiUtils.getTranslatedLabel(context, 'passLbl'),
hintStyle: Theme.of(context).textTheme.titleMedium?.copyWith(
color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.5),
),
suffixIcon: Padding(
padding: const EdgeInsetsDirectional.only(end: 12.0),
child: IconButton(
icon: isObscure
? Icon(Icons.visibility_rounded, size: 20, color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.6))
: Icon(Icons.visibility_off_rounded, size: 20, color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.6)),
splashColor: Colors.transparent,
onPressed: () {
setState(() => isObscure = !isObscure);
},
)),
filled: true,
fillColor: Theme.of(context).colorScheme.surface,
contentPadding: const EdgeInsets.symmetric(horizontal: 25, vertical: 17),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: UiUtils.getColorScheme(context).outline.withOpacity(0.7)),
borderRadius: BorderRadius.circular(10.0),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.circular(10.0),
),
),
),
);
}
showTabBarView() {
return Expanded(
child: Container(
alignment: Alignment.center,
height: MediaQuery.of(context).size.height * 1.0,
child: TabBarView(
controller: _tabController,
dragStartBehavior: DragStartBehavior.start,
children: [
//Login
SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 5.0),
child: Column(children: [
loginTxt(),
SetEmail(currFocus: emailFocus, nextFocus: passFocus, emailC: emailC!, email: email ?? '', topPad: 20),
SetPassword(currFocus: passFocus, passC: passC!, pass: pass ?? '', topPad: 20, isLogin: true),
setForgotPass(context),
SetLoginAndSignUpBtn(
onTap: () async {
FocusScope.of(context).unfocus(); //dismiss keyboard
if (validateAndSave()) {
if (await InternetConnectivity.isNetworkAvailable()) {
context.read<SocialSignUpCubit>().socialSignUpUser(email: emailC!.text.trim(), password: passC!.text, authProvider: AuthProviders.email, context: context);
} else {
showSnackBar(UiUtils.getTranslatedLabel(context, 'internetmsg'), context);
}
}
},
text: 'loginTxt',
topPad: 20),
SetDividerOR(),
bottomBtn(),
BlocConsumer<PrivacyTermsCubit, PrivacyTermsState>(listener: (context, state) {
if (state is PrivacyTermsFetchSuccess) {
isPolicyAvailable = true;
}
if (state is PrivacyTermsFetchFailure) {
isPolicyAvailable = false;
}
}, builder: (context, state) {
return (state is PrivacyTermsFetchSuccess) ? setTermPolicyTxt(context, state) : const SizedBox.shrink();
})
]),
)),
//SignUp
SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 5.0),
child: Column(
children: [
signUpTxt(),
SetName(currFocus: nameFocus, nextFocus: emailSFocus, nameC: sNameC!, name: sNameC!.text),
SetEmail(currFocus: emailSFocus, nextFocus: passSFocus, emailC: sEmailC!, email: sEmailC!.text, topPad: 20),
setPassword(currFocus: passSFocus, nextFocus: confPassFocus, passC: sPassC!, pass: sPassC!.text, topPad: 20, isLogin: false),
SetConfirmPass(currFocus: confPassFocus, confPassC: sConfPassC!, confPass: sConfPassC!.text, pass: sPassC!.text),
SetLoginAndSignUpBtn(
onTap: () async {
FocusScope.of(context).unfocus(); //dismiss keyboard
final form = _formkey.currentState;
if (form!.validate()) {
form.save();
if (await InternetConnectivity.isNetworkAvailable()) {
registerWithEmailPassword(sEmailC!.text.trim(), sPassC!.text.trim());
} else {
showSnackBar(UiUtils.getTranslatedLabel(context, 'internetmsg'), context);
}
}
},
text: 'signupBtn',
topPad: 25)
],
),
))
],
)),
);
}
registerWithEmailPassword(String email, String password) async {
try {
final credential = await FirebaseAuth.instance.createUserWithEmailAndPassword(email: email, password: password);
User? user = credential.user;
user!.updateDisplayName(sNameC!.text.trim()).then((value) => debugPrint("updated name is - ${user.displayName}"));
user.reload();
user.sendEmailVerification().then((value) => showSnackBar('${UiUtils.getTranslatedLabel(context, 'verifSentMail')} $email', context));
clearSignUpTextFields();
_tabController!.animateTo(0);
FocusScope.of(context).requestFocus(emailFocus);
} on FirebaseAuthException catch (e) {
if (e.code == 'weakPassword') {
showSnackBar(UiUtils.getTranslatedLabel(context, 'weakPassword'), context);
}
if (e.code == 'email-already-in-use') {
showSnackBar(UiUtils.getTranslatedLabel(context, 'emailAlreadyInUse'), context);
}
} catch (e) {}
}
loginTxt() {
return Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsetsDirectional.only(top: 20.0, start: 10.0),
child: CustomTextLabel(
text: 'loginDescr',
textStyle: Theme.of(context).textTheme.headlineSmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer, fontWeight: FontWeight.w800, letterSpacing: 0.5),
textAlign: TextAlign.left,
),
));
}
signUpTxt() {
return Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsetsDirectional.only(top: 35.0, start: 10.0),
child: CustomTextLabel(
text: 'signupDescr',
textStyle: Theme.of(context).textTheme.headlineSmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer, fontWeight: FontWeight.w800, letterSpacing: 0.5),
textAlign: TextAlign.left,
),
));
}
bottomBtn() {
return Padding(
padding: const EdgeInsets.only(top: 20.0),
child: Column(
mainAxisAlignment: (fblogInEnabled) ? MainAxisAlignment.spaceBetween : MainAxisAlignment.spaceAround,
children: [
BottomCommButton(
btnCaption: 'google',
onTap: () {
if (!isPolicyAvailable) {
showSnackBar(UiUtils.getTranslatedLabel(context, 'addTCFirst'), context);
} else {
context.read<SocialSignUpCubit>().socialSignUpUser(authProvider: AuthProviders.gmail, context: context);
}
},
img: 'google_button'),
if (fblogInEnabled)
BottomCommButton(
onTap: () {
if (!isPolicyAvailable) {
showSnackBar(UiUtils.getTranslatedLabel(context, 'addTCFirst'), context);
} else {
context.read<SocialSignUpCubit>().socialSignUpUser(authProvider: AuthProviders.fb, context: context);
}
},
img: 'facebook_button',
btnCaption: 'fb'),
if (Platform.isIOS)
BottomCommButton(
onTap: () {
if (!isPolicyAvailable) {
showSnackBar(UiUtils.getTranslatedLabel(context, 'addTCFirst'), context);
} else {
context.read<SocialSignUpCubit>().socialSignUpUser(authProvider: AuthProviders.apple, context: context);
}
},
img: 'apple_logo',
btnCaption: 'apple',
btnColor: UiUtils.getColorScheme(context).primaryContainer),
if (context.read<AppConfigurationCubit>().getMobileLoginMode() != "" && context.read<AppConfigurationCubit>().getMobileLoginMode() != "0")
BottomCommButton(
onTap: () {
if (!isPolicyAvailable) {
showSnackBar(UiUtils.getTranslatedLabel(context, 'addTCFirst'), context);
} else {
Navigator.of(context).pushNamed(Routes.requestOtp);
}
},
img: 'phone_button',
btnCaption: 'mobileLbl',
btnColor: UiUtils.getColorScheme(context).primaryContainer)
],
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: showContent(),
);
}
}

View File

@@ -0,0 +1,275 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:news/app/routes.dart';
import 'package:news/cubits/Author/authorNewsCubit.dart';
import 'package:news/cubits/NewsByIdCubit.dart';
import 'package:news/cubits/appLocalizationCubit.dart';
import 'package:news/data/models/NewsModel.dart';
import 'package:news/ui/screens/auth/Widgets/svgPictureWidget.dart';
import 'package:news/ui/styles/colors.dart';
import 'package:news/ui/widgets/customBackBtn.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/ui/widgets/errorContainerWidget.dart';
import 'package:news/ui/widgets/networkImage.dart';
import 'package:news/utils/constant.dart';
import 'package:news/utils/uiUtils.dart';
import 'package:url_launcher/url_launcher.dart';
class AuthorDetailsScreen extends StatefulWidget {
final String authorId;
const AuthorDetailsScreen({super.key, required this.authorId});
@override
State<AuthorDetailsScreen> createState() => _AuthorDetailsScreenState();
static Route route(RouteSettings routeSettings) {
final arguments = routeSettings.arguments as Map<String, dynamic>;
return CupertinoPageRoute(builder: (_) => AuthorDetailsScreen(authorId: arguments['authorId']));
}
}
class _AuthorDetailsScreenState extends State<AuthorDetailsScreen> {
AuthorLayoutType layout = AuthorLayoutType.list;
double borderRadius = 10;
late NewsModel newsData;
String totalViews = "0", totalLikes = "0";
@override
void initState() {
getNewsByAuthor();
super.initState();
}
void getNewsByAuthor() {
context.read<AuthorNewsCubit>().getAuthorNews(authorId: widget.authorId);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const CustomTextLabel(text: 'authorLbl'),
leading: CustomBackButton(),
actions: [
Padding(
padding: const EdgeInsets.all(3.0),
child: Container(
margin: EdgeInsets.all(10),
decoration: BoxDecoration(border: Border.all(color: UiUtils.getColorScheme(context).outline), borderRadius: BorderRadius.circular(5)),
child: GestureDetector(
child: Icon(layout == AuthorLayoutType.list ? Icons.grid_view : Icons.view_list, size: 25),
onTap: () {
setState(() {
layout = layout == AuthorLayoutType.list ? AuthorLayoutType.grid : AuthorLayoutType.list;
});
},
),
),
),
],
),
body: BlocBuilder<AuthorNewsCubit, AuthorNewsState>(
builder: (context, state) {
if (state is AuthorNewsFetchSuccess) {
return Column(
children: [
authorHeader(state: state),
const SizedBox(height: 10),
Expanded(child: layout == AuthorLayoutType.list ? authorNewsListView(state: state) : authorNewsGridView(state: state)),
],
);
} else if (state is AuthorNewsFetchFailed) {
return ErrorContainerWidget(errorMsg: state.errorMessage, onRetry: getNewsByAuthor);
} else {
return const SizedBox.shrink();
}
},
),
);
}
Widget authorHeader({required AuthorNewsFetchSuccess state}) {
return Container(
margin: const EdgeInsets.all(16),
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: UiUtils.getColorScheme(context).surface,
borderRadius: BorderRadius.circular(borderRadius),
),
child: Column(
children: [
Row(
children: [
// author image
ClipRRect(borderRadius: BorderRadius.circular(6), child: CustomNetworkImage(networkImageUrl: state.authorData.profile ?? "")),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CustomTextLabel(text: state.authorData.name ?? "", textStyle: TextStyle(fontSize: 18, fontWeight: FontWeight.w600)),
SizedBox(height: 6),
CustomTextLabel(text: state.authorData.authorData!.bio ?? "", textStyle: TextStyle(fontSize: 13)),
],
),
),
],
),
Padding(
padding: const EdgeInsets.all(8),
child: Row(
children: [
CustomTextLabel(text: 'followLbl'),
SizedBox(width: 6),
(state.authorData.authorData != null)
? Row(spacing: 6.5, children: [
showSocialMediaLinks(socialMediaLink: state.authorData.authorData!.telegramLink ?? "", socialMediaIconName: "telegram"),
showSocialMediaLinks(socialMediaLink: state.authorData.authorData!.facebookLink ?? "", socialMediaIconName: "facebook"),
showSocialMediaLinks(socialMediaLink: state.authorData.authorData!.whatsappLink ?? "", socialMediaIconName: "whatsapp"),
showSocialMediaLinks(socialMediaLink: state.authorData.authorData!.linkedinLink ?? "", socialMediaIconName: "linkedin"),
])
: SizedBox.shrink()
],
),
)
],
),
);
}
Widget showSocialMediaLinks({required String socialMediaLink, required String socialMediaIconName}) {
return Container(
height: 30,
width: 30,
padding: EdgeInsets.all(3),
decoration: BoxDecoration(shape: BoxShape.rectangle, borderRadius: BorderRadius.circular(7), color: UiUtils.getColorScheme(context).outline.withOpacity(0.2)),
child: GestureDetector(
onTap: () async {
if (await canLaunchUrl(Uri.parse(socialMediaLink))) {
await launchUrl(Uri.parse(socialMediaLink), mode: LaunchMode.externalApplication);
}
},
child: ClipRRect(
borderRadius: BorderRadius.circular(7),
child: SvgPictureWidget(
assetName: socialMediaIconName,
height: 11,
width: 11,
fit: BoxFit.contain,
assetColor: ColorFilter.mode(UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.7), BlendMode.srcIn)),
)),
);
}
Widget authorNewsListView({required AuthorNewsFetchSuccess state}) {
return ListView.builder(
padding: const EdgeInsets.symmetric(horizontal: 16), itemCount: state.AuthorNewsList.length, itemBuilder: (BuildContext, index) => newsListTile(newsItem: state.AuthorNewsList[index]));
}
Widget newsListTile({required NewsModel newsItem}) {
return GestureDetector(
onTap: () => redirectToNewsDetailsScreen(newsItem: newsItem),
child: Container(
margin: const EdgeInsets.only(bottom: 10),
decoration: BoxDecoration(color: UiUtils.getColorScheme(context).surface, borderRadius: BorderRadius.circular(12)),
child: Row(
children: [
ClipRRect(
borderRadius: BorderRadius.only(topLeft: Radius.circular(borderRadius), bottomLeft: Radius.circular(borderRadius)),
child: CustomNetworkImage(networkImageUrl: newsItem.image!, width: 110, height: 110, fit: BoxFit.cover)),
const SizedBox(width: 12),
Expanded(
child: Padding(
padding: const EdgeInsets.all(8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CustomTextLabel(text: newsItem.title ?? "", maxLines: 2, overflow: TextOverflow.ellipsis, textStyle: TextStyle(fontSize: 15, fontWeight: FontWeight.w600)),
SizedBox(height: 6),
CustomTextLabel(text: newsItem.desc ?? "", maxLines: 3, overflow: TextOverflow.ellipsis, textStyle: TextStyle(fontSize: 13)),
SizedBox(height: 6),
Divider(),
SizedBox(height: 3),
Row(
children: [
CustomTextLabel(
text: UiUtils.convertToAgo(context, DateTime.parse(newsItem.publishDate ?? newsItem.date!), 0)!,
textStyle: TextStyle(fontSize: 12, color: UiUtils.getColorScheme(context).outline)),
Spacer(),
showViewsAndLikes(newsId: newsItem.id ?? "0", totalViews: newsItem.totalViews ?? "0", totalLikes: newsItem.totalLikes ?? "0")
],
),
],
),
),
)
],
),
),
);
}
Widget showViewsAndLikes({required String newsId, required String totalViews, required String totalLikes}) {
return FutureBuilder(
future: context.read<NewsByIdCubit>().getNewsById(newsId: newsId, langId: context.read<AppLocalizationCubit>().state.id),
builder: (context, snapshot) {
final updated = snapshot.data![0];
totalViews = updated.totalViews ?? "0";
totalLikes = updated.totalLikes ?? "0";
return Row(
spacing: 15,
children: [
Row(
spacing: 5,
children: [Icon(Icons.remove_red_eye_rounded, size: 10), CustomTextLabel(text: totalViews, textStyle: TextStyle(fontSize: 12))],
),
Row(
spacing: 5,
children: [Icon(Icons.thumb_up_alt_outlined, size: 10), CustomTextLabel(text: totalLikes, textStyle: TextStyle(fontSize: 12))],
),
],
);
});
}
Widget authorNewsGridView({required AuthorNewsFetchSuccess state}) {
return GridView.builder(
padding: const EdgeInsets.symmetric(horizontal: 16),
itemCount: state.AuthorNewsList.length,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2, mainAxisSpacing: 16, crossAxisSpacing: 16, childAspectRatio: 0.65),
itemBuilder: (BuildContext, index) {
return newsGridTile(newsItem: state.AuthorNewsList[index]);
});
}
Widget newsGridTile({required NewsModel newsItem}) {
return GestureDetector(
onTap: () => redirectToNewsDetailsScreen(newsItem: newsItem),
child: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(borderRadius), color: UiUtils.getColorScheme(context).surface, boxShadow: const [BoxShadow(blurRadius: 5, spreadRadius: 1, color: dividerColor)]),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ClipRRect(borderRadius: BorderRadius.circular(8), child: CustomNetworkImage(networkImageUrl: newsItem.image!, height: 120, width: double.infinity, fit: BoxFit.cover)),
const SizedBox(height: 8),
CustomTextLabel(
text: UiUtils.convertToAgo(context, DateTime.parse(newsItem.publishDate ?? newsItem.date!), 0)!, textStyle: TextStyle(color: UiUtils.getColorScheme(context).outline, fontSize: 11)),
const SizedBox(height: 4),
CustomTextLabel(text: newsItem.title ?? "", maxLines: 2, overflow: TextOverflow.ellipsis, textStyle: TextStyle(fontWeight: FontWeight.w600, fontSize: 14)),
const SizedBox(height: 4),
CustomTextLabel(text: newsItem.desc ?? "", maxLines: 2, overflow: TextOverflow.ellipsis, textStyle: TextStyle(fontSize: 12)),
Divider(),
showViewsAndLikes(newsId: newsItem.id ?? "0", totalViews: newsItem.totalViews ?? "0", totalLikes: newsItem.totalLikes ?? "0")
],
),
),
);
}
void redirectToNewsDetailsScreen({required NewsModel newsItem}) {
Navigator.of(context).pushNamed(Routes.newsDetails, arguments: {"model": newsItem, "isFromBreak": false, "fromShowMore": false});
}
}

View File

@@ -0,0 +1,213 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:news/cubits/Auth/authCubit.dart';
import 'package:news/cubits/ConnectivityCubit.dart';
import 'package:news/cubits/LikeAndDislikeNews/LikeAndDislikeCubit.dart';
import 'package:news/cubits/appLocalizationCubit.dart';
import 'package:news/cubits/Bookmark/bookmarkCubit.dart';
import 'package:news/cubits/NewsByIdCubit.dart';
import 'package:news/cubits/appSystemSettingCubit.dart';
import 'package:news/cubits/breakingNewsCubit.dart';
import 'package:news/cubits/languageCubit.dart';
import 'package:news/cubits/slugNewsCubit.dart';
import 'package:news/cubits/themeCubit.dart';
import 'package:news/data/models/BreakingNewsModel.dart';
import 'package:news/data/models/NewsModel.dart';
import 'package:news/ui/screens/CategoryScreen.dart';
import 'package:news/ui/screens/HomePage/HomePage.dart';
import 'package:news/ui/screens/Profile/ProfileScreen.dart';
import 'package:news/ui/screens/RSSFeedScreen.dart';
import 'package:news/ui/screens/Videos/VideoScreen.dart';
import 'package:news/ui/widgets/SnackBarWidget.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/ui/widgets/errorContainerWidget.dart';
import 'package:news/utils/strings.dart';
import 'package:news/utils/uiUtils.dart';
import 'package:news/app/routes.dart';
GlobalKey<HomeScreenState>? homeScreenKey;
bool? isNotificationReceivedInbg, isShared;
String? notificationNewsId;
String? saleNotification;
String? routeSettingsName, newsSlug;
class DashBoard extends StatefulWidget {
const DashBoard({super.key});
@override
DashBoardState createState() => DashBoardState();
static Route route(RouteSettings routeSettings) {
return CupertinoPageRoute(builder: (_) => const DashBoard());
}
}
class DashBoardState extends State<DashBoard> {
List<Widget> fragments = [];
DateTime? currentBackPressTime;
int _selectedIndex = 0;
List<IconData> iconList = [];
List<String> itemName = [];
bool shouldPopScope = false;
@override
void initState() {
homeScreenKey = GlobalKey<HomeScreenState>();
iconList = [
Icons.home_rounded,
Icons.video_collection_rounded,
//Add only if Category Mode is enabled From Admin panel.
if (context.read<AppConfigurationCubit>().getCategoryMode() == "1") Icons.grid_view_rounded,
if (context.read<AppConfigurationCubit>().getRSSFeedMode() == "1") Icons.rss_feed_rounded,
Icons.settings_rounded
];
itemName = [
'homeLbl',
'videosLbl',
if (context.read<AppConfigurationCubit>().getCategoryMode() == "1") 'categoryLbl',
if (context.read<AppConfigurationCubit>().getRSSFeedMode() == "1") 'rssFeed',
'profile'
];
fragments = [
HomeScreen(key: homeScreenKey),
const VideoScreen(),
//Add only if Category Mode is enabled From Admin panel.
if (context.read<AppConfigurationCubit>().getCategoryMode() == "1") const CategoryScreen(),
if (context.read<AppConfigurationCubit>().getRSSFeedMode() == "1") RSSFeedScreen(),
const ProfileScreen(),
];
if ((isShared != null && isShared == true) && routeSettingsName != null && newsSlug != null) initDynamicLinks();
checkForPengingNotifications();
checkMaintenanceMode();
super.initState();
}
void checkMaintenanceMode() {
if (context.read<AppConfigurationCubit>().getMaintenanceMode() == "1") {
//app is in maintenance mode - no function should be performed
Navigator.of(context).pushReplacementNamed(Routes.maintenance);
}
}
void checkForPengingNotifications() async {
if (isNotificationReceivedInbg != null && notificationNewsId != null && notificationNewsId != "0" && isNotificationReceivedInbg!) {
context.read<NewsByIdCubit>().getNewsById(newsId: notificationNewsId!, langId: context.read<AppLocalizationCubit>().state.id).then((value) {
if (value.isNotEmpty) {
Navigator.of(context).pushNamed(Routes.newsDetails, arguments: {"model": value[0], "isFromBreak": false, "fromShowMore": false});
}
});
}
}
void initDynamicLinks() async {
await Future.delayed(Duration(seconds: 2)); // Simulate API delay
if (routeSettingsName!.contains('/news/')) {
String langCodeShared = routeSettingsName!.split("/")[1];
String? langIdPass = UiUtils.rootNavigatorKey.currentContext!.read<AppLocalizationCubit>().state.id;
if (context.read<LanguageCubit>().langList().isNotEmpty) langIdPass = context.read<LanguageCubit>().langList().firstWhere((e) => e.code == langCodeShared).id;
UiUtils.rootNavigatorKey.currentContext?.read<SlugNewsCubit>().getSlugNews(langId: langIdPass ?? "0", newsSlug: newsSlug).then((value) {
NewsModel? model = (value[DATA] as List).map((e) => NewsModel.fromJson(e)).toList().first;
Navigator.pushNamed(context, Routes.newsDetails,
arguments: {"model": model, "slug": newsSlug, "isFromBreak": routeSettingsName!.contains('/breaking-news/') ? true : false, "fromShowMore": false});
});
} else if (routeSettingsName!.contains('/breaking-news/')) {
//for breaking news
UiUtils.rootNavigatorKey.currentContext?.read<BreakingNewsCubit>().getBreakingNews(langId: UiUtils.rootNavigatorKey.currentContext!.read<AppLocalizationCubit>().state.id).then((value) {
BreakingNewsModel? brModel = value[0];
Navigator.of(context).pushNamed(Routes.newsDetails, arguments: {"breakModel": brModel, "slug": newsSlug, "isFromBreak": true, "fromShowMore": false});
});
}
isShared = false; //reset
}
onWillPop(bool isTrue) {
DateTime now = DateTime.now();
if (_selectedIndex != 0) {
setState(() {
_selectedIndex = 0;
shouldPopScope = false;
});
} else if (currentBackPressTime == null || now.difference(currentBackPressTime!) > const Duration(seconds: 2)) {
currentBackPressTime = now;
showSnackBar(UiUtils.getTranslatedLabel(context, 'exitWR'), context);
setState(() => shouldPopScope = false);
}
setState(() => shouldPopScope = true);
}
Widget buildNavBarItem(IconData icon, String itemName, int index) {
return InkWell(
onTap: () {
setState(() => _selectedIndex = index);
},
child: Container(
height: 60,
width: MediaQuery.of(context).size.width / iconList.length,
decoration: index == _selectedIndex ? BoxDecoration(border: Border(top: BorderSide(width: 3, color: Theme.of(context).primaryColor))) : null,
child: Column(
children: [
SizedBox(height: 3),
Icon(icon, color: index == _selectedIndex ? Theme.of(context).primaryColor : UiUtils.getColorScheme(context).outline),
SizedBox(height: 2.5),
CustomTextLabel(
text: itemName, softWrap: true, textStyle: TextStyle(color: (index == _selectedIndex) ? Theme.of(context).primaryColor : UiUtils.getColorScheme(context).outline, fontSize: 12))
],
),
),
);
}
bottomBar() {
List<Widget> navBarItemList = [];
for (var i = 0; i < iconList.length; i++) {
navBarItemList.add(buildNavBarItem(iconList[i], itemName[i], i));
}
return Container(
padding: (Platform.isIOS) ? EdgeInsets.only(bottom: 15) : EdgeInsets.zero,
decoration: BoxDecoration(
color: UiUtils.getColorScheme(context).secondary,
borderRadius: const BorderRadius.only(topLeft: Radius.circular(10.0), topRight: Radius.circular(10.0)),
boxShadow: [BoxShadow(blurRadius: 6, offset: const Offset(5.0, 5.0), color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.4), spreadRadius: 0)],
),
child: ClipRRect(borderRadius: const BorderRadius.only(topLeft: Radius.circular(10.0), topRight: Radius.circular(10.0)), child: Row(children: navBarItemList)));
}
@override
Widget build(BuildContext context) {
UiUtils.setUIOverlayStyle(appTheme: context.read<ThemeCubit>().state.appTheme); //set UiOverlayStyle according to selected theme
return PopScope(
canPop: shouldPopScope,
onPopInvoked: onWillPop,
child: BlocConsumer<AuthCubit, AuthState>(
listener: (context, state) {
if (state is Authenticated) {
Future.delayed(Duration.zero, () {
context.read<BookmarkCubit>().getBookmark(langId: context.read<AppLocalizationCubit>().state.id);
context.read<LikeAndDisLikeCubit>().getLike(langId: context.read<AppLocalizationCubit>().state.id);
});
}
},
builder: (context, state) {
return Scaffold(
bottomNavigationBar: bottomBar(),
body: BlocBuilder<ConnectivityCubit, ConnectivityState>(
builder: (context, connectivityStatus) {
if (connectivityStatus is ConnectivityDisconnected) {
return ErrorContainerWidget(errorMsg: UiUtils.getTranslatedLabel(context, 'internetmsg'), onRetry: () {});
} else {
return IndexedStack(index: _selectedIndex, children: fragments);
}
},
),
);
},
),
);
}
}

View File

@@ -0,0 +1,413 @@
// ignore_for_file: public_member_api_docs, sort_constructors_first
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:news/cubits/appLocalizationCubit.dart';
import 'package:news/cubits/categoryCubit.dart';
import 'package:news/cubits/tagCubit.dart';
import 'package:news/data/models/CategoryModel.dart';
import 'package:news/data/models/TagModel.dart';
import 'package:news/ui/screens/filter/widgets/custom_date_selector.dart';
import 'package:news/ui/screens/filter/widgets/duration_filter_widget.dart';
import 'package:news/ui/styles/colors.dart';
import 'package:news/utils/uiUtils.dart';
class NewsFilterData {
final List<CategoryModel> selectedCategories;
final List<TagModel> selectedTags;
final DateTime? selectedDate;
final DurationFilter? durationFilter;
NewsFilterData({required this.selectedCategories, required this.selectedTags, this.selectedDate, this.durationFilter});
@override
String toString() {
return 'NewsFilterData(selectedCategories: $selectedCategories, selectedTags: $selectedTags, selectedDate: $selectedDate, durationFilter: $durationFilter)';
}
}
class FilterBottomSheet extends StatefulWidget {
final NewsFilterData initialFilters;
final bool isCategoryModeON;
const FilterBottomSheet({
Key? key,
required this.isCategoryModeON,
required this.initialFilters,
}) : super(key: key);
@override
State<FilterBottomSheet> createState() => _FilterBottomSheetState();
}
class _FilterBottomSheetState extends State<FilterBottomSheet> {
late final ValueNotifier<int> selectedFilterTabIndex = ValueNotifier(0);
late List<CategoryModel> selectedCategories;
late List<TagModel> selectedTags;
DateTime? selectedDate;
List<String> filterTabs = [];
late DurationFilter? durationFilter = widget.initialFilters.durationFilter;
@override
void initState() {
super.initState();
filterTabs = [if (widget.isCategoryModeON) "catLbl", "tagLbl", "date"];
selectedCategories = List.from(widget.initialFilters.selectedCategories);
selectedTags = List.from(widget.initialFilters.selectedTags);
selectedDate = widget.initialFilters.selectedDate;
}
@override
void dispose() {
selectedFilterTabIndex.dispose();
super.dispose();
}
Widget _buildFilterTabItem({required String title, required int index}) {
return ValueListenableBuilder<int>(
valueListenable: selectedFilterTabIndex,
builder: (context, value, child) {
final isSelected = value == index;
return InkWell(
onTap: () {
selectedFilterTabIndex.value = index;
},
child: Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(vertical: 15),
decoration: BoxDecoration(
color: isSelected ? UiUtils.getColorScheme(context).secondaryContainer : Colors.transparent,
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: isSelected ? UiUtils.getColorScheme(context).secondary : UiUtils.getColorScheme(context).primaryContainer,
),
),
child: Text(
title,
textAlign: TextAlign.center,
style: TextStyle(
color: isSelected ? UiUtils.getColorScheme(context).surface : UiUtils.getColorScheme(context).onSurface,
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
),
);
},
);
}
Widget _buildCategoryList() {
return BlocBuilder<CategoryCubit, CategoryState>(
builder: (context, state) {
if (state is CategoryFetchInProgress) {
return const Center(child: CircularProgressIndicator());
}
if (state is CategoryFetchFailure) {
return Center(child: Text(state.errorMessage));
}
if (state is CategoryFetchSuccess) {
return NotificationListener(
onNotification: (notification) {
if (notification is ScrollUpdateNotification) {
if (notification.metrics.pixels == notification.metrics.maxScrollExtent) {
if (context.read<CategoryCubit>().hasMoreCategory()) {
context.read<CategoryCubit>().getMoreCategory(langId: context.read<AppLocalizationCubit>().state.id);
}
}
}
return true;
},
child: ListView.builder(
shrinkWrap: true,
physics: const BouncingScrollPhysics(),
itemCount: state.category.length,
itemBuilder: (context, index) {
final category = state.category[index];
final isSelected = selectedCategories.contains(category);
return CheckboxListTile(
title: Text(
category.categoryName ?? "",
style: TextStyle(
color: UiUtils.getColorScheme(context).onSurface,
fontWeight: FontWeight.w500,
),
),
value: isSelected,
activeColor: UiUtils.getColorScheme(context).primary,
checkColor: UiUtils.getColorScheme(context).onPrimary,
onChanged: (bool? value) {
setState(() {
if (value == true) {
if (!selectedCategories.contains(category)) {
selectedCategories.add(category);
}
} else {
selectedCategories.remove(category);
}
});
},
controlAffinity: ListTileControlAffinity.trailing,
);
},
),
);
}
return Container();
},
);
}
Widget _buildTagList() {
return BlocBuilder<TagCubit, TagState>(
builder: (context, state) {
if (state is TagFetchInProgress) {
return const Center(child: CircularProgressIndicator());
}
if (state is TagFetchFailure) {
return Center(child: Text(state.errorMessage));
}
if (state is TagFetchSuccess) {
return NotificationListener(
onNotification: (notification) {
if (notification is ScrollUpdateNotification) {
if (notification.metrics.pixels == notification.metrics.maxScrollExtent) {
if (context.read<TagCubit>().hasMoreTags()) {
context.read<TagCubit>().getMoreTags(langId: context.read<AppLocalizationCubit>().state.id);
}
}
}
return true;
},
child: ListView.builder(
shrinkWrap: true,
physics: const BouncingScrollPhysics(),
itemCount: state.tag.length,
itemBuilder: (context, index) {
final tag = state.tag[index];
final isSelected = selectedTags.contains(tag);
return CheckboxListTile(
title: Text(
tag.tagName ?? "",
style: TextStyle(
color: UiUtils.getColorScheme(context).onSurface,
fontWeight: FontWeight.w500,
),
),
value: isSelected,
activeColor: UiUtils.getColorScheme(context).primary,
checkColor: UiUtils.getColorScheme(context).onPrimary,
onChanged: (bool? value) {
setState(() {
if (value == true) {
if (!selectedTags.contains(tag)) {
selectedTags.add(tag);
}
} else {
selectedTags.remove(tag);
}
});
},
controlAffinity: ListTileControlAffinity.trailing,
);
},
),
);
}
return Container();
},
);
}
Widget _buildDatePicker() {
return Padding(
padding: const EdgeInsets.all(16.0),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: 250,
child: CustomCalendarView(
selectedDate: selectedDate,
onDateSelected: (day) {
selectedDate = day;
setState(() {});
},
)),
DurationFilterWidget(
selectedFilter: durationFilter,
filters: [
LastDays(daysCount: 1),
LastDays(daysCount: 7),
LastDays(daysCount: 30),
LastDays(daysCount: 60),
LastDays(daysCount: 90),
...List.generate(
7,
(int index) {
return Year(year: DateTime.now().year - index);
},
),
],
onSelected: (DurationFilter? filter) {
durationFilter = filter;
setState(() {});
},
)
],
),
),
);
}
Widget _buildFilterContent() {
return ValueListenableBuilder<int>(
valueListenable: selectedFilterTabIndex,
builder: (context, selectedIndex, _) {
final views = widget.isCategoryModeON ? [_buildCategoryList(), _buildTagList(), _buildDatePicker()] : [_buildTagList(), _buildDatePicker()];
return views[selectedIndex];
},
);
}
@override
Widget build(BuildContext context) {
return Container(
height: MediaQuery.of(context).size.height * 0.7,
decoration: BoxDecoration(
color: UiUtils.getColorScheme(context).surface,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20),
),
),
child: Column(
children: [
Container(
width: 40,
height: 5,
margin: const EdgeInsets.symmetric(vertical: 10),
decoration: BoxDecoration(
color: Colors.grey[300],
borderRadius: BorderRadius.circular(10),
),
),
Text(
UiUtils.getTranslatedLabel(context, "filter"),
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: UiUtils.getColorScheme(context).onSurface,
),
),
Divider(),
const SizedBox(height: 15),
Expanded(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
flex: 3,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: filterTabs
.asMap()
.entries
.map((entry) => Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0),
child: _buildFilterTabItem(
title: UiUtils.getTranslatedLabel(context, entry.value),
index: entry.key,
),
))
.toList(),
),
),
),
Container(
width: 1,
color: Colors.grey[300],
height: MediaQuery.of(context).size.height * 0.5,
),
Expanded(
flex: 7,
child: _buildFilterContent(),
),
],
),
),
const Divider(height: 1),
Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
children: [
Expanded(
child: OutlinedButton(
onPressed: () {
setState(() {
selectedCategories = <CategoryModel>[];
selectedTags = <TagModel>[];
selectedDate = null;
durationFilter = null;
});
},
style: OutlinedButton.styleFrom(
side: BorderSide(color: primaryColor),
padding: const EdgeInsets.symmetric(vertical: 12),
),
child: Text(
UiUtils.getTranslatedLabel(context, 'clear'),
style: TextStyle(color: primaryColor, fontWeight: FontWeight.bold),
),
),
),
const SizedBox(width: 16),
Expanded(
child: ElevatedButton(
onPressed: () {
final result = NewsFilterData(selectedCategories: selectedCategories, selectedTags: selectedTags, selectedDate: selectedDate, durationFilter: durationFilter);
Navigator.pop(context, result);
},
style: ElevatedButton.styleFrom(
backgroundColor: primaryColor,
padding: const EdgeInsets.symmetric(vertical: 12),
),
child: Text(
UiUtils.getTranslatedLabel(context, 'apply'),
style: TextStyle(
color: UiUtils.getColorScheme(context).onPrimary,
fontWeight: FontWeight.bold,
),
),
),
),
],
),
),
],
),
);
}
}
// Function to show the filter bottom sheet
Future<NewsFilterData?> showFilterBottomSheet({
required BuildContext context,
required bool isCategoryModeON,
required NewsFilterData initialFilters,
}) async {
return await showModalBottomSheet<NewsFilterData>(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (context) => FilterBottomSheet(
isCategoryModeON: isCategoryModeON,
initialFilters: initialFilters,
));
}

View File

@@ -0,0 +1,169 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:news/ui/styles/colors.dart';
import 'package:news/utils/uiUtils.dart';
class CustomDateSelector extends StatefulWidget {
final DateTime? selectedDate;
final void Function(DateTime? date) onDateChanged;
const CustomDateSelector({
Key? key,
required this.onDateChanged,
this.selectedDate,
}) : super(key: key);
@override
_CustomDateSelectorState createState() => _CustomDateSelectorState();
}
class _CustomDateSelectorState extends State<CustomDateSelector> {
late DateTime? selectedDate = widget.selectedDate;
@override
void didUpdateWidget(covariant CustomDateSelector oldWidget) {
if (oldWidget.selectedDate != widget.selectedDate) {
selectedDate = widget.selectedDate;
setState(() {});
}
super.didUpdateWidget(oldWidget);
}
@override
Widget build(BuildContext context) {
return Container(
child: Theme(
data: Theme.of(context).copyWith(
colorScheme:
ColorScheme.of(context).copyWith(primary: primaryColor),
datePickerTheme: UiUtils.buildDatePickerTheme(context)),
child: CalendarDatePicker(
initialDate: DateTime.now(),
firstDate: DateTime(1999),
currentDate: selectedDate,
lastDate: DateTime.now(),
onDateChanged: (DateTime date) {
setState(() {
selectedDate = date;
});
widget.onDateChanged(date);
},
),
),
);
}
}
class CustomCalendarView extends StatefulWidget {
final Function(DateTime date) onDateSelected;
final DateTime? selectedDate;
const CustomCalendarView(
{super.key, required this.onDateSelected, this.selectedDate});
@override
_CustomCalendarViewState createState() => _CustomCalendarViewState();
}
class _CustomCalendarViewState extends State<CustomCalendarView> {
DateTime selectedDate = DateTime.now();
@override
Widget build(BuildContext context) {
final firstDayOfMonth = DateTime(selectedDate.year, selectedDate.month, 1);
final startWeekday = firstDayOfMonth.weekday; // Mon = 1, Sun = 7
final daysInMonth =
DateUtils.getDaysInMonth(selectedDate.year, selectedDate.month);
// Previous month filler days
final prevMonth = DateTime(selectedDate.year, selectedDate.month - 1);
final daysInPrevMonth =
DateUtils.getDaysInMonth(prevMonth.year, prevMonth.month);
final prefixDays = List.generate(
startWeekday - 1,
(i) => DateTime(prevMonth.year, prevMonth.month,
daysInPrevMonth - (startWeekday - 2 - i)));
// Current month days
final currentMonthDays = List.generate(daysInMonth,
(i) => DateTime(selectedDate.year, selectedDate.month, i + 1));
// Fillers from next month
final totalSlots = prefixDays.length + currentMonthDays.length;
final suffixDays = List.generate(42 - totalSlots,
(i) => DateTime(selectedDate.year, selectedDate.month + 1, i + 1));
final allDays = [...prefixDays, ...currentMonthDays, ...suffixDays];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Text(
DateFormat.yMMMM().format(selectedDate),
style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
.map((d) => Expanded(
child: Center(
child: Text(d,
style: TextStyle(fontWeight: FontWeight.w500)),
),
))
.toList(),
),
const SizedBox(height: 8),
Expanded(
child: GridView.builder(
padding: const EdgeInsets.all(8),
physics: NeverScrollableScrollPhysics(),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 7,
crossAxisSpacing: 6,
mainAxisSpacing: 6,
),
itemCount: allDays.length,
itemBuilder: (_, i) {
final day = allDays[i];
final isCurrentMonth = day.month == selectedDate.month;
final isToday = DateUtils.isSameDay(day, DateTime.now());
return GestureDetector(
onTap: () {
final bool isFutureDate = day.isAfter(DateTime.now());
if (isCurrentMonth && !isFutureDate) {
widget.onDateSelected(day);
}
},
child: Container(
decoration: BoxDecoration(
color:
(widget.selectedDate?.day == day.day && isCurrentMonth)
? UiUtils.getColorScheme(context).primary
: (isToday
? UiUtils.getColorScheme(context)
.primary
.withValues(alpha: 0.1)
: null),
borderRadius: BorderRadius.circular(8),
),
child: Center(
child: Text(
'${day.day}',
style: TextStyle(
color: isCurrentMonth ? null : Colors.grey,
fontWeight:
isToday ? FontWeight.bold : FontWeight.normal,
),
),
),
),
);
},
),
),
],
);
}
}

View File

@@ -0,0 +1,128 @@
import 'package:flutter/material.dart';
import 'package:news/utils/strings.dart';
import 'package:news/utils/uiUtils.dart';
abstract class DurationFilter {
Map<String, String> toMap();
String toText(BuildContext context);
}
class LastDays extends DurationFilter {
final int daysCount;
LastDays({required this.daysCount});
@override
toMap() {
return {
LAST_N_DAYS: daysCount.toString(),
};
}
@override
String toText(BuildContext context) {
if (daysCount == 1) {
return UiUtils.getTranslatedLabel(context, 'today');
}
return UiUtils.getTranslatedLabel(context, 'last') + ' $daysCount ' + UiUtils.getTranslatedLabel(context, 'days');
}
@override
bool operator ==(covariant DurationFilter other) {
if (identical(this, other)) return true;
if (other is LastDays) {
return other.daysCount == daysCount;
}
return false;
}
@override
int get hashCode => daysCount.hashCode;
}
class Year extends DurationFilter {
final int year;
Year({required this.year});
@override
Map<String, String> toMap() {
return {
YEAR: year.toString(),
};
}
@override
String toText(BuildContext context) {
return year.toString();
}
@override
bool operator ==(covariant DurationFilter other) {
if (identical(this, other)) return true;
if (other is Year) {
return other.year == year;
}
return false;
}
@override
int get hashCode => year.hashCode;
}
class DurationFilterWidget extends StatefulWidget {
final DurationFilter? selectedFilter;
final List<DurationFilter> filters;
final void Function(DurationFilter? filter)? onSelected;
const DurationFilterWidget({super.key, required this.filters, this.onSelected, this.selectedFilter});
@override
State<DurationFilterWidget> createState() => _DurationFilterWidgetState();
}
class _DurationFilterWidgetState extends State<DurationFilterWidget> {
late DurationFilter? selectedFilter = widget.selectedFilter;
@override
void didUpdateWidget(DurationFilterWidget oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.selectedFilter != oldWidget.selectedFilter) {
selectedFilter = widget.selectedFilter;
}
}
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: widget.filters.map(
(DurationFilter e) {
return _buildDurationFilter(context, e);
},
).toList(),
);
}
Widget _buildDurationFilter(BuildContext context, DurationFilter filter) {
return GestureDetector(
onTap: () {
if (selectedFilter == filter) {
selectedFilter = null;
} else {
selectedFilter = filter;
}
widget.onSelected?.call(selectedFilter);
setState(() {});
},
child: Container(
width: 100,
decoration: BoxDecoration(
color: selectedFilter == filter ? UiUtils.getColorScheme(context).secondaryContainer.withValues(alpha: 0.2) : UiUtils.getColorScheme(context).surface,
borderRadius: BorderRadius.circular(4),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(filter.toText(context)),
),
),
);
}
}

View File

@@ -0,0 +1,196 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:news/ui/screens/auth/Widgets/svgPictureWidget.dart';
import 'package:news/ui/styles/colors.dart';
import 'package:news/ui/widgets/customTextBtn.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/utils/uiUtils.dart';
import 'package:news/app/routes.dart';
import 'package:news/cubits/settingCubit.dart';
class Slide {
final String? imageUrl;
final String? title;
final String? description;
Slide({@required this.imageUrl, @required this.title, @required this.description});
}
class IntroSliderScreen extends StatefulWidget {
const IntroSliderScreen({super.key});
@override
GettingStartedScreenState createState() => GettingStartedScreenState();
}
class GettingStartedScreenState extends State<IntroSliderScreen> with TickerProviderStateMixin {
PageController pageController = PageController();
int currentIndex = 0;
late List<Slide> slideList = [
Slide(imageUrl: 'onboarding1', title: UiUtils.getTranslatedLabel(context, 'welTitle1'), description: UiUtils.getTranslatedLabel(context, 'welDes1')),
Slide(imageUrl: 'onboarding2', title: UiUtils.getTranslatedLabel(context, 'welTitle2'), description: UiUtils.getTranslatedLabel(context, 'welDes2')),
Slide(imageUrl: 'onboarding3', title: UiUtils.getTranslatedLabel(context, 'welTitle3'), description: UiUtils.getTranslatedLabel(context, 'welDes3')),
];
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
bottomNavigationBar: nextButton(),
appBar: AppBar(automaticallyImplyLeading: false, centerTitle: false, actions: [setSkipButton()], title: SvgPictureWidget(assetName: "intro_icon", fit: BoxFit.cover)),
body: _buildIntroSlider());
}
gotoNext() {
context.read<SettingsCubit>().changeShowIntroSlider(false);
Navigator.of(context).pushReplacementNamed(Routes.login);
}
void onPageChanged(int index) {
setState(() {
currentIndex = index; //update current index for Next button
});
}
Widget _buildIntroSlider() {
return PageView.builder(
onPageChanged: onPageChanged,
controller: pageController,
itemCount: slideList.length,
itemBuilder: (context, index) {
final size = MediaQuery.of(context).size;
final imagePath = slideList[index].imageUrl;
return SizedBox(
width: size.width * 0.99,
height: size.height * 0.75,
child: Transform(
alignment: Alignment.center,
transform: Matrix4.identity()
..setEntry(3, 2, 0.001)
..rotateX(0.1),
child: Stack(
children: [
Positioned(
left: size.width * 0.3,
right: size.width * 0.3,
top: size.height * 0.71,
child: Container(
height: 7,
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: UiUtils.getColorScheme(context).primary.withOpacity(0.3),
blurRadius: 20,
spreadRadius: 5,
offset: Offset.zero,
),
],
shape: BoxShape.rectangle,
),
),
),
Card(
color: secondaryColor,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(40.0)),
elevation: 0,
margin: const EdgeInsets.fromLTRB(24, 30, 24, 20),
child: Column(
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.all(20),
child: imagePath != null ? SvgPictureWidget(assetName: imagePath, fit: BoxFit.contain) : SizedBox.shrink(),
),
),
titleText(index),
subtitleText(index),
progressIndicator(index),
const SizedBox(height: 25),
],
),
),
],
),
),
);
},
);
}
Widget setSkipButton() {
return (currentIndex != slideList.length - 1)
? CustomTextButton(
onTap: () {
gotoNext();
},
color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.7),
text: UiUtils.getTranslatedLabel(context, 'skip'))
: const SizedBox.shrink();
}
Widget titleText(int index) {
return Container(
padding: const EdgeInsets.only(left: 20, right: 10),
margin: const EdgeInsets.only(bottom: 20.0, left: 10, right: 10),
alignment: Alignment.center,
child: CustomTextLabel(text: slideList[index].title!, textStyle: Theme.of(context).textTheme.headlineSmall?.copyWith(color: darkSecondaryColor, fontWeight: FontWeight.bold, letterSpacing: 0.5)),
);
}
Widget subtitleText(int index) {
return Container(
padding: const EdgeInsets.only(left: 10),
margin: const EdgeInsets.only(bottom: 55.0, left: 10, right: 10),
child: CustomTextLabel(
text: slideList[index].description!,
textAlign: TextAlign.left,
textStyle: Theme.of(context).textTheme.titleMedium?.copyWith(color: darkSecondaryColor.withOpacity(0.5), fontWeight: FontWeight.normal, letterSpacing: 0.5),
maxLines: 3,
overflow: TextOverflow.ellipsis));
}
Widget progressIndicator(int index) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List<Widget>.generate(
slideList.length,
(index) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 5),
child: InkWell(
onTap: () => pageController.animateToPage(index, duration: const Duration(seconds: 1), curve: Curves.fastLinearToSlowEaseIn),
child: (currentIndex == index)
? ClipRRect(borderRadius: BorderRadius.circular(10.0), child: Container(height: 10, width: 40.0, color: darkSecondaryColor))
: CircleAvatar(radius: 5, backgroundColor: darkSecondaryColor.withOpacity(0.6))))));
}
Widget nextButton() {
return MaterialButton(
splashColor: Colors.transparent,
highlightColor: Colors.transparent,
padding: EdgeInsets.symmetric(horizontal: MediaQuery.of(context).size.width * 0.28, vertical: 20),
onPressed: () {
if (currentIndex == slideList.length - 1 && currentIndex != 0) {
gotoNext();
} else {
currentIndex += 1;
pageController.animateToPage(currentIndex, duration: const Duration(seconds: 1), curve: Curves.fastLinearToSlowEaseIn);
}
},
child: Container(
height: 50,
width: 162,
alignment: Alignment.center,
decoration: BoxDecoration(color: Theme.of(context).primaryColor, borderRadius: BorderRadius.circular(14)),
child: CustomTextLabel(
text: (currentIndex == (slideList.length - 1)) ? 'loginBtn' : 'nxt',
textStyle: Theme.of(context).textTheme.bodyLarge!.copyWith(color: secondaryColor, fontWeight: FontWeight.bold),
textAlign: TextAlign.center)));
}
}

View File

@@ -0,0 +1,255 @@
import 'dart:convert';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:news/app/routes.dart';
import 'package:news/cubits/Auth/registerTokenCubit.dart';
import 'package:news/cubits/generalNewsCubit.dart';
import 'package:news/cubits/rssFeedCubit.dart';
import 'package:news/cubits/settingCubit.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:intl/intl.dart';
import 'package:news/cubits/appSystemSettingCubit.dart';
import 'package:news/cubits/breakingNewsCubit.dart';
import 'package:news/data/repositories/Settings/settingsLocalDataRepository.dart';
import 'package:news/ui/screens/dashBoard/dashBoardScreen.dart';
import 'package:news/ui/styles/colors.dart';
import 'package:news/ui/widgets/customAppBar.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/ui/widgets/errorContainerWidget.dart';
import 'package:news/ui/widgets/networkImage.dart';
import 'package:news/utils/ErrorMessageKeys.dart';
import 'package:news/utils/internetConnectivity.dart';
import 'package:news/utils/uiUtils.dart';
import 'package:news/cubits/Auth/authCubit.dart';
import 'package:news/cubits/languageCubit.dart';
import 'package:news/cubits/Bookmark/bookmarkCubit.dart';
import 'package:news/cubits/appLocalizationCubit.dart';
import 'package:news/cubits/categoryCubit.dart';
import 'package:news/cubits/featureSectionCubit.dart';
import 'package:news/cubits/languageJsonCubit.dart';
import 'package:news/cubits/liveStreamCubit.dart';
import 'package:news/cubits/otherPagesCubit.dart';
import 'package:news/cubits/videosCubit.dart';
import 'package:news/cubits/LikeAndDislikeNews/LikeAndDislikeCubit.dart';
class LanguageList extends StatefulWidget {
final String? from;
const LanguageList({super.key, this.from});
@override
LanguageListState createState() => LanguageListState();
static Route route(RouteSettings routeSettings) {
final arguments = routeSettings.arguments as Map<String, dynamic>;
return CupertinoPageRoute(builder: (_) => LanguageList(from: arguments['from']));
}
}
class LanguageListState extends State<LanguageList> {
String? selLanCode, selLanId;
late String latitude, longitude;
int? selLanRTL;
bool isNetworkAvail = true;
@override
void initState() {
isNetworkAvailable();
getLanguageData();
setLatitudeLongitude();
super.initState();
}
Future getLanguageData() async {
Future.delayed(Duration.zero, () {
context.read<LanguageCubit>().getLanguage();
});
}
void setLatitudeLongitude() {
latitude = SettingsLocalDataRepository().getLocationCityValues().first;
longitude = SettingsLocalDataRepository().getLocationCityValues().last;
}
Widget getLangList() {
return BlocBuilder<AppLocalizationCubit, AppLocalizationState>(builder: (context, stateLocale) {
return BlocBuilder<LanguageCubit, LanguageState>(builder: (context, state) {
if (state is LanguageFetchSuccess) {
return ListView.separated(
padding: const EdgeInsets.only(bottom: 20, top: 10),
physics: const AlwaysScrollableScrollPhysics(),
itemBuilder: ((context, index) {
return Padding(
padding: const EdgeInsets.fromLTRB(20.0, 5.0, 20.0, 5.0),
child: Container(
height: MediaQuery.of(context).size.height * 0.08,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15.0),
color: (selLanCode ?? stateLocale.languageCode) == state.language[index].code! ? UiUtils.getColorScheme(context).primaryContainer : null),
child: InkWell(
onTap: () {
setState(() {
Intl.defaultLocale = state.language[index].code;
selLanCode = state.language[index].code!;
selLanId = state.language[index].id!;
selLanRTL = state.language[index].isRTL!;
});
},
child: Container(
margin: const EdgeInsets.only(left: 15, right: 15),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
CustomNetworkImage(networkImageUrl: state.language[index].image!, isVideo: false, height: 40, fit: BoxFit.fill, width: 40),
SizedBox(width: MediaQuery.of(context).size.width * 0.05),
CustomTextLabel(
text: state.language[index].languageDisplayName ?? state.language[index].language!,
textStyle: Theme.of(this.context).textTheme.titleLarge?.copyWith(
color: ((selLanCode ?? (stateLocale).languageCode) == state.language[index].code!)
? UiUtils.getColorScheme(context).secondary
: UiUtils.getColorScheme(context).primaryContainer)),
],
),
),
),
));
}),
separatorBuilder: (context, index) {
return const SizedBox(height: 1.0);
},
itemCount: state.language.length);
}
if (state is LanguageFetchFailure) {
return Padding(
padding: const EdgeInsets.only(left: 30.0, right: 30.0),
child: ErrorContainerWidget(
errorMsg: (state.errorMessage.contains(ErrorMessageKeys.noInternet)) ? UiUtils.getTranslatedLabel(context, 'internetmsg') : state.errorMessage, onRetry: getLanguageData),
);
}
return const Padding(padding: EdgeInsets.only(bottom: 10.0, left: 30.0, right: 30.0), child: SizedBox.shrink());
});
});
}
saveBtn() {
return BlocConsumer<LanguageJsonCubit, LanguageJsonState>(
bloc: context.read<LanguageJsonCubit>(),
listener: (context, state) {
if (state is LanguageJsonFetchSuccess) {
final langId = selLanId ?? context.read<AppLocalizationCubit>().state.id;
UiUtils.setDynamicStringValue(
context.read<AppLocalizationCubit>().state.languageCode,
jsonEncode(state.languageJson),
).then((_) {
// Update languageId
homeScreenKey?.currentState?.languageId = langId;
// Fetch general data
context.read<OtherPageCubit>().getOtherPage(langId: langId);
context.read<SectionCubit>().getSection(langId: langId, latitude: latitude, longitude: longitude);
context.read<LiveStreamCubit>().getLiveStream(langId: langId);
context.read<GeneralNewsCubit>().getGeneralNews(langId: langId, latitude: latitude, longitude: longitude);
context.read<VideoCubit>().getVideo(langId: langId, latitude: latitude, longitude: longitude);
context.read<CategoryCubit>().getCategory(langId: langId);
// Conditional based on config
final config = context.read<AppConfigurationCubit>();
if (config.getWeatherMode() == "1") {
homeScreenKey?.currentState?.getWeatherData();
}
if (config.getRSSFeedMode() == "1") {
context.read<RSSFeedCubit>().getRSSFeed(langId: langId);
}
if (config.getBreakingNewsMode() == "1") {
context.read<BreakingNewsCubit>().getBreakingNews(langId: langId);
}
// If user is logged in
final auth = context.read<AuthCubit>();
if (auth.getUserId() != "0") {
context.read<LikeAndDisLikeCubit>().getLike(langId: langId);
context.read<BookmarkCubit>().getBookmark(langId: langId);
updateUserLanguageWithFCMid();
}
});
if (widget.from != null && widget.from == "firstLogin" && context.read<CategoryCubit>().getCatList().isNotEmpty) {
//check if it is firstLogin - then goto Home or else pop
Navigator.of(context).pushNamedAndRemoveUntil(Routes.managePref, (route) => false, arguments: {"from": 2});
} else if (widget.from == "firstLogin") {
Navigator.of(context).pushReplacementNamed(Routes.home, arguments: false);
} else {
Navigator.pop(context);
}
}
},
builder: (context, state) {
if (state is LanguageJsonFetchSuccess) {
return InkWell(
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
child: Container(
height: 45.0,
margin: const EdgeInsetsDirectional.all(20),
width: MediaQuery.of(context).size.width * 0.9,
alignment: Alignment.center,
decoration: BoxDecoration(color: Theme.of(context).primaryColor, borderRadius: BorderRadius.circular(15.0)),
child: CustomTextLabel(text: 'saveLbl', textStyle: Theme.of(context).textTheme.titleLarge?.copyWith(color: backgroundColor, fontWeight: FontWeight.bold))),
onTap: () {
setState(() {
if (selLanCode != null && context.read<AppLocalizationCubit>().state.languageCode != selLanCode) {
context.read<AppLocalizationCubit>().changeLanguage(selLanCode!, selLanId!, selLanRTL!);
context.read<LanguageJsonCubit>().getLanguageJson(lanCode: selLanCode!);
} else {
if (widget.from != null && widget.from == "firstLogin" && context.read<CategoryCubit>().getCatList().isNotEmpty) {
Navigator.of(context).pushNamedAndRemoveUntil(Routes.managePref, (route) => false, arguments: {"from": 2});
} else if (widget.from == "firstLogin") {
Navigator.of(context).pushReplacementNamed(Routes.home, arguments: false);
} else {
Navigator.pop(context);
}
}
});
});
}
return const SizedBox.shrink();
});
}
void updateUserLanguageWithFCMid() async {
String currentFCMId = context.read<SettingsCubit>().getSettings().token.trim();
if (currentFCMId.isEmpty) {
final newToken = await FirebaseMessaging.instance.getToken();
if (newToken != null) {
currentFCMId = newToken;
context.read<RegisterTokenCubit>().registerToken(fcmId: currentFCMId, context: context);
}
} else {
context.read<RegisterTokenCubit>().registerToken(fcmId: currentFCMId, context: context);
}
context.read<SettingsCubit>().changeFcmToken(currentFCMId);
}
isNetworkAvailable() async {
if (await InternetConnectivity.isNetworkAvailable()) {
setState(() {
isNetworkAvail = true;
});
} else {
setState(() {
isNetworkAvail = false;
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(height: 45, isBackBtn: true, label: 'chooseLanLbl', horizontalPad: 15, isConvertText: true),
bottomNavigationBar: (isNetworkAvail) ? saveBtn() : const SizedBox.shrink(),
body: getLangList());
}
}

View File

@@ -0,0 +1,52 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:news/ui/screens/auth/Widgets/svgPictureWidget.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/cubits/appSystemSettingCubit.dart';
class MaintenanceScreen extends StatefulWidget {
const MaintenanceScreen({super.key});
@override
MaintenanceScreenState createState() => MaintenanceScreenState();
}
class MaintenanceScreenState extends State<MaintenanceScreen> {
@override
void initState() {
super.initState();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsetsDirectional.only(start: 15.0, end: 15.0, top: 10.0, bottom: 10.0),
child: BlocBuilder<AppConfigurationCubit, AppConfigurationState>(
builder: (context, state) {
if (state is AppConfigurationFetchSuccess && state.appConfiguration.maintenanceMode == "1") {
return Column(mainAxisAlignment: MainAxisAlignment.center, children: [
SvgPictureWidget(assetName: "maintenance"),
const SizedBox(height: 20),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: CustomTextLabel(
textStyle: TextStyle(color: Theme.of(context).colorScheme.primaryContainer, fontSize: 18, fontWeight: FontWeight.w600),
textAlign: TextAlign.center,
text: 'maintenanceMessageLbl'))
]);
} else if (state is AppConfigurationFetchSuccess && state.appConfiguration.maintenanceMode == "0") {
Navigator.of(context).pop();
}
//default/Processing state
return Padding(padding: const EdgeInsets.only(bottom: 10.0, left: 10.0, right: 10.0), child: CircularProgressIndicator());
},
),
));
}
}

View File

@@ -0,0 +1,134 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:hive/hive.dart';
import 'package:news/cubits/appLocalizationCubit.dart';
import 'package:news/cubits/languageCubit.dart';
import 'package:news/cubits/languageJsonCubit.dart';
import 'package:news/cubits/settingCubit.dart';
import 'package:news/ui/screens/auth/Widgets/svgPictureWidget.dart';
import 'package:news/ui/styles/appTheme.dart';
import 'package:news/ui/styles/colors.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/ui/widgets/errorContainerWidget.dart';
import 'package:news/utils/ErrorMessageKeys.dart';
import 'package:news/utils/uiUtils.dart';
import 'package:news/app/routes.dart';
import 'package:news/cubits/appSystemSettingCubit.dart';
import 'package:news/utils/hiveBoxKeys.dart';
class Splash extends StatefulWidget {
const Splash({super.key});
@override
SplashState createState() => SplashState();
}
class SplashState extends State<Splash> with TickerProviderStateMixin {
@override
void initState() {
super.initState();
fetchAppConfigurations();
}
fetchAppConfigurations() {
context.read<AppConfigurationCubit>().fetchAppConfiguration();
}
fetchLanguages({required AppConfigurationFetchSuccess state}) async {
String currentLanguage = Hive.box(settingsBoxKey).get(currentLanguageCodeKey) ?? "";
if (currentLanguage == "" && state.appConfiguration.defaultLangDataModel != null) {
context
.read<AppLocalizationCubit>()
.changeLanguage(state.appConfiguration.defaultLangDataModel!.code!, state.appConfiguration.defaultLangDataModel!.id!, state.appConfiguration.defaultLangDataModel!.isRTL!);
context.read<LanguageJsonCubit>().fetchCurrentLanguageAndLabels(state.appConfiguration.defaultLangDataModel!.code!);
} else {
context.read<LanguageJsonCubit>().fetchCurrentLanguageAndLabels(currentLanguage);
}
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
if (Platform.isAndroid) UiUtils.setUIOverlayStyle(appTheme: AppTheme.Dark); //set UiOverlayStyle to dark - due to fixed splashScreen backgroundColor // according to selected theme
return Scaffold(backgroundColor: Theme.of(context).secondaryHeaderColor, body: buildScale());
}
Future<void> navigationPage() async {
Future.delayed(const Duration(seconds: 4), () async {
final currentSettings = context.read<SettingsCubit>().state.settingsModel;
if (context.read<AppConfigurationCubit>().getMaintenanceMode() == "1") {
//app is in maintenance mode - no function should be performed
Navigator.of(context).pushReplacementNamed(Routes.maintenance);
} else if (currentSettings!.showIntroSlider) {
Navigator.of(context).pushReplacementNamed(Routes.introSlider);
} else {
Navigator.of(context).pushReplacementNamed(Routes.home, arguments: false);
}
});
}
Widget buildScale() {
return BlocConsumer<AppConfigurationCubit, AppConfigurationState>(
bloc: context.read<AppConfigurationCubit>(),
listener: (context, state) {
if (state is AppConfigurationFetchSuccess) {
fetchLanguages(state: state);
}
},
builder: (context, state) {
return BlocConsumer<LanguageJsonCubit, LanguageJsonState>(
bloc: context.read<LanguageJsonCubit>(),
listener: (context, state) {
if (state is LanguageJsonFetchSuccess) {
navigationPage();
context.read<LanguageCubit>().getLanguage(); //Load languages for dynamic link
}
},
builder: (context, langState) {
if (state is AppConfigurationFetchFailure) {
return ErrorContainerWidget(
errorMsg: (state.errorMessage.contains(ErrorMessageKeys.noInternet)) ? UiUtils.getTranslatedLabel(context, 'internetmsg') : state.errorMessage,
onRetry: () {
fetchAppConfigurations();
},
);
} else if (langState is LanguageJsonFetchFailure) {
return ErrorContainerWidget(
errorMsg: (langState.errorMessage.contains(ErrorMessageKeys.noInternet)) ? UiUtils.getTranslatedLabel(context, 'internetmsg') : langState.errorMessage,
onRetry: () {
fetchLanguages(state: state as AppConfigurationFetchSuccess);
},
);
} else {
return Container(
width: double.maxFinite,
decoration: BoxDecoration(color: primaryColor),
child: Column(mainAxisSize: MainAxisSize.min, children: [const SizedBox(height: 220), splashLogoIcon(), newsTextIcon(), subTitle(), const Spacer(), bottomText()]),
);
}
});
});
}
Widget splashLogoIcon() {
return Center(child: SvgPictureWidget(assetName: "splash_icon", height: 110.0, fit: BoxFit.fill));
}
Widget newsTextIcon() {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 20.0),
child: Center(child: SvgPictureWidget(assetName: "caribe_blanco", height: 58.0, width: 300, fit: BoxFit.fill)),
);
}
Widget subTitle() =>
CustomTextLabel(text: 'fastTrendNewsLbl', textAlign: TextAlign.center, textStyle: Theme.of(context).textTheme.bodyMedium!.copyWith(color: backgroundColor, fontWeight: FontWeight.bold));
Widget bottomText() => Container(margin: const EdgeInsetsDirectional.only(bottom: 20), child: SvgPictureWidget(assetName: "wrteam_logo", height: 40.0, fit: BoxFit.fill));
}

View File

@@ -0,0 +1,59 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'colors.dart';
enum AppTheme { Light, Dark }
final appThemeData = {
AppTheme.Light: ThemeData(
useMaterial3: false,
fontFamily: 'Roboto',
brightness: Brightness.light,
primaryColor: primaryColor,
canvasColor: backgroundColor,
textTheme: const TextTheme().apply(bodyColor: darkSecondaryColor, displayColor: darkSecondaryColor),
appBarTheme: AppBarTheme(
elevation: 0.0,
backgroundColor: backgroundColor,
systemOverlayStyle: SystemUiOverlayStyle(statusBarBrightness: Brightness.light, statusBarIconBrightness: Brightness.dark, statusBarColor: backgroundColor.withOpacity(0.8))),
iconTheme: const IconThemeData(color: darkSecondaryColor),
colorScheme: ColorScheme.fromSeed(
seedColor: primaryColor,
brightness: Brightness.light,
surface: secondaryColor,
secondary: secondaryColor,
onPrimary: darkBackgroundColor,
secondaryContainer: darkSecondaryColor,
outline: borderColor,
primaryContainer: darkSecondaryColor),
dialogBackgroundColor: backgroundColor //for datePicker
),
AppTheme.Dark: ThemeData(
useMaterial3: false,
fontFamily: 'Roboto',
brightness: Brightness.dark,
primaryColor: primaryColor,
canvasColor: darkSecondaryColor,
appBarTheme: AppBarTheme(
elevation: 0.0,
backgroundColor: darkBackgroundColor,
systemOverlayStyle: SystemUiOverlayStyle(
statusBarBrightness: Brightness.dark,
statusBarIconBrightness: Brightness.light,
statusBarColor: darkSecondaryColor.withOpacity(0.8),
)),
textTheme: const TextTheme().apply(bodyColor: secondaryColor, displayColor: secondaryColor),
iconTheme: const IconThemeData(color: secondaryColor),
colorScheme: ColorScheme.fromSeed(
seedColor: primaryColor,
onPrimary: secondaryColor,
surface: darkBackgroundColor,
brightness: Brightness.dark,
secondary: darkSecondaryColor,
secondaryContainer: primaryColor,
outline: backgroundColor,
primaryContainer: secondaryColor //for datePicker
),
dialogBackgroundColor: darkBackgroundColor),
};

View File

@@ -0,0 +1,18 @@
import 'package:flutter/material.dart';
Color primaryColor = const Color(0xff008DA8);
const Color secondaryColor = Color(0xffffffff);
const Color backgroundColor = Color(0xffF1F6F9);
const Color borderColor = Color(0xff6B6B6B);
const Color warningColor = Colors.amberAccent;
const Color iconColor = Color(0xffBE151E);
const Color darkSecondaryColor = Color(0xff1B2D51);
const Color darkBackgroundColor = Color(0xff1F345E);
const Color darkIconColor = Color(0xffFF787F);
const Color dividerColor = Color(0x1F000000);
const Color authorRequestColor = darkSecondaryColor;
const Color authorReviewColor = Color(0xff017A80);
const Color authorApprovedColor = Color(0xff060F72);

View File

@@ -0,0 +1,256 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/ui/widgets/newsCard.dart';
import 'package:news/utils/uiUtils.dart';
import 'package:news/utils/internetConnectivity.dart';
import 'package:news/app/routes.dart';
import 'package:news/cubits/Auth/authCubit.dart';
import 'package:news/cubits/Bookmark/UpdateBookmarkCubit.dart';
import 'package:news/cubits/Bookmark/bookmarkCubit.dart';
import 'package:news/cubits/appLocalizationCubit.dart';
import 'package:news/cubits/appSystemSettingCubit.dart';
import 'package:news/cubits/LikeAndDislikeNews/LikeAndDislikeCubit.dart';
import 'package:news/cubits/LikeAndDislikeNews/updateLikeAndDislikeCubit.dart';
import 'package:news/data/models/NewsModel.dart';
import 'package:news/data/repositories/Bookmark/bookmarkRepository.dart';
import 'package:news/data/repositories/LikeAndDisLikeNews/LikeAndDisLikeNewsRepository.dart';
import 'package:news/ui/styles/colors.dart';
import 'package:news/ui/widgets/networkImage.dart';
import 'package:news/ui/widgets/SnackBarWidget.dart';
class NewsItem extends StatefulWidget {
final NewsModel model;
final int index;
final List<NewsModel> newslist;
final bool fromShowMore;
const NewsItem({super.key, required this.model, required this.index, required this.newslist, required this.fromShowMore});
@override
NewsItemState createState() => NewsItemState();
}
class NewsItemState extends State<NewsItem> {
late BannerAd bannerAd;
@override
void initState() {
super.initState();
if (context.read<AppConfigurationCubit>().getInAppAdsMode() == "1" &&
(context.read<AppConfigurationCubit>().getAdsType() != "unity" || context.read<AppConfigurationCubit>().getIOSAdsType() != "unity")) bannerAd = UiUtils.createBannerAd(context: context);
}
Widget setButton({required Widget childWidget}) {
return Container(padding: const EdgeInsets.all(3), decoration: BoxDecoration(borderRadius: BorderRadius.circular(15), color: UiUtils.getColorScheme(context).secondary), child: childWidget);
}
Widget setNewsImage() {
return ClipRRect(
borderRadius: BorderRadius.circular(10),
child: ShaderMask(
shaderCallback: (rect) => LinearGradient(begin: Alignment.center, end: Alignment.bottomCenter, colors: [Colors.transparent, darkSecondaryColor.withOpacity(0.9)]).createShader(rect),
blendMode: BlendMode.darken,
child: Container(
color: primaryColor.withAlpha(5),
width: double.maxFinite,
height: MediaQuery.of(context).size.height / 3.3,
child: CustomNetworkImage(networkImageUrl: widget.model.image!, width: double.infinity, height: MediaQuery.of(context).size.height / 4.2, fit: BoxFit.cover, isVideo: false))));
}
Widget setTagsList({required List<String> tagList, required List<String> tagId}) {
return Positioned.directional(
textDirection: Directionality.of(context),
bottom: 80.0,
start: 7.0,
child: widget.model.tagName! != ""
? SizedBox(
height: 16.0,
child: ListView.builder(
physics: const AlwaysScrollableScrollPhysics(),
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemCount: tagList.length,
itemBuilder: (context, index) {
return Padding(
padding: EdgeInsetsDirectional.only(start: index == 0 ? 0 : 5.5),
child: InkWell(
child: Container(
height: 20.0,
width: 65,
alignment: Alignment.center,
padding: const EdgeInsetsDirectional.only(start: 3.0, end: 3.0, top: 1.0, bottom: 1.0),
decoration: BoxDecoration(
borderRadius: const BorderRadius.only(bottomLeft: Radius.circular(10.0), topRight: Radius.circular(10.0)),
color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.85)),
child: CustomTextLabel(
text: tagList[index],
textStyle: Theme.of(context).textTheme.bodyMedium?.copyWith(color: UiUtils.getColorScheme(context).secondary, fontSize: 8.5),
overflow: TextOverflow.ellipsis,
softWrap: true)),
onTap: () async {
Navigator.of(context).pushNamed(Routes.tagScreen, arguments: {"tagId": tagId[index], "tagName": tagList[index]});
},
),
);
}))
: const SizedBox.shrink());
}
Widget setTitleAndDate() {
return Positioned.directional(
textDirection: Directionality.of(context),
bottom: 20,
start: 10,
end: 10,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
alignment: Alignment.bottomLeft,
padding: const EdgeInsetsDirectional.only(top: 4.0, start: 5.0, end: 5.0),
child: CustomTextLabel(
text: widget.model.title!, textStyle: Theme.of(context).textTheme.titleSmall?.copyWith(color: secondaryColor), maxLines: 2, softWrap: true, overflow: TextOverflow.ellipsis)),
Padding(
padding: const EdgeInsetsDirectional.only(top: 4.0, start: 5.0, end: 5.0),
child: CustomTextLabel(
text: UiUtils.convertToAgo(context, DateTime.parse(widget.model.publishDate ?? widget.model.date!), 0)!,
textStyle: Theme.of(context).textTheme.bodySmall!.copyWith(color: secondaryColor.withOpacity(0.6)))),
],
),
);
}
Widget shareButton() {
return Padding(
padding: const EdgeInsetsDirectional.only(start: 13.0),
child: InkWell(
child: setButton(childWidget: const Icon(Icons.share_rounded, size: 20)),
onTap: () async {
if (await InternetConnectivity.isNetworkAvailable()) {
UiUtils.shareNews(context: context, slug: widget.model.slug!, title: widget.model.title!, isVideo: false, videoId: "", isBreakingNews: false, isNews: true);
} else {
showSnackBar(UiUtils.getTranslatedLabel(context, 'internetmsg'), context);
}
},
),
);
}
Widget bookmarkButton() {
return BlocProvider(
create: (context) => UpdateBookmarkStatusCubit(BookmarkRepository()),
child: BlocBuilder<BookmarkCubit, BookmarkState>(
bloc: context.read<BookmarkCubit>(),
builder: (context, bookmarkState) {
bool isBookmark = context.read<BookmarkCubit>().isNewsBookmark(widget.model.id!);
return BlocConsumer<UpdateBookmarkStatusCubit, UpdateBookmarkStatusState>(
bloc: context.read<UpdateBookmarkStatusCubit>(),
listener: ((context, state) {
if (state is UpdateBookmarkStatusSuccess) {
(state.wasBookmarkNewsProcess) ? context.read<BookmarkCubit>().addBookmarkNews(state.news) : context.read<BookmarkCubit>().removeBookmarkNews(state.news);
}
}),
builder: (context, state) {
return InkWell(
onTap: () {
if (context.read<AuthCubit>().getUserId() != "0") {
if (state is UpdateBookmarkStatusInProgress) return;
context.read<UpdateBookmarkStatusCubit>().setBookmarkNews(news: widget.model, status: (isBookmark) ? "0" : "1");
} else {
UiUtils.loginRequired(context);
}
},
child: setButton(
childWidget: (state is UpdateBookmarkStatusInProgress)
? SizedBox(height: 20, width: 20, child: UiUtils.showCircularProgress(true, Theme.of(context).primaryColor))
: ((isBookmark) ? const Icon(Icons.bookmark_added_rounded, size: 20) : const Icon(Icons.bookmark_add_outlined, size: 20))));
});
}),
);
}
Widget likeButton() {
bool isLike = context.read<LikeAndDisLikeCubit>().isNewsLikeAndDisLike(widget.model.id!);
return BlocProvider(
create: (context) => UpdateLikeAndDisLikeStatusCubit(LikeAndDisLikeRepository()),
child: BlocConsumer<LikeAndDisLikeCubit, LikeAndDisLikeState>(
bloc: context.read<LikeAndDisLikeCubit>(),
listener: ((context, state) {
if (state is LikeAndDisLikeFetchSuccess) {
isLike = context.read<LikeAndDisLikeCubit>().isNewsLikeAndDisLike(widget.model.id!);
}
}),
builder: (context, likeAndDislikeState) {
return BlocConsumer<UpdateLikeAndDisLikeStatusCubit, UpdateLikeAndDisLikeStatusState>(
bloc: context.read<UpdateLikeAndDisLikeStatusCubit>(),
listener: ((context, state) {
if (state is UpdateLikeAndDisLikeStatusSuccess) {
context.read<LikeAndDisLikeCubit>().getLike(langId: context.read<AppLocalizationCubit>().state.id);
widget.model.totalLikes = (!isLike)
? (int.parse(widget.model.totalLikes.toString()) + 1).toString()
: (widget.model.totalLikes!.isNotEmpty)
? (int.parse(widget.model.totalLikes.toString()) - 1).toString()
: "0";
}
}),
builder: (context, state) {
return InkWell(
splashColor: Colors.transparent,
onTap: () {
if (context.read<AuthCubit>().getUserId() != "0") {
if (state is UpdateLikeAndDisLikeStatusInProgress) {
return;
}
context.read<UpdateLikeAndDisLikeStatusCubit>().setLikeAndDisLikeNews(news: widget.model, status: (isLike) ? "0" : "1");
} else {
UiUtils.loginRequired(context);
}
},
child: setButton(
childWidget: (state is UpdateLikeAndDisLikeStatusInProgress)
? SizedBox(height: 20, width: 20, child: UiUtils.showCircularProgress(true, Theme.of(context).primaryColor))
: ((isLike) ? const Icon(Icons.thumb_up_alt, size: 20) : const Icon(Icons.thumb_up_off_alt, size: 20))));
});
}));
}
Widget setShareBookmarkLikeOptions() {
return Positioned.directional(
end: 5,
bottom: 5,
textDirection: Directionality.of(context),
child: Row(
children: [shareButton(), SizedBox(width: MediaQuery.of(context).size.width / 98.0), bookmarkButton(), SizedBox(width: MediaQuery.of(context).size.width / 98.0), likeButton()],
),
);
}
Widget newsData() {
return Builder(builder: (context) {
return Padding(
padding: EdgeInsetsDirectional.only(top: widget.index == 0 ? 0 : 15.0, start: 15, end: 15),
child: Column(children: [
if (context.read<AppConfigurationCubit>().getInAppAdsMode() == "1" &&
(context.read<AppConfigurationCubit>().getAdsType() != "unity" || context.read<AppConfigurationCubit>().getIOSAdsType() != "unity"))
nativeAdsShow(context: context, index: widget.index),
NewsCard(
newsDetail: widget.model,
showTags: true,
onTap: () {
List<NewsModel> newsList = [];
newsList.addAll(widget.newslist);
newsList.removeAt(widget.index);
Navigator.of(context).pushNamed(Routes.newsDetails, arguments: {"model": widget.model, "newsList": newsList, "isFromBreak": false, "fromShowMore": widget.fromShowMore});
})
]));
});
}
@override
Widget build(BuildContext context) {
return newsData();
}
}

View File

@@ -0,0 +1,15 @@
import 'package:flutter/material.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/utils/uiUtils.dart';
showSnackBar(String msg, BuildContext context, {int? durationInMiliSeconds}) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: CustomTextLabel(text: msg, textAlign: TextAlign.center, textStyle: TextStyle(color: Theme.of(context).colorScheme.surface)),
showCloseIcon: false,
duration: Duration(milliseconds: durationInMiliSeconds ?? 1500), //bydefault 4000 ms
backgroundColor: UiUtils.getColorScheme(context).primaryContainer,
elevation: 1.0,
),
);
}

View File

@@ -0,0 +1,43 @@
//sponsored Ads
import 'package:flutter/material.dart';
import 'package:news/data/models/adSpaceModel.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/ui/widgets/networkImage.dart';
import 'package:news/utils/uiUtils.dart';
import 'package:url_launcher/url_launcher.dart';
class AdSpaces extends StatelessWidget {
AdSpaceModel adsModel;
AdSpaces({super.key, required this.adsModel});
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.only(top: 10),
child: InkWell(
splashColor: Colors.transparent,
onTap: () async {
if (await canLaunchUrl(Uri.parse(adsModel.adUrl!))) {
//To open link in other apps or outside of Current App
//Add -> , mode: LaunchMode.externalApplication
await launchUrl(Uri.parse(adsModel.adUrl!));
}
},
child: Column(mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [
Container(
alignment: AlignmentDirectional.centerEnd,
padding: const EdgeInsetsDirectional.only(end: 5),
child: CustomTextLabel(
text: 'sponsoredLbl',
textStyle: Theme.of(context).textTheme.bodySmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.6), fontWeight: FontWeight.w800),
),
),
Padding(
padding: const EdgeInsets.only(top: 2),
child: CustomNetworkImage(networkImageUrl: adsModel.adImage!, isVideo: false, width: MediaQuery.of(context).size.width, fit: BoxFit.values.first),
),
])),
);
}
}

View File

@@ -0,0 +1,116 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:news/ui/widgets/networkImage.dart';
import 'package:news/utils/constant.dart';
import 'package:news/utils/uiUtils.dart';
import 'package:news/app/routes.dart';
import 'package:news/cubits/appSystemSettingCubit.dart';
import 'package:news/data/models/BreakingNewsModel.dart';
class BreakNewsItem extends StatefulWidget {
final BreakingNewsModel model;
final int index;
final List<BreakingNewsModel> breakNewsList;
const BreakNewsItem({super.key, required this.model, required this.index, required this.breakNewsList});
@override
BreakNewsItemState createState() => BreakNewsItemState();
}
class BreakNewsItemState extends State<BreakNewsItem> {
late BannerAd _bannerAd;
@override
void initState() {
if (context.read<AppConfigurationCubit>().getInAppAdsMode() == "1") createBannerAd();
super.initState();
}
Widget newsData() {
return Builder(builder: (context) {
return Padding(
padding: EdgeInsetsDirectional.only(top: widget.index == 0 ? 0 : 15.0),
child: Column(children: [
if (context.read<AppConfigurationCubit>().getInAppAdsMode() == "1") nativeAdsShow(),
InkWell(
child: Column(
children: <Widget>[
ClipRRect(
borderRadius: BorderRadius.circular(10.0),
child: CustomNetworkImage(networkImageUrl: widget.model.image!, width: double.infinity, height: MediaQuery.of(context).size.height / 4.2, fit: BoxFit.cover, isVideo: false)),
Container(
alignment: Alignment.bottomLeft,
padding: const EdgeInsetsDirectional.only(top: 4.0, start: 5.0, end: 5.0),
child: CustomTextLabel(
text: widget.model.title!,
textStyle: Theme.of(context).textTheme.titleSmall?.copyWith(color: UiUtils.getColorScheme(context).primaryContainer.withOpacity(0.9)),
maxLines: 2,
softWrap: true,
overflow: TextOverflow.ellipsis))
],
),
onTap: () {
List<BreakingNewsModel> newsList = [];
newsList.addAll(widget.breakNewsList);
newsList.removeAt(widget.index);
Navigator.of(context).pushNamed(Routes.newsDetails, arguments: {"breakModel": widget.model, "breakNewsList": newsList, "isFromBreak": true, "fromShowMore": false});
},
),
]));
});
}
BannerAd createBannerAd() {
if (context.read<AppConfigurationCubit>().bannerId() != "") {
_bannerAd = BannerAd(
adUnitId: context.read<AppConfigurationCubit>().bannerId()!,
request: const AdRequest(),
size: AdSize.mediumRectangle,
listener: BannerAdListener(
onAdLoaded: (_) {},
onAdFailedToLoad: (ad, err) {
ad.dispose();
},
onAdOpened: (Ad ad) => debugPrint('Native ad opened.'),
// Called when an ad opens an overlay that covers the screen.
onAdClosed: (Ad ad) => debugPrint('Native ad closed.'),
// Called when an ad removes an overlay that covers the screen.
onAdImpression: (Ad ad) => debugPrint('Native ad impression.')),
);
}
return _bannerAd;
}
Widget bannerAdsShow() {
return AdWidget(key: UniqueKey(), ad: createBannerAd()..load());
}
Widget nativeAdsShow() {
if (context.read<AppConfigurationCubit>().getInAppAdsMode() == "1" &&
context.read<AppConfigurationCubit>().checkAdsType() != null &&
context.read<AppConfigurationCubit>().getAdsType() != "unity" &&
widget.index != 0 &&
widget.index % nativeAdsIndex == 0) {
return Padding(
padding: const EdgeInsets.only(bottom: 15.0),
child: Container(
padding: const EdgeInsets.all(7.0),
height: 300,
width: double.infinity,
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.5),
borderRadius: BorderRadius.circular(10.0),
),
child: context.read<AppConfigurationCubit>().checkAdsType() == "google" && (context.read<AppConfigurationCubit>().bannerId() != "") ? bannerAdsShow() : SizedBox.shrink()));
} else {
return const SizedBox.shrink();
}
}
@override
Widget build(BuildContext context) {
return newsData();
}
}

View File

@@ -0,0 +1,58 @@
import 'package:flutter/material.dart';
import 'package:news/ui/widgets/customTextLabel.dart';
import 'package:youtube_player_flutter/youtube_player_flutter.dart';
import 'package:news/app/routes.dart';
import 'package:news/data/models/BreakingNewsModel.dart';
import 'package:news/ui/widgets/networkImage.dart';
class BreakVideoItem extends StatefulWidget {
final BreakingNewsModel model;
const BreakVideoItem({super.key, required this.model});
@override
BreakVideoItemState createState() => BreakVideoItemState();
}
class BreakVideoItemState extends State<BreakVideoItem> {
Widget videoData(BreakingNewsModel video) {
return Padding(
padding: const EdgeInsets.only(top: 15.0),
child: Column(
children: <Widget>[
ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(10.0)),
child: GestureDetector(
onTap: () {
Navigator.of(context).pushNamed(Routes.newsVideo, arguments: {"from": 3, "breakModel": video});
},
child: Stack(
alignment: Alignment.center,
children: [
(video.contentType == 'video_youtube')
? CustomNetworkImage(
networkImageUrl: 'https://img.youtube.com/vi/${YoutubePlayer.convertUrlToId(video.contentValue!)!}/0.jpg',
fit: BoxFit.cover,
width: double.maxFinite,
height: 220,
isVideo: true)
: CustomNetworkImage(networkImageUrl: video.image!, fit: BoxFit.cover, width: double.maxFinite, height: 220, isVideo: true),
const CircleAvatar(radius: 30, backgroundColor: Colors.black45, child: Icon(Icons.play_arrow, size: 40, color: Colors.white))
],
),
),
),
Padding(
padding: const EdgeInsets.only(left: 5, right: 5),
child:
Align(alignment: Alignment.centerLeft, child: CustomTextLabel(text: video.title!, textStyle: Theme.of(context).textTheme.titleSmall, maxLines: 2, overflow: TextOverflow.ellipsis))),
],
),
);
}
@override
Widget build(BuildContext context) {
return videoData(widget.model);
}
}

Some files were not shown because too many files have changed in this diff Show More