티스토리 뷰

- flutter

kakao앱 만들기 (5) - 채팅 화면

나둥식 2022. 11. 11. 16:47

✅ 채팅 화면

 

1️⃣ 채팅방 기본 세팅

① 채팅방 파일 만들기 ( chat_room_screen.dart )

import 'package:flutter/material.dart';

class ChatRoomScreen extends StatefulWidget {
  const ChatRoomScreen({Key? key}) : super(key: key);

  @override
  State<ChatRoomScreen> createState() => _ChatRoomScreenState();
}

class _ChatRoomScreenState extends State<ChatRoomScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Text("ChatRoomScreen"),
      ),
    );
  }
}

 

② 채팅목록에 탭 이벤트 넣기 ( chat_card.dart )

return InkWell(
      onTap: () {
        Navigator.push(
            context,
            MaterialPageRoute(
              builder: (context) => ChatRoomScreen(),
            ));
      },

 

 

 

2️⃣ 배경 및 앱바 만들기

import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';

class ChatRoomScreen extends StatefulWidget {
  const ChatRoomScreen({Key? key}) : super(key: key);

  @override
  State<ChatRoomScreen> createState() => _ChatRoomScreenState();
}

class _ChatRoomScreenState extends State<ChatRoomScreen> {
  @override
  Widget build(BuildContext context) {
    return Container(
      color: Color(0xffb2c7da),
      child: Scaffold(
        backgroundColor: Colors.transparent,
        appBar: AppBar(
          backgroundColor: Colors.transparent,
          iconTheme: IconThemeData(color: Colors.black),
          title: Text(
            "홍길동",
            style: Theme.of(context).textTheme.headline6,
          ),
          actions: [
            Icon(FontAwesomeIcons.search, size: 20),
            SizedBox(width: 25),
            Icon(FontAwesomeIcons.bars, size: 20),
            SizedBox(width: 25),
          ],
        ),
      ),
    );
  }
}

- Scaffold가 컨테이너의 배경을 가리면 안되므로, transparent(투명색) 처리함.

 

 

 

3️⃣ TimeLine 위젯 컴포넌트 만들기

import 'package:flutter/material.dart';

class TimeLine extends StatelessWidget {
  const TimeLine({Key? key, required this.time}) : super(key: key);
  final String time;

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.all(7),
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(15),
        color: Color(0xff9cafbe),
      ),
      child: Text(
        time,
        style: TextStyle(color: Colors.white),
      ),
    );
  }
}

 

 

 

4️⃣ TimeLine 위젯 채팅방 화면에 띄우기

body: Column(
            children: [
              Expanded(
                child: SingleChildScrollView(
                  child: Column(
                    children: [
                      TimeLine(time: "2021년 1월 1일 금요일"),
                    ],
                  ),
                ),
              ),
            ],
          ),

➡ width가 TimeLine 만큼만 잡혀있기 때문에 좌측에 쏠려 있음. 추후 Column의 width가 늘어나게 되면 자동으로 위치 정렬됨!

🔎 SingleChildScrollView + Column
- Column으로 나열한 위젯이 작은 화면에서 넘치는 것을 방지하기 위해 사용
- 가운데 정렬해서 사용하는 로그인 화면 등에서 사용할 수 있는 기법

➡ 단순히 정가운데 배치를 하고 싶은 경우에는 SingleChildScrollView를 Center 위젯으로 감싸주어야 함!

 

 

 

 

5️⃣ 상대방이 작성한 채팅 위젯 만들기 - 컴포넌트

import 'package:flutter/material.dart';
import 'package:flutter_kakao/models/user.dart';

