Flutter高仿微信-第44篇-群聊

news2025/7/15 22:54:24

Flutter高仿微信系列共59篇,从Flutter客户端、Kotlin客户端、Web服务器、数据库表结构、Xmpp即时通讯服务器、视频通话服务器、腾讯云服务器全面讲解。

 详情请查看

效果图:

实现代码:

group_chat_main.dart

/**
 * Author : wangning
 * Email : maoning20080809@163.com
 * Date : 2022/11/6 19:42
 * Description : 群聊
 */

class GroupChatMain extends StatefulWidget {

  String toGroupId;
  String account = SpUtils.getString(CommonUtils.LOGIN_ACCOUNT);

  GroupChatMain({required this.toGroupId});

  @override
  _GroupChatMainState createState() => _GroupChatMainState(toGroupId);
}

class _GroupChatMainState extends State<GroupChatMain> with TickerProviderStateMixin {

  String _toGroupId;
  _GroupChatMainState(this._toGroupId);

  //我的账户
  UserBean? _meUserBean;

  GroupBean? _groupBean;

  List<String> addTimeList = [];
  List<GroupChatBean> items = [];
  ScrollController _controller = ScrollController();
  var chatEvent;
  var baseEvent;
  //每页13条
  static const PAGE_SIZE = 13;
  //当前页
  var PAGE_NUM = 1;
  //从那一条开始(为保证最新的先显示, 先查询最后的,并且不能用desc查询)
  var startNum = 0;
  //总共多少条
  var CHAT_TOTAL = 0;

  //群成员个数
  int? memberCount = 0;

  @override
  void initState() {
    super.initState();
    AppManager.getInstance().toGroupId = _toGroupId;
    _checkAvailable();
    _updateChatStatus();

    baseEvent = eventBus.on<BaseEvent>((baseBean) {
      //刷新群聊记录
      if(baseEvent != null && baseBean.type == BaseEvent.TYPE_REFRESH_GROUP){
        loadAllChat();
        loadGroupBean();
      }
    });

    chatEvent = eventBus.on<GroupChatBean>((groupChatBean) {
      LogUtils.d('群聊发送消息接收:${groupChatBean.runtimeType}');
      setState(() {
        groupChatBean as GroupChatBean;
        items.add(groupChatBean);
        setState(() {

        });
      });
    });

    loadGroupBean();
    loadGroupUserCount();
    loadUserBean();
    loadAllChat();

    jumpToBottom(400);
    // 监听滚动事件
    _controller.addListener((){
      if(_controller.position.pixels>_controller.position.maxScrollExtent-40){
      }
    });

  }

  //加载群成员个数
  void loadGroupUserCount() async{
    memberCount = await GroupUserRepository.getInstance().findGroupUserCountByGroupId(widget.toGroupId);
    LogUtils.d("加载群成员个数 groupId = ${widget.toGroupId} ,  count = ${memberCount}");
    /*setState(() {
    });*/
  }

  @override
  void dispose() {
    super.dispose();
    eventBus.off(chatEvent);
    eventBus.off(baseEvent);
    AppManager.getInstance().toGroupId = "";
  }

  final controller = TextEditingController();
  FocusNode _chatContentFocus = FocusNode();

