import 'dart:io'; import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:just_audio/just_audio.dart'; import 'package:mohem_flutter_app/api/chat/chat_api_client.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; import 'package:mohem_flutter_app/classes/my_custom_stream.dart'; import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/extensions/int_extensions.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart'; import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; import 'package:mohem_flutter_app/main.dart'; import 'package:mohem_flutter_app/models/chat/get_group_chat_history.dart'; import 'package:mohem_flutter_app/models/chat/get_single_user_chat_list_model.dart'; import 'package:mohem_flutter_app/provider/chat_provider_model.dart'; import 'package:mohem_flutter_app/ui/chat/chat_full_image_preview.dart'; import 'package:mohem_flutter_app/ui/chat/common.dart'; import 'package:path_provider/path_provider.dart'; import 'package:provider/provider.dart'; import 'package:rxdart/rxdart.dart'; import 'package:video_player/video_player.dart'; class GroupChatBubble extends StatelessWidget { GroupChatBubble({Key? key, required this.dateTime, required this.cItem}) : super(key: key); final String dateTime; final GetGroupChatHistoryAsync cItem; bool isCurrentUser = false; bool isSeen = false; bool isReplied = false; bool isVoice = false; int? fileTypeID; String? fileTypeName; late ChatProviderModel provider; String? fileTypeDescription; bool isDelivered = false; String userName = ''; late Offset screenOffset; void makeAssign() { isCurrentUser = cItem.currentUserId == AppState().chatDetails!.response!.id ? true : false; isSeen = cItem.isSeen == true ? true : false; isReplied = cItem.groupChatReplyResponse != null ? true : false; // isVoice = cItem.fileTypeId == 13 && cItem.voiceController != null ? true : false; fileTypeID = cItem.fileTypeId; fileTypeName = cItem.fileTypeResponse != null ? cItem.fileTypeResponse!.fileTypeName : ""; fileTypeDescription = cItem.fileTypeResponse != null ? cItem.fileTypeResponse!.fileTypeDescription : ""; isDelivered = cItem.currentUserId == AppState().chatDetails!.response!.id && cItem.isDelivered == true ? true : false; userName = AppState().chatDetails!.response!.userName == cItem.currentUserName.toString() ? "You" : cItem.currentUserName.toString(); } void playVoice( BuildContext context, { required SingleUserChatModel data, }) async { if (data.voice != null && data.voice!.existsSync()) { if (Platform.isIOS) { Duration? duration = await data.voiceController! .setAudioSource(MyCustomStream(data.voice!.readAsBytesSync())); await data.voiceController!.seek(duration); await data.voiceController!.setLoopMode(LoopMode.off); await data.voiceController!.setVolume(1.0); await data.voiceController!.load(); data.voiceController!.play(); } else { await data.voiceController!.setFilePath(data!.voice!.path); Duration? duration = await data.voiceController!.load(); await data.voiceController!.seek(duration); await data.voiceController!.setLoopMode(LoopMode.off); await data.voiceController!.play(); } } else { Utils.showLoading(context); Uint8List encodedString = await ChatApiClient().downloadURL( fileName: data.contant!, fileTypeDescription: provider.getFileTypeDescription( data.fileTypeResponse!.fileTypeName ?? ""), fileSource: 2); // try { File sFile = await provider.downChatVoice( encodedString, data.fileTypeResponse!.fileTypeName ?? "", data); if (sFile.path.isEmpty) { logger.d("Path Is Emptyyyyyyy"); } else { logger.d("Path Exsists"); } data.voice = sFile; if (Platform.isIOS) { logger.d("isIOS"); Duration? duration = await data.voiceController! .setAudioSource(MyCustomStream(data.voice!.readAsBytesSync())); await data.voiceController!.seek(duration); await data.voiceController!.setLoopMode(LoopMode.off); await data.voiceController!.setVolume(1.0); await data.voiceController!.load(); Utils.hideLoading(context); data.voiceController!.play(); } else { Duration? duration = await data.voiceController!.setFilePath(sFile.path); await data.voiceController!.setLoopMode(LoopMode.off); await data.voiceController!.seek(duration); Utils.hideLoading(context); await data.voiceController!.play(); } } } void pausePlaying(BuildContext context, {required SingleUserChatModel data}) async { await data.voiceController!.pause(); } void rePlay(BuildContext context, {required SingleUserChatModel data}) async { if (data.voice != null && data.voice!.existsSync()) { await data.voiceController!.seek(Duration.zero); await data.voiceController!.play(); } } Stream get _positionDataStream => Rx.combineLatest3( cItem.voiceController!.positionStream, cItem.voiceController!.bufferedPositionStream, cItem.voiceController!.durationStream, (Duration position, Duration bufferedPosition, Duration? duration) => PositionData( position, bufferedPosition, duration ?? Duration.zero)); @override Widget build(BuildContext context) { Size windowSize = MediaQuery.of(context).size; screenOffset = Offset(windowSize.width / 2, windowSize.height / 2); makeAssign(); provider = Provider.of(context, listen: false); return isCurrentUser ? currentUser(context) : receiptUser(context); } Widget currentUser(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ if (isReplied) ClipRRect( borderRadius: BorderRadius.circular(5.0), child: Container( width: double.infinity, decoration: BoxDecoration( border: Border( left: BorderSide( width: 6, color: isCurrentUser ? MyColors.gradiantStartColor : MyColors.white), ), color: isCurrentUser ? MyColors.black.withOpacity(0.10) : MyColors.black.withOpacity(0.30), ), child: Row( children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ (userName) .toText12( color: MyColors.gradiantStartColor, isBold: false) .paddingOnly(right: 5, top: 5, bottom: 0, left: 5), Directionality( textDirection: provider.getTextDirection( cItem.groupChatReplyResponse != null ? cItem.groupChatReplyResponse!.contant .toString() : ""), child: (cItem.groupChatReplyResponse != null ? cItem.groupChatReplyResponse!.contant .toString() : "") .toText10( color: isCurrentUser ? MyColors.grey71Color : MyColors.white.withOpacity(0.5), isBold: false, maxlines: 4) .paddingOnly(right: 5, top: 5, bottom: 8, left: 5), ), ], ).expanded, if (cItem.groupChatReplyResponse != null) if (cItem.groupChatReplyResponse!.fileTypeId == 12 || cItem.groupChatReplyResponse!.fileTypeId == 3 || cItem.groupChatReplyResponse!.fileTypeId == 4) ClipRRect( borderRadius: BorderRadius.circular(8.0), child: SizedBox( height: 32, width: 32, child: showImage( isReplyPreview: false, fileName: cItem.groupChatReplyResponse!.contant!, fileTypeDescription: cItem .groupChatReplyResponse! .fileTypeResponse! .fileTypeDescription ?? "image/jpg")), ).paddingOnly(left: 10, right: 10, bottom: 16, top: 16), ], ), ), ).paddingOnly(bottom: 7).onPress(() { // provider.scrollToMsg(cItem); }), if (fileTypeID == 12 || fileTypeID == 4 || fileTypeID == 3) ClipRRect( borderRadius: BorderRadius.circular(5.0), child: SizedBox( height: 140, width: 227, child: showImage( isReplyPreview: false, fileName: cItem.contant!, fileTypeDescription: cItem.fileTypeResponse != null && cItem.fileTypeResponse!.fileTypeDescription != null ? cItem.fileTypeResponse!.fileTypeDescription : cItem.fileTypeResponse!.fileTypeName) .onPress(() { showDialog( context: context, anchorPoint: screenOffset, builder: (BuildContext context) => ChatImagePreviewScreen( imgTitle: cItem.contant!, img: cItem.image!), ); }), ), ).paddingOnly(bottom: 4), if (fileTypeID == 13 && cItem.voiceController != null) currentWaveBubble(context, cItem), if (fileTypeID == 16) showVideoThumb(context, cItem) else Row( children: [ if (fileTypeID == 1 || fileTypeID == 5 || fileTypeID == 7 || fileTypeID == 6 || fileTypeID == 8 // || fileTypeID == 2 ) SvgPicture.asset(provider.getType(fileTypeName ?? ""), height: 30, width: 22, alignment: Alignment.center, fit: BoxFit.cover) .paddingOnly(left: 0, right: 10), Directionality( textDirection: provider.getTextDirection(cItem.contant ?? ""), child: (cItem.contant ?? "").toText12().expanded), if (fileTypeID == 1 || fileTypeID == 5 || fileTypeID == 7 || fileTypeID == 6 || fileTypeID == 8 //|| fileTypeID == 2 ) const Icon(Icons.remove_red_eye, size: 16) ], ), Align( alignment: Alignment.centerRight, child: Row( mainAxisSize: MainAxisSize.min, children: [ dateTime.toText10( color: MyColors.grey41Color.withOpacity(.5), ), 7.width, Icon(isDelivered ? Icons.done_all : Icons.done_all, color: isSeen ? MyColors.textMixColor : MyColors.grey9DColor, size: 14), ], ), ), ], ) .paddingOnly(top: 11, left: 13, right: 13, bottom: 5) .objectContainerView(disablePadding: true) .paddingOnly(left: MediaQuery.of(context).size.width * 0.3); } Widget receiptUser(BuildContext context) { return Container( padding: const EdgeInsets.only(top: 5, left: 8, right: 13, bottom: 5), decoration: BoxDecoration( borderRadius: BorderRadius.circular(10), gradient: const LinearGradient( transform: GradientRotation(.83), begin: Alignment.topRight, end: Alignment.bottomLeft, colors: [ MyColors.gradiantEndColor, MyColors.gradiantStartColor ], ), ), child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ if (isReplied) ClipRRect( borderRadius: BorderRadius.circular(5.0), child: Container( width: double.infinity, decoration: BoxDecoration( border: Border( left: BorderSide( width: 6, color: isCurrentUser ? MyColors.gradiantStartColor : MyColors.white)), color: isCurrentUser ? MyColors.black.withOpacity(0.10) : MyColors.black.withOpacity(0.30), ), child: Row( children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ (userName) .toText12( color: MyColors.gradiantStartColor, isBold: false) .paddingOnly(right: 5, top: 5, bottom: 0, left: 5), Directionality( textDirection: provider.getTextDirection( cItem.groupChatReplyResponse != null ? cItem.groupChatReplyResponse!.contant .toString() : ""), child: (cItem.groupChatReplyResponse != null ? cItem.groupChatReplyResponse!.contant .toString() : "") .toText10( color: isCurrentUser ? MyColors.grey71Color : MyColors.white.withOpacity(0.5), isBold: false, maxlines: 4) .paddingOnly( right: 5, top: 5, bottom: 8, left: 5), ), ], ).expanded, if (cItem.groupChatReplyResponse != null) if (cItem.groupChatReplyResponse!.fileTypeId == 12 || cItem.groupChatReplyResponse!.fileTypeId == 3 || cItem.groupChatReplyResponse!.fileTypeId == 4) ClipRRect( borderRadius: BorderRadius.circular(8.0), child: SizedBox( height: 32, width: 32, child: showImage( isReplyPreview: true, fileName: cItem.groupChatReplyResponse!.contant!, fileTypeDescription: cItem .groupChatReplyResponse! .fileTypeResponse! .fileTypeDescription ?? "image/jpg"), ), ).paddingOnly(left: 10, right: 10, bottom: 16, top: 16) ], ), ), ).paddingOnly(bottom: 7).onPress(() { // provider.scrollToMsg(cItem); }), if (fileTypeID == 12 || fileTypeID == 4 || fileTypeID == 3) ClipRRect( borderRadius: BorderRadius.circular(5.0), child: SizedBox( height: 140, width: 227, child: showImage( isReplyPreview: false, fileName: cItem.contant ?? "", fileTypeDescription: cItem.fileTypeResponse!.fileTypeDescription ?? "image/jpg") .onPress(() { showDialog( context: context, anchorPoint: screenOffset, builder: (BuildContext context) => ChatImagePreviewScreen( imgTitle: cItem.contant ?? "", img: cItem.image!), ); }), ), ).paddingOnly(bottom: 4), if (fileTypeID == 13 && cItem.voiceController != null) recipetWaveBubble(context, cItem), if (fileTypeID == 16) showVideoThumb(context, cItem) else Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ cItem.currentUserName! .toText10( color: Colors.black, ) .paddingOnly(bottom: 5), Row( children: [ if (fileTypeID == 1 || fileTypeID == 5 || fileTypeID == 7 || fileTypeID == 6 || fileTypeID == 8 // || fileTypeID == 2 ) SvgPicture.asset(provider.getType(fileTypeName ?? ""), height: 30, width: 22, alignment: Alignment.center, fit: BoxFit.cover) .paddingOnly(left: 0, right: 10), Directionality( textDirection: provider.getTextDirection(cItem.contant ?? ""), child: (cItem.contant ?? "") .toText12(color: Colors.white) .expanded), if (fileTypeID == 1 || fileTypeID == 5 || fileTypeID == 7 || fileTypeID == 6 || fileTypeID == 8 //|| fileTypeID == 2 ) const Icon(Icons.remove_red_eye, color: Colors.white, size: 16) ], ), Align( alignment: Alignment.topRight, child: dateTime .toText10( color: Colors.white.withOpacity(.71), ) .paddingOnly(top: 5), ), ], ), ])).paddingOnly(right: MediaQuery.of(context).size.width * 0.3); } Widget voiceMsg(BuildContext context) { return Container(); } Widget showImage( {required bool isReplyPreview, required String fileName, required String fileTypeDescription}) { if (cItem.isImageLoaded != null && cItem.image != null) { return Image.memory( cItem.image!, height: isReplyPreview ? 32 : 140, width: isReplyPreview ? 32 : 227, fit: BoxFit.cover, alignment: Alignment.center, ); } else { return FutureBuilder( future: ChatApiClient().downloadURL( fileName: fileName, fileTypeDescription: fileTypeDescription, fileSource:2), builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.connectionState != ConnectionState.waiting) { if (snapshot.data == null) { return const SizedBox(); } else { cItem.image = snapshot.data; cItem.isImageLoaded = true; return Image.memory( snapshot.data, height: isReplyPreview ? 32 : 140, width: isReplyPreview ? 32 : 227, fit: BoxFit.cover, alignment: Alignment.center, ); } } else { return SizedBox( height: isReplyPreview ? 32 : 140, width: isReplyPreview ? 32 : 227, ).toShimmer(); } }, ); } } Widget currentWaveBubble( BuildContext context, GetGroupChatHistoryAsync data) { return Container( margin: const EdgeInsets.all(0), decoration: BoxDecoration( border: Border( left: BorderSide( width: 6, color: isCurrentUser ? MyColors.gradiantStartColor : MyColors.white), ), color: isCurrentUser ? MyColors.black.withOpacity(0.10) : MyColors.black.withOpacity(0.30), ), child: Row( children: [ //need to check and verify for group hence for now commented // getPlayer(player: data.voiceController!, modelData: data), StreamBuilder( stream: _positionDataStream, builder: (BuildContext context, AsyncSnapshot snapshot) { PositionData? positionData = snapshot.data; return SeekBar( duration: positionData?.duration ?? Duration.zero, position: positionData?.position ?? Duration.zero, bufferedPosition: positionData?.bufferedPosition ?? Duration.zero, onChangeEnd: data.voiceController!.seek, ).expanded; }, ), ], ), ).circle(5); } Widget showVideoThumb(BuildContext context, GetGroupChatHistoryAsync data) { return LoadVideo(data: data); } Widget recipetWaveBubble( BuildContext context, GetGroupChatHistoryAsync data) { return Container( margin: const EdgeInsets.all(0), decoration: BoxDecoration( border: Border( left: BorderSide( width: 6, color: isCurrentUser ? MyColors.gradiantStartColor : MyColors.white), ), color: isCurrentUser ? MyColors.black.withOpacity(0.10) : MyColors.black.withOpacity(0.30), ), child: Row( mainAxisSize: MainAxisSize.max, children: [ //commented to verify after //getPlayer(player: data.voiceController!, modelData: data), StreamBuilder( stream: _positionDataStream, builder: (BuildContext context, AsyncSnapshot snapshot) { PositionData? positionData = snapshot.data; return SeekBar( duration: positionData?.duration ?? Duration.zero, position: positionData?.position ?? Duration.zero, bufferedPosition: positionData?.bufferedPosition ?? Duration.zero, onChangeEnd: data.voiceController!.seek, ).expanded; }, ), ], ), ).circle(5); } Widget getPlayer( {required AudioPlayer player, required SingleUserChatModel modelData}) { return StreamBuilder( stream: player.playerStateStream, builder: (BuildContext context, AsyncSnapshot snapshot) { PlayerState? playerState = snapshot.data; ProcessingState? processingState = playerState?.processingState; bool? playing = playerState?.playing; if (processingState == ProcessingState.loading || processingState == ProcessingState.buffering) { return Container( margin: const EdgeInsets.all(8.0), width: 30.0, height: 30.0, child: const CircularProgressIndicator(), ); } else if (playing != true) { return const Icon( Icons.play_arrow, size: 30, color: MyColors.lightGreenColor, ).onPress(() { playVoice(context, data: modelData); }); } else if (processingState != ProcessingState.completed) { return const Icon( Icons.pause, size: 30, color: MyColors.lightGreenColor, ).onPress(() { pausePlaying(context, data: modelData); }); } else { return const Icon( Icons.replay, size: 30, color: MyColors.lightGreenColor, ).onPress(() { rePlay(context, data: modelData); }); } }, ); } } class LoadVideo extends StatefulWidget { final GetGroupChatHistoryAsync data; const LoadVideo({Key? key, required this.data}) : super(key: key); @override State createState() => _LoadVideoState(); } class _LoadVideoState extends State { late VideoPlayerController videoController; @override void initState() { videoController = VideoPlayerController.networkUrl(Uri.parse( 'https://apiderichat.hmg.com/groupattachments/${widget.data.fileTypeResponse?.fileName}')) ..initialize().then((_) { }); super.initState(); } @override Widget build(BuildContext context) { return Container( margin: const EdgeInsets.all(10), decoration: BoxDecoration( borderRadius: BorderRadius.circular(10), ), child: AspectRatio( aspectRatio: videoController.value.aspectRatio, child: Stack( alignment: Alignment.bottomCenter, children: [ VideoPlayer(videoController), Align( alignment: Alignment.center, child: Icon( Icons.play_arrow, color: Colors.white.withOpacity(.7), size: 56, ), ) ], ), )); } }