Flutter聊天界面(静态)

功能有,链接,发送文字,发送表情

本篇文章存静态效果、没用socket连接的实现

效果图

代码如下:

Dart 复制代码
//在线聊天
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';

import '../home/house_detail_page.dart';
import '../library/network/network.dart';

class Message {
  final String type;
  final String sender;
  final String? text;
  final Map? cardInfo;

  Message({required this.sender, this.text, required this.type, this.cardInfo});
}

//文字信息==============================================================================
class Bubble extends StatelessWidget {
  final Message message;
  final bool isMe;

  Bubble({required this.message, required this.isMe});

  @override
  Widget build(BuildContext context) {
    return Row(
      mainAxisAlignment: isMe ? MainAxisAlignment.end : MainAxisAlignment.start,
      children: [
        Visibility(
          visible: !isMe,
          child: const Icon(
            Icons.paid,
            size: 30,
          ),
        ),
        Container(
          margin: const EdgeInsets.symmetric(vertical: 5.0, horizontal: 10.0),
          padding: const EdgeInsets.all(10.0),
          decoration: BoxDecoration(
            color: isMe ? Colors.blue : Colors.grey[300],
            borderRadius: BorderRadius.circular(12.0),
          ),
          child: Text(
            message.text ?? '',
            style: TextStyle(color: isMe ? Colors.white : Colors.black),
          ),
        ),
        Visibility(
          visible: isMe,
          child: const Icon(
            Icons.pages,
            size: 30,
          ),
        )
      ],
    );
  }
}

//卡片================================================================================
class Card extends StatelessWidget {
  final Message message;
  final bool isMe;

  Card({required this.message, required this.isMe});

  @override
  Widget build(BuildContext context) {
    return Row(
      mainAxisAlignment: isMe ? MainAxisAlignment.end : MainAxisAlignment.start,
      children: [
        Visibility(
          visible: !isMe,
          child: const Icon(
            Icons.paid,
            size: 30,
          ),
        ),
        SizedBox(child: _CardPage(cardInfo: message.cardInfo ?? {})),
        Visibility(
          visible: isMe,
          child: const Icon(
            Icons.pages,
            size: 30,
          ),
        )
      ],
    );
  }
}

class _CardPage extends StatelessWidget {
  late Map cardInfo;

  _CardPage({required this.cardInfo});

  @override
  Widget build(BuildContext context) {
    return Container(
        width: MediaQuery.of(context).size.width * 0.8,
        margin: EdgeInsets.only(top: 5),
        padding: EdgeInsets.all(5),
        decoration: BoxDecoration(
            color: Colors.white, borderRadius: BorderRadius.circular(12.0)),
        child: Row(
          children: [
            GestureDetector(
                onTap: () {
                  // Add your click event handling code here
                  // 去详情页
                  Navigator.push(
                    context,
                    MaterialPageRoute(
                      // fullscreenDialog: true,
                      builder: (context) => MyHomeDetailPage(
                          houseId: cardInfo['id'], type: cardInfo['type']),
                    ),
                  );
                },
                child: Container(
                  width: 100,
                  height: 84,
                  margin: const EdgeInsets.all(8),
                  decoration: BoxDecoration(
                    image: DecorationImage(
                      image: NetworkImage(
                          kFileRootUrl + (cardInfo['styleImgPath'] ?? '')),
                      fit: BoxFit.fill,
                      repeat: ImageRepeat.noRepeat,
                    ),
                    borderRadius: BorderRadius.circular(10),
                  ),
                )),
            GestureDetector(
                onTap: () {
                  // Add your click event handling code here
                  // 去详情页
                  Navigator.push(
                    context,
                    MaterialPageRoute(
                      // fullscreenDialog: true,
                      builder: (context) => MyHomeDetailPage(
                          houseId: cardInfo['id'], type: cardInfo['type']),
                    ),
                  );
                },
                child: Container(
                  alignment: Alignment.topLeft,
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.start,
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Row(
                        crossAxisAlignment: CrossAxisAlignment.center,
                        children: [
                          Text(
                            cardInfo['name'],
                            style: const TextStyle(fontSize: 18),
                          ),
                        ],
                      ),
                      Row(
                        children: [
                          Text(cardInfo['zoneName'] ?? ''),
                          const Text(' | '),
                          Text('${"mianji".tr} '),
                          Text(cardInfo['area']),
                        ],
                      ),
                      Container(
                        alignment: Alignment.centerLeft,
                        child: Text(
                          '${cardInfo['price'] ?? ''}/㎡',
                          style: const TextStyle(
                              color: Colors.orange, fontSize: 16),
                        ),
                      ),
                    ],
                  ),
                )), //小标题
          ],
        ));
  }
}

//主页
class CommunicatePage extends StatefulWidget {
  const CommunicatePage({super.key});

  @override
  State<CommunicatePage> createState() => _CommunicatePageState();
}

class _CommunicatePageState extends State<CommunicatePage> {
//变量 start==========================================================
  final TextEditingController _ContentController =
      TextEditingController(text: '');