  @override
  Widget build(BuildContext context) {
    if(!isLoadMore){
      //每次发送消息滚动到底部0.1秒
      jumpToBottom(100);
    }

    return Scaffold(
      appBar: WnAppBar.getAppBar(context, Text("${_groupBean?.groupName}(${memberCount})"), addGroup : true, addGroupClick: (){
            Navigator.pushNamed(context, Routes.show_group_member, arguments: {
              "groupId":widget.toGroupId  //参数map
            });
       }),
      bottomNavigationBar: Text(""),//占用底部位置
      body: GestureDetector(
        onTap: (){
          _processClickBlank();
        },
        child: Container(
          child: Column(
            children: <Widget>[
              Expanded(
                child: RefreshIndicator(
                    displacement: 2,
                    onRefresh: _onRefresh,
                    child: ListView.builder(
                      controller: _controller,
                      itemBuilder: (BuildContext context, int index) {
                        return GroupChatContentView(items: items,account: widget.account, groupChatBean: items[index], index: index, meUserBean: _meUserBean,addTimeList: addTimeList,
                          otherAccount: items[index].account, deleteCallback: (data){
                            setState(() {
                              items.remove(items[index]);
                            });
                          },clickVoiceCallback: (data){
                            //点击播放录音,暂停后再播放
                            for(int i = 0; i < items.length; i++){
                              if(i == index){
                                items[i].isPlayVoice = true;
                              } else {
                                items[i].isPlayVoice = false;
                              }
                            }
                            setState(() {

                            });
                          }, refreshTransfer: (position){
                            LogUtils.d("回调刷新:${position}");

                          },);
                      },
                      itemCount: items.length,

                    )
                ),
              ),
              Divider(height: 12.0, color: Color(0xFFF7F8F8),),
              Container(
                padding: EdgeInsets.only(top: 5.0, bottom: 5.0, right: 2.0, left: 2.0),
                color: Color(0xFFF3F3F3),
                width: double.infinity,
                child: Row(

                  children: <Widget>[
                    Container(
                      margin: EdgeInsets.symmetric(horizontal: 2.0),
                      child: IconButton(
                        //按下语音说活
                          icon: isPressVoice ? Image.asset("assets/chat/button_keyboard.png"):Image.asset("assets/chat/button_voice.png"),
                          onPressed: () =>{
                            _processPressVoice()
                          }
                      ), //触发发送消息事件执行的函数_handleSubmitted
                    ),
                    Expanded(
                      child: Stack(
                        children: [
                          Offstage(
                            offstage: !isPressVoice,
                            child: ChatVoiceView(
                              refreshMediaCallback: (type, mediaURL, thumbnailFileName, second, messageId){
                                _refreshMedia(type, mediaURL, thumbnailFileName, mediaSecond: second, messageId: messageId);
                              },
                              sendMedialCallback: (type, mediaURL, second, messageId){
                                _sendMedia(type, mediaURL, mediaSecond: second, messageId: messageId);
                              },
                              stopPlayVoiceCallback: (){
                                hidePlayVoiceList();
                              },
                            ),
                          ),
                          Offstage(
                            offstage: isPressVoice,
                            child: Container(
                              padding: EdgeInsets.only(top: 8.0, bottom: 8.0, left: 8.0),
                              decoration: BoxDecoration(borderRadius: BorderRadius.all(Radius.circular(5.0),),color: Colors.white),
                              child: TextField(
                                controller: controller,
                                focusNode: _chatContentFocus,
                                decoration: InputDecoration.collapsed(hintText: null),
                                autocorrect: true,
                                //是否自动更正
                                autofocus: false,
                                maxLines: 5,
                                minLines: 1,
                                textAlign: TextAlign.start,
                                style: TextStyle(color: Colors.black, fontSize: 20),
                                cursorColor: Colors.green,
                                onTap: (){
                                  //点击编辑框
                                  jumpToBottom(400);
                                  hideEmoji = true;
                                  hideAdd = true;
                                  setState(() {
                                  });
                                },
                                onChanged: (value){
                                  //录入文字
                                  setState(() {
                                    if(value.length>0){
                                      hideSend = false;
                                      hideAddIcon = true;
                                    } else {
                                      hideSend = true;
                                      hideAddIcon = false;
                                    }
                                  });
                                },
                                onSubmitted: _handleSubmitted,
                                enabled: true, //是否禁用
                              ),
                            ),
                          ),
                        ],
                      ),

                    ),

                    Container(
                      child: IconButton(
                          icon: Image.asset("assets/chat/button_emoji.png"),
                          onPressed: () => _processEmoji()),
                    ),


                    Offstage(
                      offstage: hideAddIcon,
                      child: Container(
                        //margin: EdgeInsets.only(right: 4.0),
                        child: IconButton(
                          //添加按钮
                            icon: Image.asset("assets/chat/button_add.png"),
                            onPressed: () => {
                              _processAdd()
                            }
                        ),
                      ),
                    ),

                    Offstage(
                      offstage: hideSend,
                      child: Container(
                        margin: EdgeInsets.symmetric(horizontal: 4.0),
                        child: IconButton(
                          //发送按钮
                            icon: new Icon(Icons.send), //发送按钮图标
                            onPressed: () => _handleSubmitted(
                                controller.text)), //触发发送消息事件执行的函数_handleSubmitted
                      ),
                    ),
                  ],
                ),
              ),

              Offstage(
                offstage: hideAdd,
                child: ChatAddView(
                  viewType: CommonUtils.VIEW_TYPE_GROUP_CHAT,
                  toChatId: widget.toGroupId,
                  refreshMediaCallback: (type, mediaURL, thumbnailFileName, second, messageId){
                    _refreshMedia(type, mediaURL, thumbnailFileName, mediaSecond: second , messageId: messageId);
                  },
                  sendMedialCallback: (type, mediaURL, second, messageId){
                    _sendMedia(type, mediaURL, mediaSecond: second, messageId: messageId);
                  },
                  refreshRedpacketAndTransfer: (type, text){

                  },
                ),
              ),

              Offstage(
                offstage: hideEmoji,
                child: getEmojiWidget(),
              ),

            ],
          ),
        ),
      ),
    );
  }

  //进入聊天页面,把聊天状态更新为已读
  void _updateChatStatus() async{
    int newMessageCount = await ChatRepository.getInstance().getAllChatUnReadByAccount(_toGroupId)??0;
    if(newMessageCount >= 0){
      await ChatRepository.getInstance().updateChatReadByAccount(_toGroupId);
      Map<String, Object> result = HashMap<String, Object>();
      result["from_account"] = _toGroupId;
      eventBus.emit(BaseEvent(BaseEvent.TYPE_UPDATE_CHAT_STATUS, result: result));
    }

  }

  // 下拉刷新
  Future<void> _onRefresh() async{
    // 持续两秒
    await Future.delayed(Duration(milliseconds:20),(){
      //LogUtils.d("下拉刷新--开始 ${startNum} , ${PAGE_SIZE} , ${CHAT_TOTAL}");

      if(startNum >= PAGE_SIZE){
        startNum = CHAT_TOTAL - PAGE_SIZE * PAGE_NUM;
        _loadMoreData(widget.account, widget.toGroupId, startNum, PAGE_SIZE);
      } else if(startNum > 0 && startNum < PAGE_SIZE){
        //不够1页数据,查询全部,然后就不能下一页
        _loadMoreData(widget.account, widget.toGroupId, 0, startNum);
        startNum = 0;
      }

    });
  }

