| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273 |
- import 'package:flutter/material.dart';
- import '../model/msg.dart';
- import '../util/ToastUtil.dart';
- import '../util/clipboard_util.dart';
- import '../widget/bubble/bubble_widget.dart';
- /// 聊天页面
- class ChatPage extends StatefulWidget {
- const ChatPage({super.key});
- @override
- State<StatefulWidget> createState() {
- return ChatPageState();
- }
- }
- class ChatPageState extends State<ChatPage> {
- /// TextField操作控制器
- final TextEditingController _editingController = TextEditingController();
- /// ListView的滚动控制器
- final ScrollController _scrollController = ScrollController();
- /// 输入框焦点
- final _inputFocusNode = FocusNode();
- /// 消息列表
- final List<Msg> _msgList = [];
- @override
- void initState() {
- super.initState();
- _inputFocusNode.addListener(_handleTextFieldFocusChange);
- // 进入页面,就获取输入框焦点
- _inputFocusNode.requestFocus();
- }
- @override
- void dispose() {
- // 取消监听
- _inputFocusNode.removeListener(_handleTextFieldFocusChange);
- _inputFocusNode.dispose();
- _editingController.dispose();
- _scrollController.dispose();
- super.dispose();
- }
- /// 处理输入框的焦点变化
- void _handleTextFieldFocusChange() {
- if (_inputFocusNode.hasFocus) {
- // 输入框获取焦点,滚动列表到底部
- _scrollToBottom();
- }
- }
- /// 添加消息到消息列表中
- void _addMsg2List(String msg, bool isMe) {
- setState(() {
- _msgList.add(
- Msg(isMe: isMe, msg: msg, createTime: DateTime.now().millisecond),
- );
- });
- _scrollToBottom();
- }
- /// 滚动到列表底部
- void _scrollToBottom() {
- //延迟300毫秒,再滚动到列表底部
- Future.delayed(const Duration(milliseconds: 300), () {
- _scrollController.jumpTo(_scrollController.position.maxScrollExtent);
- });
- }
- /// 发送消息
- void _sendMsg(String msg) {
- if (msg.isEmpty) {
- ToastUtil.showToast("请输入要发送的消息内容");
- return;
- }
- //添加消息到列表中
- _addMsg2List(msg, true);
- // 延迟生成对方的回复消息
- Future.delayed(const Duration(milliseconds: 150), () {
- //添加消息到列表中
- _addMsg2List(replyMessage2Client(msg), false);
- });
- //清除输入框的内容
- _editingController.clear();
- }
- /// 生成回复消息
- String replyMessage2Client(String clientMsg) {
- return clientMsg
- .replaceAll("我", "你")
- .replaceAll("吗", "")
- .replaceAll("?", "!")
- .replaceAll("?", "!");
- }
- /// 构建聊天气泡
- Widget _buildMsgBubble(Msg msg) {
- // 设置气泡的外边距,让气泡不易过长
- double marginValue = 50;
- EdgeInsets marginEdgeInsets;
- if (msg.isMe) {
- marginEdgeInsets = EdgeInsets.only(left: marginValue);
- } else {
- marginEdgeInsets = EdgeInsets.only(right: marginValue);
- }
- // Flexible,文本超过一行时,自动换行,并且不超过最大宽度,不超过一行时,则自动包裹内容
- return Flexible(
- child: BubbleWidget(
- // 箭头方向
- arrowDirection: msg.isMe ? AxisDirection.right : AxisDirection.left,
- arrowOffset: 22,
- arrowLength: 8,
- arrowRadius: 4,
- arrowWidth: 14,
- padding: const EdgeInsets.all(12),
- borderRadius: BorderRadius.circular(8),
- margin: marginEdgeInsets,
- backgroundColor:
- msg.isMe
- ? const Color.fromARGB(255, 164, 208, 238)
- : const Color.fromARGB(255, 153, 231, 169),
- contentBuilder: (context) {
- return SelectableText(msg.msg);
- },
- ),
- );
- }
- /// 构建聊天消息列表项
- Widget _buildMsgItem(Msg msg) {
- Widget content;
- // 自己发的
- if (msg.isMe) {
- content = Row(
- // 如果是自己发的,则在右边
- mainAxisAlignment: MainAxisAlignment.end,
- // 顶部对齐
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- // 聊天气泡
- _buildMsgBubble(msg),
- // 头像
- _buildAvatar(msg),
- ],
- );
- } else {
- // 对方发的
- content = Row(
- // 如果是自己发的,则在右边
- mainAxisAlignment: MainAxisAlignment.start,
- // 顶部对齐
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- // 头像
- _buildAvatar(msg),
- // 聊天气泡
- _buildMsgBubble(msg),
- ],
- );
- }
- // return GestureDetector(
- // onLongPress: () {
- // // 长按消息,复制文本到剪切板
- // ClipboardUtil.copyToClipboard(msg.msg);
- // ToastUtil.showToast("已复制到剪切板");
- // },
- // child: Container(padding: const EdgeInsets.all(8.0), child: content),
- // );
- return Container(padding: const EdgeInsets.all(8.0), child: content);
- }
- /// 构建头像
- Widget _buildAvatar(Msg msg) {
- return Container(
- margin: const EdgeInsets.symmetric(horizontal: 8.0),
- child: CircleAvatar(
- radius: 20,
- backgroundColor: Colors.grey,
- child: Text(
- msg.isMe ? "我" : "对方",
- style: const TextStyle(color: Colors.white),
- ),
- ),
- );
- }
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- appBar: AppBar(
- leading: IconButton(
- onPressed: () {
- Navigator.pop(context, null);
- },
- icon: const Icon(Icons.arrow_back),
- ),
- title: const Text("聊天页"),
- ),
- body: Column(
- children: [
- Expanded(
- flex: 1,
- child: ListView.builder(
- controller: _scrollController,
- itemCount: _msgList.length,
- itemBuilder: (BuildContext context, int index) {
- return _buildMsgItem(_msgList[index]);
- },
- ),
- ),
- Center(
- child: Column(
- children: [
- Container(height: 0.3, color: Colors.grey),
- Padding(
- padding: const EdgeInsets.symmetric(
- vertical: 15.0,
- horizontal: 18.0,
- ),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.start,
- children: [
- Expanded(
- flex: 1,
- child: TextField(
- // 设置按钮显示为发送
- textInputAction: TextInputAction.send,
- onSubmitted: (value) {
- // 用户点击软键盘的发送按钮
- var msg = _editingController.text;
- _sendMsg(msg);
- },
- // 输入框焦点
- focusNode: _inputFocusNode,
- // 点击外部区域,关闭软键盘
- onTapUpOutside: (event) {
- _inputFocusNode.unfocus();
- },
- controller: _editingController,
- decoration: const InputDecoration(
- //提示文字
- hintText: "选择粘贴TA的话,选择人设风格回复",
- //边框
- border: OutlineInputBorder(),
- ),
- ),
- ),
- Container(
- margin: const EdgeInsets.only(left: 15.0),
- child: ElevatedButton(
- child: const Text("发送"),
- onPressed: () {
- var msg = _editingController.text;
- _sendMsg(msg);
- },
- ),
- ),
- ],
- ),
- ),
- ],
- ),
- ),
- ],
- ),
- );
- }
- }
|