MIF_E31222656/lib/screens/community/components/message_input.dart

271 lines
8.9 KiB
Dart

import 'package:flutter/material.dart';
import 'dart:io';
import 'package:emoji_picker_flutter/emoji_picker_flutter.dart';
import 'package:tugas_akhir_supabase/screens/community/models/message.dart';
import 'package:flutter/foundation.dart' as foundation;
import 'package:tugas_akhir_supabase/core/theme/app_colors.dart';
import 'package:tugas_akhir_supabase/screens/community/components/reply_bar.dart';
class MessageInputWidget extends StatelessWidget {
final TextEditingController messageController;
final FocusNode focusNode;
final bool isUploading;
final File? selectedImage;
final bool showEmojiKeyboard;
final bool isReplying;
final Message? replyToMessage;
final VoidCallback onSend;
final VoidCallback onImageOptions;
final VoidCallback onEmojiToggle;
final VoidCallback onClearImage;
final VoidCallback onCancelReply;
final Color themeColor;
const MessageInputWidget({
super.key,
required this.messageController,
required this.focusNode,
required this.isUploading,
required this.selectedImage,
required this.showEmojiKeyboard,
required this.isReplying,
required this.replyToMessage,
required this.onSend,
required this.onImageOptions,
required this.onEmojiToggle,
required this.onClearImage,
required this.onCancelReply,
required this.themeColor,
});
@override
Widget build(BuildContext context) {
// Wrap everything in a Column to contain emoji keyboard
return Column(
mainAxisSize: MainAxisSize.min,
children: [
// Selected image preview
if (selectedImage != null) _buildImagePreview(),
// Reply bar
if (isReplying) _buildReplyBar(),
// Input bar
Container(
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 6.0),
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 3,
offset: Offset(0, -1),
),
],
),
child: SafeArea(
top: false,
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
// Emoji button
IconButton(
icon: Icon(
showEmojiKeyboard
? Icons.keyboard
: Icons.emoji_emotions_outlined,
color: themeColor,
),
onPressed: onEmojiToggle,
padding: EdgeInsets.zero,
constraints: BoxConstraints(minWidth: 36, minHeight: 36),
),
// Text field
Expanded(
child: ConstrainedBox(
constraints: BoxConstraints(
maxHeight: 120.0, // Limit max height
),
child: TextField(
controller: messageController,
focusNode: focusNode,
minLines: 1,
maxLines: 5, // Allow multiple lines but not too many
textCapitalization: TextCapitalization.sentences,
decoration: InputDecoration(
hintText: 'Ketik pesan...',
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(
horizontal: 8.0,
vertical: 10.0,
),
),
),
),
),
// Attachment button - Make this more visible
Container(
decoration: BoxDecoration(
color: Colors.grey.shade200,
borderRadius: BorderRadius.circular(18),
),
child: IconButton(
icon: Icon(
Icons.photo_camera, // Change to camera icon for clarity
color: themeColor,
),
onPressed: () {
// Debug print to verify button is responding
print('Image button pressed');
onImageOptions();
},
tooltip: 'Add Image', // Add tooltip
padding: EdgeInsets.zero,
constraints: BoxConstraints(minWidth: 36, minHeight: 36),
),
),
SizedBox(width: 8), // Add spacing
// Send button
_buildSendButton(),
],
),
),
),
// Emoji keyboard - Wrap in AnimatedContainer for smooth transitions
AnimatedContainer(
duration: Duration(milliseconds: 200),
height: showEmojiKeyboard ? _getEmojiKeyboardHeight(context) : 0,
child: showEmojiKeyboard ? _buildEmojiPicker(context) : SizedBox(),
),
],
);
}
Widget _buildImagePreview() {
return Container(
width: double.infinity,
padding: EdgeInsets.all(8.0),
color: Colors.grey[200],
constraints: BoxConstraints(
maxHeight: 200, // Limit maximum height to prevent overflow
),
child: Stack(
alignment: Alignment.center,
children: [
// Image preview with fixed height and width constraints
Container(
constraints: BoxConstraints(maxHeight: 180, minHeight: 100),
width: double.infinity,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: Colors.black,
),
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.file(
selectedImage!,
fit:
BoxFit
.contain, // Use contain instead of cover to prevent cropping
errorBuilder: (context, error, stackTrace) {
// Handle image loading errors
print('[ERROR] Failed to load image preview: $error');
return Container(
height: 100,
color: Colors.grey[300],
child: Center(
child: Icon(Icons.broken_image, color: Colors.grey[600]),
),
);
},
),
),
),
// Loading indicator
if (isUploading)
Container(
constraints: BoxConstraints(maxHeight: 180),
width: double.infinity,
color: Colors.black54,
child: Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
),
),
// Close button
Positioned(
top: 0,
right: 0,
child: Material(
color: Colors.transparent,
child: InkWell(
borderRadius: BorderRadius.circular(20),
onTap: onClearImage,
child: Container(
padding: EdgeInsets.all(4),
decoration: BoxDecoration(
color: Colors.black54,
shape: BoxShape.circle,
),
child: Icon(Icons.close, color: Colors.white, size: 20),
),
),
),
),
],
),
);
}
Widget _buildReplyBar() {
if (replyToMessage == null) return SizedBox.shrink();
return ReplyBar(message: replyToMessage!, onCancel: onCancelReply);
}
Widget _buildSendButton() {
final bool canSend =
messageController.text.trim().isNotEmpty || selectedImage != null;
return GestureDetector(
onTap: canSend ? onSend : null,
child: Container(
width: 36,
height: 36,
margin: EdgeInsets.only(left: 4, right: 4),
decoration: BoxDecoration(
color: canSend ? themeColor : Colors.grey,
shape: BoxShape.circle,
),
child: Icon(Icons.send, color: Colors.white, size: 18),
),
);
}
Widget _buildEmojiPicker(BuildContext context) {
return EmojiPicker(
onEmojiSelected: (category, emoji) {
messageController.text = messageController.text + emoji.emoji;
},
textEditingController: messageController,
config: Config(checkPlatformCompatibility: true),
);
}
// Calculate emoji keyboard height based on screen size and keyboard visibility
double _getEmojiKeyboardHeight(BuildContext context) {
final screenHeight = MediaQuery.of(context).size.height;
final keyboardVisible = MediaQuery.of(context).viewInsets.bottom > 0;
// Use smaller height when hardware keyboard is visible
return keyboardVisible ? 200 : screenHeight * 0.35;
}
}