  bool isLoadMore = false;
  //上拉加载更多数据
  void _loadMoreData(String fromAccount, String toAccount, int sNum , int pageSize){

  }

  //检查状态, 如果不可以,先登录
  void _checkAvailable() async{
    var isAvailable = await XmppManager.getInstance().isAvailable();
    if(!isAvailable){
      String account = SpUtils.getString(CommonUtils.LOGIN_ACCOUNT);
      String password = SpUtils.getString(CommonUtils.LOGIN_PASSWORD);
      XmppManager.getInstance().connect(account, password);
    }
  }

  void loadAllChat() async {
    CHAT_TOTAL = await GroupChatRepository.getInstance().findGroupChatCountByGroupId(widget.toGroupId)??0;
    startNum = CHAT_TOTAL - PAGE_SIZE * PAGE_NUM;
    GroupChatRepository.getInstance().findAllGroupChatByGroupIdPage(widget.toGroupId, startNum, CHAT_TOTAL).then((chatList) {
      if(startNum > 0){
        PAGE_NUM++;
      }
      setState(() {
        items = chatList??[];
        items.forEach((element) {
          LogUtils.d("群aa聊:${element.toJson()}");
        });
      });
    });
  }

  void loadUserBean() async {
    UserRepository.getInstance().findUserByAccount(widget.account).then((userBean) {
      //setState(() {
        _meUserBean = userBean!;
      //});
    });
  }

  //加载群信息
  void loadGroupBean() async {
    _groupBean = await GroupRepository.getInstance().findGroupByGroupId(widget.toGroupId);
    //setState(() {
    //});
  }

  _sendMessage(var message){
    int id = DateTime.now().millisecondsSinceEpoch;
    String toJid = "${widget.toGroupId}@conference.wangning";
    XmppManager.getInstance().sendGroupMessageWithType(toJid, message, "${id}", id);

    Map<String, Object> result = HashMap<String, Object>();
    eventBus.emit(BaseEvent(BaseEvent.TYPE_NEW_MESSAGE, result: result));
  }

  //默认滚动到底部
  void jumpToBottom(int milliseconds){
    //LogUtils.d("滚动到底部");
    //第一次进入,滚动到底部要0.4秒
    if (items.length > 0) {
      Timer(Duration(milliseconds: milliseconds),
              () => _controller.jumpTo(_controller.position.maxScrollExtent));
    }
  }

  //隐藏播放列表,停止播放录音
  hidePlayVoiceList(){
    for(int i = 0; i < items.length;i++){
      items[i].isPlayVoice = false;
    }
    AudioPlayer.getInstance().stop();
    setState(() {
    });
  }

  //刷新多媒体(图片、语音、小视频) (先刷新本地,然后小视频压缩完成再慢慢发送)
  void _refreshMedia(int type, String mediaURL, String thumbnailFileName, {int mediaSecond=0, String messageId = "" }) async {

    bool isNetwork = await CommonNetwork.isNetwork();
    if(!isNetwork) {
      CommonUtils.showNetworkError(context);
      return;
    }

    String addTime = WnDateUtils.getCurrentTime();

    //先刷新本地聊天
    //ChatBean chatBean = ChatBean(fromAccount: widget.account, toAccount: widget.toGroupId,addTime:addTime,messageId: messageId,isRead: 1);
    GroupChatBean groupChatBean = GroupChatBean(account: widget.account, groupId: widget.toGroupId, addTime: addTime, messageId: messageId);

    groupChatBean.contentType = type;
    if(type == CommonUtils.CHAT_CONTENT_TYPE_VOICE){
      groupChatBean.localMediaUrl = mediaURL;
      groupChatBean.second = mediaSecond;
      //状态变更,向聊天记录中插入新记录
      setState(() {
        items.add(groupChatBean);
      });
      await GroupChatRepository.getInstance().insertGroupChat(groupChatBean);
    } else if(type == CommonUtils.CHAT_CONTENT_TYPE_IMG){
      groupChatBean.localMediaUrl = mediaURL;
      //状态变更,向聊天记录中插入新记录
      setState(() {
        items.add(groupChatBean);
      });
      await GroupChatRepository.getInstance().insertGroupChat(groupChatBean);
    } else if(type == CommonUtils.CHAT_CONTENT_TYPE_VIDEO){
      groupChatBean.localMediaUrl = mediaURL;
      groupChatBean.content = thumbnailFileName;
      groupChatBean.second = mediaSecond;

      GroupChatBean? localGroupChatBean = await GroupChatRepository.getInstance().findGroupChatByMessageId(messageId);
      //状态变更,向聊天记录中插入新记录
      if(localGroupChatBean == null){
        items.add(groupChatBean);
        GroupChatRepository.getInstance().insertGroupChat(groupChatBean);
      } else {
        groupChatBean.id = localGroupChatBean.id;
        GroupChatRepository.getInstance().updateGroupChat(groupChatBean);
        //如果已经存在,先删除在添加
        for(int i = 0; i < items.length; i++){
          GroupChatBean item = items[i];
          if(item.messageId == messageId){
            items.remove(item);
            break;
          }
        }
        items.add(groupChatBean);
      }
      setState(() {
      });

    }

    jumpToBottom(100);
  }

