본문 바로가기

Flutter/12 Clone 당근마켓

[Flutter] Clone - 당근마켓50(Chat - 2) 상단/하단 정보 표시

이번에는 채팅 화면에 입력창(하단)과 아이템 간략 정보(상단)를 표시해 보겠습니다.

개발환경 : 윈도우11, 안드로이드 스튜디오, flutter 3.0.1

 

 

 

화면 구현은 아래와 같습니다. 

 

 

 

 

./src/models/chat_model.dart - 구현중 수정이 필요한 부분이 있어서 수정함

 

class ChatModel2 {
  String? chatKey; // 자동생성되므로 ? 처리
  String msg;
  DateTime createdDate;
  String userKey;
  DocumentReference? reference;

  ChatModel2({
    // required this.chatKey, // 자동생성되므로 필수처리 제외.
    required this.msg,
    required this.createdDate,
    required this.userKey,
    this.reference,
  });

  factory ChatModel2.fromJson(Map<String, dynamic> json) => ChatModel2(
    // chatKey: json[DOC_CHATKEY], // 자동생성되므로 필수처리 제외.
    msg: json[DOC_MSG],
    createdDate: json[DOC_CREATEDDATE] == null
        ? DateTime.now().toUtc()
        : (json[DOC_CREATEDDATE] as Timestamp).toDate(),
    userKey: json[DOC_USERKEY],
    // reference: json["reference"],
  );

  Map<String, dynamic> toJson() => {
    // DOC_CHATKEY: chatKey, // 자동생성되므로 필수처리 제외.
    DOC_MSG: msg,
    DOC_CREATEDDATE: createdDate,
    DOC_USERKEY: userKey,
    // "reference": reference,
  };
}

 

 

 

./src/screens/chat/chatroom_screen.dart - 정보 및 입력 창 구현

 

import 'package:apple_market3/src/states/user_controller.dart';
import 'package:extended_image/extended_image.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:shimmer/shimmer.dart';

import '../../models/chat_model.dart';
import '../../models/user_model.dart';
import '../../repo/chat_service.dart';
import '../../utils/logger.dart';

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

  @override
  State<ChatroomScreen> createState() => _ChatroomScreenState();
}

class _ChatroomScreenState extends State<ChatroomScreen> {
  late String chatroomKey;
  final TextEditingController _textEditingController = TextEditingController();

  @override
  void initState() {
    // TODO: implement initState
    chatroomKey = Get.parameters['chatroomKey']!;
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    logger.d('${Get.parameters['chatroomKey']}');
    UserModel1 userModel = UserController.to.userModel.value!;

    return Scaffold(
      appBar: AppBar(),
      backgroundColor: Colors.grey[200],
      // 화면 하단의 메뉴바 때문에 SafeArea 로 wrapping 해야 오동작을 방지함.
      body: SafeArea(
        child: Column(
          children: [
            // 게시글 정보를 간략히 표시
            _buildItemInfo(context),
            // 채팅 메시지 표시 부분
            Expanded(
              child: Container(
                color: Colors.white,
              ),
            ),
            const Padding(padding: EdgeInsets.all(4)),
            // 메시지 입력 창
            _buildInputBar(userModel)
          ],
        ),
      ),
    );
  }

  // 컬럼내부에 리스트타일(높이 관련 오류가 있어서 나중에 row+column 조함으로 변경함)과 버튼으로 구성 예정
  MaterialBanner _buildItemInfo(BuildContext context) {

    return MaterialBanner(
      // 공간 조절을 위해서 패딩을 수정함
      padding: EdgeInsets.zero,
      leadingPadding: EdgeInsets.zero,
      // actions 는 빈칸
      actions: [Container()],
      // content 에만 위젯을 넣어서 표시 예정임.
      content: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Row(
            children: [
              Padding(
                padding: const EdgeInsets.only(left: 16, right: 8, top: 8, bottom: 8),
                child:
                ExtendedImage.network(
                        'https://randomuser.me/api/portraits/lego/4.jpg', // lego pic
                        width: 48,
                        height: 48,
                        fit: BoxFit.cover,
                      ),
              ),
              Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  RichText(
                    text: TextSpan(
                        text: '거래완료',
                        style: Theme.of(context).textTheme.bodyText1,
                        children: [
                          TextSpan(
                              // text: chatroomModel == null ? '' : ' ' + chatroomModel.itemTitle,
                              text: ' 복숭아 떨이',
                              style: Theme.of(context).textTheme.bodyText2)
                        ]),
                  ),
                  RichText(
                    text: TextSpan(
                      text: '30,000원',
                        style: Theme.of(context).textTheme.bodyText1,
                        children: [
                          TextSpan(
                              text: ' (가격제한불가)',
                              style: Theme.of(context)
                                  .textTheme
                                  .bodyText2!
                                  .copyWith(color: Colors.black26))
                        ]),
                  ),
                ],
              ),
            ],
          ),
          Padding(
            padding: const EdgeInsets.only(left: 16, bottom: 8),
            // 버튼 사이즈를 줄이기 위해서 SizedBox 추가
            child: SizedBox(
              height: 32,
              child: TextButton.icon(
                onPressed: () { debugPrint('후기남기기 클릭~~');},
                icon: const Icon(
                  Icons.edit,
                  // 버튼 사이즈를 줄이기 위해서 size 추가 설정,
                  size: 16,
                  color: Colors.black87,
                ),
                label: Text(
                  '후기 남기기',
                  style: Theme.of(context).textTheme.bodyText1!.copyWith(color: Colors.black87),
                ),
                style: TextButton.styleFrom(
                  backgroundColor: Colors.white,
                  shape: RoundedRectangleBorder(
                    borderRadius: BorderRadius.circular(4),
                    side: BorderSide(color: Colors.grey[300]!, width: 2),
                  ),
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildInputBar(UserModel1 userModel) {
    return SizedBox(
      height: 48,
      child: Row(
        children: [
          IconButton(
            onPressed: () {},
            icon: const Icon(
              Icons.add,
              color: Colors.grey,
            ),
          ),
          Expanded(
            child: TextFormField(
              controller: _textEditingController,
              decoration: InputDecoration(
                hintText: '메시지를 입력하세요',
                // 메시지 입력박스를 조금 작게 줄임,
                isDense: true,
                // fillColor & filled 동시에 설정해야함.
                fillColor: Colors.white,
                filled: true,
                suffixIcon: GestureDetector(
                  onTap: () {
                    logger.d('Icon clicked');
                  },
                  child: const Icon(
                    Icons.emoji_emotions_outlined,
                    color: Colors.grey,
                  ),
                ),
                // 아이콘때문에 입력 박스가 커져서 줄여줌 설정,
                suffixIconConstraints: BoxConstraints.tight(const Size(40, 40)),
                // 입력 박스의 패딩 공간을 줄임, 미설정시 16쯤 됨,
                contentPadding: const EdgeInsets.all(10),
                border: OutlineInputBorder(
                  borderRadius: BorderRadius.circular(20),
                  borderSide: const BorderSide(color: Colors.redAccent),
                ),
              ),
            ),
          ),
          IconButton(
            onPressed: () async {
              ChatModel2 chatModel = ChatModel2(
                userKey: userModel.userKey,
                msg: _textEditingController.text,
                createdDate: DateTime.now().toUtc(),
              );

              await ChatService().createNewChat(chatroomKey, chatModel);
              logger.d(_textEditingController.text.toString());
              _textEditingController.clear();
            },
            icon: const Icon(
              Icons.send,
              color: Colors.grey,
            ),
          )
        ],
      ),
    );
  }
}