- flutter
flutter - 채팅방 디자인 하기
나둥식
2022. 12. 19. 23:16
1️⃣ 채팅 리스트 페이지
import 'package:flutter/material.dart';
import 'package:riverpod_firestore_steam1/core/theme.dart';
import 'package:riverpod_firestore_steam1/models/test/users.dart';
import 'package:riverpod_firestore_steam1/view/pages/main/chat/chat_room_page.dart';
class ChatList extends StatelessWidget {
const ChatList({Key? key, required this.user}) : super(key: key);
final dynamic user;
@override
Widget build(BuildContext context) {
return InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ChatRoomPage(),
),
);
},
child: Padding(
padding: const EdgeInsets.only(bottom: 10, top: 0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: ListTile(
leading: ClipRRect(
borderRadius: BorderRadius.circular(14),
child: Container(
width: 40,
height: 40,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(user.profileImg),
fit: BoxFit.cover,
),
),
),
),
title: Padding(
padding: const EdgeInsets.only(top: 10),
child: Text(
user.sender,
style: textTheme(color: kPrimaryColor(), weight: FontWeight.bold).headline3,
),
),
subtitle: Padding(
padding: const EdgeInsets.only(top: 2),
child: Text(
user.message,
style: textTheme(color: kchacholGreyColor()).bodyText1,
overflow: TextOverflow.ellipsis,
maxLines: 2,
),
),
contentPadding: EdgeInsets.only(left: 0, right: 34),
),
),
Column(
children: [
Padding(
padding: const EdgeInsets.only(top: 14),
child: Text(
user.sendDate,
style: textTheme(color: kchacholGreyColor(), weight: FontWeight.w600).bodyText2,
),
)
],
),
],
),
),
);
}
}
① InkWell 버튼으로 리스트 클릭 시, 채팅룸 페이지로 이동하게 함
② 채팅리스트는 ListTile로 만들고,
③ img와 text를 각각 Column / Row로 디자인 함!
🔎 ListTile
: 여러개를 아래로 나열할 때 사용하기 좋은 위젯
- 텍스트는 3줄까지 가능함 (isThreeLine : true)
- 체크박스 혹은 더보기와 같은 아이콘을 넣을 수 있음
- 아이콘은 leading , trailing 속성에 설정함 (Title의 앞/뒤)
- text는 Title, subtitle 속성에 설정함
⭐ 보통은 ListView와 함께 쓰이지만, Colunm, Drawer, Card와도 함께 쓰일 수 있음!
2️⃣ 채팅룸 페이지
① 상대방 채팅 입력창 component
import 'package:flutter/material.dart';
import 'package:riverpod_firestore_steam1/core/theme.dart';
import 'package:riverpod_firestore_steam1/models/test/users.dart';
class OtherChat extends StatelessWidget {
const OtherChat({Key? key, required this.time, required this.name, required this.text}) : super(key: key);
final String name;
final String text;
final String time;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 20),
child: Row(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(14),
child: Container(
width: 36,
height: 36,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/woman1.png"),
fit: BoxFit.cover,
),
),
),
),
SizedBox(width: 8),
Flexible(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(top: 24),
child: Text(
name,
style: textTheme(color: kPrimaryColor(), weight: FontWeight.bold).bodyText1,
),
),
SizedBox(height: 4),
Container(
padding: EdgeInsets.symmetric(horizontal: 15, vertical: 10),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(14),
color: Color(0xfff2f2f2),
),
child: Text(
text,
style: textTheme(color: kPrimaryColor(), weight: FontWeight.w500).bodyText1,
),
),
],
),
),
SizedBox(width: 6),
Padding(
padding: const EdgeInsets.only(top: 60),
child: Text(
time,
style: textTheme().bodyText2,
),
)
],
),
);
}
}
② 내 채팅 입력창 component
import 'package:flutter/material.dart';
import 'package:riverpod_firestore_steam1/core/theme.dart';
class MyChat extends StatelessWidget {
const MyChat({Key? key, required this.text, required this.time}) : super(key: key);
final String text;
final String time;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Padding(
padding: const EdgeInsets.only(bottom: 2),
child: Text(
time,
style: textTheme(color: kchacholGreyColor(), weight: FontWeight.w600).bodyText2,
),
),
SizedBox(width: 6),
Flexible(
child: Container(
padding: EdgeInsets.symmetric(horizontal: 15, vertical: 10),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(14),
color: theme().primaryColor,
),
child: Text(
text,
style: textTheme(color: Colors.white, weight: FontWeight.w500).bodyText1,
),
),
),
],
),
);
}
}
3️⃣ 채팅 입력창 & 전송버튼
Container _buildSubmitContainer() {
return Container(
//padding: EdgeInsets.symmetric(horizontal: 20),
height: 70,
decoration: BoxDecoration(
color: Colors.white,
border: Border(
top: BorderSide(
width: 1,
color: Color(0xffd9d9d9),
),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(width: 10),
IconButton(
onPressed: () {},
icon: SvgPicture.asset("assets/icon_bottom_plus.svg", width: 24),
padding: EdgeInsets.zero,
),
Expanded(
child: Container(
padding: EdgeInsets.symmetric(horizontal: 14),
child: TextField(
controller: _textController,
maxLines: 1,
style: textTheme().headline3,
decoration: InputDecoration(
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
),
onSubmitted: _handleSubmitted,
),
height: 40,
//width: 226,
decoration: BoxDecoration(
color: Color(0xfff2f2f2),
borderRadius: BorderRadius.circular(10),
),
),
),
SizedBox(width: 6),
Padding(
padding: const EdgeInsets.only(right: 20),
child: Container(
height: 40,
padding: EdgeInsets.symmetric(horizontal: 4),
decoration: BoxDecoration(
color: theme().primaryColor,
borderRadius: BorderRadius.circular(10),
),
child: ElevatedButton(
onPressed: () {
setState(() {
_handleSubmitted;
});
},
style: ElevatedButton.styleFrom(
elevation: 0.0,
),
child: Text(
"전송",
style: textTheme(color: Colors.white, weight: FontWeight.w600).headline3,
),
),
),
),
],
),
);
}
① 전송 버튼 클릭시 입력한 텍스트를 전송할 수 있게 _handleSubmitted를 설정하고,
onPressed: () {
setState(() {
_handleSubmitted;
});
},
② setState로 MyChat(내 채팅)을 추가(add)해주었음
void _handleSubmitted(text) {
_textController.clear();
setState(() {
chats.add(
MyChat(
text: text,
time: DateFormat("a K:m").format(new DateTime.now()).replaceAll("AM", "오전").replaceAll("PM", "오후"),
),
);
});
}
- 추가할 때는 text를 받고
- 시간을 오전&오후, 24시 기준으로 받음
4️⃣ 나중에 firebase를 적용할 것이기 때문에 component를 예시로 하나씩 넣어줌!
Scaffold(
//backgroundColor: Colors.white,
body: Column(
children: [
Expanded(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 2),
child: Column(
children: [
OtherChat(
time: "오전 10:25",
name: "홍길동",
text: "야ㅑㅑㅑㅑㅑㅑㅑㅑㅑㅑ이것봐바!"),
SizedBox(height: 6),
MyChat(text: "오 나 필요한건데 ㄳㄳ", time: "오후 17:38"),
...List.generate(
chats.length, (index) => chats[index]),
],
),
),
),
),
_buildSubmitContainer(),
],
),
),
① SingleChildScrollView를 사용해서 채팅이 화면에 넘칠 경우 스크롤되게 함
② chats의 length만큼 index를 오름차순으로 호출함
🔎 List.generate
: length의 길이만큼 index의 0부터 -1까지 범위에서 각각 오름차순으로 호출하여 만든 값으로 리스트를 생성함
⭐각 생성 요소들이 독립된 주소 값을 사용하여 우리가 흔히 아는 배열처럼 사용할 수 있음!