  //发送多媒体(图片、语音、小视频)
  void _sendMedia(int type, String mediaURL, {int mediaSecond = 0, String messageId = ""}) async {

    bool isNetwork = await CommonNetwork.isNetwork();
    if(!isNetwork) {
      return;
    }

    //上传文件
    GroupChatBean? serverChatBean;
    String message = "";
    String addTime = WnDateUtils.getCurrentTime();
    ChatSendBean chatSendBean = ChatSendBean();
    chatSendBean.fromAccount = SpUtils.getAccount();
    chatSendBean.contentType = type;
    chatSendBean.messageId = messageId;
    chatSendBean.addTime = addTime;
    if(type == CommonUtils.CHAT_CONTENT_TYPE_IMG){
      //图片
      serverChatBean = await UploadUtils.getInstance().uploadGroupChatImage(widget.account, widget.toGroupId, mediaURL, messageId, addTime);

    } else if(type == CommonUtils.CHAT_CONTENT_TYPE_VOICE){
      //语音
      serverChatBean = await UploadUtils.getInstance().uploadGroupChatVoice(widget.account, widget.toGroupId, mediaURL, messageId, addTime, mediaSecond);
      chatSendBean.second = mediaSecond;
    } else if(type == CommonUtils.CHAT_CONTENT_TYPE_VIDEO){
      //小视频
      serverChatBean = await UploadUtils.getInstance().uploadGroupChatVideo(widget.account, widget.toGroupId, mediaURL, messageId, addTime, mediaSecond);
      chatSendBean.second = mediaSecond;
    } else {
      return ;
    }
    chatSendBean.content = serverChatBean?.serverMediaUrl??"";
    GroupChatRepository.getInstance().updateGroupChatByMessageId(serverChatBean?.messageId??"", serverChatBean?.serverMediaUrl??"");
    message = jsonEncode(chatSendBean);
    _sendMessage(message);
  }

  //是否隐藏文件
  bool hideAdd = true;
  //是否隐藏emoji表情
  bool hideEmoji = true;
  //是否隐藏发送按钮
  bool hideSend = true;
  //是否隐藏添加按钮
  bool hideAddIcon = false;
  //是否按下语音说话
  bool isPressVoice = false;

  //点击空白地方,隐藏文件、emoji
  void _processClickBlank(){
    setState(() {
      hideAdd = true;
      hideEmoji = true;
      _chatContentFocus.unfocus();    // 失去焦点
    });
  }

  void _processPressVoice(){
    setState(() {
      isPressVoice = !isPressVoice;
      hideEmoji = true;
      hideAdd = true;
      _processFocus();
    });
  }

  void _processEmoji(){
    LogUtils.d("点击emoji");
    setState(() {
      hideEmoji = !hideEmoji;
      isPressVoice = false;
      hideAdd = true;
      _processFocus();
    });
  }

  void _processAdd(){
    setState(() {
      hideAdd = !hideAdd;
      isPressVoice = false;
      hideEmoji = true;
      _processFocus();
    });
  }

  void _processFocus(){
    if(!hideAdd || !hideEmoji || isPressVoice){
      _chatContentFocus.unfocus();    // 失去焦点
    } else {
      FocusScope.of(context).requestFocus(_chatContentFocus);     // 获取焦点
    }
  }

  emoticonClick(String name){
    //LogUtils.d("选择表情:" + name);
    controller.text = name;
  }

  ///选中表情
  _onEmojiSelected(Emoji emoji) {
    controller
      ..text += emoji.emoji
      ..selection = TextSelection.fromPosition(TextPosition(offset: controller.text.length));
    hideAddIcon = true;
    hideSend = false;
    setState(() {
    });
  }

  ///表情删除按钮
  _onBackspacePressed() {
    controller
      ..text = controller.text.characters.skipLast(1).toString()
      ..selection = TextSelection.fromPosition(
          TextPosition(offset: controller.text.length));
    if (controller.text.isNotEmpty) {
      setState(() {
      });
    }
  }

  //定义发送文本事件的处理函数
  void _handleSubmitted(String text) async {
    if (text.length > 0) {

      bool isNetwork = await CommonNetwork.isNetwork();
      if(!isNetwork) {
        CommonUtils.showNetworkError(context);
        return;
      }

      int contentType = CommonUtils.CHAT_CONTENT_TYPE_TEXT;
      String addTime = WnDateUtils.getCurrentTime();
      String messageId = UUID.getUUID();
      ChatSendBean chatSendBean = ChatSendBean();
      chatSendBean.contentType = contentType;
      chatSendBean.content = text;
      chatSendBean.addTime = addTime;
      chatSendBean.second = 0;
      chatSendBean.messageId = messageId;
      chatSendBean.fromAccount = SpUtils.getAccount();
      String message = jsonEncode(chatSendBean);


      controller.clear(); //清空输入框
      GroupChatBean groupChatBean = GroupChatBean(account: widget.account, groupId: widget.toGroupId, content: text, contentType: contentType, addTime: addTime, messageId: messageId, localMediaUrl: "", serverMediaUrl: "", second: 0);
      LogUtils.d("插入数据:${groupChatBean.toJson()}");
      //状态变更,向聊天记录中插入新记录
      setState(() {
        hideAddIcon = false;
        hideSend = true;
        items.add(groupChatBean);
      });
      await GroupChatRepository.getInstance().insertGroupChat(groupChatBean);

      _sendMessage(message);

      jumpToBottom(100);
    }
  }

