/// 群聊模板
library;

import 'dart:convert';
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:image_picker/image_picker.dart';
import 'package:loopin/IM/controller/chat_detail_controller.dart';
import 'package:loopin/IM/controller/im_user_info_controller.dart';
import 'package:loopin/IM/im_message.dart';
import 'package:loopin/IM/im_result.dart';
import 'package:loopin/IM/im_service.dart';
import 'package:loopin/api/common_api.dart';
import 'package:loopin/components/image_viewer.dart';
import 'package:loopin/components/my_toast.dart';
import 'package:loopin/components/network_or_asset_image.dart';
import 'package:loopin/components/preview_video.dart';
import 'package:loopin/models/summary_type.dart';
import 'package:loopin/pages/groupChat/controller/group_detail_controller.dart';
import 'package:loopin/pages/groupChat/groupDetail.dart';
import 'package:loopin/service/http.dart';
import 'package:loopin/utils/audio_player_service.dart';
import 'package:loopin/utils/parse_message_summary.dart';
import 'package:loopin/utils/permissions.dart';
import 'package:loopin/utils/snapshot.dart';
import 'package:loopin/utils/voice_service.dart';
import 'package:mime/mime.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:shirne_dialog/shirne_dialog.dart';
import 'package:tencent_cloud_chat_sdk/enum/group_type.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_info.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart';
import 'package:video_player/video_player.dart';
import 'package:wechat_assets_picker/wechat_assets_picker.dart';

import '../../utils/index.dart';
import './components/redpacket.dart';
import './components/richtext.dart';
import 'mock/emoj_json.dart';

class ChatGroup extends StatefulWidget {
  const ChatGroup({super.key});

  @override
  State<ChatGroup> createState() => _ChatGroupState();
}

class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMixin {
  final ImagePicker _picker = ImagePicker();
  bool hasMicPermission = false; // 麦克风权限

  late final ChatDetailController controller;
  // 接收参数
  final V2TimConversation arguments = Get.arguments;

  late String selfUserId;

  // 聊天消息模块
  final bool isNeedScrollBottom = true;

  bool isLoading = false; // 是否在加载中
  bool hasMore = true; // 是否还有更多数据
  final RxBool _throttleFlag = false.obs; // 滚动节流锁
  RxMap<String, bool> voicePlayingMap = <String, bool>{}.obs;

  // 表情json
  List emoJson = emotionData;

  // 底部操作栏模块
  TextEditingController editorController = TextEditingController();
  FocusNode editorFocusNode = FocusNode();
  bool voiceBtnEnable = false; // 语音按钮
  bool voicePanelEnable = false; // 语音操作面板
  bool voiceToTransfer = false; // 语音转文字中
  int voiceType = 0; // 语音操作类型
  Map voiceTypeMap = {
    0: '按住 说话', // 按住说话
    1: '松开 发送', // 松开发送
    2: '松开 取消', // 松开取消(左滑)
    3: '语音转文字', // 语音转文字(右滑)
  };
  bool toolbarEnable = false; // 显示表情/选择区域
  int toolbarIndex = 0; // 0 表情 1 选择
  double keyboardHeight = 157.6; // 键盘高度
  List chooseOptions = [
    {'key': 'photo', 'name': '相册', 'icon': 'assets/images/icon_photo.webp'},
    {'key': 'camera', 'name': '拍摄', 'icon': 'assets/images/icon_camera.webp'},
    // {'key': 'location', 'name': '位置', 'icon': 'assets/images/icon_location.webp'},
    {'key': 'redpacket', 'name': '红包', 'icon': 'assets/images/icon_hb.webp'},
  ];

  // controller监听
  // ScrollController chatController = ScrollController();
  late ScrollController chatController;

  ScrollController emojController = ScrollController();

  // 模拟开红包按钮动画
  late AnimationController animController;

  // 创建一个从 0 到 π 的旋转动画
  late Animation<double> animTurns;

  // 初始化状态
  @override
  void initState() {
    super.initState();
    controller = Get.find<ChatDetailController>();
    chatController = controller.chatController;

    animController = AnimationController(
      vsync: this,
      duration: Duration(milliseconds: 500),
    );
    animTurns = Tween<double>(begin: 0, end: 3.1415926).animate(animController);

    isInGroup();

    cleanUnRead();

    getUserId();

    getMsgData(initFlag: true);

    // 编辑框获取焦点
    editorFocusNode.addListener(() {
      if (editorFocusNode.hasFocus) {
        setState(() {
          toolbarEnable = false;
        });
        controller.scrollToBottom();
      }
    });
    // 滚动监听
    // Future.delayed(Duration(milliseconds: 1000), () {

    // });
    chatController.addListener(() {
      if (_throttleFlag.value) return;
      if (chatController.position.pixels >= chatController.position.maxScrollExtent - 50) {
        // if (chatController.position.pixels <= 50) {
        _throttleFlag.value = true;
        getMsgData().then((_) {
          // 解锁
          Future.delayed(Duration(milliseconds: 1000), () {
            _throttleFlag.value = false;
          });
        });
      }
    });
  }

  @override
  void dispose() {
    if (Get.isRegistered<ChatDetailController>()) {
      Get.delete<ChatDetailController>();
    }
    emojController.dispose();
    editorFocusNode.dispose();
    animController.dispose();
    super.dispose();
  }

