418 lines
17 KiB
Dart
418 lines
17 KiB
Dart
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))
|
|
]),
|
|
),
|
|
);
|
|
}
|
|
}
|