  Widget getEmojiWidget(){

    return SizedBox(
      height: 200.0,
      width: 1000.0,
      child: EmojiPicker(
          onEmojiSelected: (Category category, Emoji emoji) {
            _onEmojiSelected(emoji);
          },
          onBackspacePressed: _onBackspacePressed,
          config: const Config(
              columns: 7,
              emojiSizeMax: 25.0,
              verticalSpacing: 0,
              horizontalSpacing: 0,
              initCategory: Category.RECENT,
              bgColor: Color(0xFFF2F2F2),
              indicatorColor: Color(0xff65DAC5),
              iconColor: Colors.orange,
              iconColorSelected: Color(0xff65DAC5),
              progressIndicatorColor: Color(0xff65DAC5),
              backspaceColor: Color(0xff65DAC5),
              showRecentsTab: true,
              recentsLimit: 28,
              categoryIcons: CategoryIcons(),
              buttonMode: ButtonMode.MATERIAL)),
    );
  }


}

group_chat_content_view.dart

/**
 * Author : wangning
 * Email : maoning20080809@163.com
 * Date : 2022/11/6 19:46
 * Description : 群聊
 */
class GroupChatContentView extends StatefulWidget {

  final List<GroupChatBean> items;
  final GroupChatBean groupChatBean;
  final int index;
  final String? otherAccount;
  final UserBean? meUserBean;
  final String account;
  final deleteCallback;
  final List<String>? addTimeList;
  //点击语音播放回调
  final clickVoiceCallback;

  //点击领取转账,刷新页面
  final refreshTransfer;

  GroupChatContentView({required this.items,  required this.account, required this.groupChatBean, required this.index,
    required this.meUserBean, required this.otherAccount, this.addTimeList, required this.deleteCallback, required this.clickVoiceCallback, required this.refreshTransfer});

  @override
  State<GroupChatContentView> createState() => _GroupChatContentViewState();

}


class _GroupChatContentViewState extends State<GroupChatContentView> {

  UserBean? otherUserBean;

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

  void _loadOtherUserBean() async {
    otherUserBean = await UserRepository.getInstance().findUserByAccount(widget.otherAccount??"");
    setState(() {

    });
  }

  void goNewFriends(String account) async{
    UserBean? userBean = await UserRepository.getInstance().findUserByAccount(account);
    if(userBean != null){
      Navigator.push(context,MaterialPageRoute(builder: (context)=>AddFriends(userBean: userBean!,)));
    } else {
      userBean = await UserRepository.getInstance().getUserServer(account);
      Navigator.push(context,MaterialPageRoute(builder: (context)=>AddFriends(userBean: userBean!,)));
    }
  }

  @override
  Widget build(BuildContext context) {

    String addTimeResult = _getAddTime("${widget.groupChatBean.addTime}");
    bool isExistTime = isExistAddTime(addTimeResult);
    if(!isExistTime){
      widget.addTimeList?.add(addTimeResult);
    }
    //如果是最后一个,清除标志
    if(widget.index == widget.items.length -1){
      widget.addTimeList?.clear();
    }
    return Column(
      children: [
        Offstage(
          offstage: isExistTime,
          child: Container(
            margin: EdgeInsets.only(top: 12),
            child: Text("${addTimeResult}"),
          ),
        ),

        Container(
          child: widget.account == widget.groupChatBean.account
              ? fromAccountWidget()
              : toAccountWidget(),
        )
      ],
    );
  }

  //小视频缩略图
  Widget getCommonThumbnail(int second){
    return CommonThumbnailWidget(
        padding: EdgeInsets.only(
            top: 0.0,
            right: (widget.account == widget.groupChatBean.account ? 0.0 : 5.0),
            left: (widget.account == widget.groupChatBean.account ? 2.0 : 0.0)),
        image: widget.groupChatBean.content??"",
        second: second,
        onPressed: () {
          Navigator.push(context, MaterialPageRoute(builder: (context) => VideoPlayLocalPreview(widget.groupChatBean.localMediaUrl!)));
        });
  }