  /// 输入框焦点
  FocusNode focusNode = FocusNode();
  final List<Message> messages = [
    Message(
        sender: "ta",
        cardInfo: {
          "id": "4",
          "code": "fxhsud",
          "title": "test1",
          "name": "test1",
          "zoneName": null,
          "area": "90",
          "roomType": "2室1厅1卫",
          "directions": ["2"],
          "price": "200.00",
          "type": 2,
          "status": 2,
          "seeCount": null,
          "floorNum": "24/30",
          "styleImgPath":
              "/upload/upload/2024/03/26/IMG_20230303_183318(1)_20240326170733A001.jpg",
          "time": "2022-03-26"
        },
        type: "card"),
    Message(sender: "me", text: "hi!", type: "text"),
    Message(sender: "me", text: "你是?!", type: "text"),
    Message(sender: "ta", text: "hello!", type: "text")
  ];
  var isEmojiShow = false;
  final List unicodeArr = [
    '\u{1F600}',
    '\u{1F601}',
    '\u{1F602}',
    '\u{1F603}',
    '\u{1F604}',
    '\u{1F60A}',
    '\u{1F60B}',
    '\u{1F60C}',
    '\u{1F60D}',
    '\u{2764}',
    '\u{1F44A}',
    '\u{1F44B}',
    '\u{1F44C}',
    '\u{1F44D}'
  ];

//变量 end==========================================================

  @override
  void initState() {
    super.initState();
  }

  @override
  void dispose() {
    super.dispose();
    _ContentController.dispose();
    focusNode.dispose();
  }

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
        backgroundColor: Color(0xFFebebeb),
        resizeToAvoidBottomInset: true,
        appBar: AppBar(
          title: Text('张三'),
        ),
        body: Stack(alignment: Alignment.bottomCenter, children: [
          ListView.builder(
            itemCount: messages.length,
            itemBuilder: (BuildContext context, int index) {
              return messages[index].type == 'text'
                  ? Bubble(
                      message: messages[index],
                      isMe: messages[index].sender == 'me',
                    )
                  : Card(
                      message: messages[index],
                      isMe: messages[index].sender == 'me',
                    );
            },
          ),
          Positioned(
              bottom: 0,
              child: SingleChildScrollView(
                  reverse: true, // 反向滚动以确保 Positioned 在键盘上方
                  child: Column(children: [
                    Container(
                      width: MediaQuery.of(context).size.width,
                      height: 50,
                      decoration: const BoxDecoration(
                          color: Color.fromRGBO(240, 240, 240, 1)),
                      child: Row(
                        mainAxisAlignment: MainAxisAlignment.spaceAround,
                        children: [
                          const Icon(
                            Icons.contactless_outlined,
                            size: 35,
                          ),
                          SizedBox(
                              width: MediaQuery.of(context).size.width *
                                  0.6, // 添加固定宽度
                              child: TextField(
                                textAlignVertical: TextAlignVertical.center,
                                controller: _ContentController,
                                decoration: const InputDecoration(
                                  contentPadding: EdgeInsets.all(5),
                                  isCollapsed: true,
                                  filled: true,
                                  fillColor: Colors.white,
                                  // 设置背景色
                                  border: OutlineInputBorder(
                                    borderRadius: BorderRadius.all(
                                        Radius.circular(10)), // 设置圆角半径
                                    borderSide: BorderSide.none, // 去掉边框
                                  ),
                                ),
                                focusNode: focusNode,
                                onTap: ()=>{setState(() {
                                  isEmojiShow =false;
                                })},
                                onTapOutside: (e) => {focusNode.unfocus()},
                                onEditingComplete: () {
                                  FocusScope.of(context)
                                      .requestFocus(focusNode);
                                },
                              )),
                          GestureDetector(
                              onTap: () => {
                                    setState(() {
                                      isEmojiShow =
                                          !isEmojiShow; // 数据加载完毕,重置标志位
                                    })
                                  },
                              child: const Icon(
                                Icons.sentiment_satisfied_alt_outlined,
                                size: 35,
                              )),
                          const Icon(
                            Icons.add_circle_outline,
                            size: 35,
                          ),
                        ],
                      ),
                    ),
                    Visibility(
                        visible: isEmojiShow,
                        child: Container(
                          width: MediaQuery.of(context).size.width,
                          height: 200,
                          decoration: const BoxDecoration(color: Colors.white),
                          child:
                          SingleChildScrollView(
                            scrollDirection: Axis.vertical,
                            child:
                            Wrap(
                            children: unicodeArr.map((emoji) {
                              return Container(
                                padding: const EdgeInsets.all(8.0),
                                width: MediaQuery.of(context).size.width / 4, // 设置每个子项的宽度为屏幕宽度的三分之一
                                height: 60,
                                child: GestureDetector(
                                  onTap: () {
                                    setState(() {
                                      messages.add(Message(
                                        sender: 'me',
                                        text: emoji,
                                        type: 'text',
                                      ));
                                    });
                                  },
                                  child: Text(
                                    emoji,
                                    style: TextStyle(fontSize: 30),
                                  ),
                                ),
                              );
                            }).toList(),
                          ),
                        )))
                  ])))
        ]));
  }
}
相关推荐
轻口味43 分钟前
命名空间与模块化概述
开发语言·前端·javascript
前端小小王1 小时前
React Hooks
前端·javascript·react.js
迷途小码农零零发1 小时前
react中使用ResizeObserver来观察元素的size变化
前端·javascript·react.js
娃哈哈哈哈呀2 小时前
vue中的css深度选择器v-deep 配合!important
前端·css·vue.js
旭东怪2 小时前
EasyPoi 使用$fe:模板语法生成Word动态行
java·前端·word
ekskef_sef4 小时前
32岁前端干了8年,是继续做前端开发,还是转其它工作
前端
sunshine6414 小时前
【CSS】实现tag选中对钩样式
前端·css·css3
真滴book理喻5 小时前
Vue(四)
前端·javascript·vue.js
蜜獾云5 小时前
npm淘宝镜像
前端·npm·node.js