class OtherChat extends StatelessWidget {
  const OtherChat(
      {Key? key, required this.name, required this.text, required this.time})
      : super(key: key);
  final String name;
  final String text;
  final String time;

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 10.0),
      child: Row(
        children: [
          CircleAvatar(
            backgroundImage: NetworkImage(friends[0].backgroundImage),
          ),
          SizedBox(width: 10),
          Flexible(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(name),
                Container(
                  child: Text(text),
                  padding: EdgeInsets.all(8),
                  decoration: BoxDecoration(
                    borderRadius: BorderRadius.circular(13),
                    color: Colors.white,
                  ),
                ),
              ],
            ),
          ),
          SizedBox(width: 5),
          Text(
            time,
            style: TextStyle(fontSize: 12),
          ),
        ],
      ),
    );
  }
}

 

 

 

6️⃣ 내가 작성한 채팅 위젯 만들기 - 컴포넌트

import 'package:flutter/material.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 Row(
      mainAxisAlignment: MainAxisAlignment.end,
      children: [
        Text(
          time,
          style: TextStyle(fontSize: 12),
        ),
        SizedBox(width: 5),
        Flexible(
          child: Container(
            padding: const EdgeInsets.all(8),
            decoration: BoxDecoration(
              borderRadius: BorderRadius.circular(13),
              color: Color(0xfffeec34),
            ),
            child: Text(text),
          ),
        ),
      ],
    );
  }
}

 

 

 

7️⃣ 작성한 채팅 위젯 화면에 구현하기

final List<MyChat> chats = [];
final TextEditingController _textController = TextEditingController();

...

body: Column(
  children: [
    Expanded(
      child: SingleChildScrollView(
        child: Padding(
          padding: const EdgeInsets.symmetric(horizontal: 16.0),
          child: Column(
            children: [
              TimeLine(time: "2021년 1월 1일 금요일"),
              OtherChat(
                  name: "홍길동",
                  text: "새해 복 많이 받으세요.",
                  time: "오전 10:10"),
              MyChat(text: "선생님도 많이 받으십시오.", time: "오후 2:15"),
              ...List.generate(chats.length, (index) => chats[index]),
            ],
          ),
        ),
      ),
    ),
  ],
),

...List.generate(chats.length, (index) => chats[index])  이후 만들 채팅 입력 UI를 이용하여 chats에 새 글이 추가되면 화면에 나열하기

 

 

 

8️⃣ 채팅 입력 UI 만들기

① 채팅 입력 아이콘

import 'package:flutter/material.dart';

class ChatIconButton extends StatelessWidget {
  const ChatIconButton({Key? key, required this.icon}) : super(key: key);
  final Icon icon;

  @override
  Widget build(BuildContext context) {
    return IconButton(
      padding: EdgeInsets.symmetric(horizontal: 15),
      onPressed: () {},
      icon: icon,
      iconSize: 25,
    );
  }
}

 

 

② 채팅 입력창 UI

final TextEditingController _textController = TextEditingController();

...

Container(
                height: 60,
                color: Colors.white,
                child: Row(
                  children: [
                    ChatIconButton(icon: Icon(FontAwesomeIcons.plusSquare)),
                    Expanded(
                      child: Container(
                        child: TextField(
                          controller: _textController,
                          maxLines: 1,
                          style: TextStyle(fontSize: 20),
                          decoration: InputDecoration(
                            focusedBorder: InputBorder.none,
                            enabledBorder: InputBorder.none,
                          ),
                        ),
                      ),
                    ),
                    ChatIconButton(icon: Icon(FontAwesomeIcons.smile)),
                    ChatIconButton(icon: Icon(FontAwesomeIcons.cog)),
                  ],
                ),
              ),

- InputDecoration :  html의 input창을 스타일링 하는 위젯

🔎 TextEditingController
: 텍스트필드에 입력된 텍스트의 변화를 감지하고 핸들링 하는 방법 ➡ 인스턴스에 핸들링 함수를 리스너로 등록

 

 

③ submit 이벤트 함수 만들기 (글을 입력하고 완료 버튼을 누르면 입력창에 글을 비워줌)