  void cleanUnRead() async {
    if ((arguments.unreadCount ?? 0) > 0) {
      final res = await ImService.instance.clearConversationUnreadCount(conversationID: arguments.conversationID);
      if (!res.success) {
        MyDialog.toast(res.desc, icon: Icon(Icons.warning), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
      }
    }
  }

  void getUserId() async {
    final idRes = await ImService.instance.selfUserId();
    if (idRes.success) {
      selfUserId = idRes.data;
    }
  }

  Future<void> getMsgData({bool initFlag = false}) async {
    if (isLoading || !hasMore) return; // 正在加载 or 没有更多了

    isLoading = true;

    // 获取最旧一条消息作为游标
    V2TimMessage? lastRealMsg;
    for (var msg in controller.chatList.reversed) {
      if (msg.localCustomData != 'time_label') {
        lastRealMsg = msg;
        break;
      }
    }
    final lastMsg = lastRealMsg ?? arguments.lastMessage; // 如果找不到，就用传入的参数
    logger.w(lastMsg?.toLogString());

    final res = await ImService.instance.getHistoryMessageList(
      groupID: arguments.groupID,
      lastMsg: lastMsg,
    );

    if (res.success) {
      final newMessages = res.data ?? [];

      if (newMessages.isEmpty) {
        hasMore = false;
      }
      if (initFlag && lastMsg != null) {
        newMessages.insert(0, lastMsg);
      }
      controller.updateChatListWithTimeLabels(newMessages);
      if (initFlag) {
        // 初始化时滚到最底部
        WidgetsBinding.instance.addPostFrameCallback((_) {
          if (chatController.hasClients) {
            chatController.jumpTo(0);
          }
        });
      }

      logger.w('群聊消息加载成功');
    } else {
      MyDialog.toast("获取聊天记录失败：${res.desc}", icon: Icon(Icons.warning), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
    }

    isLoading = false;
  }

  // 渲染聊天消息
  List<Widget> renderChatList() {
    List<Widget> msgtpl = [];
    for (var item in controller.chatList) {
      // 时间提示，公告提示
      if (item.localCustomData == 'time_label') {
        msgtpl.add(Container(
          margin: const EdgeInsets.only(bottom: 15.0),
          child: Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text(
                item.customElem!.data ?? '',
                style: TextStyle(color: Colors.grey[600], fontSize: 12.0),
              ),
            ],
          ),
        ));
      }
      // 写入记录的tips
      else if (item.cloudCustomData == 'tips') {
        msgtpl.add(
          Container(
            margin: const EdgeInsets.only(bottom: 15.0),
            width: double.infinity,
            child: Text(
              item.textElem!.text ?? '',
              style: TextStyle(color: Colors.grey[600], fontSize: 12.0),
              softWrap: true,
              maxLines: 5,
              overflow: TextOverflow.ellipsis,
              textAlign: TextAlign.center,
            ),
          ),
        );
      }
      // 文本消息模板=1
      else if (item.elemType == 1 && item.cloudCustomData != 'tips') {
        msgtpl.add(
          RenderChatItem(
            data: item,
            child: Ink(
              decoration: BoxDecoration(
                color: !(item.isSelf ?? false) ? Color(0xFFFFFFFF) : Color(0xFF89E45B),
                borderRadius: BorderRadius.circular(10.0),
              ),
              child: InkWell(
                overlayColor: WidgetStateProperty.all(Colors.transparent),
                borderRadius: BorderRadius.circular(10.0),
                child: Container(
                  padding: const EdgeInsets.all(10.0),
                  child: RichTextUtil.getRichText(item.textElem?.text ?? '', color: !(item.isSelf ?? false) ? Colors.black : Colors.white), // 可自定义解析emoj/网址/电话
                ),
                onLongPress: () {
                  contextMenuDialog();
                },
              ),
            ),
          ),
        );
      }
      // gif表情模板=8
      else if (item.elemType == 8) {
        msgtpl.add(RenderChatItem(
          data: item,
          child: Ink(
            child: InkWell(
              overlayColor: WidgetStateProperty.all(Colors.transparent),
              child: Container(
                constraints: const BoxConstraints(
                  maxHeight: 100.0,
                  maxWidth: 100.0,
                ),
                // child: Image.asset('assets/images/emotion/${item.faceElem?.data}'),
                child: Image.asset('${item.faceElem?.data}'),
              ),
              onLongPress: () {
                contextMenuDialog();
              },
            ),
          ),
        ));
      }
      // 图片模板=3
      else if (item.elemType == 3) {
        // List<String> imagePaths = item.imageElem?.imageList?.where((e) => e != null && e.url != null).map((e) => e!.url!).toList() ?? [];
        final originImage = item.imageElem?.imageList?.firstWhere((e) => e?.type == 0 && e?.url != null, orElse: () => null);
        List<String> imagePaths = originImage != null ? [originImage.url!] : [];
        msgtpl.add(RenderChatItem(
          data: item,
          child: Ink(
            child: InkWell(
              onTap: () {
                // 预览图片
                Get.to(() => ImageViewer(
                      images: [imagePaths.first],
                      index: 0,
                    ));
              },
              overlayColor: WidgetStateProperty.all(Colors.transparent),
              child: ClipRRect(
                borderRadius: BorderRadius.circular(10.0),
                // child: ImageGroup(
                //   images: imagePaths,
                //   width: 120,
                // ),
                child: Image.network(
                  imagePaths.first,
                  width: 120,
                  fit: BoxFit.cover,
                  loadingBuilder: (context, child, loadingProgress) {
                    if (loadingProgress == null) {
                      // controller.scrollToBottom();
                      return child; // 加载完成，显示图片
                    }
                    return Container(
                      width: 120,
                      height: 240,
                      color: Colors.grey[300],
                      alignment: Alignment.center,
                      child: CircularProgressIndicator(
                        value: loadingProgress.expectedTotalBytes != null ? loadingProgress.cumulativeBytesLoaded / loadingProgress.expectedTotalBytes! : null,
                      ),
                    );
                  },
                  errorBuilder: (context, error, stackTrace) {
                    return Container(
                      color: Colors.grey[300],
                      alignment: Alignment.center,
                      child: Icon(Icons.broken_image, color: Colors.grey, size: 40),
                    );
                  },
                ),
              ),
              onLongPress: () {
                contextMenuDialog();
              },
            ),
          ),
        ));
      }
      // 视频模板=5
      else if (item.elemType == 5) {
        // print(item.videoElem!.toLogString());
        msgtpl.add(RenderChatItem(
          data: item,
          child: Ink(
            child: InkWell(
              overlayColor: WidgetStateProperty.all(Colors.transparent),
              child: SizedBox(
                width: 120.0,
                child: Stack(
                  alignment: Alignment.center,
                  children: [
                    ClipRRect(
                      borderRadius: BorderRadius.circular(10.0),
                      child: NetworkOrAssetImage(
                        imageUrl: item.videoElem?.snapshotUrl ?? '',
                        placeholderAsset: 'assets/images/bk.jpg',
                        width: 120,
                      ),
                    ),
                    const Align(
                      alignment: Alignment.center,
                      child: Icon(
                        Icons.play_circle,
                        color: Colors.white,
                        size: 30.0,
                      ),
                    ),
                  ],
                ),
              ),
              onTap: () {
                showGeneralDialog(
                  context: context,
                  // barrierDismissible: true,
                  barrierColor: Colors.black.withAlpha((1.0 * 255).round()),
                  pageBuilder: (_, __, ___) {
                    return SafeArea(
                      child: PreviewVideo(
                        videoUrl: item.videoElem?.videoUrl ?? '',
                        width: item.videoElem?.snapshotWidth?.toDouble(),
                        height: item.videoElem?.snapshotHeight?.toDouble(),
                      ),
                    );
                  },
                  transitionBuilder: (_, anim, __, child) {
                    return FadeTransition(opacity: anim, child: child);
                  },
                  transitionDuration: const Duration(milliseconds: 200),
                );
              },
              onLongPress: () {
                contextMenuDialog();
              },
            ),
          ),
        ));
      }
      // 语音模板=4
      else if (item.elemType == 4) {
        final durationMs = item.soundElem?.duration ?? 0;
        final durationSeconds = (durationMs / 1000).round();
        final maxWidth = (durationSeconds / 60 * 230).clamp(80.0, 230.0);

        List<Widget> audiobody = [
          Ink(
            decoration: BoxDecoration(
              color: !(item.isSelf ?? false) ? const Color(0xFFFFFFFF) : const Color(0xFF89E45B),
              borderRadius: BorderRadius.circular(10.0),
            ),
            child: InkWell(
              overlayColor: WidgetStateProperty.all(Colors.transparent),
              borderRadius: BorderRadius.circular(10.0),
              child: Container(
                padding: const EdgeInsets.all(10.0),
                constraints: BoxConstraints(
                  maxWidth: maxWidth,
                  // maxWidth: (item.soundElem!.duration! / 1000) / 60 * 230,
                ),
                child: Row(
                  mainAxisAlignment: !(item.isSelf ?? false) ? MainAxisAlignment.start : MainAxisAlignment.end,
                  children: !(item.isSelf ?? false)
                      ? [
                          const Icon(Icons.multitrack_audio),
                          const SizedBox(
                            width: 5.0,
                          ),
                          Text('$durationSeconds"'),
                        ]
                      : [
                          Text('$durationSeconds"'),
                          const SizedBox(
                            width: 5.0,
                          ),
                          const Icon(Icons.multitrack_audio),
                        ],
                ),
              ),
              onTap: () async {
                final locUrl = item.soundElem?.path ?? '';
                final netUrl = item.soundElem?.url ?? '';
                final msgId = item.id ?? '${item.timestamp ?? 0}';
                logger.w('本地地址$locUrl');
                logger.w('网络地址$netUrl');
                if (locUrl.isNotEmpty) {
                  voicePlayingMap[msgId] = true;
                  await AudioPlayerService().playLocal(locUrl);
                  voicePlayingMap[msgId] = false;
                } else if (netUrl.isNotEmpty) {
                  voicePlayingMap[msgId] = true;
                  await AudioPlayerService().playNetwork(netUrl);
                  voicePlayingMap[msgId] = false;
                } else {
                  MyDialog.toast('音频文件已过期', icon: Icon(Icons.warning), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
                }
              },
              onLongPress: () {
                contextMenuDialog();
              },
            ),
          ),
          const SizedBox(
            width: 5.0,
          ),

          // FStyle.badge(0, isdot: true),
        ];

        if (item.isSelf ?? false) {
          // 内容反转
          audiobody = audiobody.reversed.toList();
        } else {
          audiobody = audiobody;
        }

        msgtpl.add(RenderChatItem(
            data: item,
            child: Row(
              mainAxisAlignment: !(item.isSelf ?? false) ? MainAxisAlignment.start : MainAxisAlignment.end,
              children: audiobody,
            )));
      }
      // 分享团购商品
      else if (item.elemType == 2 && item.cloudCustomData == SummaryType.shareTuangou) {
        //price,title,url,sell
        final obj = jsonDecode(item.customElem!.data!);
        logger.e(obj);
        final goodsId = obj['goodsId'];
        final userID = obj['userID'];
        final url = obj['url'];
        final title = obj['title'];
        final price = obj['price'];
        final sell = Utils.graceNumber(int.tryParse(obj['sell'])!);
        msgtpl.add(RenderChatItem(
          data: item,
          child: GestureDetector(
            onTap: () {
              // 这里带上分享人的ID
              Get.toNamed('/goods', arguments: {'goodsId': goodsId, 'userID': userID});
            },
            child: Container(
              width: 160,
              clipBehavior: Clip.antiAlias,
              decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(15.0), boxShadow: [
                BoxShadow(
                  color: Colors.black.withAlpha(5),
                  offset: Offset(0.0, 1.0),
                  blurRadius: 1.0,
                  spreadRadius: 0.0,
                ),
              ]),
              child: Column(
                children: [
                  NetworkOrAssetImage(
                    imageUrl: url,
                    placeholderAsset: 'assets/images/bk.jpg',
                    width: 160.0,
                  ),
                  Container(
                    padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 5.0),
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      spacing: 5.0,
                      children: [
                        Text(
                          '$title',
                          style: TextStyle(fontSize: 14.0, height: 1.2),
                          maxLines: 2,
                          overflow: TextOverflow.ellipsis,
                        ),
                        Row(
                          mainAxisAlignment: MainAxisAlignment.spaceBetween,
                          children: [
                            Flexible(
                              child: Text.rich(
                                overflow: TextOverflow.ellipsis,
                                maxLines: 1,
                                TextSpan(style: TextStyle(color: Colors.red, fontSize: 12.0, fontWeight: FontWeight.w700, fontFamily: 'Arial'), children: [
                                  TextSpan(text: '¥'),
                                  TextSpan(
                                      text: '$price',
                                      style: TextStyle(
                                        fontSize: 14.0,
                                      )),
                                ]),
                              ),
                            ),
                            SizedBox(
                              width: 5,
                            ),
                            Text(
                              '已售$sell件',
                              style: TextStyle(color: Colors.grey, fontSize: 10.0),
                            ),
                          ],
                        ),
                      ],
                    ),
                  )
                ],
              ),
            ),
          ),
        ));
      }
      // 分享短视频
      else if (item.elemType == 2 && item.cloudCustomData == SummaryType.shareVideo) {
        /// {imgUrl,videoUrl,width,height}
        final obj = jsonDecode(item.customElem!.data!);
        logger.e(obj);
        final videoId = obj['videoId'];
        final videoUrl = obj['videoUrl'];
        final imgUrl = obj['imgUrl'];
        final width = obj['width'] as num;
        final height = obj['height'] as num;
        final isHorizontal = width > height;
        msgtpl.add(RenderChatItem(
          data: item,
          child: Ink(
            child: InkWell(
              overlayColor: WidgetStateProperty.all(Colors.transparent),
              child: SizedBox(
                width: 120.0,
                child: Stack(
                  alignment: Alignment.center,
                  children: [
                    ClipRRect(
                      borderRadius: BorderRadius.circular(10.0),
                      child: Container(
                        width: 120,
                        height: 240,
                        color: Colors.black,
                        child: NetworkOrAssetImage(
                          imageUrl: imgUrl,
                          fit: isHorizontal ? BoxFit.contain : BoxFit.cover,
                          placeholderAsset: 'assets/images/bk.jpg',
                        ),
                      ),
                    ),
                    const Align(
                      alignment: Alignment.center,
                      child: Icon(
                        Icons.play_circle,
                        color: Colors.white,
                        size: 30.0,
                      ),
                    ),
                  ],
                ),
              ),
              onTap: () {
                Get.toNamed('/videoDetail', arguments: {'videoId': videoId});
                // showGeneralDialog(
                //   context: context,
                //   barrierColor: Colors.black.withAlpha((1.0 * 255).round()),
                //   pageBuilder: (_, __, ___) {
                //     return SafeArea(
                //       bottom: true,
                //       child: Padding(
                //         padding: const EdgeInsets.only(bottom: 4),
                //         child: PreviewVideo(
                //           videoUrl: videoUrl,
                //           width: width.toDouble(),
                //           height: height.toDouble(),
                //         ),
                //       ),
                //     );
                //   },
                //   transitionBuilder: (_, anim, __, child) {
                //     return FadeTransition(opacity: anim, child: child);
                //   },
                //   transitionDuration: const Duration(milliseconds: 200),
                // );
              },
              // onLongPress: () {
              // contextMenuDialog();
              // },
            ),
          ),
        ));
      }
      // 红包模板=自定义=2；
      else if (item.elemType == 2 && item.cloudCustomData == SummaryType.hongbao) {
        // 定位
        final selfId = Get.find<ImUserInfoController>().userID.value;
        final obj = jsonDecode(item.customElem!.data!);
        final open = obj['open'] ?? false; // 默认未开启，总开关，直到消费完才改成true；
        final remark = obj['remark'];
        final totalCount = obj['totalCount']; // 红包数量
        String idsString = obj['ids'] ?? '';
        final ids = idsString.isEmpty ? <String>[] : idsString.split(',');
        // 自己是否抢过了
        final hasGet = ids.contains(selfId);
        msgtpl.add(RenderChatItem(
          data: item,
          child: Ink(
            decoration: BoxDecoration(
              color: const Color(0xFFFF7F43),
              borderRadius: BorderRadius.circular(10.0),
            ),
            child: InkWell(
              overlayColor: WidgetStateProperty.all(Colors.transparent),
              borderRadius: BorderRadius.circular(10.0),
              child: Container(
                constraints: const BoxConstraints(
                  maxWidth: 210.0,
                ),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Container(
                      width: 210.0,
                      padding: const EdgeInsets.all(10.0),
                      child: Row(
                        children: <Widget>[
                          open || hasGet
                              ? Icon(Icons.check_circle, size: 32.0, color: Colors.white70)
                              : Image.asset(
                                  'assets/images/hbico.png',
                                  width: 32.0,
                                  fit: BoxFit.contain,
                                ),
                          const SizedBox(width: 10),
                          Expanded(
                            child: Text(
                              '$remark',
                              style: const TextStyle(color: Colors.white, fontSize: 14.0),
                              maxLines: 1,
                              overflow: TextOverflow.ellipsis,
                            ),
                          ),
                        ],
                      ),
                    ),
                    Container(
                      margin: const EdgeInsets.symmetric(horizontal: 10.0),
                      padding: const EdgeInsets.symmetric(vertical: 5.0),
                      width: double.infinity,
                      decoration: const BoxDecoration(border: Border(top: BorderSide(color: Colors.white30, width: .5))),
                      child: const Text(
                        '红包',
                        style: TextStyle(color: Colors.white70, fontSize: 11.0),
                      ),
                    ),
                  ],
                ),
              ),
              onTap: () {
                receiveRedPacketDialog(item);
              },
              onLongPress: () {
                contextMenuDialog();
              },
            ),
          ),
        ));
      }
      // 位置模板=7
      else if (item.elemType == 7) {
        msgtpl.add(RenderChatItem(
          data: item,
          child: Ink(
            decoration: BoxDecoration(
              color: const Color(0xFFFFFFFF),
              borderRadius: BorderRadius.circular(10.0),
            ),
            child: InkWell(
              // splashColor: Colors.transparent,
              overlayColor: WidgetStateProperty.all(Colors.transparent),
              borderRadius: BorderRadius.circular(10.0),
              child: Container(
                constraints: const BoxConstraints(
                  maxWidth: 210.0,
                ),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Container(
                      padding: const EdgeInsets.symmetric(
                        horizontal: 10.0,
                        vertical: 5.0,
                      ),
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: <Widget>[
                          Text(
                            item.locationElem?.desc ?? '位置信息异常',
                            overflow: TextOverflow.ellipsis,
                          ),
                          Text(
                            "${item.locationElem?.latitude},${item.locationElem?.longitude}",
                            style: const TextStyle(color: Colors.grey, fontSize: 12.0),
                            overflow: TextOverflow.ellipsis,
                          ),
                        ],
                      ),
                    ),
                    ClipRRect(
                      borderRadius: const BorderRadius.vertical(bottom: Radius.circular(10.0)),
                      child: Image.asset('assets/images/map.jpg', width: 210.0, height: 70.0, fit: BoxFit.cover),
                    )
                  ],
                ),
              ),
              onTap: () {
                MyDialog.toast('该功能暂未支持~', icon: Icon(Icons.warning), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
              },
              onLongPress: () {
                contextMenuDialog();
              },
            ),
          ),
        ));
      }
      // 群tips
      else if (item.elemType == 9) {
        msgtpl.add(
          Container(
            margin: const EdgeInsets.only(bottom: 15.0),
            width: double.infinity,
            child: Text(
              parseMessageSummary(item),
              style: TextStyle(color: Colors.grey[600], fontSize: 12.0),
              softWrap: true,
              maxLines: 5,
              overflow: TextOverflow.ellipsis,
              textAlign: TextAlign.center,
            ),
          ),
        );
      }
    }
    return msgtpl;
  }

  // 表情列表集合
  List<Widget> renderEmojWidget() {
    return [
      // Tab切换
      Container(
        padding: const EdgeInsets.symmetric(horizontal: 5.0),
        child: Row(
          children: emoJson.map((item) {
            return InkWell(
              child: Container(
                margin: const EdgeInsets.all(5.0),
                alignment: Alignment.center,
                height: 40.0,
                width: 40.0,
                decoration: BoxDecoration(color: item['selected'] ? Colors.white : Colors.transparent, borderRadius: BorderRadius.circular(5.0)),
                child: item['index'] == 0
                    ? Text(
                        item['pathLabel'],
                        style: const TextStyle(fontSize: 22.0),
                      )
                    : Image.asset(item['pathLabel'], height: 24.0, width: 24.0, fit: BoxFit.cover),
              ),
              onTap: () {
                handleEmojTab(item['index']);
              },
            );
          }).toList(),
        ),
      ),
      Expanded(
        child: Container(
          decoration: BoxDecoration(
            color: Colors.grey[200],
            border: const Border(top: BorderSide(color: Colors.black54, width: .1)),
          ),
          child: ListView(
            controller: emojController,
            padding: const EdgeInsets.all(10.0),
            children: emoJson.map((item) {
              return Visibility(
                visible: item['selected'],
                child: GridView(
                  shrinkWrap: true,
                  physics: const NeverScrollableScrollPhysics(),
                  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                    // 横轴元素个数
                    crossAxisCount: item['type'] == 'emoj' ? 8 : 5,
                    // 纵轴间距
                    mainAxisSpacing: 5.0,
                    // 横轴间距
                    crossAxisSpacing: 5.0,
                    // 子组件宽高比例
                    childAspectRatio: 1,
                  ),
                  children: item['nodes'].map<Widget>((emoj) {
                    if (item['type'] == 'emoj') {
                      return Material(
                        type: MaterialType.transparency,
                        child: InkWell(
                          borderRadius: BorderRadius.circular(5.0),
                          child: Container(
                            alignment: Alignment.center,
                            height: 40.0,
                            width: 40.0,
                            child: Text(
                              emoj,
                              style: const TextStyle(fontSize: 24.0),
                            ),
                          ),
                          onTap: () {
                            handleEmojClick(emoj);
                          },
                        ),
                      );
                    } else {
                      return Material(
                        type: MaterialType.transparency,
                        child: InkWell(
                          borderRadius: BorderRadius.circular(5.0),
                          child: Container(
                            alignment: Alignment.center,
                            padding: const EdgeInsets.all(5.0),
                            height: 68.0,
                            width: 68.0,
                            child: Image.asset(emoj),
                          ),
                          onTap: () {
                            handleGIFClick(emoj, item['index']);
                          },
                        ),
                      );
                    }
                  }).toList(),
                ),
              );
            }).toList(),
          ),
        ),
      ),
    ];
  }

  // 选择功能列表
  List<Widget> renderChooseWidget() {
    return [
      Expanded(
        child: Container(
          padding: const EdgeInsets.fromLTRB(30.0, 35.0, 30.0, 15.0),
          decoration: BoxDecoration(
            color: Colors.grey[200],
            border: const Border(top: BorderSide(color: Colors.black38, width: .1)),
          ),
          child: GridView(
            shrinkWrap: true,
            physics: const NeverScrollableScrollPhysics(),
            gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
              // 横轴元素个数
              crossAxisCount: 4,
              // 纵轴间距
              mainAxisSpacing: 30.0,
              // 横轴间距
              crossAxisSpacing: 25.0,
              // 子组件宽高比例
              childAspectRatio: .8,
            ),
            children: chooseOptions.map((item) {
              return Column(
                children: [
                  Expanded(
                    child: Material(
                      type: MaterialType.transparency,
                      child: Ink(
                        decoration: BoxDecoration(
                          color: Colors.white,
                          borderRadius: BorderRadius.circular(15.0),
                        ),
                        child: InkWell(
                          borderRadius: BorderRadius.circular(15.0),
                          child: Image.asset(item['icon'], height: 40.0, fit: BoxFit.cover),
                          onTap: () {
                            handleChooseAction(item['key']);
                          },
                        ),
                      ),
                    ),
                  ),
                  const SizedBox(height: 5.0),
                  Text(
                    item['name'],
                    style: const TextStyle(color: Colors.black87, fontSize: 12.0),
                  )
                ],
              );
            }).toList(),
          ),
        ),
      ),
    ];
  }

  /* ---------- { 聊天消息模块 } ---------- */
  // 聊天消息滚动到底部
  // void scrollToBottom() async {
  //   chatList = await fetchChatList();
  //   chatController.animateTo(isNeedScrollBottom ? 0 : chatController.position.maxScrollExtent,
  //       duration: const Duration(milliseconds: 200), curve: Curves.easeIn);
  // }

  // void scrollToBottom() {
  //   Future.delayed(Duration(milliseconds: 300), () {
  //     if (chatController.hasClients) {
  //       chatController.animateTo(
  //         // 0, // reverse: true 时滚动到底部是 offset: 0
  //         chatController.position.maxScrollExtent,
  //         duration: Duration(milliseconds: 300),
  //         curve: Curves.easeOut,
  //       );
  //     }
  //   });
  // }

  // 点击消息区域
  void handleClickChatArea() {
    hideKeyboard();
    setState(() {
      toolbarEnable = false;
    });
  }

  /* ---------- { 底部Toolbar模块 } ---------- */
  // 光标处插入内容
  void insertTextAtCursor(String html) {
    var editorNotifier = editorController.value; // The current value stored in this notifier.
    var start = editorNotifier.selection.baseOffset;
    var end = editorNotifier.selection.extentOffset;
    if (editorNotifier.selection.isValid) {
      String newText = '';
      if (editorNotifier.selection.isCollapsed) {
        if (end > 0) {
          newText += editorNotifier.text.substring(0, end);
        }
        newText += html;
        if (editorNotifier.text.length > end) {
          newText += editorNotifier.text.substring(end, editorNotifier.text.length);
        }
      } else {
        newText = editorNotifier.text.replaceRange(start, end, html);
        end = start;
      }
      editorController.value =
          editorNotifier.copyWith(text: newText, selection: editorNotifier.selection.copyWith(baseOffset: end + html.length, extentOffset: end + html.length));
    } else {
      editorController.value = TextEditingValue(
        text: html,
        selection: TextSelection.fromPosition(TextPosition(offset: html.length)),
      );
    }
  }

  // 发送消息队列
  Future<void> sendMessage(message) async {
    // 待插入的消息
    List<V2TimMessage> messagesToInsert = [];
    V2TimMessage? lastRealMsg;

    for (var msg in controller.chatList) {
      if (msg.localCustomData != 'time_label') {
        lastRealMsg = msg;
        break;
      }
    }

    // 如果有数据，检测时间，是否需要插入伪消息
    if (lastRealMsg != null &&
        needInsertTimeLabel(
          (lastRealMsg.timestamp ?? 0) * 1000, // 转为毫秒级
          DateTime.now().millisecondsSinceEpoch,
        )) {
      // 消息时间间隔超过3分钟插入伪消息
      final showLabel = Utils.formatChatTime(DateTime.now().millisecondsSinceEpoch ~/ 1000);
      final resMsg = await IMMessage().insertTimeLabel(showLabel, selfUserId);
      messagesToInsert.add(resMsg.data);
    } else {
      // 没数据的时候直接插入伪消息
      final showLabel = Utils.formatChatTime(DateTime.now().millisecondsSinceEpoch ~/ 1000);

      final resMsg = await IMMessage().insertTimeLabel(showLabel, selfUserId);
      messagesToInsert.add(resMsg.data);
    }

    // 不需要时间标签
    // 消息类型
    late final ImResult res;
    res = await IMMessage().sendMessage(
      msg: message,
      groupID: arguments.groupID,
      groupName: arguments.showName,
    );

    if (res.success && res.data != null) {
      messagesToInsert.insert(0, res.data); // 加入消息本体
      // messagesToInsert.add(res.data); // 加入消息本体

      controller.chatList.insertAll(0, messagesToInsert);
      // controller.chatList.addAll(messagesToInsert);

      controller.scrollToBottom();
      logger.w('发送成功');
    } else {
      logger.w('消息发送失败: ${res.code} - ${res.desc}');
    }
  }

  bool needInsertTimeLabel(int lastTimestamp, int newTimestamp, {int interval = 3 * 60}) {
    return (newTimestamp - lastTimestamp) > interval * 1000;
  }

  // 隐藏键盘
  void hideKeyboard() {
    if (editorFocusNode.hasFocus) {
      editorFocusNode.unfocus();
    }
  }

  // 表情/选择切换
  void handleEmojChooseState(index) {
    hideKeyboard();
    setState(() {
      toolbarEnable = true;
      toolbarIndex = index;
      voiceBtnEnable = false;
    });
    controller.scrollToBottom();
  }

  // 表情Tab切换
  void handleEmojTab(index) {
    var emols = emoJson;
    for (var i = 0, len = emols.length; i < len; i++) {
      emols[i]['selected'] = false;
    }
    emols[index]['selected'] = true;
    setState(() {
      emoJson = emols;
    });
    emojController.jumpTo(0);
  }

  // 点击表情插入到输入框
  void handleEmojClick(emoj) {
    insertTextAtCursor(emoj);
  }

  // 点击Gif大图发送=8
  void handleGIFClick(gifpath, index) async {
    // 消息队列
    // Map message = {
    //   'contentType': 8,
    //   'content': gifpath,
    // };
    final res = await IMMessage().createFaceMessage(data: gifpath, index: index);
    if (res.success) {
      sendMessage(res.data?.messageInfo);
    }
  }

  // 发送文本消息=1
  void handleSubmit() async {
    if (editorController.text.isEmpty) return;
    // 消息队列
    // Map message = {
    //   'contentType': 1,
    //   'content': editorController.text,
    // };
    final res = await IMMessage().createTextMessage(text: editorController.text);
    if (res.success) {
      sendMessage(res.data?.messageInfo);
      editorController.clear();
    }
  }

  // 发红包消息
  void sendHongbao(date) async {
    final totalAmount = date['amount']; //用户输入的金额
    final remark = date['remark']; //用户输入的留言
    final totalCount = date['maxNum']; //红包数量

    // 先检测可用余额

    final makeData = {
      "chatType": "2",
      "totalAmount": totalAmount,
      "totalCount": totalCount,
      "groupId": arguments.groupID,
      "remark": remark,
    };

    // 检测钱包余额
    final resB = await Http.get(CommonApi.userAccount);
    final resData = resB['data']['wallet']; // 余额
    if (resData == null) {
      // balance.value = double.parse(resData);
      MyToast().tip(title: '余额不足');
      return;
    }
    if (double.parse(totalAmount) > double.parse(resData)) {
      MyToast().tip(title: '余额不足');
      return;
    }
    final packRes = await Http.post(CommonApi.redpacket, data: makeData);
    logger.e('');
    logger.w('红包发送成功响应:$packRes');

    final makeJson = jsonEncode({
      "packetId": packRes['data']['packetId'], // 红包id
      // "totalAmount": totalAmount,
      "totalAmount": packRes['data']['totalAmount'], //总金额
      "remark": remark,
      "totalCount": packRes['data']['totalCount'], // 数量
      // "totalCount": totalCount, // 数量
      "open": false, // 默认未开启
      "ids": '', //默认为空，记录抢红包人的数据,用,隔开
    });

    logger.e('红包数据：$makeJson');
    logger.e('请求数据：$makeData');

    final res = await IMMessage().createCustomMessage(data: makeJson);
    if (res.success && (res.data != null)) {
      final custMsg = res.data!.messageInfo;
      custMsg!.cloudCustomData = SummaryType.hongbao;
      sendMessage(res.data!.messageInfo);
      Get.back();
    }
  }

  // 发送图片消息=3
  void sendImage(imgPath) async {
    final resImg = await IMMessage().createImageMessage(imagePath: imgPath);
    if (resImg.success) {
      sendMessage(resImg.data?.messageInfo);
    }
  }

  // 发送语音消息=4
  void sendVoiceMsg() async {
    final fileMap = await VoiceService().stopRecording();
    if (fileMap != null) {
      final res = await IMMessage().createSoundMessage(
        soundPath: fileMap['path'],
        duration: fileMap['duration'],
      );
      if (res.success && res.data != null) {
        sendMessage(res.data!.messageInfo);
      } else {
        MyDialog.toast('创建语音消息失败');
      }
    } else {
      MyDialog.toast('语音限制1-60秒');
    }
  }

  // 发送视频消息=5
  void sendVideo(videoFilePath, type, duration, snapshotPath) async {
    final instance = MyDialog.loading('处理中');

    final resImg = await IMMessage().createVideoMessage(
      videoFilePath: videoFilePath,
      type: type,
      duration: duration,
      snapshotPath: snapshotPath,
    );
    if (resImg.success) {
      await sendMessage(resImg.data?.messageInfo);
      instance.close();
    }
  }

  // 拍摄
  Future<void> showPicker(BuildContext context) async {
    showModalBottomSheet(
      context: context,
      backgroundColor: Colors.white,
      builder: (context) {
        return SafeArea(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              InkWell(
                onTap: () async {
                  Navigator.pop(context);
                  await _pickPhoto();
                },
                child: Padding(
                  padding: const EdgeInsets.symmetric(vertical: 16.0),
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: const [
                      Icon(Icons.photo),
                      SizedBox(width: 8),
                      Text("拍摄照片"),
                    ],
                  ),
                ),
              ),
              InkWell(
                onTap: () async {
                  Navigator.pop(context);
                  await _pickVideo();
                },
                child: Padding(
                  padding: const EdgeInsets.symmetric(vertical: 16.0),
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: const [
                      Icon(Icons.videocam),
                      SizedBox(width: 8),
                      Text("拍摄视频"),
                    ],
                  ),
                ),
              ),
              const Divider(height: 1),
              InkWell(
                onTap: () => Navigator.pop(context),
                child: Padding(
                  padding: const EdgeInsets.symmetric(vertical: 16.0),
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: const [
                      // Icon(Icons.close),
                      // SizedBox(width: 8),
                      Text("取消"),
                    ],
                  ),
                ),
              ),
            ],
          ),
        );
      },
    );
  }

  /// 拍照片
  Future<void> _pickPhoto() async {
    final hasPer = await Permissions.requestCameraPermission(title: '相机权限使用说明', content: '聊天时发送拍摄的照片');
    if (!hasPer) {
      Permissions.showPermissionDialog('相机');
      return;
    }
    final XFile? photo = await _picker.pickImage(
      source: ImageSource.camera,
      maxWidth: 1920,
      maxHeight: 1080,
      imageQuality: 90,
    );
    if (photo != null) {
      logger.w("照片路径: ${photo.path}");
      sendImage(photo.path);
    }
  }

  /// 拍视频
  Future<void> _pickVideo() async {
    final hasPer = await Permissions.requestCameraPermission(title: '相机权限使用说明', content: '聊天时发送拍摄的视频');
    if (!hasPer) {
      Permissions.showPermissionDialog('相机');
      return;
    }
    final XFile? video = await _picker.pickVideo(
      source: ImageSource.camera,
      maxDuration: const Duration(minutes: 1),
    );

    if (video == null) return;
    logger.w("视频路径: ${video.path}");
    // 获取首帧图
    final snapshot = await generateVideoThumbnail(video.path) ?? '';
    // 获取视频时长
    final controller = VideoPlayerController.file(File(video.path));
    await controller.initialize();
    final duration = controller.value.duration.inSeconds;
    await controller.dispose();
    // mimeType
    final mimeType = lookupMimeType(video.path) ?? 'video/mp4';
    sendVideo(video.path, mimeType, duration, snapshot);
  }

  // 底部操作蓝选择区操作
  void handleChooseAction(key) {
    // MyDialog.toast('$key');
    switch (key) {
      case 'photo':
        // ....
        pickFile(context);
        break;
      case 'camera':
        // ....
        showPicker(context);
        break;
      case 'redpacket':
        sendRedPacketDialog();
        break;
    }
  }

  ///从相册选取图片/视频
  void pickFile(BuildContext context) async {
    final pickedAssets = await AssetPicker.pickAssets(
      context,
      pickerConfig: AssetPickerConfig(
        textDelegate: const AssetPickerTextDelegate(),
        pathNameBuilder: (AssetPathEntity album) {
          return Utils.translateAlbumName(album);
        },
        maxAssets: 5,
        requestType: RequestType.common,
        filterOptions: FilterOptionGroup(
          imageOption: const FilterOption(),
          videoOption: const FilterOption(
            durationConstraint: DurationConstraint(
              max: Duration(seconds: 120),
            ),
          ),
        ),
      ),
    );

    if (pickedAssets != null && pickedAssets.isNotEmpty) {
      for (final asset in pickedAssets) {
        switch (asset.type) {
          case AssetType.image:
            print("选中了图片：${asset.title}");
            var file = await asset.file;
            if (file != null) {
              var fileSizeInBytes = await file.length();
              var sizeInMB = fileSizeInBytes / (1024 * 1024);
              if (sizeInMB > 28) {
                MyDialog.toast('图片大小不能超过28MB', icon: const Icon(Icons.check_circle), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
              } else {
                print("图片合法，大小：$sizeInMB MB");
                // 执行发送逻辑
                sendImage(file.path);
              }
            }

            break;
          case AssetType.video:
            print("选中了视频：${asset.title}");
            var file = await asset.file;
            if (file != null) {
              var fileSizeInBytes = await file.length();
              var sizeInMB = fileSizeInBytes / (1024 * 1024);
              if (sizeInMB > 28) {
                MyDialog.toast('图片大小不能超过28MB', icon: const Icon(Icons.check_circle), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
              } else {
                print("图片合法，大小：$sizeInMB MB");
                // 执行发送逻辑
                var snapshot = await generateVideoThumbnail(file.path);
                String? mimeType = await asset.mimeTypeAsync;
                String vdType = mimeType?.split('/').last ?? 'mp4';
                print(vdType);
                sendVideo(file.path, vdType, asset.duration, snapshot);
              }
            }
            break;
          default:
            print("不支持的类型：${asset.type}");
        }
      }
      // final asset = pickedAssets.first;
      // final file = await asset.file; // 获取实际文件
      // if (file != null) {
      //   final fileSizeInBytes = await file.length();
      //   final sizeInMB = fileSizeInBytes / (1024 * 1024);
      //   if (sizeInMB > 100) {
      //     MyDialog.toast('图片大小不能超过100MB', icon: const Icon(Icons.check_circle), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
      //   } else {
      //     print("图片合法，大小：$sizeInMB MB");
      //     //走upload(file)上传图片拿到url地址
      //     // file;
      //   }
      // }
    }
  }

  /* ---------- { 弹窗功能模块 } ---------- */
  // 红包弹窗
  void receiveRedPacketDialog(V2TimMessage data) {
    showDialog(
        context: context,
        builder: (context) {
          final obj = jsonDecode(data.customElem!.data!);
          final totalAmount = obj['totalAmount'];
          final remark = obj['remark']; // 默认未开启，总开关，直到消费完才改成true；
          final open = obj['open'] ?? false;
          logger.e('开了吗$open');
          final packetId = obj['packetId'];

          final selfId = Get.find<ImUserInfoController>().userID.value;
          final totalCount = obj['totalCount'] ?? '0'; // 红包数量
          String idsString = obj['ids'] ?? '';
          final ids = idsString.isEmpty ? <String>[] : idsString.split(',');
          // 自己是否抢过了
          final hasGet = ids.contains(selfId);

          return Material(
              type: MaterialType.transparency,
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Container(
                    width: double.infinity,
                    margin: const EdgeInsets.symmetric(horizontal: 50.0),
                    padding: const EdgeInsets.symmetric(vertical: 50.0, horizontal: 20.0),
                    decoration: const BoxDecoration(
                      color: Color(0xFFFF7F43),
                      borderRadius: BorderRadius.all(Radius.circular(12.0)),
                    ),
                    child: Column(
                      children: [
                        ClipRRect(
                          borderRadius: BorderRadius.circular(5.0),
                          child: NetworkOrAssetImage(
                            imageUrl: data.senderProfile?.faceUrl,
                          ),
                        ),
                        const SizedBox(
                          height: 5.0,
                        ),
                        Text(
                          '${data.senderProfile?.nickName}的红包',
                          style: const TextStyle(color: Color(0xFFFFF9C7), fontWeight: FontWeight.w600),
                        ),
                        SizedBox(height: 10),
                        Text(
                          totalAmount,
                          style: const TextStyle(color: Color(0xFFFFF9C7), fontWeight: FontWeight.w500, fontSize: 20.0),
                        ),
                        SizedBox(height: 20.0),
                        Text(
                          remark,
                          style: const TextStyle(color: Color(0xFFFFF9C7), fontWeight: FontWeight.w500, fontSize: 20.0),
                        ),
                        SizedBox(
                          height: 100.0,
                        ),
                        if (open == false) ...[
                          // 总开未结束，一定显示
                          if (hasGet == false)
                            // 自己没领过才显示
                            AnimatedBuilder(
                              animation: animTurns,
                              builder: (context, child) {
                                return Transform(
                                  transform: Matrix4.rotationY(animTurns.value),
                                  alignment: Alignment.center,
                                  child: FilledButton(
                                    style: ButtonStyle(
                                      backgroundColor: WidgetStateProperty.all(const Color(0xFFFFF9C7)),
                                      padding: WidgetStateProperty.all(EdgeInsets.zero),
                                      minimumSize: WidgetStateProperty.all(const Size(80.0, 80.0)),
                                      shape: WidgetStateProperty.all(const CircleBorder()),
                                      elevation: WidgetStateProperty.all(3.0),
                                    ),
                                    child: Text(
                                      '开',
                                      style: TextStyle(color: Color(0xFF3B3B3B), fontSize: 28.0),
                                    ),
                                    onPressed: () async {
                                      // 点击开红包，开始动画
                                      animController.repeat();
                                      // 执行抢红包结果查询，（群）展示抢红包人员信息，单不用管
                                      // 执行消费红包动作
                                      //--------
                                      // 成功后修改消息体
                                      final ctl = Get.find<ImUserInfoController>();
                                      logger.w(packetId);
                                      try {
                                        final res = await Http.post(CommonApi.getpacket, data: {"packetId": packetId, "memberId": ctl.userID.value});
                                        logger.e('开群红包$res');
                                        if (res != null) {
                                          // 是否还有可抢次数
                                          final canGetNum = totalCount - ids.length;
                                          if (canGetNum > 0) {
                                            // 还有可以抢的次数
                                            obj['totalCount'] = totalCount - 1;
                                          } else {
                                            // 消费完了
                                            obj['totalCount'] = 0;
                                            obj['open'] = true; //总开关标记为true
                                          }
                                          // 记录自己的id
                                          ids.add(selfId);
                                          obj['ids'] = ids.join(',');
                                          data.customElem!.data = jsonEncode(obj);
                                          // 修改消息体
                                          await ImService.instance.modifyMessage(message: data);
                                          // 模拟开红包逻辑，1 秒后停止动画
                                          Future.delayed(Duration(seconds: 1), () {
                                            animController.stop();
                                            animController.reset();
                                            Get.back();
                                            MyToast().tip(
                                              title: '恭喜！抢到了${res['data']['receiveAmount']}元',
                                              type: 'success',
                                              position: 'center',
                                            );
                                          });
                                        } else {
                                          // 接口报500红包被抢完，关闭红包弹窗
                                          obj['totalCount'] = 0;
                                          obj['open'] = true; //总开关标记为true
                                          data.customElem!.data = jsonEncode(obj);
                                          await ImService.instance.modifyMessage(message: data);

                                          Future.delayed(Duration(seconds: 1), () {
                                            animController.stop();
                                            animController.reset();
                                            Navigator.pop(context);
                                          });
                                        }
                                      } catch (e) {
                                        logger.e(e);
                                        logger.e('开红包失败');
                                        Get.back();
                                      }
                                    },
                                  ),
                                );
                              },
                            ),
                        ]
                      ],
                    ),
                  ),
                  GestureDetector(
                    child: Container(
                      margin: const EdgeInsets.only(top: 20.0),
                      height: 30.0,
                      width: 30.0,
                      decoration: BoxDecoration(
                        border: Border.all(color: Colors.white, width: 1.5),
                        borderRadius: BorderRadius.circular(50.0),
                      ),
                      child: const Icon(
                        Icons.close_outlined,
                        color: Colors.white,
                        size: 18.0,
                      ),
                    ),
                    onTap: () {
                      Navigator.of(context).pop();
                    },
                  )
                ],
              ));
        });
  }

  // 长按消息菜单
  void contextMenuDialog() {
    // showDialog(
    //   context: context,
    //   builder: (context) {
    //     return SimpleDialog(
    //       backgroundColor: Colors.white,
    //       surfaceTintColor: Colors.white,
    //       contentPadding: const EdgeInsets.symmetric(vertical: 5.0),
    //       shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.0)),
    //       children: [
    //         SimpleDialogOption(
    //           child: const Padding(padding: EdgeInsets.symmetric(vertical: 5.0), child: Text('复制')),
    //           onPressed: () {},
    //         ),
    //         SimpleDialogOption(
    //           child: const Padding(padding: EdgeInsets.symmetric(vertical: 5.0), child: Text('发送给朋友')),
    //           onPressed: () {},
    //         ),
    //         SimpleDialogOption(
    //           child: const Padding(padding: EdgeInsets.symmetric(vertical: 5.0), child: Text('收藏')),
    //           onPressed: () {},
    //         ),
    //         SimpleDialogOption(
    //           child: const Padding(padding: EdgeInsets.symmetric(vertical: 5.0), child: Text('删除')),
    //           onPressed: () {},
    //         ),
    //       ],
    //     );
    //   },
    // );
  }

  // 发群红包弹窗
  void sendRedPacketDialog() async {
    final max = await getGroupMaxNumber();
    if (max == -1) {
      MyToast().tip(title: '获取群资料失败');

      return;
    }

    showModalBottomSheet(
      backgroundColor: Colors.grey[50],
      shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(15.0))),
      showDragHandle: true,
      clipBehavior: Clip.hardEdge,
      isScrollControlled: true, // 屏幕最大高度
      constraints: BoxConstraints(
        maxHeight: MediaQuery.of(context).size.height - 180, // 自定义最大高度
      ),
      context: context,
      builder: (context) {
        return RedPacket(
          flag: true,
          maxNum: max,
          onSend: (date) {
            sendHongbao(date);
          },
        );
      },
    );
  }

  // 获取群资料
  Future<int> getGroupMaxNumber() async {
    final res = await ImService.instance.getGroupsInfo(groupIDList: [arguments.groupID!]);
    if (res.success && res.data != null) {
      V2TimGroupInfo info = res.data!.first.groupInfo ?? V2TimGroupInfo(groupID: '', groupType: GroupType.Work);
      if (info.groupID.isEmpty) {
        return -1;
      }
      return info.memberCount ?? -1;
    } else {
      return -1;
    }
  }

  Future<void> isInGroup() async {
    final res = await ImService.instance.getJoinedGroupList();
    if (res.success && res.data != null) {
      controller.toolFlag.value = res.data!.any((group) => group.groupID == arguments.groupID);
    } else {
      logger.e('获取数据失败：${res.desc}');
      // 禁止
      controller.toolFlag.value = false;
    }
    logger.w('当前是否可以使用工具栏：${controller.toolFlag.value}');
  }

  /* ---------- { 其它功能模块 } ---------- */

  @override
  Widget build(BuildContext context) {
    //editorFocusNode

    return Stack(
      fit: StackFit.expand,
      children: [
        // 页面主体(聊天消息区/底部操作区)
        Scaffold(
          backgroundColor: Colors.grey[200],
          resizeToAvoidBottomInset: true, // 启用键盘自动避让
          appBar: AppBar(
            centerTitle: true,
            backgroundColor: Colors.transparent,
            foregroundColor: Colors.white,
            leading: IconButton(
              icon: Icon(
                Icons.arrow_back_ios_rounded,
                size: 20.0,
              ),
              onPressed: () {
                Get.back();
              },
            ),
            titleSpacing: 1.0,
            title: Text(
              arguments.showName ?? '群聊',
              style: const TextStyle(fontSize: 18.0, fontFamily: 'Arial'),
              maxLines: 1,
              overflow: TextOverflow.ellipsis,
              softWrap: false,
            ),
            flexibleSpace: Container(
              decoration: const BoxDecoration(
                  gradient: LinearGradient(
                begin: Alignment.topLeft,
                end: Alignment.bottomRight,
                colors: [Color(0xFFBE4EFF), Color(0xFF1DFFC7)],
              )),
            ),
            actions: [
              Obx(
                () => controller.toolFlag.value
                    ? IconButton(
                        icon: const Icon(
                          Icons.menu,
                          color: Colors.white,
                        ),
                        onPressed: () async {
                          editorFocusNode.unfocus();
                          final ctl = Get.put(GroupDetailController(groupID: arguments.groupID ?? ''));
                          await ctl.init();
                          final groupName = await Get.to(() => Groupdetail());
                          if (groupName is String && groupName.isNotEmpty) {
                            setState(() {
                              arguments.showName = groupName;
                            });
                          }
                        },
                      )
                    : SizedBox(),
              ),
            ],
          ),
          body: Flex(
            direction: Axis.vertical,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              // 渲染聊天消息
              Expanded(
                child: GestureDetector(
                  behavior: HitTestBehavior.opaque,
                  onTap: handleClickChatArea,
                  child: LayoutBuilder(
                    builder: (context, constraints) {
                      return Obx(() {
                        final msgWidgets = renderChatList().reversed.toList();

                        return ListView(
                          controller: chatController,
                          reverse: true,
                          padding: const EdgeInsets.all(10.0),
                          children: [
                            ConstrainedBox(
                              constraints: BoxConstraints(
                                minHeight: constraints.maxHeight - 40,
                              ),
                              child: Column(
                                mainAxisSize: MainAxisSize.min,
                                children: msgWidgets,
                              ),
                            ),
                          ],
                        );
                      });
                    },
                  ),
                ),
              ),

              // 底部操作栏
              Container(
                color: Colors.grey[100],
                child: SafeArea(
                  bottom: true,
                  child: Container(
                    decoration: BoxDecoration(
                      color: Colors.grey[100],
                      border: const Border(top: BorderSide(color: Colors.black38, width: .1)),
                    ),
                    child: Column(
                      children: [
                        // 输入框编辑器模块
                        Container(
                          padding: const EdgeInsets.all(10.0),
                          child: Row(
                            children: [
                              // 语音按钮
                              InkWell(
                                child: Icon(
                                  voiceBtnEnable ? Icons.keyboard_outlined : Icons.contactless_outlined,
                                  color: const Color(0xFF3B3B3B),
                                  size: 30.0,
                                ),
                                onTap: () {
                                  logger.w('点击了语音按钮');
                                  if (controller.toolFlag.value) {
                                    setState(() {
                                      toolbarEnable = false;
                                      if (voiceBtnEnable) {
                                        voiceBtnEnable = false;
                                        editorFocusNode.requestFocus();
                                        // editorFocusNode.unfocus();
                                      } else {
                                        voiceBtnEnable = true;
                                        editorFocusNode.unfocus();
                                      }
                                    });
                                  } else {
                                    logger.e('退出群聊无法使用');
                                  }
                                },
                              ),
                              const SizedBox(
                                width: 10.0,
                              ),
                              Expanded(
                                child: Container(
                                  constraints: const BoxConstraints(minHeight: 40.0),
                                  decoration: BoxDecoration(
                                    color: Colors.white,
                                    borderRadius: BorderRadius.circular(5),
                                  ),
                                  child: Stack(
                                    children: [
                                      // 输入框
                                      Offstage(
                                        offstage: voiceBtnEnable,
                                        child: Obx(
                                          () => TextField(
                                            enabled: controller.toolFlag.value, //true=禁止
                                            decoration: InputDecoration(
                                              hintText: controller.toolFlag.value ? null : "你已退出群聊",
                                              isDense: true,
                                              hoverColor: Colors.transparent,
                                              contentPadding: EdgeInsets.all(8.0),
                                              border: OutlineInputBorder(borderSide: BorderSide.none),
                                            ),
                                            style: const TextStyle(
                                              fontSize: 16.0,
                                            ),
                                            maxLines: null,
                                            controller: editorController,
                                            focusNode: editorFocusNode,
                                            cursorColor: const Color(0xFF07C160),
                                            onChanged: (value) {},
                                          ),
                                        ),
                                      ),
                                      // 语音
                                      Offstage(
                                        offstage: !voiceBtnEnable,
                                        child: GestureDetector(
                                          child: Container(
                                            decoration: BoxDecoration(
                                              color: Colors.white,
                                              borderRadius: BorderRadius.circular(5),
                                            ),
                                            alignment: Alignment.center,
                                            height: 40.0,
                                            width: double.infinity,
                                            child: Text(
                                              voiceTypeMap[voiceType],
                                              style: const TextStyle(fontSize: 15.0),
                                            ),
                                          ),
                                          onPanStart: (details) async {
                                            // 开始录音
                                            final hasPer = await Permission.microphone.status;
                                            logger.w('开始录音:${hasPer.toString()}');

                                            if (hasPer.isGranted) {
                                              // 有权限直接开始
                                              setState(() {
                                                hasMicPermission = true;
                                              });
                                              final res = await VoiceService().startRecording();
                                              if (res) {
                                                setState(() {
                                                  voiceType = 1;
                                                  voicePanelEnable = true;
                                                });
                                              } else {
                                                MyDialog.toast('未获得麦克风权限');
                                              }
                                            } else {
                                              // 去获取权限
                                              hasMicPermission = false;
                                              final granted = await Permissions.requestMicrophonePermission(
                                                title: '麦克风权限使用说明',
                                                content: '聊天时发送语音消息',
                                              );
                                              if (!granted) {
                                                Permissions.showPermissionDialog('麦克风');
                                              }
                                              return;
                                            }
                                          },
                                          onPanUpdate: (details) {
                                            if (!hasMicPermission) return;

                                            Offset pos = details.globalPosition;
                                            double swipeY = MediaQuery.of(context).size.height - 120;
                                            double swipeX = MediaQuery.of(context).size.width / 2 + 50;
                                            setState(() {
                                              if (pos.dy >= swipeY) {
                                                voiceType = 1; // 松开发送
                                              } else if (pos.dy < swipeY && pos.dx < swipeX) {
                                                voiceType = 2; // 左滑松开取消
                                              } else if (pos.dy < swipeY && pos.dx >= swipeX) {
                                                voiceType = 3; // 右滑语音转文字
                                              }
                                            });
                                          },
                                          onPanEnd: (details) {
                                            // print('停止录音');
                                            if (!hasMicPermission) return;

                                            setState(() {
                                              switch (voiceType) {
                                                case 1:
                                                  // MyDialog.toast('发送录音文件');
                                                  sendVoiceMsg();
                                                  voicePanelEnable = false;
                                                  break;
                                                case 2:
                                                  // MyDialog.toast('取消发送');
                                                  VoiceService().cancelRecording;
                                                  voicePanelEnable = false;
                                                  break;
                                                case 3:
                                                  MyDialog.toast('语音转文字');
                                                  voicePanelEnable = true;
                                                  voiceToTransfer = true;
                                                  break;
                                              }
                                              voiceType = 0;
                                            });
                                          },
                                        ),
                                      ),
                                    ],
                                  ),
                                ),
                              ),
                              const SizedBox(
                                width: 10.0,
                              ),
                              // 表情
                              InkWell(
                                child: const Icon(
                                  Icons.add_reaction_rounded,
                                  color: Color(0xFF3B3B3B),
                                  size: 30.0,
                                ),
                                onTap: () {
                                  if (controller.toolFlag.value) {
                                    handleEmojChooseState(0);
                                  } else {
                                    logger.w('退出群聊禁止使用');
                                  }
                                },
                              ),
                              const SizedBox(
                                width: 8.0,
                              ),
                              // +号
                              InkWell(
                                child: const Icon(
                                  Icons.add,
                                  color: Color(0xFF3B3B3B),
                                  size: 30.0,
                                ),
                                onTap: () {
                                  if (controller.toolFlag.value) {
                                    handleEmojChooseState(1);
                                  } else {
                                    logger.w('退出群聊禁止使用');
                                  }
                                },
                              ),
                              const SizedBox(
                                width: 8.0,
                              ),
                              // 发送按钮
                              InkWell(
                                child: Container(
                                  height: 25.0,
                                  width: 25.0,
                                  decoration: BoxDecoration(
                                    color: const Color(0xFF07C160),
                                    borderRadius: BorderRadius.circular(20.0),
                                  ),
                                  child: const Icon(
                                    Icons.arrow_upward,
                                    color: Colors.white,
                                    size: 20.0,
                                  ),
                                ),
                                onTap: () {
                                  if (controller.toolFlag.value) {
                                    handleSubmit();
                                  } else {
                                    logger.w('退出群聊禁止使用');
                                  }
                                },
                              ),
                            ],
                          ),
                        ),

                        // 表情+选择模块
                        Visibility(
                          visible: toolbarEnable,
                          child: SizedBox(
                            height: keyboardHeight,
                            child: Column(
                              children: toolbarIndex == 0 ? renderEmojWidget() : renderChooseWidget(),
                            ),
                          ),
                        )
                      ],
                    ),
                  ),
                ),
              )
            ],
          ),
        ),
        // 录音主体(按住说话/松开取消/语音转文本)
        IgnorePointer(
          ignoring: false,
          child: Visibility(
            visible: voicePanelEnable,
            child: Material(
              color: const Color(0xDD1B1B1B),
              child: Stack(
                children: [
                  // 取消发送+语音转文字
                  Positioned(
                    bottom: 160,
                    left: 30,
                    right: 30,
                    child: Visibility(
                      visible: !voiceToTransfer,
                      child: Column(
                        // crossAxisAlignment: voiceType == 2 ? CrossAxisAlignment.start : CrossAxisAlignment.center,
                        crossAxisAlignment: CrossAxisAlignment.center,
                        children: [
                          // 语音动画层
                          Stack(
                            alignment: Alignment.bottomCenter,
                            children: [
                              AnimatedContainer(
                                duration: Duration(milliseconds: 200),
                                height: 70.0,
                                // width: voiceType == 2 ? 70.0 : 200.0,
                                width: 200.0,
                                decoration: BoxDecoration(
                                  color: voiceType == 2 ? Colors.red : Color(0xFF89E45B),
                                  borderRadius: BorderRadius.circular(15.0),
                                ),
                                clipBehavior: Clip.antiAlias,
                                child: Row(
                                  mainAxisAlignment: MainAxisAlignment.center,
                                  children: [
                                    Image.asset('assets/images/voice_waves.gif', height: 23.0, width: voiceType == 2 ? 30.0 : 70.0, fit: BoxFit.cover)
                                  ],
                                ),
                              ),
                              RotatedBox(
                                quarterTurns: 0,
                                child: CustomPaint(painter: ArrowShape(arrowColor: voiceType == 2 ? Colors.red : Color(0xFF89E45B), arrowSize: 10.0)),
                              )
                            ],
                          ),
                          const SizedBox(
                            height: 50.0,
                          ),
                          // 操作项
                          Row(
                            mainAxisAlignment: MainAxisAlignment.center,
                            children: [
                              // 取消发送
                              Container(
                                height: 60.0,
                                width: 60.0,
                                decoration: BoxDecoration(
                                  borderRadius: BorderRadius.circular(50.0),
                                  color: voiceType == 2 ? Colors.red : Colors.black38,
                                ),
                                child: Icon(
                                  Icons.close,
                                  color: Colors.white54,
                                ),
                              ),
                              // 语音转文字
                              // Container(
                              //   height: 60.0,
                              //   width: 60.0,
                              //   decoration: BoxDecoration(
                              //     borderRadius: BorderRadius.circular(50.0),
                              //     color: voiceType == 3 ? Color(0xFF89E45B) : Colors.black38,
                              //   ),
                              //   child: Icon(
                              //     Icons.translate,
                              //     color: Colors.white54,
                              //   ),
                              // ),
                            ],
                          ),
                        ],
                      ),
                    ),
                  ),
                  // 语音转文字(识别结果状态)
                  Positioned(
                    bottom: 120,
                    left: 30,
                    right: 30,
                    child: Visibility(
                      visible: voiceToTransfer,
                      child: Column(
                        children: [
                          // 提示结果
                          Stack(
                            children: [
                              Container(
                                height: 100.0,
                                decoration: BoxDecoration(
                                  color: Colors.red,
                                  borderRadius: BorderRadius.circular(15.0),
                                ),
                                child: const Row(
                                  mainAxisAlignment: MainAxisAlignment.center,
                                  children: [
                                    Icon(
                                      Icons.info_outlined,
                                      color: Colors.white,
                                    ),
                                    Text(
                                      '未识别到文字。',
                                      style: TextStyle(color: Colors.white),
                                    ),
                                  ],
                                ),
                              ),
                              Positioned(
                                  right: 35.0,
                                  bottom: 1,
                                  child: RotatedBox(
                                    quarterTurns: 0,
                                    child: CustomPaint(painter: ArrowShape(arrowColor: Colors.red, arrowSize: 10.0)),
                                  )),
                            ],
                          ),
                          const SizedBox(
                            height: 50.0,
                          ),
                          // 操作项
                          Row(
                            mainAxisAlignment: MainAxisAlignment.center,
                            children: [
                              GestureDetector(
                                child: Container(
                                  height: 60.0,
                                  width: 60.0,
                                  decoration: const BoxDecoration(
                                    color: Colors.transparent,
                                  ),
                                  child: const Column(
                                    mainAxisAlignment: MainAxisAlignment.center,
                                    children: [
                                      Icon(
                                        Icons.undo,
                                        color: Colors.white54,
                                      ),
                                      Text(
                                        '取消',
                                        style: TextStyle(color: Colors.white70),
                                      )
                                    ],
                                  ),
                                ),
                                onTap: () {
                                  setState(() {
                                    voicePanelEnable = false;
                                    voiceToTransfer = false;
                                  });
                                },
                              ),
                              GestureDetector(
                                child: Container(
                                  height: 60.0,
                                  width: 100.0,
                                  decoration: const BoxDecoration(
                                    color: Colors.transparent,
                                  ),
                                  child: const Column(
                                    mainAxisAlignment: MainAxisAlignment.center,
                                    children: [
                                      Icon(
                                        Icons.graphic_eq_rounded,
                                        color: Colors.white54,
                                      ),
                                      Text(
                                        '发送原语音',
                                        style: TextStyle(color: Colors.white70),
                                      )
                                    ],
                                  ),
                                ),
                                onTap: () {},
                              ),
                              GestureDetector(
                                child: Container(
                                  height: 60.0,
                                  width: 60.0,
                                  decoration: BoxDecoration(
                                    borderRadius: BorderRadius.circular(50.0),
                                    color: Colors.white12,
                                  ),
                                  child: const Icon(
                                    Icons.check,
                                    color: Colors.white12,
                                  ),
                                ),
                                onTap: () {},
                              ),
                            ],
                          ),
                        ],
                      ),
                    ),
                  ),
                  // 提示文字(操作状态)
                  Positioned(
                    bottom: 120,
                    left: 0,
                    width: MediaQuery.of(context).size.width,
                    child: Visibility(
                      visible: !voiceToTransfer,
                      child: Align(
                        child: Obx(() => Text(
                              controller.toolFlag.value ? voiceTypeMap[voiceType] : '无法在已退出的群聊发送消息',
                              style: const TextStyle(color: Colors.white70),
                            )),
                      ),
                    ),
                  ),
                  // 背景
                  Align(
                    alignment: Alignment.bottomCenter,
                    child: Visibility(
                      visible: !voiceToTransfer,
                      child: Image.asset(
                        'assets/images/voice_bg.webp',
                        width: double.infinity,
                        height: 100.0,
                        fit: BoxFit.fill,
                      ),
                    ),
                  ),
                  // 背景图标
                  Positioned(
                    bottom: 25,
                    left: 0,
                    width: MediaQuery.of(context).size.width,
                    child: Visibility(
                      visible: !voiceToTransfer,
                      child: const Align(
                        child: Icon(
                          Icons.graphic_eq_rounded,
                          color: Colors.black54,
                        ),
                      ),
                    ),
                  ),
                ],
              ),
            ),
          ),
        )
      ],
    );
  }
}

