import 'dart:io';
import 'package:audio_waveforms_fix/audio_waveforms.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:image_picker/image_picker.dart';
import 'package:womenentrepreneur/blocs/auth/auth_bloc.dart';
import 'package:womenentrepreneur/blocs/chat/chat_bloc.dart';
import 'package:womenentrepreneur/configs/app_colors.dart';
import 'package:womenentrepreneur/configs/app_constant.dart';
import 'package:womenentrepreneur/configs/app_route.dart';
import 'package:womenentrepreneur/configs/app_sizes.dart';
import 'package:womenentrepreneur/configs/app_text_style.dart';
import 'package:womenentrepreneur/configs/app_urls.dart';
import 'package:womenentrepreneur/models/chat/chat_mod.dart';
import 'package:womenentrepreneur/services/chat/send_message_ser.dart';
import 'package:womenentrepreneur/views/chat_room/components/chat_list.dart';
import 'package:womenentrepreneur/views/chat_room/components/file_select_item.dart';
import 'package:womenentrepreneur/widgets/app_alert_dialog.dart';
import 'package:womenentrepreneur/widgets/app_empty_avatar.dart';
import 'package:womenentrepreneur/widgets/app_expand_img.dart';
import 'package:womenentrepreneur/widgets/app_expanded_video.dart';
import 'package:womenentrepreneur/widgets/app_shimmer.dart';
import 'package:womenentrepreneur/widgets/app_snack_bar.dart';
import 'package:socket_io_client/socket_io_client.dart' as io;
class ChatRoomScreen extends StatefulWidget {
const ChatRoomScreen(
{super.key,
required this.name,
required this.imageUrl,
required this.id,
required this.token,
required this.type});
final String name, imageUrl, type, token;
final int id;
@override
State<ChatRoomScreen> createState() => _ChatRoomScreenState();
}
class _ChatRoomScreenState extends State<ChatRoomScreen> {
TextEditingController messageCon = TextEditingController();
bool buttonHide = true;
List<Message> messages = [];
List<File> mediaFiles = [];
List<File> documentFiles = [];
final ImagePicker picker = ImagePicker();
//for web socket
io.Socket? socket;
void connectSocket() {
final userId = context.read<AuthBloc>().myId;
socket = io.io(
AppUrls.socketUrl,
io.OptionBuilder()
.setTransports(["websocket"])
.disableAutoConnect()
.setAuth({"id": "$userId", "type": AppUrls.typeSocket})
.build());
socket?.connect();
socket?.onConnect((data) {
logger.i("Connected connectSocket");
socket?.on("message", (msg) {
logger.f("connectSocket msg throw: $msg");
List<Attachment> attachments = [];
try {
attachments = List<Attachment>.from(
msg["attachments"]!.map((x) => Attachment.fromJson(x)));
} catch (e) {
attachments = [];
}
setMessage(
message: msg['message'],
senderId: "$msg['send_by_user_id']",
attachments: attachments); //check this id key name
});
});
}
void setMessage(
{required String message,
required String senderId,
String? messageStatus,
required List<Attachment> attachments}) {
int sendById = 0;
try {
sendById = int.parse(senderId);
} catch (e) {
sendById = 0;
}
logger.f("attachmentsLocal size in setMessage: ${attachments.length}");
messages.insert(
0,
Message(
message: message,
createdAt: DateTime.now(),
messageSendByUserId: sendById,
messageStatus: messageStatus,
//messageStatus must be null or failed
attachments: attachments,
),
);
buttonHide = true;
messageCon.clear();
documentFiles = [];
mediaFiles = [];
if (!mounted) return;
setState(() {});
}
//for web socket
@override
void initState() {
super.initState();
connectSocket();
}
@override
void dispose() {
// TODO: implement dispose
super.dispose();
socket?.disconnect();
socket?.dispose();
recorderController.dispose();
}
bool isRecording = false;
final RecorderController recorderController = RecorderController();
String? _filePath;
@override
Widget build(BuildContext context) {
final myId = context.read<AuthBloc>().myId;
final members = context.read<ChatBloc>().memberList;
return Scaffold(
appBar: AppBar(
scrolledUnderElevation: 0,
backgroundColor: AppColors.bgColor,
leading: IconButton(
onPressed: () => AppRoutes.pop(context),
icon: const Icon(
CupertinoIcons.back,
color: AppColors.sendBtnColor,
size: 25,
)),
leadingWidth: 35,
title: Row(
children: [
InkWell(
onTap: () {
appExpandImg(
context, "${AppUrls.imageLink}/${widget.imageUrl}");
},
child: ClipOval(
child: CachedNetworkImage(
height: 40,
width: 40,
fit: BoxFit.cover,
imageUrl: "${AppUrls.imageLink}/${widget.imageUrl}",
placeholder: (context, url) =>
EmptyAvatar(type: widget.type),
errorWidget: (context, url, error) =>
EmptyAvatar(type: widget.type)),
),
),
const SizedBox(
width: AppSizes.bodyPadding,
),
Expanded(
child: InkWell(
onTap: widget.type != AppConstants.typeGroup
? null
: () async {
if (members.isEmpty) {
appSnackBar(context, "Member is not found");
return;
}
await showModalBottomSheet(
context: context,
builder: (context) => Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(15),
child: Text(
"${members.length} members in the ${widget.name}",
style: AppTextStyle.titleLarge(context),
),
),
Expanded(
child: ListView.builder(
itemCount: members.length,
itemBuilder: (context, index) {
final member = members[index];
return ListTile(
leading: ClipOval(
child: CachedNetworkImage(
height: 45,
width: 45,
fit: BoxFit.cover,
imageUrl:
"${AppUrls.imageLink}/${member.memberImage}",
placeholder: (context,
url) =>
const EmptyAvatar(
type: AppConstants
.typePersonal),
errorWidget: (context, url,
error) =>
const EmptyAvatar(
type: AppConstants
.typePersonal)),
),
title:
Text("${member.memberName}"),
);
}))
],
));
},
child: Wrap(
direction: Axis.vertical,
children: [
Text(
widget.name,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(color: AppColors.textBlackColor),
),
const Text(
"Active Now",
style:
TextStyle(color: AppColors.sendBtnColor, fontSize: 12),
),
],
),
)),
],
),
actions: [
IconButton(
onPressed: () => appSnackBar(context, "Developing is ongoing"),
icon: const Icon(
CupertinoIcons.phone,
color: AppColors.sendBtnColor,
)),
IconButton(
onPressed: () => appSnackBar(context, "Developing is ongoing"),
icon: const Icon(CupertinoIcons.video_camera,
color: AppColors.sendBtnColor)),
],
),
body: Column(
children: [
//for chat
Expanded(
child: Container(
padding:
const EdgeInsets.symmetric(horizontal: AppSizes.bodyPadding),
decoration: const BoxDecoration(
color: AppColors.chatRoomBgColor,
),
child: BlocBuilder<ChatBloc, ChatState>(
builder: (context, state) {
if (state is ChatLoading) {
return const ChatRoomShimmer();
} else if (state is ChatSuccess) {
messages = state.messages;
return messages.isEmpty
? const SizedBox(
width: double.maxFinite,
)
: ChatListComponents(
messages: messages,
myId: myId ?? '',
type: widget.type,
token: widget.token,
conversationId: widget.id,
);
} else if (state is ChatFailed) {
return SizedBox(
width: double.maxFinite, child: Text(state.message));
} else {
return const SizedBox(
width: double.maxFinite,
);
}
},
),
),
),
//end chat
//for image pick list crude
mediaFiles.isEmpty
? const SizedBox()
: Container(
height: 80,
width: double.maxFinite,
decoration: const BoxDecoration(
color: AppColors.chatRoomBgColor,
border: Border(
top: BorderSide(
color: AppColors.avatarBgColor, width: 0.3),),
),
padding: const EdgeInsets.only(top: 3),
child: ListView.builder(
itemCount: mediaFiles.length,
scrollDirection: Axis.horizontal,
itemBuilder: (_, z) {
// List<File> videoFiles = mediaFiles.where((file) => AppConstants.videoExtensions.contains(file.path.split('/').last.split('.').last.toLowerCase())).toList();
final isVideoFiles = AppConstants.videoExtensions.contains(mediaFiles[z].path.split('/').last.split('.').last.toLowerCase());
logger.i("message isVideoFiles: $isVideoFiles");
return Stack(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: InkWell(
onTap: () {
// final isVideoFiles = AppConstants.videoExtensions.contains(mediaFiles[z].path.split('/').last.split('.').last.toLowerCase());
if(isVideoFiles){
// appSnackBar(context, "open video");
AppRoutes.push(context, AppExpandedFileVideo(file: mediaFiles[z]));
}else{
appExpandFileImg(context, mediaFiles[z]);
}
},
child:
isVideoFiles ?
Container(
height: 60,
width: 50,
color: AppColors.greenLightColor,
child: const Icon(CupertinoIcons.play_circle,color: Colors.white54,),
)
:
Image.file(
mediaFiles[z],
height: 60,
width: 50,
fit: BoxFit.cover,
),
),
),
),
Positioned(
right: 0,
child: InkWell(
onTap: () {
appAlertDialog(context,
"Are you sure you want to remove?",
actions: [
TextButton(
onPressed: () =>
AppRoutes.pop(context),
child: const Text("Dismiss")),
TextButton(
onPressed: () {
mediaFiles
.remove(mediaFiles[z]);
setState(() {});
AppRoutes.pop(context);
},
child: const Text("Remove")),
]);
},
child: const Icon(
CupertinoIcons.multiply_circle_fill,
color: AppColors.sendBtnColor,
size: 18,
)))
],
);
}),
),
//end image pick list crude
//for document pick list crude
documentFiles.isEmpty
? const SizedBox()
: Container(
height: 80,
width: double.maxFinite,
decoration: const BoxDecoration(
color: AppColors.chatRoomBgColor,
border: Border(
top: BorderSide(
color: AppColors.avatarBgColor, width: 0.3)),
),
padding: const EdgeInsets.only(top: 8),
child: ListView.builder(
itemCount: documentFiles.length,
scrollDirection: Axis.horizontal,
itemBuilder: (_, c) {
final fileName = documentFiles[c].path.split('/').last;
return Stack(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
padding: const EdgeInsets.all(8.0),
decoration: BoxDecoration(
border: Border.all(
color: AppColors.greenLightColor,
width: 0.5),
borderRadius: BorderRadius.circular(10),
),
child: Text(fileName),
),
),
Positioned(
right: 0,
child: InkWell(
onTap: () {
appAlertDialog(context,
"Are you sure you want to remove?",
actions: [
TextButton(
onPressed: () =>
AppRoutes.pop(context),
child: const Text("Dismiss")),
TextButton(
onPressed: () {
documentFiles
.remove(documentFiles[c]);
setState(() {});
AppRoutes.pop(context);
},
child: const Text("Remove")),
]);
},
child: const Icon(
CupertinoIcons.multiply_circle_fill,
color: AppColors.sendBtnColor,
size: 18,
)))
],
);
}),
),
//end document pick list crude
//for recording
isRecording
? AudioWaveforms(
padding: const EdgeInsets.all(8),
size: const Size(double.maxFinite, 40.0),
decoration: BoxDecoration(
border: Border.all(
color: AppColors.greenLightColor, width: 0.5)),
recorderController: recorderController,
waveStyle: const WaveStyle(
showMiddleLine: false,
extendWaveform: true,
waveColor: AppColors.sendBtnColor),
)
: const SizedBox(),
//end recording
//for type
Container(
color: AppColors.chatRoomBgColor,
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
child: Row(
children: [
//for voice
!buttonHide
? const SizedBox()
: GestureDetector(
onLongPressStart: (s) async {
logger.i("message s: $s");
await recorderController.record();
isRecording = true;
setState(() {});
},
onLongPressEnd: (e) async {
_filePath = await recorderController.stop();
isRecording = false;
setMessage(
message: '',
senderId: myId ?? '0',
attachments: [
Attachment(
file: _filePath,
type: AppConstants.recordTypeFile,
isLocal: true)
]);
//for send message
await sendMessageService(
token: widget.token,
id: widget.id,
message: '',
attachments: [File(_filePath ?? '')])
.then((value) {
if (value.success == false) {
messages.removeAt(0);
setMessage(
message: '',
senderId: myId ?? '0',
messageStatus: 'failed',
attachments: [
Attachment(
file: _filePath,
type: AppConstants.recordTypeFile,
isLocal: true)
]);
}
});
//end send message
},
child: IconButton(
onPressed: () {},
style: IconButton.styleFrom(
backgroundColor: AppColors.chatRoomTextFieldColor,
),
icon: const Icon(
CupertinoIcons.mic,
color: AppColors.sendBtnColor,
)),
),
//end voice recorder
//for file
IconButton(
onPressed: () async {
//for Attachment Popup
showModalBottomSheet(
backgroundColor: AppColors.transparent,
context: context,
builder: (builder) => Container(
margin: const EdgeInsets.all(8),
decoration: BoxDecoration(
borderRadius:
BorderRadius.circular(AppSizes.radius),
color: AppColors.chatRoomBgColor,
),
padding:
const EdgeInsets.all(AppSizes.bodyPadding),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceEvenly,
children: [
FileSelectItem(
name: "Document",
icon: CupertinoIcons.doc_text_fill,
topColor: Colors.deepPurple,
bottomColor: Colors.deepPurpleAccent,
onTap: () async {
FilePickerResult? result =
await FilePicker.platform.pickFiles(
allowMultiple: true,
type: FileType.custom,
allowedExtensions: [
'pdf',
'doc',
'docx',
'xls',
'xlsx',
'ppt',
'pptx'
]).then((value) =>
AppRoutes.pop(context));
if (result != null) {
documentFiles = [];
mediaFiles = [];
documentFiles = result.paths
.map((path) => File(path!))
.toList();
setState(() {});
} else {
// User canceled the picker
logger
.e("documentFiles result is null");
}
},
),
FileSelectItem(
name: "Camera",
icon: CupertinoIcons.camera_fill,
topColor: Colors.pink,
bottomColor: Colors.pinkAccent,
onTap: () async {
await picker
.pickImage(
source: ImageSource.camera)
.then((value) {
if (value == null) return;
documentFiles = [];
mediaFiles = [];
mediaFiles.add(File(value.path));
setState(() {});
AppRoutes.pop(context);
});
}),
FileSelectItem(
name: "Gallery",
icon: CupertinoIcons
.photo_fill_on_rectangle_fill,
topColor: Colors.purple,
bottomColor: Colors.purpleAccent,
onTap: () async {
await FilePicker.platform.pickFiles(
allowMultiple: true,
type: FileType.custom,
allowedExtensions: [
'jpg',
'png',
'jpeg',
'gif',
'mp4',
'webp',
'JPG',
'JPEG',
'mov',
'MP4',
'MOV',
'AVI',
'avi',
'WebM',
'webm',
'FLV',
'flv'
]).then((value) {
if (value != null) {
documentFiles = [];
mediaFiles = [];
mediaFiles = value.paths
.map((path) => File(path!))
.toList();
setState(() {});
} else {
// User canceled the picker
logger
.e("mediaFiles result is null");
}
AppRoutes.pop(context);
});
})
],
),
));
//end Attachment Popup
},
style: IconButton.styleFrom(
backgroundColor: AppColors.chatRoomTextFieldColor,
),
icon: const Icon(
Icons.attach_file,
color: AppColors.sendBtnColor,
),
),
//end input
const SizedBox(
width: 5,
),
//for input
Expanded(
child: Container(
decoration: BoxDecoration(
color: AppColors.chatRoomTextFieldColor,
borderRadius: BorderRadius.circular(25)),
child: Row(
children: [
Expanded(
child: TextField(
textInputAction: TextInputAction.newline,
minLines: 1,
maxLines: 3,
controller: messageCon,
onChanged: (v) {
if (v.isNotEmpty) {
buttonHide = false;
} else {
buttonHide = true;
}
setState(() {});
},
decoration: const InputDecoration(
hintText: "Type message here..",
contentPadding: EdgeInsets.symmetric(
horizontal: 15, vertical: 10),
border: InputBorder.none,
isDense: true),
onTapOutside: (event) =>
FocusScope.of(context).unfocus(),
),
),
buttonHide &&
mediaFiles.isEmpty &&
documentFiles.isEmpty
? const SizedBox()
: IconButton(
style: IconButton.styleFrom(
backgroundColor: AppColors.sendBtnColor,
),
onPressed: () async {
final lastMessage = messageCon.text.trim();
List<Attachment> attachmentsLocal = [];
if (mediaFiles.isNotEmpty) {
for (var file in mediaFiles) {
attachmentsLocal.add(Attachment(
file: file.path,
type:
AppConstants.attachmentTypeMedia,
isLocal: true));
}
}
if (documentFiles.isNotEmpty) {
for (var file in documentFiles) {
attachmentsLocal.add(Attachment(
file: file.path,
type: AppConstants.attachmentTypeFile,
isLocal: true));
}
}
setMessage(
message: lastMessage,
senderId: myId ?? '0',
attachments: attachmentsLocal);
//for send message
await sendMessageService(
token: widget.token,
id: widget.id,
message: lastMessage,
attachments: attachmentsLocal
.map((e) => File(e.file ?? ''))
.toList())
.then((value) {
if (value.success == false) {
messages.removeAt(0);
setMessage(
message: lastMessage,
senderId: myId ?? '0',
messageStatus: 'failed',
attachments: attachmentsLocal);
}
});
//end send message
},
icon: const Icon(
Icons.arrow_upward,
color: AppColors.btnTextColor,
size: 20,
)),
],
),
),
),
//end input
],
),
),
//end type
],
),
);
}
}
import 'package:cached_network_image/cached_network_image.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:image_picker/image_picker.dart';
import 'package:womenentrepreneur/blocs/auth/auth_bloc.dart';
import 'package:womenentrepreneur/blocs/chat/chat_bloc.dart';
import 'package:womenentrepreneur/configs/app_colors.dart';
import 'package:womenentrepreneur/configs/app_constant.dart';
import 'package:womenentrepreneur/configs/app_route.dart';
import 'package:womenentrepreneur/configs/app_sizes.dart';
import 'package:womenentrepreneur/configs/app_text_style.dart';
import 'package:womenentrepreneur/configs/app_urls.dart';
import 'package:womenentrepreneur/models/chat/chat_mod.dart';
import 'package:womenentrepreneur/services/chat/send_message_ser.dart';
import 'package:womenentrepreneur/views/chat_room/components/chat_list.dart';
import 'package:womenentrepreneur/views/chat_room/components/file_select_item.dart';
import 'package:womenentrepreneur/widgets/app_alert_dialog.dart';
import 'package:womenentrepreneur/widgets/app_empty_avatar.dart';
import 'package:womenentrepreneur/widgets/app_expand_img.dart';
import 'package:womenentrepreneur/widgets/app_expanded_video.dart';
import 'package:womenentrepreneur/widgets/app_shimmer.dart';
import 'package:womenentrepreneur/widgets/app_snack_bar.dart';
import 'package:socket_io_client/socket_io_client.dart' as io;
class ChatRoomScreen extends StatefulWidget {
const ChatRoomScreen(
{super.key,
required this.name,
required this.imageUrl,
required this.id,
required this.token,
required this.type});
final String name, imageUrl, type, token;
final int id;
@override
State<ChatRoomScreen> createState() => _ChatRoomScreenState();
}
class _ChatRoomScreenState extends State<ChatRoomScreen> {
TextEditingController messageCon = TextEditingController();
bool buttonHide = true;
List<Message> messages = [];
List<File> mediaFiles = [];
List<File> documentFiles = [];
final ImagePicker picker = ImagePicker();
//for web socket
io.Socket? socket;
void connectSocket() {
final userId = context.read<AuthBloc>().myId;
socket = io.io(
AppUrls.socketUrl,
io.OptionBuilder()
.setTransports(["websocket"])
.disableAutoConnect()
.setAuth({"id": "$userId", "type": AppUrls.typeSocket})
.build());
socket?.connect();
socket?.onConnect((data) {
logger.i("Connected connectSocket");
socket?.on("message", (msg) {
logger.f("connectSocket msg throw: $msg");
List<Attachment> attachments = [];
try {
attachments = List<Attachment>.from(
msg["attachments"]!.map((x) => Attachment.fromJson(x)));
} catch (e) {
attachments = [];
}
setMessage(
message: msg['message'],
senderId: "$msg['send_by_user_id']",
attachments: attachments); //check this id key name
});
});
}
void setMessage(
{required String message,
required String senderId,
String? messageStatus,
required List<Attachment> attachments}) {
int sendById = 0;
try {
sendById = int.parse(senderId);
} catch (e) {
sendById = 0;
}
logger.f("attachmentsLocal size in setMessage: ${attachments.length}");
messages.insert(
0,
Message(
message: message,
createdAt: DateTime.now(),
messageSendByUserId: sendById,
messageStatus: messageStatus,
//messageStatus must be null or failed
attachments: attachments,
),
);
buttonHide = true;
messageCon.clear();
documentFiles = [];
mediaFiles = [];
if (!mounted) return;
setState(() {});
}
//for web socket
@override
void initState() {
super.initState();
connectSocket();
}
@override
void dispose() {
// TODO: implement dispose
super.dispose();
socket?.disconnect();
socket?.dispose();
recorderController.dispose();
}
bool isRecording = false;
final RecorderController recorderController = RecorderController();
String? _filePath;
@override
Widget build(BuildContext context) {
final myId = context.read<AuthBloc>().myId;
final members = context.read<ChatBloc>().memberList;
return Scaffold(
appBar: AppBar(
scrolledUnderElevation: 0,
backgroundColor: AppColors.bgColor,
leading: IconButton(
onPressed: () => AppRoutes.pop(context),
icon: const Icon(
CupertinoIcons.back,
color: AppColors.sendBtnColor,
size: 25,
)),
leadingWidth: 35,
title: Row(
children: [
InkWell(
onTap: () {
appExpandImg(
context, "${AppUrls.imageLink}/${widget.imageUrl}");
},
child: ClipOval(
child: CachedNetworkImage(
height: 40,
width: 40,
fit: BoxFit.cover,
imageUrl: "${AppUrls.imageLink}/${widget.imageUrl}",
placeholder: (context, url) =>
EmptyAvatar(type: widget.type),
errorWidget: (context, url, error) =>
EmptyAvatar(type: widget.type)),
),
),
const SizedBox(
width: AppSizes.bodyPadding,
),
Expanded(
child: InkWell(
onTap: widget.type != AppConstants.typeGroup
? null
: () async {
if (members.isEmpty) {
appSnackBar(context, "Member is not found");
return;
}
await showModalBottomSheet(
context: context,
builder: (context) => Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(15),
child: Text(
"${members.length} members in the ${widget.name}",
style: AppTextStyle.titleLarge(context),
),
),
Expanded(
child: ListView.builder(
itemCount: members.length,
itemBuilder: (context, index) {
final member = members[index];
return ListTile(
leading: ClipOval(
child: CachedNetworkImage(
height: 45,
width: 45,
fit: BoxFit.cover,
imageUrl:
"${AppUrls.imageLink}/${member.memberImage}",
placeholder: (context,
url) =>
const EmptyAvatar(
type: AppConstants
.typePersonal),
errorWidget: (context, url,
error) =>
const EmptyAvatar(
type: AppConstants
.typePersonal)),
),
title:
Text("${member.memberName}"),
);
}))
],
));
},
child: Wrap(
direction: Axis.vertical,
children: [
Text(
widget.name,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(color: AppColors.textBlackColor),
),
const Text(
"Active Now",
style:
TextStyle(color: AppColors.sendBtnColor, fontSize: 12),
),
],
),
)),
],
),
actions: [
IconButton(
onPressed: () => appSnackBar(context, "Developing is ongoing"),
icon: const Icon(
CupertinoIcons.phone,
color: AppColors.sendBtnColor,
)),
IconButton(
onPressed: () => appSnackBar(context, "Developing is ongoing"),
icon: const Icon(CupertinoIcons.video_camera,
color: AppColors.sendBtnColor)),
],
),
body: Column(
children: [
//for chat
Expanded(
child: Container(
padding:
const EdgeInsets.symmetric(horizontal: AppSizes.bodyPadding),
decoration: const BoxDecoration(
color: AppColors.chatRoomBgColor,
),
child: BlocBuilder<ChatBloc, ChatState>(
builder: (context, state) {
if (state is ChatLoading) {
return const ChatRoomShimmer();
} else if (state is ChatSuccess) {
messages = state.messages;
return messages.isEmpty
? const SizedBox(
width: double.maxFinite,
)
: ChatListComponents(
messages: messages,
myId: myId ?? '',
type: widget.type,
token: widget.token,
conversationId: widget.id,
);
} else if (state is ChatFailed) {
return SizedBox(
width: double.maxFinite, child: Text(state.message));
} else {
return const SizedBox(
width: double.maxFinite,
);
}
},
),
),
),
//end chat
//for image pick list crude
mediaFiles.isEmpty
? const SizedBox()
: Container(
height: 80,
width: double.maxFinite,
decoration: const BoxDecoration(
color: AppColors.chatRoomBgColor,
border: Border(
top: BorderSide(
color: AppColors.avatarBgColor, width: 0.3),),
),
padding: const EdgeInsets.only(top: 3),
child: ListView.builder(
itemCount: mediaFiles.length,
scrollDirection: Axis.horizontal,
itemBuilder: (_, z) {
// List<File> videoFiles = mediaFiles.where((file) => AppConstants.videoExtensions.contains(file.path.split('/').last.split('.').last.toLowerCase())).toList();
final isVideoFiles = AppConstants.videoExtensions.contains(mediaFiles[z].path.split('/').last.split('.').last.toLowerCase());
logger.i("message isVideoFiles: $isVideoFiles");
return Stack(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: InkWell(
onTap: () {
// final isVideoFiles = AppConstants.videoExtensions.contains(mediaFiles[z].path.split('/').last.split('.').last.toLowerCase());
if(isVideoFiles){
// appSnackBar(context, "open video");
AppRoutes.push(context, AppExpandedFileVideo(file: mediaFiles[z]));
}else{
appExpandFileImg(context, mediaFiles[z]);
}
},
child:
isVideoFiles ?
Container(
height: 60,
width: 50,
color: AppColors.greenLightColor,
child: const Icon(CupertinoIcons.play_circle,color: Colors.white54,),
)
:
Image.file(
mediaFiles[z],
height: 60,
width: 50,
fit: BoxFit.cover,
),
),
),
),
Positioned(
right: 0,
child: InkWell(
onTap: () {
appAlertDialog(context,
"Are you sure you want to remove?",
actions: [
TextButton(
onPressed: () =>
AppRoutes.pop(context),
child: const Text("Dismiss")),
TextButton(
onPressed: () {
mediaFiles
.remove(mediaFiles[z]);
setState(() {});
AppRoutes.pop(context);
},
child: const Text("Remove")),
]);
},
child: const Icon(
CupertinoIcons.multiply_circle_fill,
color: AppColors.sendBtnColor,
size: 18,
)))
],
);
}),
),
//end image pick list crude
//for document pick list crude
documentFiles.isEmpty
? const SizedBox()
: Container(
height: 80,
width: double.maxFinite,
decoration: const BoxDecoration(
color: AppColors.chatRoomBgColor,
border: Border(
top: BorderSide(
color: AppColors.avatarBgColor, width: 0.3)),
),
padding: const EdgeInsets.only(top: 8),
child: ListView.builder(
itemCount: documentFiles.length,
scrollDirection: Axis.horizontal,
itemBuilder: (_, c) {
final fileName = documentFiles[c].path.split('/').last;
return Stack(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
padding: const EdgeInsets.all(8.0),
decoration: BoxDecoration(
border: Border.all(
color: AppColors.greenLightColor,
width: 0.5),
borderRadius: BorderRadius.circular(10),
),
child: Text(fileName),
),
),
Positioned(
right: 0,
child: InkWell(
onTap: () {
appAlertDialog(context,
"Are you sure you want to remove?",
actions: [
TextButton(
onPressed: () =>
AppRoutes.pop(context),
child: const Text("Dismiss")),
TextButton(
onPressed: () {
documentFiles
.remove(documentFiles[c]);
setState(() {});
AppRoutes.pop(context);
},
child: const Text("Remove")),
]);
},
child: const Icon(
CupertinoIcons.multiply_circle_fill,
color: AppColors.sendBtnColor,
size: 18,
)))
],
);
}),
),
//end document pick list crude
//for recording
isRecording
? AudioWaveforms(
padding: const EdgeInsets.all(8),
size: const Size(double.maxFinite, 40.0),
decoration: BoxDecoration(
border: Border.all(
color: AppColors.greenLightColor, width: 0.5)),
recorderController: recorderController,
waveStyle: const WaveStyle(
showMiddleLine: false,
extendWaveform: true,
waveColor: AppColors.sendBtnColor),
)
: const SizedBox(),
//end recording
//for type
Container(
color: AppColors.chatRoomBgColor,
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
child: Row(
children: [
//for voice
!buttonHide
? const SizedBox()
: GestureDetector(
onLongPressStart: (s) async {
logger.i("message s: $s");
await recorderController.record();
isRecording = true;
setState(() {});
},
onLongPressEnd: (e) async {
_filePath = await recorderController.stop();
isRecording = false;
setMessage(
message: '',
senderId: myId ?? '0',
attachments: [
Attachment(
file: _filePath,
type: AppConstants.recordTypeFile,
isLocal: true)
]);
//for send message
await sendMessageService(
token: widget.token,
id: widget.id,
message: '',
attachments: [File(_filePath ?? '')])
.then((value) {
if (value.success == false) {
messages.removeAt(0);
setMessage(
message: '',
senderId: myId ?? '0',
messageStatus: 'failed',
attachments: [
Attachment(
file: _filePath,
type: AppConstants.recordTypeFile,
isLocal: true)
]);
}
});
//end send message
},
child: IconButton(
onPressed: () {},
style: IconButton.styleFrom(
backgroundColor: AppColors.chatRoomTextFieldColor,
),
icon: const Icon(
CupertinoIcons.mic,
color: AppColors.sendBtnColor,
)),
),
//end voice recorder
//for file
IconButton(
onPressed: () async {
//for Attachment Popup
showModalBottomSheet(
backgroundColor: AppColors.transparent,
context: context,
builder: (builder) => Container(
margin: const EdgeInsets.all(8),
decoration: BoxDecoration(
borderRadius:
BorderRadius.circular(AppSizes.radius),
color: AppColors.chatRoomBgColor,
),
padding:
const EdgeInsets.all(AppSizes.bodyPadding),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceEvenly,
children: [
FileSelectItem(
name: "Document",
icon: CupertinoIcons.doc_text_fill,
topColor: Colors.deepPurple,
bottomColor: Colors.deepPurpleAccent,
onTap: () async {
FilePickerResult? result =
await FilePicker.platform.pickFiles(
allowMultiple: true,
type: FileType.custom,
allowedExtensions: [
'pdf',
'doc',
'docx',
'xls',
'xlsx',
'ppt',
'pptx'
]).then((value) =>
AppRoutes.pop(context));
if (result != null) {
documentFiles = [];
mediaFiles = [];
documentFiles = result.paths
.map((path) => File(path!))
.toList();
setState(() {});
} else {
// User canceled the picker
logger
.e("documentFiles result is null");
}
},
),
FileSelectItem(
name: "Camera",
icon: CupertinoIcons.camera_fill,
topColor: Colors.pink,
bottomColor: Colors.pinkAccent,
onTap: () async {
await picker
.pickImage(
source: ImageSource.camera)
.then((value) {
if (value == null) return;
documentFiles = [];
mediaFiles = [];
mediaFiles.add(File(value.path));
setState(() {});
AppRoutes.pop(context);
});
}),
FileSelectItem(
name: "Gallery",
icon: CupertinoIcons
.photo_fill_on_rectangle_fill,
topColor: Colors.purple,
bottomColor: Colors.purpleAccent,
onTap: () async {
await FilePicker.platform.pickFiles(
allowMultiple: true,
type: FileType.custom,
allowedExtensions: [
'jpg',
'png',
'jpeg',
'gif',
'mp4',
'webp',
'JPG',
'JPEG',
'mov',
'MP4',
'MOV',
'AVI',
'avi',
'WebM',
'webm',
'FLV',
'flv'
]).then((value) {
if (value != null) {
documentFiles = [];
mediaFiles = [];
mediaFiles = value.paths
.map((path) => File(path!))
.toList();
setState(() {});
} else {
// User canceled the picker
logger
.e("mediaFiles result is null");
}
AppRoutes.pop(context);
});
})
],
),
));
//end Attachment Popup
},
style: IconButton.styleFrom(
backgroundColor: AppColors.chatRoomTextFieldColor,
),
icon: const Icon(
Icons.attach_file,
color: AppColors.sendBtnColor,
),
),
//end input
const SizedBox(
width: 5,
),
//for input
Expanded(
child: Container(
decoration: BoxDecoration(
color: AppColors.chatRoomTextFieldColor,
borderRadius: BorderRadius.circular(25)),
child: Row(
children: [
Expanded(
child: TextField(
textInputAction: TextInputAction.newline,
minLines: 1,
maxLines: 3,
controller: messageCon,
onChanged: (v) {
if (v.isNotEmpty) {
buttonHide = false;
} else {
buttonHide = true;
}
setState(() {});
},
decoration: const InputDecoration(
hintText: "Type message here..",
contentPadding: EdgeInsets.symmetric(
horizontal: 15, vertical: 10),
border: InputBorder.none,
isDense: true),
onTapOutside: (event) =>
FocusScope.of(context).unfocus(),
),
),
buttonHide &&
mediaFiles.isEmpty &&
documentFiles.isEmpty
? const SizedBox()
: IconButton(
style: IconButton.styleFrom(
backgroundColor: AppColors.sendBtnColor,
),
onPressed: () async {
final lastMessage = messageCon.text.trim();
List<Attachment> attachmentsLocal = [];
if (mediaFiles.isNotEmpty) {
for (var file in mediaFiles) {
attachmentsLocal.add(Attachment(
file: file.path,
type:
AppConstants.attachmentTypeMedia,
isLocal: true));
}
}
if (documentFiles.isNotEmpty) {
for (var file in documentFiles) {
attachmentsLocal.add(Attachment(
file: file.path,
type: AppConstants.attachmentTypeFile,
isLocal: true));
}
}
setMessage(
message: lastMessage,
senderId: myId ?? '0',
attachments: attachmentsLocal);
//for send message
await sendMessageService(
token: widget.token,
id: widget.id,
message: lastMessage,
attachments: attachmentsLocal
.map((e) => File(e.file ?? ''))
.toList())
.then((value) {
if (value.success == false) {
messages.removeAt(0);
setMessage(
message: lastMessage,
senderId: myId ?? '0',
messageStatus: 'failed',
attachments: attachmentsLocal);
}
});
//end send message
},
icon: const Icon(
Icons.arrow_upward,
color: AppColors.btnTextColor,
size: 20,
)),
],
),
),
),
//end input
],
),
),
//end type
],
),
);
}
}
Comments
Post a Comment