  //显示我的
  Widget fromAccountWidget(){
    return Container(
      margin: EdgeInsets.only(top: 8.0, left: 68.0, right: 8),
      padding: EdgeInsets.all(2.0),
      child: Row(
        children: <Widget>[
          Expanded(
            child: GestureDetector(
              onLongPress: (){
                _showDeleteDialog(widget.groupChatBean);
              },
              onTap: () {
              },
              child: Stack(

                alignment: AlignmentDirectional.bottomEnd,
                children: [
                  Offstage(
                    offstage: !(widget.groupChatBean.contentType == CommonUtils.CHAT_CONTENT_TYPE_TEXT),
                    child: Column(
                      // Column被Expanded包裹起来,使其内部文本可自动换行
                      crossAxisAlignment: CrossAxisAlignment.end,
                      children: <Widget>[
                        Container(
                          padding: EdgeInsets.symmetric(vertical: 8.0, horizontal: 10.0),
                          decoration: BoxDecoration(borderRadius: BorderRadius.all(Radius.circular(5.0),),color: Color(0xFF9EEA6A),),
                          child: Text(
                            widget.groupChatBean.content??"",
                            textAlign: TextAlign.left,
                            style: TextStyle(color: Colors.black, fontSize: 20.0),
                          ),
                        )
                      ],
                    ),
                  ),
                  Offstage(
                    offstage: !(widget.groupChatBean.contentType == CommonUtils.CHAT_CONTENT_TYPE_VOICE),
                    child: InkWell(
                        onTap: () {
                          widget.clickVoiceCallback(true);
                          setState(() {
                            widget.groupChatBean.isPlayVoice = true;
                          });
                          //点击语音
                          AudioPlayer.getInstance().playLocal(widget.groupChatBean.localMediaUrl??"", callback: (data){
                            //录音回调
                            setState(() {
                              widget.groupChatBean.isPlayVoice = false;
                            });
                          });
                        },
                        child : Container(
                          width: 120,
                          height: 40,
                          padding: EdgeInsets.symmetric(vertical: 4.0, horizontal: 2.0),
                          decoration: BoxDecoration(borderRadius: BorderRadius.all(Radius.circular(1.0),),color: Color(0xFF9EEA6A),),
                          child: Row(
                            crossAxisAlignment: CrossAxisAlignment.center,
                            mainAxisAlignment: MainAxisAlignment.center,
                            children: [
                              Text("${widget.groupChatBean.second}''"),
                              SizedBox(width: 4,),
                              widget.groupChatBean.isPlayVoice?Image.asset("assets/chat/wn_chat_me_animator.gif", height: 24,):Image.asset("assets/chat/wn_chat_me_volume_3.png", height: 24,),
                            ],
                          ),
                        )
                    ),
                  ),
                  Offstage(
                    offstage: !(widget.groupChatBean.contentType == CommonUtils.CHAT_CONTENT_TYPE_IMG),
                    child: CommonUtils.showGroupChatImage(widget.groupChatBean.serverMediaUrl??"", widget.groupChatBean.localMediaUrl??"", width:100, height:200, angle:1, onPressed: (data){
                      Navigator.push(context,MaterialPageRoute(builder: (context)=>CommonImagePreview(fileName: data)));
                    }),
                  ),
                  widget.groupChatBean.contentType == CommonUtils.CHAT_CONTENT_TYPE_VIDEO?getCommonThumbnail(widget.groupChatBean.second??0):SizedBox(),
                  
                ],
              ),
            ),
          ),
          //userImage
          Container(
            padding: EdgeInsets.only(left: 6, right: 6),
            child: GestureDetector(
              onTap: (){
                //Navigator.push(context,MaterialPageRoute(builder: (context)=>ContactsDetails(toChatId: widget.chatBean.fromAccount??"")));
              },
              child: CommonAvatarView.showBaseImage(widget.meUserBean?.avatar??"", 38, 38),
            ),
          ),

        ],
      ),
    );
  }

  //显示好友
  Widget toAccountWidget(){
    return Container(
      margin: EdgeInsets.only(top: 8.0, right: 68.0),
      padding: EdgeInsets.all(2.0),
      child: Row(
        //crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          //userImage,
          Container(
            margin: EdgeInsets.only(left: 6, right: 6),
            child: GestureDetector(
              onTap : (){
                Navigator.push(context,MaterialPageRoute(builder: (context)=>ContactsDetails(toChatId: otherUserBean?.account??"")));
              },
              child: CommonAvatarView.showBaseImage(otherUserBean?.avatar??"", 38, 38),
            ),
          ),
          Expanded(
            child: GestureDetector(

                onLongPress: (){
                  _showDeleteDialog(widget.groupChatBean);
                },
                onTap: () {
                },

                child: Stack(
                  alignment: AlignmentDirectional.centerStart,
                  children: [
                    Offstage(
                      offstage: !(widget.groupChatBean.contentType == CommonUtils.CHAT_CONTENT_TYPE_TEXT),
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: <Widget>[
                          Container(
                            padding: EdgeInsets.symmetric(vertical: 8.0, horizontal: 10.0),
                            decoration: BoxDecoration(borderRadius: BorderRadius.all(Radius.circular(5.0),),color: Color(0xFFEDEDED)),
                            child: Text(
                              widget.groupChatBean.content??"",
                              textAlign: TextAlign.left,
                              style: TextStyle(color: Colors.black, fontSize: 20.0),
                            ),
                          )
                        ],
                      ),
                    ),
                    Offstage(
                      offstage: !(widget.groupChatBean.contentType == CommonUtils.CHAT_CONTENT_TYPE_VOICE),
                      child: InkWell(
                          onTap: () {
                            setState(() {
                              widget.groupChatBean.isPlayVoice = true;
                            });
                            LogUtils.d("点击语音");
                            AudioPlayer.getInstance().playLocal(widget.groupChatBean.localMediaUrl??"", callback: (data){
                              LogUtils.d("录音回调:${data}");
                              setState(() {
                                widget.groupChatBean.isPlayVoice = false;
                              });
                            });
                          },
                          child : Container(
                            width: 120,
                            height: 40,
                            padding: EdgeInsets.symmetric(vertical: 4.0, horizontal: 2.0),
                            decoration: BoxDecoration(borderRadius: BorderRadius.all(Radius.circular(5.0),),color: Color(0xFFEDEDED)),
                            child: Row(
                              crossAxisAlignment: CrossAxisAlignment.center,
                              mainAxisAlignment: MainAxisAlignment.center,
                              children: [
                                widget.groupChatBean.isPlayVoice?Image.asset("assets/chat/wn_chat_other_animator.gif", height: 34,):Image.asset("assets/chat/wn_chat_other_volume_3.png",  height: 34,),
                                SizedBox(width: 4,),
                                Text("${widget.groupChatBean.second}''"),
                              ],
                            ),

                          )
                      ),
                    ),
                    Offstage(
                      offstage: !(widget.groupChatBean.contentType == CommonUtils.CHAT_CONTENT_TYPE_IMG),
                      child: CommonUtils.showGroupChatImage(widget.groupChatBean.serverMediaUrl??"", widget.groupChatBean.localMediaUrl??"",  width:100, height:200, angle:1, onPressed: (data){
                        Navigator.push(context,MaterialPageRoute(builder: (context)=>CommonImagePreview(fileName: data)));
                      }),
                    ),

                    widget.groupChatBean.contentType == CommonUtils.CHAT_CONTENT_TYPE_VIDEO?getCommonThumbnail(widget.groupChatBean.second??0):SizedBox(),
                  ],
                )
            ),
          ),
          /**/
        ],
      ),
    );
  }

  bool isExistAddTime(String addTimeResult){
    return widget.addTimeList?.contains(addTimeResult)??false;
  }

  String _getAddTime(String addTime){
    return WnTimeUtils.timeUtils(startTime:  addTime);
  }

  //删除对话框
  Future<void> _showDeleteDialog(GroupChatBean groupChatBean) async {
    return showDialog<Null>(
        context: context,
        barrierDismissible: false,
        builder: (BuildContext context) {
          return AlertDialog(
            title: Text('确定要删除该消息吗?', style: new TextStyle(fontSize: 17.0)),
            actions: <Widget>[
              MaterialButton(
                child: Text('取消'),
                onPressed: (){
                  LogUtils.d("确定取消");
                  Navigator.of(context).pop();
                },
              ),
              MaterialButton(
                child: Text('确定'),
                onPressed: (){
                  LogUtils.d("确定删除");
                  Navigator.pop(context);
                  _deleteChatBean(groupChatBean);
                },
              )
            ],
          );
        }
    );
  }

  //删除消息
  _deleteChatBean(GroupChatBean groupChatBean) async{
    int id = groupChatBean.id??0;
    await ChatRepository.getInstance().deleteChatById(id);
    widget.deleteCallback(true);
  }

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/36354.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Unity VR 开发教程: Oculus 一体机开发 (一) 环境配置(基于 Oculus Integration v46)