void _handleSubmitted(text) {
    _textController.clear();

    setState(() {
      chats.add(
        MyChat(
          text: text,
          time: DateFormat("a K:m")
              .format(new DateTime.now())
              .replaceAll("AM", "오전")
              .replaceAll("PM", "오후"),
        ),
      );
    });
  }

- _textController.clear()로 내가 입력한 글을 비워줌

- setState를 이용하여 새로 작성한 글을 chats에 추가함과 동시에 화면에 그려지도록 함

🔎 DateFormat ( intl package )
- 원하는 형태로 시간, 날짜, 요일을 표현할 수 있음
- format 메소드에 DateTime을 넣어주면 formatted string을 구할 수 있음
- new DateTime.now() = 현재 시간, 날짜, 요일 가져오기

a = 오전/오후
h = 시   (K로 해도 시간이 나오네...?)
m = 분
s = 초
EEEE = 요일
yyy = 년
MMM = 월
d = 일

 

 

 

[chat_room_screen.dart 전체코드]

import 'package:flutter/material.dart';
import 'package:flutter_kakao/components/chat_icon_button.dart';
import 'package:flutter_kakao/components/other_chat.dart';
import 'package:flutter_kakao/components/time_line.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:intl/intl.dart';

import '../components/my_chat.dart';

class ChatRoomScreen extends StatefulWidget {
  const ChatRoomScreen({Key? key}) : super(key: key);

  @override
  State<ChatRoomScreen> createState() => _ChatRoomScreenState();
}

class _ChatRoomScreenState extends State<ChatRoomScreen> {
  final List<MyChat> chats = [];
  final TextEditingController _textController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        color: Color(0xffb2c7da),
        child: Scaffold(
          backgroundColor: Colors.transparent,
          appBar: AppBar(
            backgroundColor: Colors.transparent,
            iconTheme: IconThemeData(color: Colors.black),
            title: Text(
              "홍길동",
              style: Theme.of(context).textTheme.headline6,
            ),
            actions: [
              Icon(FontAwesomeIcons.search, size: 20),
              SizedBox(width: 25),
              Icon(FontAwesomeIcons.bars, size: 20),
              SizedBox(width: 25),
            ],
          ),
          body: Column(
            children: [
              Expanded(
                child: SingleChildScrollView(
                  child: Padding(
                    padding: const EdgeInsets.symmetric(horizontal: 16.0),
                    child: Column(
                      children: [
                        TimeLine(time: "2021년 1월 1일 금요일"),
                        OtherChat(
                            name: "홍길동",
                            text: "새해 복 많이 받으세요.",
                            time: "오전 10:10"),
                        MyChat(text: "선생님도 많이 받으십시오.", time: "오후 2:15"),
                        ...List.generate(chats.length, (index) => chats[index]),
                      ],
                    ),
                  ),
                ),
              ),
              Container(
                height: 60,
                color: Colors.white,
                child: Row(
                  children: [
                    ChatIconButton(icon: Icon(FontAwesomeIcons.plusSquare)),
                    Expanded(
                      child: Container(
                        child: TextField(
                          controller: _textController,
                          maxLines: 1,
                          style: TextStyle(fontSize: 20),
                          decoration: InputDecoration(
                            focusedBorder: InputBorder.none,
                            enabledBorder: InputBorder.none,
                          ),
                          onSubmitted: _handleSubmitted,
                        ),
                      ),
                    ),
                    ChatIconButton(icon: Icon(FontAwesomeIcons.smile)),
                    ChatIconButton(icon: Icon(FontAwesomeIcons.cog)),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  void _handleSubmitted(text) {
    _textController.clear();

    setState(() {
      chats.add(
        MyChat(
          text: text,
          time: DateFormat("a K:m")
              .format(new DateTime.now())
              .replaceAll("AM", "오전")
              .replaceAll("PM", "오후"),
        ),
      );
    });
  }
}
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
글 보관함