271 lines
8.9 KiB
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;
|
|
}
|
|
}
|