文章目录&#x1f4d5;教程说明&#x1f4d5;安装 Unity 时需要添加的模块&#x1f4d5;设置 Unity 的 Build Settings&#x1f4d5;导入 Oculus Integration&#x1f4d5;设置 Project Settings⭐通用设置⭐Rendering 设置⭐Identification 设置⭐Configuration 设置⭐XR Plug…

信号类型(雷达)——雷达波形认识(一)

系列文章目录 《信号类型&#xff08;雷达通信&#xff09;》 文章目录 前言 简述 总结 前言 本文将结合个人研究经验&#xff0c;从雷达波形简单谈谈我对雷达的认识。之后将对常见的雷达波形进行简单分析。 简述 雷达的波形决定了信号处理的手段以及对应的雷达功能&#x…

关于WEB端实现电子海图研究二GeoServer

记笔记&#xff0c;免忘记&#xff01; 接前面思路篇。本文章主要是讲&#xff0c;利用GeoServer对shp文件进行数据样式调整 &#xff0c;数据拼接&#xff0c;shp文件发布&#xff0c;矢量切片。 Geoserver官网&#xff1a;GeoServer 可以使用最新版本&#xff0c;我使用的…

边缘计算那些事儿--网络切片技术(1)

0 背景 边缘计算可以支持就近的计算卸载&#xff0c;让数据在靠近数据源的设备上处理。对于边缘计算低时延的应用场景来说&#xff0c;网络性能的好坏&#xff0c;直接影响着卸载算法的整体耗时&#xff0c;决定着整个算法模型的可行性。因此如果想实现低时延卸载算法&#xff…

Go语言入门【7】指针

指针 在go语言中&#xff0c;每一个变量在运行时都会拥有一个地址&#xff0c;这个地址代表的就是变量在内存中的位置&#xff0c;而指针就是指向这个地址的变量。使用go语言中的指针很简单&#xff0c;只需要记住两个关键字符&#xff0c;&&#xff08;取地址符&#xff…

[附源码]java毕业设计在线二手车交易信息管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

【App自动化测试】(九)移动端复杂测试环境模拟——来电、短信、网络切换

目录1. 发送短信2. GSM电话3. 设置模拟信号强弱4. 设置网速5. 设置网络连接类型前言&#xff1a; 本文为在霍格沃兹测试开发学社中学习到的一些技术写出来分享给大家&#xff0c;希望有志同道合的小伙伴可以一起交流技术&#xff0c;一起进步~ &#x1f618; 当我们使用模拟器来…

C#重启 --- 类和对象

​​​​​​ 1.类是抽象的&#xff0c;对象是具体的 2.类中有数据成员和方法成员&#xff08;数据成员是名词性的&#xff0c;方法成员是动词性的&#xff09; 1.类的关键字是class &#xff08;变量&#xff1a;首字母小写&#xff0c;方法&#xff1a;首字母大写&#x…

