import 'dart:io'; import 'package:Envelope/models/text_messages.dart'; import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; import '/components/custom_title_bar.dart'; import '/components/file_picker.dart'; import '/models/conversations.dart'; import '/models/messages.dart'; import '/models/my_profile.dart'; import '/utils/storage/messages.dart'; import '/utils/time.dart'; import '/views/main/conversation/settings.dart'; class ConversationDetail extends StatefulWidget{ final Conversation conversation; const ConversationDetail({ Key? key, required this.conversation, }) : super(key: key); @override _ConversationDetailState createState() => _ConversationDetailState(); } class _ConversationDetailState extends State { List messages = []; MyProfile profile = MyProfile( id: '', username: '', messageExpiryDefault: 'no_expiry', ); TextEditingController msgController = TextEditingController(); bool showFilePicker = false; List selectedImages = []; @override Widget build(BuildContext context) { return Scaffold( appBar: CustomTitleBar( title: Text( widget.conversation.name, style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: Theme.of(context).appBarTheme.toolbarTextStyle?.color ), ), showBack: true, rightHandButton: IconButton( onPressed: (){ Navigator.of(context).push( MaterialPageRoute(builder: (context) => ConversationSettings( conversation: widget.conversation )), ); }, icon: Icon( Icons.settings, color: Theme.of(context).appBarTheme.iconTheme?.color, ), ), ), body: Stack( children: [ messagesView(), newMessageContent(), ], ), ); } Future fetchMessages() async { profile = await MyProfile.getProfile(); messages = await getMessagesForThread(widget.conversation); setState(() {}); } @override void initState() { super.initState(); fetchMessages(); } Widget usernameOrFailedToSend(int index) { if (messages[index].senderUsername != profile.username) { return Text( messages[index].senderUsername, style: TextStyle( fontSize: 12, color: Colors.grey[300], ), ); } if (messages[index].failedToSend) { return Row( mainAxisAlignment: MainAxisAlignment.end, children: const [ Icon( Icons.warning_rounded, color: Colors.red, size: 20, ), Text( 'Failed to send', style: TextStyle(color: Colors.red, fontSize: 12), textAlign: TextAlign.right, ), ], ); } return const SizedBox.shrink(); } Widget messagesView() { if (messages.isEmpty) { return const Center( child: Text('No Messages'), ); } return ListView.builder( itemCount: messages.length, shrinkWrap: true, padding: const EdgeInsets.only(top: 10,bottom: 90), reverse: true, itemBuilder: (context, index) { return Container( padding: const EdgeInsets.only(left: 14,right: 14,top: 0,bottom: 0), child: Align( alignment: ( messages[index].senderUsername == profile.username ? Alignment.topRight : Alignment.topLeft ), child: Column( crossAxisAlignment: messages[index].senderUsername == profile.username ? CrossAxisAlignment.end : CrossAxisAlignment.start, children: [ Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(20), color: ( messages[index].senderUsername == profile.username ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.tertiary ), ), padding: const EdgeInsets.all(12), child: messageContent(index), ), const SizedBox(height: 1.5), Row( mainAxisAlignment: messages[index].senderUsername == profile.username ? MainAxisAlignment.end : MainAxisAlignment.start, children: [ const SizedBox(width: 10), usernameOrFailedToSend(index), ], ), const SizedBox(height: 1.5), Row( mainAxisAlignment: messages[index].senderUsername == profile.username ? MainAxisAlignment.end : MainAxisAlignment.start, children: [ const SizedBox(width: 10), Text( convertToAgo(messages[index].createdAt), textAlign: messages[index].senderUsername == profile.username ? TextAlign.left : TextAlign.right, style: TextStyle( fontSize: 12, color: Colors.grey[500], ), ), ], ), index != 0 ? const SizedBox(height: 20) : const SizedBox.shrink(), ], ) ), ); }, ); } Widget messageContent(int index) { return Text( messages[index].getContent(), style: TextStyle( fontSize: 15, color: messages[index].senderUsername == profile.username ? Theme.of(context).colorScheme.onPrimary : Theme.of(context).colorScheme.onTertiary, ) ); } Widget showSelectedImages() { if (selectedImages.isEmpty) { return const SizedBox.shrink(); } return SizedBox( height: 80, width: double.infinity, child: ListView.builder( itemCount: selectedImages.length, shrinkWrap: true, scrollDirection: Axis.horizontal, padding: const EdgeInsets.all(5), itemBuilder: (context, i) { return Stack( children: [ Column( children: [ const SizedBox(height: 5), Container( alignment: Alignment.center, height: 65, width: 65, child: Image.file( selectedImages[i], fit: BoxFit.fill, ), ), ], ), SizedBox( height: 60, width: 70, child: Align( alignment: Alignment.topRight, child: GestureDetector( onTap: () { setState(() { selectedImages.removeAt(i); }); }, child: Container( height: 20, width: 20, decoration: BoxDecoration( color: Theme.of(context).colorScheme.onPrimary, borderRadius: BorderRadius.circular(30), ), child: Icon( Icons.cancel, color: Theme.of(context).primaryColor, size: 20 ), ), ), ), ), ], ); }, ) ); } Widget newMessageContent() { return Align( alignment: Alignment.bottomLeft, child: ConstrainedBox( constraints: BoxConstraints( maxHeight: selectedImages.isEmpty ? 200.0 : 270.0, ), child: Container( padding: const EdgeInsets.only(left: 10,bottom: 10,top: 10), width: double.infinity, color: Theme.of(context).backgroundColor, child: Column( mainAxisSize: MainAxisSize.min, children: [ showSelectedImages(), Row( children: [ GestureDetector( onTap: (){ setState(() { showFilePicker = !showFilePicker; }); }, child: Container( height: 30, width: 30, decoration: BoxDecoration( color: Theme.of(context).primaryColor, borderRadius: BorderRadius.circular(30), ), child: Icon( Icons.add, color: Theme.of(context).colorScheme.onPrimary, size: 20 ), ), ), const SizedBox(width: 15,), Expanded( child: TextField( decoration: InputDecoration( hintText: 'Write message...', hintStyle: TextStyle( color: Theme.of(context).hintColor, ), border: InputBorder.none, ), maxLines: null, controller: msgController, ), ), const SizedBox(width: 15), SizedBox( width: 45, height: 45, child: FittedBox( child: FloatingActionButton( onPressed: () async { if (msgController.text == '' || selectedImages.isEmpty) { return; } await sendMessage( widget.conversation, data: msgController.text != '' ? msgController.text : null, files: selectedImages, ); messages = await getMessagesForThread(widget.conversation); setState(() {}); msgController.text = ''; }, child: Icon( Icons.send, color: Theme.of(context).colorScheme.onPrimary, size: 22 ), backgroundColor: Theme.of(context).primaryColor, ), ), ), const SizedBox(width: 10), ], ), showFilePicker ? FilePicker( cameraHandle: () {}, galleryHandleMultiple: (List images) async { for (var img in images) { selectedImages.add(File(img.path)); } setState(() { showFilePicker = false; }); }, fileHandle: () {}, ) : const SizedBox.shrink(), ], ), ), ), ); } }