chat_page.dart 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. import 'package:flutter/material.dart';
  2. import '../model/msg.dart';
  3. import '../util/ToastUtil.dart';
  4. import '../util/clipboard_util.dart';
  5. import '../widget/bubble/bubble_widget.dart';
  6. /// 聊天页面
  7. class ChatPage extends StatefulWidget {
  8. const ChatPage({super.key});
  9. @override
  10. State<StatefulWidget> createState() {
  11. return ChatPageState();
  12. }
  13. }
  14. class ChatPageState extends State<ChatPage> {
  15. /// TextField操作控制器
  16. final TextEditingController _editingController = TextEditingController();
  17. /// ListView的滚动控制器
  18. final ScrollController _scrollController = ScrollController();
  19. /// 输入框焦点
  20. final _inputFocusNode = FocusNode();
  21. /// 消息列表
  22. final List<Msg> _msgList = [];
  23. @override
  24. void initState() {
  25. super.initState();
  26. _inputFocusNode.addListener(() {
  27. // 输入框获取焦点,滚动列表到底部
  28. _scrollToBottom();
  29. });
  30. // 进入页面,就获取输入框焦点
  31. _inputFocusNode.requestFocus();
  32. }
  33. /// 添加消息到消息列表中
  34. void _addMsg2List(String msg, bool isMe) {
  35. setState(() {
  36. _msgList.add(
  37. Msg(isMe: isMe, msg: msg, createTime: DateTime.now().millisecond),
  38. );
  39. });
  40. _scrollToBottom();
  41. }
  42. /// 滚动到列表底部
  43. void _scrollToBottom() {
  44. //延迟300毫秒,再滚动到列表底部
  45. Future.delayed(const Duration(milliseconds: 300), () {
  46. _scrollController.jumpTo(_scrollController.position.maxScrollExtent);
  47. });
  48. }
  49. /// 发送消息
  50. void _sendMsg(String msg) {
  51. if (msg.isEmpty) {
  52. ToastUtil.showToast("请输入要发送的消息内容");
  53. return;
  54. }
  55. //添加消息到列表中
  56. _addMsg2List(msg, true);
  57. // 延迟生成对方的回复消息
  58. Future.delayed(const Duration(milliseconds: 150), () {
  59. //添加消息到列表中
  60. _addMsg2List(replyMessage2Client(msg), false);
  61. });
  62. //清除输入框的内容
  63. _editingController.clear();
  64. }
  65. /// 生成回复消息
  66. String replyMessage2Client(String clientMsg) {
  67. return clientMsg
  68. .replaceAll("我", "你")
  69. .replaceAll("吗", "")
  70. .replaceAll("?", "!")
  71. .replaceAll("?", "!");
  72. }
  73. /// 构建聊天气泡
  74. Widget _buildMsgBubble(Msg msg) {
  75. return BubbleWidget(
  76. // 箭头方向
  77. arrowDirection: msg.isMe ? AxisDirection.right : AxisDirection.left,
  78. arrowOffset: 22,
  79. arrowLength: 8,
  80. arrowRadius: 4,
  81. arrowWidth: 14,
  82. padding: const EdgeInsets.all(12),
  83. borderRadius: BorderRadius.circular(8),
  84. backgroundColor:
  85. msg.isMe
  86. ? const Color.fromARGB(255, 164, 208, 238)
  87. : const Color.fromARGB(255, 153, 231, 169),
  88. contentBuilder: (context) {
  89. return Text(msg.msg);
  90. },
  91. );
  92. }
  93. /// 构建聊天消息列表项
  94. Widget _buildMsgItem(Msg msg) {
  95. Widget content;
  96. // 自己发的
  97. if (msg.isMe) {
  98. content = Row(
  99. // 如果是自己发的,则在右边
  100. mainAxisAlignment: MainAxisAlignment.end,
  101. children: [
  102. // 聊天气泡
  103. _buildMsgBubble(msg),
  104. // 头像
  105. _buildAvatar(msg),
  106. ],
  107. );
  108. } else {
  109. // 对方发的
  110. content = Row(
  111. // 如果是自己发的,则在右边
  112. mainAxisAlignment: MainAxisAlignment.start,
  113. children: [
  114. // 头像
  115. _buildAvatar(msg),
  116. // 聊天气泡
  117. _buildMsgBubble(msg),
  118. ],
  119. );
  120. }
  121. return GestureDetector(
  122. onLongPress: () {
  123. // 长按消息,复制文本到剪切板
  124. ClipboardUtil.copyToClipboard(msg.msg);
  125. ToastUtil.showToast("已复制到剪切板");
  126. },
  127. child: Container(padding: const EdgeInsets.all(8.0), child: content),
  128. );
  129. }
  130. /// 构建头像
  131. Widget _buildAvatar(Msg msg) {
  132. return Container(
  133. margin: const EdgeInsets.symmetric(horizontal: 8.0),
  134. child: CircleAvatar(
  135. radius: 20,
  136. backgroundColor: Colors.grey,
  137. child: Text(
  138. msg.isMe ? "我" : "对方",
  139. style: const TextStyle(color: Colors.white),
  140. ),
  141. ),
  142. );
  143. }
  144. @override
  145. Widget build(BuildContext context) {
  146. return Scaffold(
  147. appBar: AppBar(
  148. leading: IconButton(
  149. onPressed: () {
  150. Navigator.pop(context, null);
  151. },
  152. icon: const Icon(Icons.arrow_back),
  153. ),
  154. title: const Text("聊天页"),
  155. ),
  156. body: Column(
  157. children: [
  158. Expanded(
  159. flex: 1,
  160. child: ListView.builder(
  161. controller: _scrollController,
  162. itemCount: _msgList.length,
  163. itemBuilder: (BuildContext context, int index) {
  164. return _buildMsgItem(_msgList[index]);
  165. },
  166. ),
  167. ),
  168. Center(
  169. child: Column(
  170. children: [
  171. Container(height: 0.3, color: Colors.grey),
  172. Padding(
  173. padding: const EdgeInsets.symmetric(
  174. vertical: 15.0,
  175. horizontal: 18.0,
  176. ),
  177. child: Row(
  178. mainAxisAlignment: MainAxisAlignment.start,
  179. children: [
  180. Expanded(
  181. flex: 1,
  182. child: TextField(
  183. // 设置按钮显示为发送
  184. textInputAction: TextInputAction.send,
  185. onSubmitted: (value) {
  186. // 用户点击软键盘的发送按钮
  187. var msg = _editingController.text;
  188. _sendMsg(msg);
  189. },
  190. // 输入框焦点
  191. focusNode: _inputFocusNode,
  192. // 点击外部区域,关闭软键盘
  193. onTapUpOutside: (event) {
  194. _inputFocusNode.unfocus();
  195. },
  196. controller: _editingController,
  197. decoration: const InputDecoration(
  198. //提示文字
  199. hintText: "请输入要发送的消息",
  200. //边框
  201. border: OutlineInputBorder(),
  202. ),
  203. ),
  204. ),
  205. Container(
  206. margin: const EdgeInsets.only(left: 15.0),
  207. child: ElevatedButton(
  208. child: const Text("发送"),
  209. onPressed: () {
  210. var msg = _editingController.text;
  211. _sendMsg(msg);
  212. },
  213. ),
  214. ),
  215. ],
  216. ),
  217. ),
  218. ],
  219. ),
  220. ),
  221. ],
  222. ),
  223. );
  224. }
  225. }