【毕业设计】机器学习的员工离职模型研究-python

目录 前言 课题背景和意义 实现技术思路 变量分析 数据导入 构建机器学习模型 1. 1 复制数据删除不需要的变量 1.2 列变量属性分类 实现效果图样例 前言 &#x1f4c5;大四是整个大学期间最忙碌的时光,一边要忙着备考或实习为毕业后面临的就业升学做准备,一边要为毕业设…

Charles证书安装windows11抓包 Https协议

1、Charles 的CA证书 如果需要截取分析 Https 协议相关的内容&#xff0c;那么需要安装 Charles 的 CA 证书&#xff0c;需要在 win11 电脑上安装证书。 2、证书安装路径 点击 Charles 的顶部菜单&#xff0c;选择 “Help” –> “SSL Proxying” –> “Install Charl…

Revit修改:网格角度,体量轮廓,梁随斜板

一、Revit中使幕墙系统网格改变角度 绘制幕墙系统时&#xff0c;若幕墙系统出现如下图情况&#xff1a; 若想改变该网格的角度&#xff0c;使其与该幕墙上下边界平行或垂直则选中该幕墙&#xff0c;修改属性栏的中的网格角度。如下图所示&#xff1a; 修改完所需角度后&#xf…

web网页设计期末课程大作业——汉中印象旅游景点介绍网页设计与实现19页面HTML+CSS+JavaScript

&#x1f468;‍&#x1f393;学生HTML静态网页基础水平制作&#x1f469;‍&#x1f393;&#xff0c;页面排版干净简洁。使用HTMLCSS页面布局设计,web大学生网页设计作业源码&#xff0c;这是一个不错的旅游网页制作&#xff0c;画面精明&#xff0c;排版整洁&#xff0c;内容…

基于FPGA的Hamiton方程--辛几何算法实现(全网唯一)

1、本文实验基于冯康院士的《哈密尔顿系统的辛几何算法》开展&#xff0c;链接&#xff1a;https://pan.baidu.com/s/1GM0Px7SLWBWzh4sXmAdcwg 提取码&#xff1a;fmkt 2、虽然题目写的是基于FPGA的求解&#xff0c;但实际上采用的是VHLS来实现的&#xff0c;最近根本不想写v…

m基于rbf神经网络和遗传算法优化的MIMO-OFDM系统信道估计算法matlab仿真

目录 1.算法描述 2.仿真效果预览 3.MATLAB部分代码预览 4.完整MATLAB程序 1.算法描述 MIMO-OFDM的信道估计&#xff1a;时&#xff0c;频&#xff0c;空三个域都要考虑&#xff0c;尤其是在空域&#xff0c;不同天线发射的导频序列需要相互正交&#xff0c;否则在接收端无法…

使用星凸随机超曲面模型对扩展对象和分组目标进行形状跟踪(Matlab代码实现)

&#x1f352;&#x1f352;&#x1f352;欢迎关注&#x1f308;&#x1f308;&#x1f308; &#x1f4dd;个人主页&#xff1a;我爱Matlab &#x1f44d;点赞➕评论➕收藏 养成习惯&#xff08;一键三连&#xff09;&#x1f33b;&#x1f33b;&#x1f33b; &#x1f34c;希…

学习Python要学习哪些课程?

通过学习 Python数据分析与应用课程&#xff0c;可以掌握Python进行科学计算、可视化绘图、数据处理&#xff0c;分析与建模、构建聚类、回归、分类模型的主要方法和技能&#xff0c;并为后续相关课程学习及将来从事数据分析挖掘研究、数据分析工作奠定基础。 Python数据分析与…

进程互斥以及进程互斥实现方法(包含代码)

进程互斥有关概念&#xff1a; 两种资源共享方式&#xff1a; 1.互斥共享&#xff1a;一个时间段内只允许一个进程进行访问 2.同时共享&#xff1a;一个时间段内允许多个进程进行“同时”访问 临界资源&#xff1a;一个时间段内只允许一个进程进行访问的资源 访问临界区的…

第二章 爬虫的实现原理和技术(一)

2.1 爬虫的实现原理 不同类型的爬虫,具体的实现原理也不尽相同,但是这些爬虫之间存在许多共性。下面我将以通用爬虫与聚焦爬虫为例,具体来讲解爬虫是如何来运作的。 通用爬虫的工作原理 通用爬虫是一个自动提取网页的程序,能够从Internet上下载网页,是大多的搜索引擎的…

关于FFmepg的冷知识,这一篇就够了

每一个从事音视频技术开发的工程师对FFmpeg都不会感到陌生&#xff0c;即使是刚刚踏入这个行业的初学者&#xff0c;但对他们来说这条路上好像有着一条不可逾越的鸿沟&#xff0c;“雷神”和许多大神都总结过一些FFmpeg的学习方法&#xff0c;小编在这里为大家做一个整理&#…

《恋上数据结构与算法》第1季:动态数组原理实现(图文并茂,一文带你了解ArrayList底层实现)

动态数组原理实现一、数组&#xff08;Array&#xff09;二、动态数组三、动态数组的设计四、动态数组的实现1. 添加元素2. 数组扩容3. 删除元素4. 数组缩容5. 清空元素6. 修改元素7. 查询元素8. 插入元素9. 查看元素位置10. 是否包含某个元素11. 元素的数量12. 数组是否为空13…