- 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까지 범위에서 각각 오름차순으로 호출하여 만든 값으로 리스트를 생성함

⭐각 생성 요소들이 독립된 주소 값을 사용하여 우리가 흔히 아는 배열처럼 사용할 수 있음!