완성된 UI입니다
class ChatScreen extends StatefulWidget {
const ChatScreen(
{super.key,
required this.chatroomId,
});
final String chatroomId;
ChatScreenState createState() => ChatScreenState();
}
class ChatScreenState extends State<ChatScreen> {
final _controller = TextEditingController();
String newMessage = '';
void dispose() {
_controller.dispose();
super.dispose();
}
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
FocusScope.of(context).unfocus(); // 키보드 닫기 이벤트
},
child: Scaffold(
appBar: AppBar(
toolbarHeight: MediaQuery.of(context).size.height * 0.12,
backgroundColor: Color.fromARGB(255, 42, 42, 42),
title: GestureDetector(
onTap: () => onProfilePressed(context, widget.counselor),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
ProfileImage(
onProfileImagePressed: () =>
onProfilePressed(context, widget.counselor),
isChoosedPicture: false,
path: widget.counselor.profileUrl,
type: 1,
imageSize:
MediaQuery.of(context).size.height * 0.07,
),
const SizedBox(width: 20),
SizedBox(
width: MediaQuery.of(context).size.width * 0.45,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(widget.counselor.name,
style: const TextStyle(
color: Palette.appColor,
fontSize: 23,
fontWeight: FontWeight.bold)),
Text(widget.counselor.address,
style: const TextStyle(
color: Palette.appColor,
fontSize: 12,
fontWeight: FontWeight.w400)),
],
),
)
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: const [
Icon(
Icons.arrow_right,
color: Palette.appColor,
),
],
)
]),
),
centerTitle: true,
elevation: 0,
),
backgroundColor: Color.fromARGB(255, 42, 42, 42),
extendBodyBehindAppBar: false,
body: Stack(
children: [
Container(
decoration: const BoxDecoration(
color: Palette.appColor,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(30),
topRight: Radius.circular(30)),
),
child: Column(
children: [
Expanded(
child: StreamBuilder(
stream:
ChatService().getChatRoomData(widget.chatroomId),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Messages(
messages:
snapshot.data!.messages.reversed.toList(),
userId: userId,
counselor: widget.counselor);
} else {
return const Center(
child: Text('sendMessage'),
);
}
},
)),
sendMesssage()
],
))
],
)));
}
Widget sendMesssage() => Container(
height: MediaQuery.of(context).size.height * 0.07,
decoration: const BoxDecoration(
boxShadow: [
BoxShadow(color: Color.fromARGB(18, 0, 0, 0), blurRadius: 10)
],
color: Palette.appColor,
),
padding: const EdgeInsets.only(left: 10, right: 10, bottom: 10),
child: Row(children: [
IconButton(
padding: EdgeInsets.zero,
onPressed: onSendImagePressed,
icon: const Icon(Icons.camera_alt),
color: Colors.blue,
iconSize: 25,
),
const SizedBox(
width: 10,
),
Expanded(
child: TextField(
maxLines: null,
keyboardType: TextInputType.multiline,
textCapitalization: TextCapitalization.sentences,
controller: _controller,
decoration: InputDecoration(
suffixIcon: IconButton(
onPressed: onSendMessage,
icon: const Icon(Icons.send),
color: Colors.blue,
iconSize: 25,
),
hintText: "Type your message here",
hintMaxLines: 1,
contentPadding:
const EdgeInsets.symmetric(horizontal: 8.0, vertical: 10),
hintStyle: const TextStyle(
fontSize: 16,
),
fillColor: Colors.white,
filled: true,
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30.0),
borderSide: const BorderSide(
color: Colors.white,
width: 0.2,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(30.0),
borderSide: const BorderSide(
color: Colors.black26,
width: 0.2,
),
),
),
onChanged: (value) {
newMessage = value;
},
)),
]));
class Messages extends StatelessWidget {
const Messages(
{super.key,
required this.messages,
required this.userId,
});
final List<Message> messages;
final String userId;
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.only(top: 20, bottom: 10, left: 10, right: 10),
child: ListView.builder(
reverse: true,
itemCount: messages.length,
itemBuilder: (context, index) {
return ChatBubbles(
messages[index], messages[index].sender == userId);
},
));
}
}
사용 라이브러리 : chat_bubble
class ChatBubbles extends StatelessWidget {
const ChatBubbles(this.message, this.isMe, {Key? key})
: super(key: key);
final Message message;
final bool isMe;
Widget build(BuildContext context) {
return Row(
mainAxisAlignment:
isMe ? MainAxisAlignment.end : MainAxisAlignment.start,
children: [
if (isMe) ...{
Padding(
padding: const EdgeInsets.only(bottom: 10),
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(dataTimeFormat(message.createdAt),
style: TextStyles.shadowTextStyle),
BubbleSpecialOne(
text: message.content,
isSender: true,
color: Colors.blue,
textStyle: TextStyles.blueBottonTextStyle),
]))
},
if (!isMe)
Padding(
padding: const EdgeInsets.only(bottom: 15),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ProfileButton(
nickname: counselor.name,
path: counselor.profileUrl,
onProfilePressed: onProfilePressed),
Padding(
padding: const EdgeInsets.only(left: 40),
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
BubbleSpecialOne(
text: message.content,
isSender: false,
color: Palette.appColor2.withOpacity(0.3),
textStyle:
TextStyles.chatNotMeBubbleTextStyle,
),
Text(dataTimeFormat(message.createdAt),
style: TextStyles.shadowTextStyle),
]))
]))
]);
}
해당 코드 깃링크
https://github.com/PoomAt-E/hobby-mate
https://github.com/2023-GSC-Diviction/Diviction-User