// 渲染聊天消息公共部分
class RenderChatItem extends StatelessWidget {
  const RenderChatItem({
    super.key,
    required this.data,
    required this.child,
  });
  final V2TimMessage data; // 消息数据
  final Widget? child; // 消息体

  // 设置箭头颜色
  // Color arrowColor(data) {
  //   Color color = Colors.transparent;
  //   if ([8].contains(data.elemType)) {
  //     // 红包箭头颜色
  //     color = const Color(0xFFFFA52F);
  //   } else if ([9].contains(data.elemType)) {
  //     // 位置箭头颜色
  //     color = const Color(0xFFFFFFFF);
  //   } else {
  //     color = !data['isme'] ? const Color(0xFFFFFFFF) : const Color(0xFF9543FF);
  //   }
  //   return color;
  // }

  @override
  Widget build(BuildContext context) {
    String? displayName = (data.friendRemark?.isNotEmpty ?? false)
        ? data.friendRemark
        : (data.nameCard?.isNotEmpty ?? false)
            ? data.nameCard
            : (data.nickName?.isNotEmpty ?? false)
                ? data.nickName
                : '未知昵称';
    return Container(
      margin: const EdgeInsets.only(bottom: 10.0),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          !(data.isSelf ?? false)
              ? GestureDetector(
                  onTap: () {
                    // 头像点击事件
                    logger.e("点击了头像");
                    Get.toNamed('/vloger', arguments: {'memberId': data.sender});
                  },
                  child: SizedBox(
                    height: 35.0,
                    width: 35.0,
                    child: ClipRRect(
                      borderRadius: const BorderRadius.all(Radius.circular(20.0)),
                      child: NetworkOrAssetImage(imageUrl: data.faceUrl),
                    ),
                  ),
                )
              : const SizedBox.shrink(),
          Expanded(
            child: Padding(
              padding: !(data.isSelf ?? false) ? const EdgeInsets.only(left: 10.0, right: 40.0) : const EdgeInsets.only(left: 40.0, right: 10.0),
              child: Column(
                crossAxisAlignment: !(data.isSelf ?? false) ? CrossAxisAlignment.start : CrossAxisAlignment.end,
                children: [
                  Text(
                    displayName ?? '未知昵称',
                    style: const TextStyle(color: Colors.grey, fontSize: 12.0),
                  ),
                  const SizedBox(
                    height: 3.0,
                  ),
                  Stack(
                    children: [
                      // 气泡箭头
                      /* Visibility(
                        // 显示箭头(消息+语音+红包+位置)
                        visible: [3, 7, 8, 9].contains(data['contentType']),
                        child: Positioned(
                          left: !data['isme'] ? 1 : null,
                          right: data['isme'] ? 1 : null,
                          top: 20.0,
                          child: RotatedBox(
                            quarterTurns: !data['isme'] ? 1 : -1,
                            child: CustomPaint(painter: ArrowShape(arrowColor: arrowColor(data))),
                          )
                        ),
                      ), */
                      Container(
                        child: child,
                      ),
                    ],
                  ),
                ],
              ),
            ),
          ),
          data.isSelf ?? false
              ? SizedBox(
                  height: 35.0,
                  width: 35.0,
                  child: ClipRRect(
                    borderRadius: const BorderRadius.all(Radius.circular(20.0)),
                    child: NetworkOrAssetImage(imageUrl: data.faceUrl),
                  ),
                )
              : const SizedBox.shrink(),
        ],
      ),
    );
  }
}

// 绘制气泡箭头
class ArrowShape extends CustomPainter {
  ArrowShape({
    required this.arrowColor,
    this.arrowSize = 7,
  });

  final Color arrowColor; // 箭头颜色
  final double arrowSize; // 箭头大小

  @override
  void paint(Canvas canvas, Size size) {
    var paint = Paint()..color = arrowColor;

    var path = Path();
    path.lineTo(-arrowSize, 0);
    path.lineTo(0, arrowSize);
    path.lineTo(arrowSize, 0);
    canvas.drawPath(path, paint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return false;
  }
}
