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

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