merge
This commit is contained in:
parent
c4c59d3ec3
commit
d61b852883
BIN
assets/images/group.png
Normal file
BIN
assets/images/group.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.6 KiB |
@ -6,9 +6,9 @@ import 'package:loopin/utils/index.dart';
|
|||||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart';
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart';
|
||||||
|
|
||||||
class ChatDetailController extends GetxController {
|
class ChatDetailController extends GetxController {
|
||||||
final String userID;
|
final String id;
|
||||||
|
|
||||||
ChatDetailController({required this.userID});
|
ChatDetailController({required this.id});
|
||||||
final ScrollController chatController = ScrollController();
|
final ScrollController chatController = ScrollController();
|
||||||
|
|
||||||
final RxList<V2TimMessage> chatList = <V2TimMessage>[].obs;
|
final RxList<V2TimMessage> chatList = <V2TimMessage>[].obs;
|
||||||
|
@ -24,6 +24,7 @@ class ImUserInfoController extends GetxController {
|
|||||||
"area": "",
|
"area": "",
|
||||||
"areaCode": "",
|
"areaCode": "",
|
||||||
"openId": "",
|
"openId": "",
|
||||||
|
"tag": "",
|
||||||
}.obs;
|
}.obs;
|
||||||
final role = 0.obs;
|
final role = 0.obs;
|
||||||
final level = 0.obs;
|
final level = 0.obs;
|
||||||
@ -44,6 +45,7 @@ class ImUserInfoController extends GetxController {
|
|||||||
"area": "",
|
"area": "",
|
||||||
"areaCode": "",
|
"areaCode": "",
|
||||||
"openId": "",
|
"openId": "",
|
||||||
|
"tag": "",
|
||||||
});
|
});
|
||||||
|
|
||||||
role.value = userInfo.role ?? 0;
|
role.value = userInfo.role ?? 0;
|
||||||
|
@ -71,24 +71,26 @@ class ImMessageListenerService extends GetxService {
|
|||||||
|
|
||||||
/// 处理消息
|
/// 处理消息
|
||||||
void _handleNewMessage(V2TimMessage message) async {
|
void _handleNewMessage(V2TimMessage message) async {
|
||||||
final userID = message.sender ?? '';
|
final id = message.sender ?? message.groupID ?? '';
|
||||||
if (userID.isEmpty) return;
|
final isGroup = message.groupID != null && message.groupID!.isNotEmpty;
|
||||||
|
final conversationID = isGroup ? 'group_${message.groupID}' : 'c2c_${message.userID}';
|
||||||
|
if (id.isEmpty) return;
|
||||||
|
|
||||||
/// 是否正在聊天 优先处理
|
/// 是否正在聊天 优先处理
|
||||||
if ((Get.currentRoute == '/chat' || Get.currentRoute == '/chatNoFriend' || Get.currentRoute == '/chatGroup') && Get.isRegistered<ChatDetailController>()) {
|
if ((Get.currentRoute == '/chat' || Get.currentRoute == '/chatNoFriend' || Get.currentRoute == '/chatGroup') && Get.isRegistered<ChatDetailController>()) {
|
||||||
final chatDetailController = Get.find<ChatDetailController>();
|
final chatDetailController = Get.find<ChatDetailController>();
|
||||||
// 单聊的处理
|
// 单聊和群聊的处理(chatDetailController.id是通过chat_binding传入的,按顺序取userID,没有则取groupID)
|
||||||
if (chatDetailController.userID == userID) {
|
if (chatDetailController.id == id) {
|
||||||
// 确认正在聊天,插入消息前检测是否需要打时间标签
|
// 确认正在聊天,插入消息前检测是否需要打时间标签
|
||||||
insertTimeLabel(message);
|
insertTimeLabel(message);
|
||||||
// 标注为已读
|
// 标注为已读
|
||||||
await ImService.instance.clearConversationUnreadCount(conversationID: 'c2c_$userID');
|
await ImService.instance.clearConversationUnreadCount(conversationID: conversationID);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 未杀死状态,交给在线推送处理
|
// 未杀死状态,交给在线推送处理
|
||||||
if (!LifecycleHandler.isInForeground) {
|
if (!LifecycleHandler.isInForeground) {
|
||||||
logger.i("App 当前在后台,收到来自 $userID 的消息 ${message.msgID},暂不展示弹窗");
|
logger.i("App 当前在后台,收到来自 $id 的消息 ${message.msgID},暂不展示弹窗");
|
||||||
// return;
|
// return;
|
||||||
}
|
}
|
||||||
//TODO 过滤有管理员标识的信息
|
//TODO 过滤有管理员标识的信息
|
||||||
|
@ -17,6 +17,9 @@ import 'package:tencent_cloud_chat_sdk/enum/friend_application_type_enum.dart';
|
|||||||
import 'package:tencent_cloud_chat_sdk/enum/friend_response_type_enum.dart';
|
import 'package:tencent_cloud_chat_sdk/enum/friend_response_type_enum.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/enum/friend_type_enum.dart';
|
import 'package:tencent_cloud_chat_sdk/enum/friend_type_enum.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/enum/group_add_opt_enum.dart';
|
import 'package:tencent_cloud_chat_sdk/enum/group_add_opt_enum.dart';
|
||||||
|
import 'package:tencent_cloud_chat_sdk/enum/group_application_type_enum.dart';
|
||||||
|
import 'package:tencent_cloud_chat_sdk/enum/group_member_filter_enum.dart';
|
||||||
|
import 'package:tencent_cloud_chat_sdk/enum/group_member_role_enum.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/enum/history_msg_get_type_enum.dart';
|
import 'package:tencent_cloud_chat_sdk/enum/history_msg_get_type_enum.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/manager/v2_tim_group_manager.dart';
|
import 'package:tencent_cloud_chat_sdk/manager/v2_tim_group_manager.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_callback.dart';
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_callback.dart';
|
||||||
@ -30,7 +33,17 @@ import 'package:tencent_cloud_chat_sdk/models/v2_tim_follow_type_check_result.da
|
|||||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_friend_info.dart';
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_friend_info.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_friend_info_result.dart';
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_friend_info_result.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_friend_operation_result.dart';
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_friend_operation_result.dart';
|
||||||
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_application.dart';
|
||||||
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_application_result.dart';
|
||||||
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_info.dart';
|
||||||
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_info_result.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member.dart';
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member.dart';
|
||||||
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member_full_info.dart';
|
||||||
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member_info_result.dart';
|
||||||
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member_operation_result.dart';
|
||||||
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member_search_param.dart';
|
||||||
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member_search_result.dart';
|
||||||
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_search_param.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart';
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message_change_info.dart';
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message_change_info.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message_search_param.dart';
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message_search_param.dart';
|
||||||
@ -40,6 +53,8 @@ import 'package:tencent_cloud_chat_sdk/models/v2_tim_user_info_result.dart';
|
|||||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_value_callback.dart';
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_value_callback.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/native_im/adapter/tim_conversation_manager.dart';
|
import 'package:tencent_cloud_chat_sdk/native_im/adapter/tim_conversation_manager.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/native_im/adapter/tim_friendship_manager.dart';
|
import 'package:tencent_cloud_chat_sdk/native_im/adapter/tim_friendship_manager.dart';
|
||||||
|
import 'package:tencent_cloud_chat_sdk/native_im/adapter/tim_group_manager.dart';
|
||||||
|
import 'package:tencent_cloud_chat_sdk/native_im/adapter/tim_manager.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/native_im/adapter/tim_message_manager.dart';
|
import 'package:tencent_cloud_chat_sdk/native_im/adapter/tim_message_manager.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/tencent_im_sdk_plugin.dart';
|
import 'package:tencent_cloud_chat_sdk/tencent_im_sdk_plugin.dart';
|
||||||
|
|
||||||
@ -734,6 +749,7 @@ class ImService {
|
|||||||
return ImResult.wrap(res);
|
return ImResult.wrap(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///------------------------------------
|
||||||
/// 创建群
|
/// 创建群
|
||||||
Future<ImResult<String>> createGroup({
|
Future<ImResult<String>> createGroup({
|
||||||
String? groupID,
|
String? groupID,
|
||||||
@ -768,4 +784,299 @@ class ImService {
|
|||||||
|
|
||||||
return ImResult.wrap(res);
|
return ImResult.wrap(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 获取当前用户已经加入的群列表
|
||||||
|
/// 注意:
|
||||||
|
/// - 直播群(AVChatRoom) 不支持该 API
|
||||||
|
/// - SDK 限制调用频率为 1 秒 10 次,超过限制后会报错 7008
|
||||||
|
Future<ImResult<List<V2TimGroupInfo>>> getJoinedGroupList() async {
|
||||||
|
final res = await V2TIMGroupManager().getJoinedGroupList();
|
||||||
|
return ImResult.wrap(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 申请加入群(不包含work群,work群只能邀请进群)
|
||||||
|
Future<ImResult<void>> joinGroup({
|
||||||
|
required String groupID,
|
||||||
|
required String message,
|
||||||
|
}) async {
|
||||||
|
final res = await TIMManager.instance.joinGroup(
|
||||||
|
groupID: groupID,
|
||||||
|
message: message,
|
||||||
|
);
|
||||||
|
|
||||||
|
return ImResult.wrapNoData(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 拉取群资料
|
||||||
|
/// 参数:
|
||||||
|
/// - [groupIDList] 群 ID 列表
|
||||||
|
Future<ImResult<List<V2TimGroupInfoResult>>> getGroupsInfo({
|
||||||
|
required List<String> groupIDList,
|
||||||
|
}) async {
|
||||||
|
final res = await V2TIMGroupManager().getGroupsInfo(groupIDList: groupIDList);
|
||||||
|
return ImResult.wrap(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 修改群资料
|
||||||
|
///
|
||||||
|
/// 参数:
|
||||||
|
/// - [info] 群资料参数
|
||||||
|
Future<ImResult<void>> setGroupInfo({
|
||||||
|
required V2TimGroupInfo info,
|
||||||
|
}) async {
|
||||||
|
final res = await V2TIMGroupManager().setGroupInfo(info: info);
|
||||||
|
return ImResult.wrapNoData(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取群成员列表
|
||||||
|
///
|
||||||
|
/// 参数说明:
|
||||||
|
/// [groupID] 群 ID
|
||||||
|
/// [filter] 成员过滤类型:
|
||||||
|
/// - V2TIM_GROUP_MEMBER_FILTER_ALL:所有类型
|
||||||
|
/// - V2TIM_GROUP_MEMBER_FILTER_OWNER:群主
|
||||||
|
/// - V2TIM_GROUP_MEMBER_FILTER_ADMIN:群管理员
|
||||||
|
/// - V2TIM_GROUP_MEMBER_FILTER_COMMON:普通群成员
|
||||||
|
/// [nextSeq] 分页拉取标志,首次传 0
|
||||||
|
/// [count] 拉取数量,最大 100
|
||||||
|
/// [offset] 偏移量(仅 Web 端需要)
|
||||||
|
Future<ImResult<V2TimGroupMemberInfoResult>> getGroupMemberList({
|
||||||
|
required String groupID,
|
||||||
|
required GroupMemberFilterTypeEnum filter,
|
||||||
|
required String nextSeq,
|
||||||
|
int count = 20,
|
||||||
|
int offset = 0,
|
||||||
|
}) async {
|
||||||
|
final res = await V2TIMGroupManager().getGroupMemberList(
|
||||||
|
groupID: groupID,
|
||||||
|
filter: filter,
|
||||||
|
nextSeq: nextSeq,
|
||||||
|
count: count,
|
||||||
|
offset: offset,
|
||||||
|
);
|
||||||
|
return ImResult.wrap(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取指定的群成员资料
|
||||||
|
///
|
||||||
|
/// [groupID] 群 ID
|
||||||
|
/// [memberList] 需要获取资料的成员 ID 列表
|
||||||
|
Future<ImResult<List<V2TimGroupMemberFullInfo>>> getGroupMembersInfo({
|
||||||
|
required String groupID,
|
||||||
|
required List<String> memberList,
|
||||||
|
}) async {
|
||||||
|
final res = await V2TIMGroupManager().getGroupMembersInfo(
|
||||||
|
groupID: groupID,
|
||||||
|
memberList: memberList,
|
||||||
|
);
|
||||||
|
return ImResult.wrap(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 修改指定的群成员资料
|
||||||
|
///
|
||||||
|
/// [groupID] 群 ID
|
||||||
|
/// [userID] 成员 ID
|
||||||
|
/// [nameCard] 群名片(昵称)
|
||||||
|
/// [customInfo] 自定义字段
|
||||||
|
Future<ImResult<void>> setGroupMemberInfo({
|
||||||
|
required String groupID,
|
||||||
|
required String userID,
|
||||||
|
String? nameCard,
|
||||||
|
Map<String, String>? customInfo,
|
||||||
|
}) async {
|
||||||
|
final res = await V2TIMGroupManager().setGroupMemberInfo(
|
||||||
|
groupID: groupID,
|
||||||
|
userID: userID,
|
||||||
|
nameCard: nameCard,
|
||||||
|
customInfo: customInfo,
|
||||||
|
);
|
||||||
|
return ImResult.wrapNoData(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 邀请他人入群(work群的入群方式,任何人可邀请人入群,无法做权限设置)
|
||||||
|
///
|
||||||
|
/// [groupID] 群 ID
|
||||||
|
/// [userList] 被邀请成员的 userID 列表
|
||||||
|
///
|
||||||
|
/// 注意:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// 工作群(Work):群里的任何人都可以邀请其他人进群。
|
||||||
|
/// 会议群(Meeting)和公开群(Public):只有通过 REST API 使用 App 管理员身份才可以邀请其他人进群。
|
||||||
|
/// 直播群(AVChatRoom):不支持此功能。
|
||||||
|
/// ```
|
||||||
|
Future<ImResult<List<V2TimGroupMemberOperationResult>>> inviteUserToGroup({
|
||||||
|
required String groupID,
|
||||||
|
required List<String> userList,
|
||||||
|
}) async {
|
||||||
|
final res = await TIMGroupManager.instance.inviteUserToGroup(
|
||||||
|
groupID: groupID,
|
||||||
|
userList: userList,
|
||||||
|
);
|
||||||
|
return ImResult.wrap(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 踢人
|
||||||
|
///
|
||||||
|
/// [groupID] 群 ID
|
||||||
|
/// [memberList] 被踢成员的 userID 列表
|
||||||
|
/// [duration] 禁言时长,单位秒(可选,仅对禁言有效)
|
||||||
|
/// [reason] 踢人的原因(可选)
|
||||||
|
///
|
||||||
|
/// 注意:
|
||||||
|
/// ```
|
||||||
|
/// 工作群(Work):只有群主或 APP 管理员可以踢人。
|
||||||
|
/// 公开群(Public)、会议群(Meeting):群主、管理员和 APP 管理员可以踢人。
|
||||||
|
/// 直播群(AVChatRoom):只支持禁言(muteGroupMember),不支持踢人。
|
||||||
|
/// ```
|
||||||
|
Future<ImResult<void>> kickGroupMember({
|
||||||
|
required String groupID,
|
||||||
|
required List<String> memberList,
|
||||||
|
int? duration,
|
||||||
|
String? reason,
|
||||||
|
}) async {
|
||||||
|
final res = await TIMGroupManager.instance.kickGroupMember(
|
||||||
|
groupID: groupID,
|
||||||
|
memberList: memberList,
|
||||||
|
duration: duration,
|
||||||
|
reason: reason,
|
||||||
|
);
|
||||||
|
return ImResult.wrapNoData(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 切换群成员的角色
|
||||||
|
///
|
||||||
|
/// [groupID] 群 ID
|
||||||
|
/// [userID] 被切换角色的成员 userID
|
||||||
|
/// [role] 目标角色
|
||||||
|
///
|
||||||
|
/// 注意:
|
||||||
|
/// ```
|
||||||
|
/// 公开群(Public)和会议群(Meeting):只有群主才能对群成员进行普通成员和管理员之间的角色切换。
|
||||||
|
/// 其他群不支持设置群成员角色。
|
||||||
|
/// 转让群组请调用 transferGroupOwner 接口。
|
||||||
|
/// ```
|
||||||
|
Future<ImResult<void>> setGroupMemberRole({
|
||||||
|
required String groupID,
|
||||||
|
required String userID,
|
||||||
|
required GroupMemberRoleTypeEnum role,
|
||||||
|
}) async {
|
||||||
|
final res = await TIMGroupManager.instance.setGroupMemberRole(
|
||||||
|
groupID: groupID,
|
||||||
|
userID: userID,
|
||||||
|
role: role,
|
||||||
|
);
|
||||||
|
return ImResult.wrapNoData(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 转让群主
|
||||||
|
///
|
||||||
|
/// [groupID] 群 ID
|
||||||
|
/// [userID] 新群主的 userID
|
||||||
|
///
|
||||||
|
/// 注意:
|
||||||
|
/// ```
|
||||||
|
/// 普通类型的群(Work、Public、Meeting):只有群主才有权限进行群转让操作。
|
||||||
|
/// 直播群(AVChatRoom):不支持转让群主。
|
||||||
|
/// ```
|
||||||
|
Future<ImResult<void>> transferGroupOwner({
|
||||||
|
required String groupID,
|
||||||
|
required String userID,
|
||||||
|
}) async {
|
||||||
|
final res = await TIMGroupManager.instance.transferGroupOwner(
|
||||||
|
groupID: groupID,
|
||||||
|
userID: userID,
|
||||||
|
);
|
||||||
|
return ImResult.wrapNoData(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取加群的申请列表
|
||||||
|
Future<ImResult<V2TimGroupApplicationResult>> getGroupApplicationList() async {
|
||||||
|
final res = await TIMGroupManager.instance.getGroupApplicationList();
|
||||||
|
return ImResult.wrap(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 同意某一条加群申请
|
||||||
|
Future<ImResult<void>> acceptGroupApplication({
|
||||||
|
required String groupID,
|
||||||
|
String? reason,
|
||||||
|
required String fromUser,
|
||||||
|
required String toUser, // 处理这条数据的人,管理员或者群主
|
||||||
|
int? addTime,
|
||||||
|
GroupApplicationTypeEnum? type,
|
||||||
|
V2TimGroupApplication? application,
|
||||||
|
String? webMessageInstance,
|
||||||
|
}) async {
|
||||||
|
final res = await TIMGroupManager.instance.acceptGroupApplication(
|
||||||
|
groupID: groupID,
|
||||||
|
reason: reason,
|
||||||
|
fromUser: fromUser,
|
||||||
|
toUser: toUser,
|
||||||
|
addTime: addTime,
|
||||||
|
type: type,
|
||||||
|
application: application,
|
||||||
|
);
|
||||||
|
|
||||||
|
return ImResult.wrapNoData(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 拒绝某一条加群申请
|
||||||
|
Future<ImResult> refuseGroupApplication({
|
||||||
|
required String groupID,
|
||||||
|
String? reason,
|
||||||
|
required String fromUser,
|
||||||
|
required String toUser,
|
||||||
|
required int addTime,
|
||||||
|
required GroupApplicationTypeEnum type,
|
||||||
|
V2TimGroupApplication? application,
|
||||||
|
}) async {
|
||||||
|
final res = await TIMGroupManager.instance.refuseGroupApplication(
|
||||||
|
groupID: groupID,
|
||||||
|
fromUser: fromUser,
|
||||||
|
toUser: toUser,
|
||||||
|
addTime: addTime,
|
||||||
|
type: type,
|
||||||
|
application: application,
|
||||||
|
);
|
||||||
|
|
||||||
|
return ImResult.wrapNoData(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 标记加群申请列表为已读
|
||||||
|
Future<ImResult> setGroupApplicationRead() async {
|
||||||
|
final res = await TIMGroupManager.instance.setGroupApplicationRead();
|
||||||
|
return ImResult.wrapNoData(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 搜索本地群组资料
|
||||||
|
Future<ImResult<List<V2TimGroupInfo>>> searchGroups({
|
||||||
|
required V2TimGroupSearchParam searchParam,
|
||||||
|
}) async {
|
||||||
|
final res = await TIMGroupManager.instance.searchGroups(searchParam: searchParam);
|
||||||
|
return ImResult.wrap(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 搜索本地群成员
|
||||||
|
Future<ImResult<V2GroupMemberInfoSearchResult>> searchGroupMembers({
|
||||||
|
required V2TimGroupMemberSearchParam param,
|
||||||
|
}) async {
|
||||||
|
final res = await TIMGroupManager.instance.searchGroupMembers(param: param);
|
||||||
|
return ImResult.wrap(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 解散群(Work:任何人都无法解散群组,其他群:群主可以解散群组)
|
||||||
|
Future<ImResult<void>> dismissGroup({
|
||||||
|
required String groupID,
|
||||||
|
}) async {
|
||||||
|
final res = await TIMManager.instance.dismissGroup(groupID: groupID);
|
||||||
|
return ImResult.wrapNoData(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 退群 ,在公开群(Public)、会议(Meeting)和直播群(AVChatRoom)中,群主是不可以退群的,群主只能调用 dismissGroup 解散群组。
|
||||||
|
Future<ImResult<void>> quitGroup({
|
||||||
|
required String groupID,
|
||||||
|
}) async {
|
||||||
|
final res = await TIMManager.instance.quitGroup(groupID: groupID);
|
||||||
|
return ImResult.wrapNoData(res);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ class ChatBinding extends Bindings {
|
|||||||
@override
|
@override
|
||||||
void dependencies() {
|
void dependencies() {
|
||||||
V2TimConversation conversation = Get.arguments;
|
V2TimConversation conversation = Get.arguments;
|
||||||
Get.put(ChatDetailController(userID: conversation.userID!));
|
final id = conversation.userID ?? conversation.groupID;
|
||||||
|
Get.put(ChatDetailController(id: id!));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:loopin/IM/im_friend_listeners.dart';
|
||||||
import 'package:loopin/api/shop_api.dart';
|
import 'package:loopin/api/shop_api.dart';
|
||||||
import 'package:loopin/service/http.dart';
|
import 'package:loopin/service/http.dart';
|
||||||
|
|
||||||
@ -117,7 +118,7 @@ class ShopIndexController extends GetxController with GetSingleTickerProviderSta
|
|||||||
});
|
});
|
||||||
|
|
||||||
final data = res['data']['records'];
|
final data = res['data']['records'];
|
||||||
print('商品返回数据------------------------->${data}');
|
logger.w('商品返回数据:$index------------------------->$data');
|
||||||
tab.dataList.addAll(data);
|
tab.dataList.addAll(data);
|
||||||
// logger.w(res);
|
// logger.w(res);
|
||||||
|
|
||||||
@ -132,7 +133,7 @@ class ShopIndexController extends GetxController with GetSingleTickerProviderSta
|
|||||||
'type': 1,
|
'type': 1,
|
||||||
});
|
});
|
||||||
final data = res['data'];
|
final data = res['data'];
|
||||||
// logger.w(res);
|
logger.w(res);
|
||||||
swiperData.assignAll(data);
|
swiperData.assignAll(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,23 +2,28 @@
|
|||||||
library;
|
library;
|
||||||
|
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:get/get.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/chat_detail_controller.dart';
|
||||||
import 'package:loopin/IM/im_message.dart';
|
import 'package:loopin/IM/im_message.dart';
|
||||||
import 'package:loopin/IM/im_result.dart';
|
import 'package:loopin/IM/im_result.dart';
|
||||||
import 'package:loopin/IM/im_service.dart';
|
import 'package:loopin/IM/im_service.dart';
|
||||||
|
import 'package:loopin/components/image_viewer.dart';
|
||||||
import 'package:loopin/components/network_or_asset_image.dart';
|
import 'package:loopin/components/network_or_asset_image.dart';
|
||||||
import 'package:loopin/components/preview_video.dart';
|
import 'package:loopin/components/preview_video.dart';
|
||||||
import 'package:loopin/models/summary_type.dart';
|
import 'package:loopin/models/summary_type.dart';
|
||||||
import 'package:loopin/utils/audio_player_service.dart';
|
import 'package:loopin/utils/audio_player_service.dart';
|
||||||
import 'package:loopin/utils/snapshot.dart';
|
import 'package:loopin/utils/snapshot.dart';
|
||||||
import 'package:loopin/utils/voice_service.dart';
|
import 'package:loopin/utils/voice_service.dart';
|
||||||
|
import 'package:mime/mime.dart';
|
||||||
import 'package:shirne_dialog/shirne_dialog.dart';
|
import 'package:shirne_dialog/shirne_dialog.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart';
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.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 'package:wechat_assets_picker/wechat_assets_picker.dart';
|
||||||
|
|
||||||
import '../../styles/index.dart';
|
import '../../styles/index.dart';
|
||||||
@ -36,6 +41,8 @@ class Chat extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
|
class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
|
||||||
|
final ImagePicker _picker = ImagePicker();
|
||||||
|
|
||||||
late final ChatDetailController controller;
|
late final ChatDetailController controller;
|
||||||
// 接收参数
|
// 接收参数
|
||||||
late final Rx<V2TimConversation> arguments;
|
late final Rx<V2TimConversation> arguments;
|
||||||
@ -346,6 +353,13 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
|
|||||||
data: item,
|
data: item,
|
||||||
child: Ink(
|
child: Ink(
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
// 预览图片
|
||||||
|
Get.to(() => ImageViewer(
|
||||||
|
images: [imagePaths.first],
|
||||||
|
index: 0,
|
||||||
|
));
|
||||||
|
},
|
||||||
overlayColor: WidgetStateProperty.all(Colors.transparent),
|
overlayColor: WidgetStateProperty.all(Colors.transparent),
|
||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(10.0),
|
borderRadius: BorderRadius.circular(10.0),
|
||||||
@ -525,9 +539,18 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
|
|||||||
}
|
}
|
||||||
// 分享团购商品
|
// 分享团购商品
|
||||||
else if (item.elemType == 2 && item.cloudCustomData == SummaryType.shareTuangou) {
|
else if (item.elemType == 2 && item.cloudCustomData == SummaryType.shareTuangou) {
|
||||||
//price,title,url,sell
|
// final makeJson = jsonEncode({
|
||||||
|
// "price": shopObj['price'],
|
||||||
|
// "title": shopObj['name'],
|
||||||
|
// "url": shopObj['pic'],
|
||||||
|
// "sell": Utils.graceNumber(int.parse(shopObj['sales'] ?? '0')),
|
||||||
|
// "goodsId": shopObj['id'],
|
||||||
|
// "userID": Get.find<ImUserInfoController>().userID.value,
|
||||||
|
// });
|
||||||
final obj = jsonDecode(item.customElem!.data!);
|
final obj = jsonDecode(item.customElem!.data!);
|
||||||
logger.e(obj);
|
logger.e(obj);
|
||||||
|
final goodsId = obj['goodsId'];
|
||||||
|
final userID = obj['userID'];
|
||||||
final url = obj['url'];
|
final url = obj['url'];
|
||||||
final title = obj['title'];
|
final title = obj['title'];
|
||||||
final price = obj['price'];
|
final price = obj['price'];
|
||||||
@ -535,6 +558,10 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
|
|||||||
msgtpl.add(RenderChatItem(
|
msgtpl.add(RenderChatItem(
|
||||||
data: item,
|
data: item,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
// 这里带上分享人的ID
|
||||||
|
Get.toNamed('/goods', arguments: {'goodsId': goodsId, 'userID': userID});
|
||||||
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
width: 160,
|
width: 160,
|
||||||
clipBehavior: Clip.antiAlias,
|
clipBehavior: Clip.antiAlias,
|
||||||
@ -596,11 +623,6 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onTap: () {
|
|
||||||
// 这里带上分享人的ID
|
|
||||||
// Get.toNamed('/goods');
|
|
||||||
Get.toNamed('/goods', arguments: {});
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -609,10 +631,12 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
|
|||||||
/// {imgUrl,videoUrl,width,height}
|
/// {imgUrl,videoUrl,width,height}
|
||||||
final obj = jsonDecode(item.customElem!.data!);
|
final obj = jsonDecode(item.customElem!.data!);
|
||||||
logger.e(obj);
|
logger.e(obj);
|
||||||
|
final videoId = obj['videoId'];
|
||||||
final videoUrl = obj['videoUrl'];
|
final videoUrl = obj['videoUrl'];
|
||||||
final imgUrl = obj['imgUrl'];
|
final imgUrl = obj['imgUrl'];
|
||||||
final width = obj['width'] as num;
|
final width = obj['width'] as num;
|
||||||
final height = obj['height'] as num;
|
final height = obj['height'] as num;
|
||||||
|
final isHorizontal = width > height;
|
||||||
msgtpl.add(RenderChatItem(
|
msgtpl.add(RenderChatItem(
|
||||||
data: item,
|
data: item,
|
||||||
child: Ink(
|
child: Ink(
|
||||||
@ -625,9 +649,15 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
|
|||||||
children: [
|
children: [
|
||||||
ClipRRect(
|
ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(10.0),
|
borderRadius: BorderRadius.circular(10.0),
|
||||||
|
child: Container(
|
||||||
|
width: 120,
|
||||||
|
height: 240,
|
||||||
|
color: Colors.black,
|
||||||
child: NetworkOrAssetImage(
|
child: NetworkOrAssetImage(
|
||||||
imageUrl: imgUrl,
|
imageUrl: imgUrl,
|
||||||
width: 120,
|
fit: isHorizontal ? BoxFit.contain : BoxFit.cover,
|
||||||
|
placeholderAsset: 'assets/images/bk.jpg',
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Align(
|
const Align(
|
||||||
@ -642,31 +672,32 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
showGeneralDialog(
|
Get.toNamed('/videoDetail', arguments: {'videoId': videoId});
|
||||||
context: context,
|
// showGeneralDialog(
|
||||||
barrierColor: Colors.black.withAlpha((1.0 * 255).round()),
|
// context: context,
|
||||||
pageBuilder: (_, __, ___) {
|
// barrierColor: Colors.black.withAlpha((1.0 * 255).round()),
|
||||||
return SafeArea(
|
// pageBuilder: (_, __, ___) {
|
||||||
bottom: true,
|
// return SafeArea(
|
||||||
child: Padding(
|
// bottom: true,
|
||||||
padding: const EdgeInsets.only(bottom: 4),
|
// child: Padding(
|
||||||
child: PreviewVideo(
|
// padding: const EdgeInsets.only(bottom: 4),
|
||||||
videoUrl: videoUrl,
|
// child: PreviewVideo(
|
||||||
width: width.toDouble(),
|
// videoUrl: videoUrl,
|
||||||
height: height.toDouble(),
|
// width: width.toDouble(),
|
||||||
),
|
// height: height.toDouble(),
|
||||||
),
|
// ),
|
||||||
);
|
// ),
|
||||||
},
|
// );
|
||||||
transitionBuilder: (_, anim, __, child) {
|
// },
|
||||||
return FadeTransition(opacity: anim, child: child);
|
// transitionBuilder: (_, anim, __, child) {
|
||||||
},
|
// return FadeTransition(opacity: anim, child: child);
|
||||||
transitionDuration: const Duration(milliseconds: 200),
|
// },
|
||||||
);
|
// transitionDuration: const Duration(milliseconds: 200),
|
||||||
},
|
// );
|
||||||
onLongPress: () {
|
|
||||||
contextMenuDialog();
|
|
||||||
},
|
},
|
||||||
|
// onLongPress: () {
|
||||||
|
// contextMenuDialog();
|
||||||
|
// },
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
@ -1022,7 +1053,7 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 发送消息队列
|
// 发送消息队列
|
||||||
void sendMessage(message) async {
|
Future<void> sendMessage(message) async {
|
||||||
// 待插入的消息
|
// 待插入的消息
|
||||||
List<V2TimMessage> messagesToInsert = [];
|
List<V2TimMessage> messagesToInsert = [];
|
||||||
V2TimMessage? lastRealMsg;
|
V2TimMessage? lastRealMsg;
|
||||||
@ -1192,6 +1223,8 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
|
|||||||
|
|
||||||
// 发送视频消息=5
|
// 发送视频消息=5
|
||||||
void sendVideo(videoFilePath, type, duration, snapshotPath) async {
|
void sendVideo(videoFilePath, type, duration, snapshotPath) async {
|
||||||
|
final instance = MyDialog.loading('处理中');
|
||||||
|
|
||||||
final resImg = await IMMessage().createVideoMessage(
|
final resImg = await IMMessage().createVideoMessage(
|
||||||
videoFilePath: videoFilePath,
|
videoFilePath: videoFilePath,
|
||||||
type: type,
|
type: type,
|
||||||
@ -1199,13 +1232,116 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
|
|||||||
snapshotPath: snapshotPath,
|
snapshotPath: snapshotPath,
|
||||||
);
|
);
|
||||||
if (resImg.success) {
|
if (resImg.success) {
|
||||||
sendMessage(resImg.data?.messageInfo);
|
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 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 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) {
|
void handleChooseAction(key) {
|
||||||
MyDialog.toast('$key');
|
// MyDialog.toast('$key');
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case 'photo':
|
case 'photo':
|
||||||
// ....
|
// ....
|
||||||
@ -1213,6 +1349,7 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
|
|||||||
break;
|
break;
|
||||||
case 'camera':
|
case 'camera':
|
||||||
// ....
|
// ....
|
||||||
|
showPicker(context);
|
||||||
break;
|
break;
|
||||||
case 'redpacket':
|
case 'redpacket':
|
||||||
sendRedPacketDialog();
|
sendRedPacketDialog();
|
||||||
@ -1421,35 +1558,35 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
|
|||||||
|
|
||||||
// 长按消息菜单
|
// 长按消息菜单
|
||||||
void contextMenuDialog() {
|
void contextMenuDialog() {
|
||||||
showDialog(
|
// showDialog(
|
||||||
context: context,
|
// context: context,
|
||||||
builder: (context) {
|
// builder: (context) {
|
||||||
return SimpleDialog(
|
// return SimpleDialog(
|
||||||
backgroundColor: Colors.white,
|
// backgroundColor: Colors.white,
|
||||||
surfaceTintColor: Colors.white,
|
// surfaceTintColor: Colors.white,
|
||||||
contentPadding: const EdgeInsets.symmetric(vertical: 5.0),
|
// contentPadding: const EdgeInsets.symmetric(vertical: 5.0),
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.0)),
|
// shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.0)),
|
||||||
children: [
|
// children: [
|
||||||
SimpleDialogOption(
|
// SimpleDialogOption(
|
||||||
child: const Padding(padding: EdgeInsets.symmetric(vertical: 5.0), child: Text('复制')),
|
// child: const Padding(padding: EdgeInsets.symmetric(vertical: 5.0), child: Text('复制')),
|
||||||
onPressed: () {},
|
// onPressed: () {},
|
||||||
),
|
// ),
|
||||||
SimpleDialogOption(
|
// SimpleDialogOption(
|
||||||
child: const Padding(padding: EdgeInsets.symmetric(vertical: 5.0), child: Text('发送给朋友')),
|
// child: const Padding(padding: EdgeInsets.symmetric(vertical: 5.0), child: Text('发送给朋友')),
|
||||||
onPressed: () {},
|
// onPressed: () {},
|
||||||
),
|
// ),
|
||||||
SimpleDialogOption(
|
// SimpleDialogOption(
|
||||||
child: const Padding(padding: EdgeInsets.symmetric(vertical: 5.0), child: Text('收藏')),
|
// child: const Padding(padding: EdgeInsets.symmetric(vertical: 5.0), child: Text('收藏')),
|
||||||
onPressed: () {},
|
// onPressed: () {},
|
||||||
),
|
// ),
|
||||||
SimpleDialogOption(
|
// SimpleDialogOption(
|
||||||
child: const Padding(padding: EdgeInsets.symmetric(vertical: 5.0), child: Text('删除')),
|
// child: const Padding(padding: EdgeInsets.symmetric(vertical: 5.0), child: Text('删除')),
|
||||||
onPressed: () {},
|
// onPressed: () {},
|
||||||
),
|
// ),
|
||||||
],
|
// ],
|
||||||
);
|
// );
|
||||||
},
|
// },
|
||||||
);
|
// );
|
||||||
}
|
}
|
||||||
|
|
||||||
// 发群红包弹窗
|
// 发群红包弹窗
|
||||||
|
@ -1,31 +1,34 @@
|
|||||||
/// 聊天模板
|
/// 群聊模板
|
||||||
library;
|
library;
|
||||||
|
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:get/get.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/chat_detail_controller.dart';
|
||||||
import 'package:loopin/IM/im_message.dart';
|
import 'package:loopin/IM/im_message.dart';
|
||||||
import 'package:loopin/IM/im_result.dart';
|
import 'package:loopin/IM/im_result.dart';
|
||||||
import 'package:loopin/IM/im_service.dart';
|
import 'package:loopin/IM/im_service.dart';
|
||||||
|
import 'package:loopin/components/image_viewer.dart';
|
||||||
import 'package:loopin/components/network_or_asset_image.dart';
|
import 'package:loopin/components/network_or_asset_image.dart';
|
||||||
import 'package:loopin/components/preview_video.dart';
|
import 'package:loopin/components/preview_video.dart';
|
||||||
import 'package:loopin/models/summary_type.dart';
|
import 'package:loopin/models/summary_type.dart';
|
||||||
import 'package:loopin/utils/audio_player_service.dart';
|
import 'package:loopin/utils/audio_player_service.dart';
|
||||||
import 'package:loopin/utils/snapshot.dart';
|
import 'package:loopin/utils/snapshot.dart';
|
||||||
import 'package:loopin/utils/voice_service.dart';
|
import 'package:loopin/utils/voice_service.dart';
|
||||||
|
import 'package:mime/mime.dart';
|
||||||
import 'package:shirne_dialog/shirne_dialog.dart';
|
import 'package:shirne_dialog/shirne_dialog.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart';
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.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 'package:wechat_assets_picker/wechat_assets_picker.dart';
|
||||||
|
|
||||||
import '../../styles/index.dart';
|
import '../../styles/index.dart';
|
||||||
import '../../utils/index.dart';
|
import '../../utils/index.dart';
|
||||||
import './components/redpacket.dart';
|
import './components/redpacket.dart';
|
||||||
import './components/richtext.dart';
|
import './components/richtext.dart';
|
||||||
// import 'mock/chat_json.dart';
|
|
||||||
import 'mock/emoj_json.dart';
|
import 'mock/emoj_json.dart';
|
||||||
|
|
||||||
class ChatGroup extends StatefulWidget {
|
class ChatGroup extends StatefulWidget {
|
||||||
@ -36,9 +39,11 @@ class ChatGroup extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMixin {
|
class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMixin {
|
||||||
|
final ImagePicker _picker = ImagePicker();
|
||||||
|
|
||||||
late final ChatDetailController controller;
|
late final ChatDetailController controller;
|
||||||
// 接收参数
|
// 接收参数
|
||||||
late V2TimConversation arguments;
|
final V2TimConversation arguments = Get.arguments;
|
||||||
|
|
||||||
late String selfUserId;
|
late String selfUserId;
|
||||||
|
|
||||||
@ -91,7 +96,6 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
arguments = Get.arguments;
|
|
||||||
controller = Get.find<ChatDetailController>();
|
controller = Get.find<ChatDetailController>();
|
||||||
chatController = controller.chatController;
|
chatController = controller.chatController;
|
||||||
|
|
||||||
@ -146,56 +150,6 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
|
|||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置好友备注
|
|
||||||
void setRemark() async {
|
|
||||||
String remark = '';
|
|
||||||
await MyDialog.confirm(
|
|
||||||
Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
TextField(
|
|
||||||
onChanged: (value) => remark = value,
|
|
||||||
maxLength: 16,
|
|
||||||
maxLengthEnforcement: MaxLengthEnforcement.enforced, // 强制不能输入超过
|
|
||||||
decoration: InputDecoration(
|
|
||||||
hintText: '请输入备注',
|
|
||||||
filled: true,
|
|
||||||
fillColor: const Color(0xFFF5F5F5),
|
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 14),
|
|
||||||
enabledBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
borderSide: BorderSide.none,
|
|
||||||
),
|
|
||||||
focusedBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
borderSide: BorderSide(color: Color(0xFFBE4EFF), width: 1),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
title: '设置备注',
|
|
||||||
buttonText: '确认',
|
|
||||||
cancelText: '取消',
|
|
||||||
onConfirm: () async {
|
|
||||||
// print('备注为:$remark');
|
|
||||||
final res = await ImService.instance.setFriendInfo(userID: arguments.userID!, friendRemark: remark);
|
|
||||||
if (res.success) {
|
|
||||||
setState(() {
|
|
||||||
arguments.showName = remark;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
print(res.desc);
|
|
||||||
print(arguments.userID);
|
|
||||||
MyDialog.toast(res.desc, icon: Icon(Icons.warning), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void cleanUnRead() async {
|
void cleanUnRead() async {
|
||||||
if ((arguments.unreadCount ?? 0) > 0) {
|
if ((arguments.unreadCount ?? 0) > 0) {
|
||||||
final res = await ImService.instance.clearConversationUnreadCount(conversationID: arguments.conversationID);
|
final res = await ImService.instance.clearConversationUnreadCount(conversationID: arguments.conversationID);
|
||||||
@ -219,7 +173,6 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
|
|||||||
|
|
||||||
// 获取最旧一条消息作为游标
|
// 获取最旧一条消息作为游标
|
||||||
V2TimMessage? lastRealMsg;
|
V2TimMessage? lastRealMsg;
|
||||||
// for (var msg in controller.chatList.reversed) {
|
|
||||||
for (var msg in controller.chatList.reversed) {
|
for (var msg in controller.chatList.reversed) {
|
||||||
if (msg.localCustomData != 'time_label') {
|
if (msg.localCustomData != 'time_label') {
|
||||||
lastRealMsg = msg;
|
lastRealMsg = msg;
|
||||||
@ -227,12 +180,10 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
final lastMsg = lastRealMsg ?? arguments.lastMessage; // 如果找不到,就用传入的参数
|
final lastMsg = lastRealMsg ?? arguments.lastMessage; // 如果找不到,就用传入的参数
|
||||||
print(lastMsg?.toLogString());
|
logger.w(lastMsg?.toLogString());
|
||||||
|
|
||||||
// final lastMsg = controller.chatList.isNotEmpty ? controller.chatList.last : arguments.lastMessage;
|
|
||||||
|
|
||||||
final res = await ImService.instance.getHistoryMessageList(
|
final res = await ImService.instance.getHistoryMessageList(
|
||||||
userID: arguments.userID,
|
groupID: arguments.groupID,
|
||||||
lastMsg: lastMsg,
|
lastMsg: lastMsg,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -241,26 +192,21 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
|
|||||||
|
|
||||||
if (newMessages.isEmpty) {
|
if (newMessages.isEmpty) {
|
||||||
hasMore = false;
|
hasMore = false;
|
||||||
// MyDialog.toast('没有更多了~', icon: Icon(Icons.warning), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
|
|
||||||
}
|
}
|
||||||
if (initFlag && lastMsg != null) {
|
if (initFlag && lastMsg != null) {
|
||||||
newMessages.insert(0, lastMsg);
|
newMessages.insert(0, lastMsg);
|
||||||
// controller.scrollToBottom();
|
|
||||||
}
|
}
|
||||||
controller.updateChatListWithTimeLabels(newMessages);
|
controller.updateChatListWithTimeLabels(newMessages);
|
||||||
if (initFlag) {
|
if (initFlag) {
|
||||||
// 初始化时滚到最底部
|
// 初始化时滚到最底部
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
if (chatController.hasClients) {
|
if (chatController.hasClients) {
|
||||||
// controller.scrollToBottom();
|
|
||||||
// final bottomPadding = MediaQuery.of(context).padding.bottom; // 底部安全区域高度
|
|
||||||
// chatController.jumpTo(chatController.position.maxScrollExtent); // 60为底部操作栏高度
|
|
||||||
chatController.jumpTo(0);
|
chatController.jumpTo(0);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
print('聊天数据加载成功');
|
logger.w('群聊消息加载成功');
|
||||||
} else {
|
} else {
|
||||||
MyDialog.toast("获取聊天记录失败:${res.desc}", icon: Icon(Icons.warning), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
|
MyDialog.toast("获取聊天记录失败:${res.desc}", icon: Icon(Icons.warning), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
|
||||||
}
|
}
|
||||||
@ -343,6 +289,13 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
|
|||||||
data: item,
|
data: item,
|
||||||
child: Ink(
|
child: Ink(
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
// 预览图片
|
||||||
|
Get.to(() => ImageViewer(
|
||||||
|
images: [imagePaths.first],
|
||||||
|
index: 0,
|
||||||
|
));
|
||||||
|
},
|
||||||
overlayColor: WidgetStateProperty.all(Colors.transparent),
|
overlayColor: WidgetStateProperty.all(Colors.transparent),
|
||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(10.0),
|
borderRadius: BorderRadius.circular(10.0),
|
||||||
@ -525,6 +478,8 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
|
|||||||
//price,title,url,sell
|
//price,title,url,sell
|
||||||
final obj = jsonDecode(item.customElem!.data!);
|
final obj = jsonDecode(item.customElem!.data!);
|
||||||
logger.e(obj);
|
logger.e(obj);
|
||||||
|
final goodsId = obj['goodsId'];
|
||||||
|
final userID = obj['userID'];
|
||||||
final url = obj['url'];
|
final url = obj['url'];
|
||||||
final title = obj['title'];
|
final title = obj['title'];
|
||||||
final price = obj['price'];
|
final price = obj['price'];
|
||||||
@ -532,6 +487,10 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
|
|||||||
msgtpl.add(RenderChatItem(
|
msgtpl.add(RenderChatItem(
|
||||||
data: item,
|
data: item,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
// 这里带上分享人的ID
|
||||||
|
Get.toNamed('/goods', arguments: {'goodsId': goodsId, 'userID': userID});
|
||||||
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
width: 160,
|
width: 160,
|
||||||
clipBehavior: Clip.antiAlias,
|
clipBehavior: Clip.antiAlias,
|
||||||
@ -593,11 +552,6 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onTap: () {
|
|
||||||
// 这里带上分享人的ID
|
|
||||||
// Get.toNamed('/goods');
|
|
||||||
Get.toNamed('/goods', arguments: {});
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -606,10 +560,12 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
|
|||||||
/// {imgUrl,videoUrl,width,height}
|
/// {imgUrl,videoUrl,width,height}
|
||||||
final obj = jsonDecode(item.customElem!.data!);
|
final obj = jsonDecode(item.customElem!.data!);
|
||||||
logger.e(obj);
|
logger.e(obj);
|
||||||
|
final videoId = obj['videoId'];
|
||||||
final videoUrl = obj['videoUrl'];
|
final videoUrl = obj['videoUrl'];
|
||||||
final imgUrl = obj['imgUrl'];
|
final imgUrl = obj['imgUrl'];
|
||||||
final width = obj['width'] as num;
|
final width = obj['width'] as num;
|
||||||
final height = obj['height'] as num;
|
final height = obj['height'] as num;
|
||||||
|
final isHorizontal = width > height;
|
||||||
msgtpl.add(RenderChatItem(
|
msgtpl.add(RenderChatItem(
|
||||||
data: item,
|
data: item,
|
||||||
child: Ink(
|
child: Ink(
|
||||||
@ -622,9 +578,15 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
|
|||||||
children: [
|
children: [
|
||||||
ClipRRect(
|
ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(10.0),
|
borderRadius: BorderRadius.circular(10.0),
|
||||||
|
child: Container(
|
||||||
|
width: 120,
|
||||||
|
height: 240,
|
||||||
|
color: Colors.black,
|
||||||
child: NetworkOrAssetImage(
|
child: NetworkOrAssetImage(
|
||||||
imageUrl: imgUrl,
|
imageUrl: imgUrl,
|
||||||
width: 120,
|
fit: isHorizontal ? BoxFit.contain : BoxFit.cover,
|
||||||
|
placeholderAsset: 'assets/images/bk.jpg',
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Align(
|
const Align(
|
||||||
@ -639,31 +601,32 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
showGeneralDialog(
|
Get.toNamed('/videoDetail', arguments: {'videoId': videoId});
|
||||||
context: context,
|
// showGeneralDialog(
|
||||||
barrierColor: Colors.black.withAlpha((1.0 * 255).round()),
|
// context: context,
|
||||||
pageBuilder: (_, __, ___) {
|
// barrierColor: Colors.black.withAlpha((1.0 * 255).round()),
|
||||||
return SafeArea(
|
// pageBuilder: (_, __, ___) {
|
||||||
bottom: true,
|
// return SafeArea(
|
||||||
child: Padding(
|
// bottom: true,
|
||||||
padding: const EdgeInsets.only(bottom: 4),
|
// child: Padding(
|
||||||
child: PreviewVideo(
|
// padding: const EdgeInsets.only(bottom: 4),
|
||||||
videoUrl: videoUrl,
|
// child: PreviewVideo(
|
||||||
width: width.toDouble(),
|
// videoUrl: videoUrl,
|
||||||
height: height.toDouble(),
|
// width: width.toDouble(),
|
||||||
),
|
// height: height.toDouble(),
|
||||||
),
|
// ),
|
||||||
);
|
// ),
|
||||||
},
|
// );
|
||||||
transitionBuilder: (_, anim, __, child) {
|
// },
|
||||||
return FadeTransition(opacity: anim, child: child);
|
// transitionBuilder: (_, anim, __, child) {
|
||||||
},
|
// return FadeTransition(opacity: anim, child: child);
|
||||||
transitionDuration: const Duration(milliseconds: 200),
|
// },
|
||||||
);
|
// transitionDuration: const Duration(milliseconds: 200),
|
||||||
},
|
// );
|
||||||
onLongPress: () {
|
|
||||||
contextMenuDialog();
|
|
||||||
},
|
},
|
||||||
|
// onLongPress: () {
|
||||||
|
// contextMenuDialog();
|
||||||
|
// },
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
@ -1019,7 +982,7 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 发送消息队列
|
// 发送消息队列
|
||||||
void sendMessage(message) async {
|
Future<void> sendMessage(message) async {
|
||||||
// 待插入的消息
|
// 待插入的消息
|
||||||
List<V2TimMessage> messagesToInsert = [];
|
List<V2TimMessage> messagesToInsert = [];
|
||||||
V2TimMessage? lastRealMsg;
|
V2TimMessage? lastRealMsg;
|
||||||
@ -1054,7 +1017,7 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
|
|||||||
late final ImResult res;
|
late final ImResult res;
|
||||||
res = await IMMessage().sendMessage(
|
res = await IMMessage().sendMessage(
|
||||||
msg: message,
|
msg: message,
|
||||||
toUserID: arguments.userID,
|
toUserID: arguments.groupID,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (res.success && res.data != null) {
|
if (res.success && res.data != null) {
|
||||||
@ -1189,6 +1152,8 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
|
|||||||
|
|
||||||
// 发送视频消息=5
|
// 发送视频消息=5
|
||||||
void sendVideo(videoFilePath, type, duration, snapshotPath) async {
|
void sendVideo(videoFilePath, type, duration, snapshotPath) async {
|
||||||
|
final instance = MyDialog.loading('处理中');
|
||||||
|
|
||||||
final resImg = await IMMessage().createVideoMessage(
|
final resImg = await IMMessage().createVideoMessage(
|
||||||
videoFilePath: videoFilePath,
|
videoFilePath: videoFilePath,
|
||||||
type: type,
|
type: type,
|
||||||
@ -1196,13 +1161,116 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
|
|||||||
snapshotPath: snapshotPath,
|
snapshotPath: snapshotPath,
|
||||||
);
|
);
|
||||||
if (resImg.success) {
|
if (resImg.success) {
|
||||||
sendMessage(resImg.data?.messageInfo);
|
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 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 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) {
|
void handleChooseAction(key) {
|
||||||
MyDialog.toast('$key');
|
// MyDialog.toast('$key');
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case 'photo':
|
case 'photo':
|
||||||
// ....
|
// ....
|
||||||
@ -1210,6 +1278,7 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
|
|||||||
break;
|
break;
|
||||||
case 'camera':
|
case 'camera':
|
||||||
// ....
|
// ....
|
||||||
|
showPicker(context);
|
||||||
break;
|
break;
|
||||||
case 'redpacket':
|
case 'redpacket':
|
||||||
sendRedPacketDialog();
|
sendRedPacketDialog();
|
||||||
@ -1418,35 +1487,35 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
|
|||||||
|
|
||||||
// 长按消息菜单
|
// 长按消息菜单
|
||||||
void contextMenuDialog() {
|
void contextMenuDialog() {
|
||||||
showDialog(
|
// showDialog(
|
||||||
context: context,
|
// context: context,
|
||||||
builder: (context) {
|
// builder: (context) {
|
||||||
return SimpleDialog(
|
// return SimpleDialog(
|
||||||
backgroundColor: Colors.white,
|
// backgroundColor: Colors.white,
|
||||||
surfaceTintColor: Colors.white,
|
// surfaceTintColor: Colors.white,
|
||||||
contentPadding: const EdgeInsets.symmetric(vertical: 5.0),
|
// contentPadding: const EdgeInsets.symmetric(vertical: 5.0),
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.0)),
|
// shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.0)),
|
||||||
children: [
|
// children: [
|
||||||
SimpleDialogOption(
|
// SimpleDialogOption(
|
||||||
child: const Padding(padding: EdgeInsets.symmetric(vertical: 5.0), child: Text('复制')),
|
// child: const Padding(padding: EdgeInsets.symmetric(vertical: 5.0), child: Text('复制')),
|
||||||
onPressed: () {},
|
// onPressed: () {},
|
||||||
),
|
// ),
|
||||||
SimpleDialogOption(
|
// SimpleDialogOption(
|
||||||
child: const Padding(padding: EdgeInsets.symmetric(vertical: 5.0), child: Text('发送给朋友')),
|
// child: const Padding(padding: EdgeInsets.symmetric(vertical: 5.0), child: Text('发送给朋友')),
|
||||||
onPressed: () {},
|
// onPressed: () {},
|
||||||
),
|
// ),
|
||||||
SimpleDialogOption(
|
// SimpleDialogOption(
|
||||||
child: const Padding(padding: EdgeInsets.symmetric(vertical: 5.0), child: Text('收藏')),
|
// child: const Padding(padding: EdgeInsets.symmetric(vertical: 5.0), child: Text('收藏')),
|
||||||
onPressed: () {},
|
// onPressed: () {},
|
||||||
),
|
// ),
|
||||||
SimpleDialogOption(
|
// SimpleDialogOption(
|
||||||
child: const Padding(padding: EdgeInsets.symmetric(vertical: 5.0), child: Text('删除')),
|
// child: const Padding(padding: EdgeInsets.symmetric(vertical: 5.0), child: Text('删除')),
|
||||||
onPressed: () {},
|
// onPressed: () {},
|
||||||
),
|
// ),
|
||||||
],
|
// ],
|
||||||
);
|
// );
|
||||||
},
|
// },
|
||||||
);
|
// );
|
||||||
}
|
}
|
||||||
|
|
||||||
// 发群红包弹窗
|
// 发群红包弹窗
|
||||||
@ -1496,13 +1565,13 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
titleSpacing: 1.0,
|
titleSpacing: 1.0,
|
||||||
title: Obx(() {
|
title: Text(
|
||||||
return Text(
|
|
||||||
// '${arguments['title']}',
|
|
||||||
'${arguments.showName}',
|
'${arguments.showName}',
|
||||||
style: const TextStyle(fontSize: 18.0, fontFamily: 'Arial'),
|
style: const TextStyle(fontSize: 18.0, fontFamily: 'Arial'),
|
||||||
);
|
maxLines: 1,
|
||||||
}),
|
overflow: TextOverflow.ellipsis,
|
||||||
|
softWrap: false,
|
||||||
|
),
|
||||||
flexibleSpace: Container(
|
flexibleSpace: Container(
|
||||||
decoration: const BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
gradient: LinearGradient(
|
gradient: LinearGradient(
|
||||||
@ -1603,7 +1672,6 @@ class _ChatGroupState extends State<ChatGroup> with SingleTickerProviderStateMix
|
|||||||
switch (selected) {
|
switch (selected) {
|
||||||
case 'remark':
|
case 'remark':
|
||||||
print('点击了备注');
|
print('点击了备注');
|
||||||
setRemark();
|
|
||||||
break;
|
break;
|
||||||
case 'not':
|
case 'not':
|
||||||
print('点击了免打扰');
|
print('点击了免打扰');
|
||||||
@ -2189,13 +2257,13 @@ class RenderChatItem extends StatelessWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: !(data.isSelf ?? false) ? CrossAxisAlignment.start : CrossAxisAlignment.end,
|
crossAxisAlignment: !(data.isSelf ?? false) ? CrossAxisAlignment.start : CrossAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
// Text(
|
Text(
|
||||||
// displayName ?? '未知昵称',
|
displayName ?? '未知昵称',
|
||||||
// style: const TextStyle(color: Colors.grey, fontSize: 12.0),
|
style: const TextStyle(color: Colors.grey, fontSize: 12.0),
|
||||||
// ),
|
),
|
||||||
// const SizedBox(
|
const SizedBox(
|
||||||
// height: 3.0,
|
height: 3.0,
|
||||||
// ),
|
),
|
||||||
Stack(
|
Stack(
|
||||||
children: [
|
children: [
|
||||||
// 气泡箭头
|
// 气泡箭头
|
||||||
|
@ -270,7 +270,12 @@ class ChatPageState extends State<ChatPage> {
|
|||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
children: [
|
children: [
|
||||||
Column(
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
// 去群聊列表
|
||||||
|
Get.toNamed('/groupList');
|
||||||
|
},
|
||||||
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
SvgPicture.asset(
|
SvgPicture.asset(
|
||||||
'assets/images/svg/order.svg',
|
'assets/images/svg/order.svg',
|
||||||
@ -280,6 +285,7 @@ class ChatPageState extends State<ChatPage> {
|
|||||||
Text('群聊'),
|
Text('群聊'),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
//互关
|
//互关
|
||||||
@ -351,6 +357,7 @@ class ChatPageState extends State<ChatPage> {
|
|||||||
return Ink(
|
return Ink(
|
||||||
// color: chatList[index]['topMost'] == null ? Colors.white : Colors.grey[100], //置顶颜色
|
// color: chatList[index]['topMost'] == null ? Colors.white : Colors.grey[100], //置顶颜色
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
|
key: ValueKey(chatList[index].conversation.conversationID),
|
||||||
splashColor: Colors.grey[200],
|
splashColor: Colors.grey[200],
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 10.0),
|
padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 10.0),
|
||||||
|
@ -8,6 +8,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_html/flutter_html.dart';
|
import 'package:flutter_html/flutter_html.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:loopin/IM/controller/chat_controller.dart';
|
import 'package:loopin/IM/controller/chat_controller.dart';
|
||||||
|
import 'package:loopin/IM/controller/im_user_info_controller.dart';
|
||||||
import 'package:loopin/IM/im_message.dart';
|
import 'package:loopin/IM/im_message.dart';
|
||||||
import 'package:loopin/IM/im_service.dart';
|
import 'package:loopin/IM/im_service.dart';
|
||||||
import 'package:loopin/api/shop_api.dart';
|
import 'package:loopin/api/shop_api.dart';
|
||||||
@ -33,7 +34,7 @@ class Goods extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _GoodsState extends State<Goods> {
|
class _GoodsState extends State<Goods> {
|
||||||
// late int shopId; //商品id
|
final shareUserId = Get.arguments['userID'] ?? ''; //分享人的id,生成订单请求时必须携带的参数
|
||||||
dynamic shopObj;
|
dynamic shopObj;
|
||||||
late ScrollController scrollController = ScrollController();
|
late ScrollController scrollController = ScrollController();
|
||||||
final ChatController chatController = Get.find<ChatController>();
|
final ChatController chatController = Get.find<ChatController>();
|
||||||
@ -124,6 +125,8 @@ class _GoodsState extends State<Goods> {
|
|||||||
"title": shopObj['name'],
|
"title": shopObj['name'],
|
||||||
"url": shopObj['pic'],
|
"url": shopObj['pic'],
|
||||||
"sell": Utils.graceNumber(int.parse(shopObj['sales'] ?? '0')),
|
"sell": Utils.graceNumber(int.parse(shopObj['sales'] ?? '0')),
|
||||||
|
"goodsId": shopObj['id'],
|
||||||
|
"userID": Get.find<ImUserInfoController>().userID.value,
|
||||||
});
|
});
|
||||||
final res = await IMMessage().createCustomMessage(
|
final res = await IMMessage().createCustomMessage(
|
||||||
data: makeJson,
|
data: makeJson,
|
||||||
|
202
lib/pages/groupChat/groupList.dart
Normal file
202
lib/pages/groupChat/groupList.dart
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
/// 已加入的群列表
|
||||||
|
library;
|
||||||
|
|
||||||
|
import 'package:easy_refresh/easy_refresh.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:loopin/IM/im_service.dart';
|
||||||
|
import 'package:loopin/behavior/custom_scroll_behavior.dart';
|
||||||
|
import 'package:loopin/components/network_or_asset_image.dart';
|
||||||
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart';
|
||||||
|
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_info.dart';
|
||||||
|
|
||||||
|
class Grouplist extends StatefulWidget {
|
||||||
|
const Grouplist({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<Grouplist> createState() => GrouplistState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class GrouplistState extends State<Grouplist> with SingleTickerProviderStateMixin {
|
||||||
|
bool isLoading = false; // 是否在加载中
|
||||||
|
bool hasMore = true; // 是否还有更多数据
|
||||||
|
String page = '';
|
||||||
|
List<V2TimGroupInfo> dataList = <V2TimGroupInfo>[];
|
||||||
|
|
||||||
|
///-------------------
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
getData();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页获取已加入的群聊数据
|
||||||
|
Future<void> getData() async {
|
||||||
|
final res = await ImService.instance.getJoinedGroupList();
|
||||||
|
if (res.success && res.data != null) {
|
||||||
|
for (var item in res.data!) {
|
||||||
|
logger.i('获取成功:${item.toLogString()}');
|
||||||
|
}
|
||||||
|
|
||||||
|
final isFinished = true;
|
||||||
|
if (isFinished) {
|
||||||
|
setState(() {
|
||||||
|
hasMore = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setState(() {
|
||||||
|
dataList.addAll(res.data!);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
logger.e('获取数据失败:${res.desc}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下拉刷新
|
||||||
|
Future<void> handleRefresh() async {
|
||||||
|
dataList.clear();
|
||||||
|
page = '';
|
||||||
|
getData();
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: Colors.grey[50],
|
||||||
|
appBar: AppBar(
|
||||||
|
centerTitle: true,
|
||||||
|
forceMaterialTransparency: true,
|
||||||
|
bottom: PreferredSize(
|
||||||
|
preferredSize: const Size.fromHeight(1.0),
|
||||||
|
child: Container(
|
||||||
|
color: Colors.grey[300],
|
||||||
|
height: 1.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
title: const Text(
|
||||||
|
'群聊',
|
||||||
|
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
actions: [],
|
||||||
|
),
|
||||||
|
body: ScrollConfiguration(
|
||||||
|
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: EasyRefresh.builder(
|
||||||
|
callLoadOverOffset: 20, //触底距离
|
||||||
|
callRefreshOverOffset: 20, // 下拉距离
|
||||||
|
header: ClassicHeader(
|
||||||
|
dragText: '下拉刷新',
|
||||||
|
armedText: '释放刷新',
|
||||||
|
readyText: '加载中...',
|
||||||
|
processingText: '加载中...',
|
||||||
|
processedText: '加载完成',
|
||||||
|
failedText: '加载失败,请重试',
|
||||||
|
messageText: '最后更新于 %T',
|
||||||
|
),
|
||||||
|
footer: ClassicFooter(
|
||||||
|
dragText: '加载更多',
|
||||||
|
armedText: '释放加载',
|
||||||
|
readyText: '加载中...',
|
||||||
|
processingText: '加载中...',
|
||||||
|
processedText: hasMore ? '加载完成' : '没有更多了~',
|
||||||
|
failedText: '加载失败,请重试',
|
||||||
|
messageText: '最后更新于 %T',
|
||||||
|
),
|
||||||
|
onRefresh: () async {
|
||||||
|
await handleRefresh();
|
||||||
|
},
|
||||||
|
onLoad: () async {
|
||||||
|
if (hasMore) {
|
||||||
|
await getData();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
childBuilder: (context, physics) {
|
||||||
|
return ListView.builder(
|
||||||
|
physics: physics,
|
||||||
|
itemCount: dataList.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final item = dataList[index];
|
||||||
|
return Ink(
|
||||||
|
key: ValueKey(item.groupID),
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 10.0),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
// 左侧部分(头像 + 群名称 + 群介绍)
|
||||||
|
Expanded(
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () async {
|
||||||
|
// 这里点击跳转去群聊,先获取会话
|
||||||
|
final res = await ImService.instance.getConversation(conversationID: 'group_${item.groupID}');
|
||||||
|
if (res.success) {
|
||||||
|
V2TimConversation conversation = res.data;
|
||||||
|
logger.w(conversation.toLogString());
|
||||||
|
conversation.showName = conversation.showName ?? item.groupName;
|
||||||
|
Get.toNamed(
|
||||||
|
'/chatGroup',
|
||||||
|
arguments: conversation,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
ClipOval(
|
||||||
|
child: NetworkOrAssetImage(
|
||||||
|
imageUrl: item.faceUrl,
|
||||||
|
width: 50,
|
||||||
|
height: 50,
|
||||||
|
placeholderAsset: 'assets/images/group.png',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
Text(
|
||||||
|
item.groupName?.isNotEmpty == true ? item.groupName! : '群聊',
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.normal,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (item.introduction?.isNotEmpty ?? false) ...[
|
||||||
|
const SizedBox(height: 2.0),
|
||||||
|
Text(
|
||||||
|
item.introduction!,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.grey,
|
||||||
|
fontSize: 13.0,
|
||||||
|
),
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -2,14 +2,17 @@
|
|||||||
library;
|
library;
|
||||||
|
|
||||||
import 'package:card_swiper/card_swiper.dart';
|
import 'package:card_swiper/card_swiper.dart';
|
||||||
|
import 'package:dynamic_tabbar/dynamic_tabbar.dart';
|
||||||
import 'package:easy_refresh/easy_refresh.dart';
|
import 'package:easy_refresh/easy_refresh.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
|
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:loopin/IM/im_friend_listeners.dart';
|
||||||
import 'package:loopin/components/backtop.dart';
|
import 'package:loopin/components/backtop.dart';
|
||||||
import 'package:loopin/components/loading.dart';
|
import 'package:loopin/components/loading.dart';
|
||||||
import 'package:loopin/components/network_or_asset_image.dart';
|
import 'package:loopin/components/network_or_asset_image.dart';
|
||||||
import 'package:loopin/controller/shop_index_controller.dart';
|
import 'package:loopin/controller/shop_index_controller.dart';
|
||||||
|
import 'package:loopin/styles/index.dart';
|
||||||
import 'package:loopin/utils/index.dart';
|
import 'package:loopin/utils/index.dart';
|
||||||
|
|
||||||
class IndexPage extends StatefulWidget {
|
class IndexPage extends StatefulWidget {
|
||||||
@ -127,15 +130,64 @@ class _IndexPageState extends State<IndexPage> with SingleTickerProviderStateMix
|
|||||||
// 内容区域
|
// 内容区域
|
||||||
Expanded(
|
Expanded(
|
||||||
child: controller.tabController == null
|
child: controller.tabController == null
|
||||||
? const Center(child: CircularProgressIndicator())
|
? Center(child: CircularProgressIndicator())
|
||||||
: TabBarView(
|
: Obx(
|
||||||
controller: controller.tabController,
|
() {
|
||||||
children: List.generate(
|
final tabs = controller.tabList.asMap().entries.map((entry) {
|
||||||
controller.tabList.length,
|
final idx = entry.key;
|
||||||
(index) => _buildTabContent(index),
|
final item = entry.value;
|
||||||
|
return TabData(
|
||||||
|
index: idx,
|
||||||
|
title: Tab(
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
item['name'] ?? '',
|
||||||
|
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
softWrap: false,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
content: _buildTabContent(idx),
|
||||||
|
);
|
||||||
|
}).toList();
|
||||||
|
return DynamicTabBarWidget(
|
||||||
|
onTabControllerUpdated: (tabController) {
|
||||||
|
controller.tabController = tabController;
|
||||||
|
// 强制选中第一个
|
||||||
|
if (tabController.index != 0) {
|
||||||
|
tabController.animateTo(0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onTabChanged: (index) {
|
||||||
|
logger.w("当前选中tab: $index");
|
||||||
|
},
|
||||||
|
dynamicTabs: tabs,
|
||||||
|
tabAlignment: TabAlignment.start,
|
||||||
|
isScrollable: true,
|
||||||
|
showNextIcon: false, // 显示前进图标
|
||||||
|
showBackIcon: false, // 显示后退图标
|
||||||
|
labelColor: FStyle.primaryColor,
|
||||||
|
indicatorColor: FStyle.primaryColor,
|
||||||
|
overlayColor: WidgetStateProperty.all(Colors.transparent),
|
||||||
|
unselectedLabelColor: Colors.black87,
|
||||||
|
indicator: const UnderlineTabIndicator(
|
||||||
|
borderSide: BorderSide(color: Color.fromARGB(255, 236, 108, 49), width: 2.0),
|
||||||
|
),
|
||||||
|
unselectedLabelStyle: const TextStyle(
|
||||||
|
fontSize: 14.0,
|
||||||
|
fontFamily: 'Microsoft YaHei',
|
||||||
|
),
|
||||||
|
labelStyle: const TextStyle(
|
||||||
|
fontSize: 14.0,
|
||||||
|
fontFamily: 'Microsoft YaHei',
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
dividerHeight: 0,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
floatingActionButton: Obx(() {
|
floatingActionButton: Obx(() {
|
||||||
@ -156,7 +208,6 @@ class _IndexPageState extends State<IndexPage> with SingleTickerProviderStateMix
|
|||||||
if (controller.tabController == null) {
|
if (controller.tabController == null) {
|
||||||
return SizedBox();
|
return SizedBox();
|
||||||
}
|
}
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
// 轮播图
|
// 轮播图
|
||||||
@ -186,48 +237,6 @@ class _IndexPageState extends State<IndexPage> with SingleTickerProviderStateMix
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
|
|
||||||
// TabBar
|
|
||||||
Obx(() {
|
|
||||||
int tabCount = controller.tabList.length;
|
|
||||||
double screenWidth = MediaQuery.of(context).size.width;
|
|
||||||
double minTabWidth = 60;
|
|
||||||
bool isScrollable = tabCount * minTabWidth > screenWidth;
|
|
||||||
|
|
||||||
return Container(
|
|
||||||
color: Colors.white,
|
|
||||||
child: TabBar(
|
|
||||||
controller: controller.tabController,
|
|
||||||
tabs: controller.tabList.map((item) {
|
|
||||||
return Tab(
|
|
||||||
child: Text(
|
|
||||||
item['name'] ?? '',
|
|
||||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
softWrap: false,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}).toList(),
|
|
||||||
isScrollable: isScrollable,
|
|
||||||
overlayColor: WidgetStateProperty.all(Colors.transparent),
|
|
||||||
unselectedLabelColor: Colors.black87,
|
|
||||||
labelColor: const Color.fromARGB(255, 236, 108, 49),
|
|
||||||
indicator: const UnderlineTabIndicator(
|
|
||||||
borderSide: BorderSide(color: Color.fromARGB(255, 236, 108, 49), width: 2.0),
|
|
||||||
),
|
|
||||||
unselectedLabelStyle: const TextStyle(
|
|
||||||
fontSize: 14.0,
|
|
||||||
fontFamily: 'Microsoft YaHei',
|
|
||||||
),
|
|
||||||
labelStyle: const TextStyle(
|
|
||||||
fontSize: 16.0,
|
|
||||||
fontFamily: 'Microsoft YaHei',
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
dividerHeight: 0,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -554,6 +554,7 @@ class MyPageState extends State<MyPage> with SingleTickerProviderStateMixin {
|
|||||||
return InkWell(
|
return InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
//去视频详情
|
//去视频详情
|
||||||
|
Get.toNamed('/videoDetail', arguments: {'videoId': item['id']});
|
||||||
},
|
},
|
||||||
onLongPress: () {
|
onLongPress: () {
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
|
@ -6,18 +6,19 @@ import 'package:flutter/services.dart';
|
|||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:loopin/IM/controller/chat_controller.dart';
|
import 'package:loopin/IM/controller/chat_controller.dart';
|
||||||
|
import 'package:loopin/IM/controller/im_user_info_controller.dart';
|
||||||
import 'package:loopin/IM/im_core.dart';
|
import 'package:loopin/IM/im_core.dart';
|
||||||
import 'package:loopin/IM/im_message.dart';
|
import 'package:loopin/IM/im_message.dart';
|
||||||
import 'package:loopin/IM/im_service.dart' hide logger;
|
import 'package:loopin/IM/im_service.dart' hide logger;
|
||||||
import 'package:loopin/api/video_api.dart';
|
import 'package:loopin/api/video_api.dart';
|
||||||
import 'package:loopin/components/my_toast.dart';
|
import 'package:loopin/components/my_toast.dart';
|
||||||
import 'package:loopin/components/network_or_asset_image.dart';
|
import 'package:loopin/components/network_or_asset_image.dart';
|
||||||
|
import 'package:loopin/models/share_type.dart';
|
||||||
import 'package:loopin/models/summary_type.dart';
|
import 'package:loopin/models/summary_type.dart';
|
||||||
import 'package:loopin/service/http.dart';
|
import 'package:loopin/service/http.dart';
|
||||||
import 'package:loopin/utils/download_video.dart';
|
import 'package:loopin/utils/download_video.dart';
|
||||||
import 'package:loopin/utils/permissions.dart';
|
import 'package:loopin/utils/permissions.dart';
|
||||||
import 'package:loopin/utils/wxsdk.dart';
|
import 'package:loopin/utils/wxsdk.dart';
|
||||||
import 'package:loopin/models/share_type.dart';
|
|
||||||
import 'package:media_kit/media_kit.dart';
|
import 'package:media_kit/media_kit.dart';
|
||||||
import 'package:media_kit_video/media_kit_video.dart';
|
import 'package:media_kit_video/media_kit_video.dart';
|
||||||
import 'package:media_kit_video/media_kit_video_controls/src/controls/extensions/duration.dart';
|
import 'package:media_kit_video/media_kit_video_controls/src/controls/extensions/duration.dart';
|
||||||
@ -497,15 +498,19 @@ class _VideoDetailPageState extends State<VideoDetailPage> {
|
|||||||
void handlCoverClick(V2TimConversation conv) async {
|
void handlCoverClick(V2TimConversation conv) async {
|
||||||
// 发送VideoMsg,获取当前视频信息
|
// 发送VideoMsg,获取当前视频信息
|
||||||
final userId = conv.userID;
|
final userId = conv.userID;
|
||||||
|
logger.w(videoData);
|
||||||
final String url = videoData['url'];
|
final String url = videoData['url'];
|
||||||
final img = videoData['firstFrameImg'];
|
final img = (videoData['cover'] != null && videoData['cover'].toString().isNotEmpty) ? videoData['cover'] : videoData['firstFrameImg'];
|
||||||
final width = videoData['width'];
|
final width = videoData['width'];
|
||||||
final height = videoData['height'];
|
final height = videoData['height'];
|
||||||
|
final videoId = videoData['id'];
|
||||||
final makeJson = jsonEncode({
|
final makeJson = jsonEncode({
|
||||||
"width": width,
|
"width": width,
|
||||||
"height": height,
|
"height": height,
|
||||||
"imgUrl": img,
|
"imgUrl": img,
|
||||||
"videoUrl": url,
|
"videoUrl": url,
|
||||||
|
"videoId": videoId,
|
||||||
|
"userID": Get.find<ImUserInfoController>().userID.value,
|
||||||
});
|
});
|
||||||
final res = await IMMessage().createCustomMessage(
|
final res = await IMMessage().createCustomMessage(
|
||||||
data: makeJson,
|
data: makeJson,
|
||||||
@ -570,7 +575,9 @@ class _VideoDetailPageState extends State<VideoDetailPage> {
|
|||||||
onPressed: () => Navigator.pop(context),
|
onPressed: () => Navigator.pop(context),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
body: Stack(
|
body: SafeArea(
|
||||||
|
bottom: true,
|
||||||
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
// 视频区域
|
// 视频区域
|
||||||
Positioned.fill(
|
Positioned.fill(
|
||||||
@ -908,6 +915,7 @@ class _VideoDetailPageState extends State<VideoDetailPage> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,18 +9,19 @@ import 'package:flutter/services.dart';
|
|||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:loopin/IM/controller/chat_controller.dart';
|
import 'package:loopin/IM/controller/chat_controller.dart';
|
||||||
|
import 'package:loopin/IM/controller/im_user_info_controller.dart';
|
||||||
import 'package:loopin/IM/im_core.dart';
|
import 'package:loopin/IM/im_core.dart';
|
||||||
import 'package:loopin/IM/im_message.dart';
|
import 'package:loopin/IM/im_message.dart';
|
||||||
import 'package:loopin/IM/im_service.dart' hide logger;
|
import 'package:loopin/IM/im_service.dart' hide logger;
|
||||||
import 'package:loopin/api/video_api.dart';
|
import 'package:loopin/api/video_api.dart';
|
||||||
import 'package:loopin/components/my_toast.dart';
|
import 'package:loopin/components/my_toast.dart';
|
||||||
import 'package:loopin/components/network_or_asset_image.dart';
|
import 'package:loopin/components/network_or_asset_image.dart';
|
||||||
|
import 'package:loopin/models/share_type.dart';
|
||||||
import 'package:loopin/models/summary_type.dart';
|
import 'package:loopin/models/summary_type.dart';
|
||||||
import 'package:loopin/service/http.dart';
|
import 'package:loopin/service/http.dart';
|
||||||
import 'package:loopin/utils/download_video.dart';
|
import 'package:loopin/utils/download_video.dart';
|
||||||
import 'package:loopin/utils/permissions.dart';
|
import 'package:loopin/utils/permissions.dart';
|
||||||
import 'package:loopin/utils/wxsdk.dart';
|
import 'package:loopin/utils/wxsdk.dart';
|
||||||
import 'package:loopin/models/share_type.dart';
|
|
||||||
import 'package:media_kit/media_kit.dart';
|
import 'package:media_kit/media_kit.dart';
|
||||||
import 'package:media_kit_video/media_kit_video.dart';
|
import 'package:media_kit_video/media_kit_video.dart';
|
||||||
import 'package:media_kit_video/media_kit_video_controls/src/controls/extensions/duration.dart';
|
import 'package:media_kit_video/media_kit_video_controls/src/controls/extensions/duration.dart';
|
||||||
@ -982,15 +983,20 @@ class _FriendModuleState extends State<FriendModule> {
|
|||||||
void handlCoverClick(V2TimConversation conv) async {
|
void handlCoverClick(V2TimConversation conv) async {
|
||||||
// 发送VideoMsg,获取当前视频信息
|
// 发送VideoMsg,获取当前视频信息
|
||||||
final userId = conv.userID;
|
final userId = conv.userID;
|
||||||
final String url = videoList[videoModuleController.videoPlayIndex.value]['url'];
|
final currentVideo = videoList[videoModuleController.videoPlayIndex.value];
|
||||||
final img = videoList[videoModuleController.videoPlayIndex.value]['firstFrameImg'];
|
logger.w(currentVideo);
|
||||||
final width = videoList[videoModuleController.videoPlayIndex.value]['width'];
|
final img = (currentVideo['cover'] != null && currentVideo['cover'].toString().isNotEmpty) ? currentVideo['cover'] : currentVideo['firstFrameImg'];
|
||||||
final height = videoList[videoModuleController.videoPlayIndex.value]['height'];
|
final url = currentVideo['url'];
|
||||||
|
final width = currentVideo['width'];
|
||||||
|
final height = currentVideo['height'];
|
||||||
|
final videoId = currentVideo['id'];
|
||||||
final makeJson = jsonEncode({
|
final makeJson = jsonEncode({
|
||||||
"width": width,
|
"width": width,
|
||||||
"height": height,
|
"height": height,
|
||||||
"imgUrl": img,
|
"imgUrl": img,
|
||||||
"videoUrl": url,
|
"videoUrl": url,
|
||||||
|
"videoId": videoId,
|
||||||
|
"userID": Get.find<ImUserInfoController>().userID.value,
|
||||||
});
|
});
|
||||||
final res = await IMMessage().createCustomMessage(
|
final res = await IMMessage().createCustomMessage(
|
||||||
data: makeJson,
|
data: makeJson,
|
||||||
@ -1265,16 +1271,13 @@ class _FriendModuleState extends State<FriendModule> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
onTap: ()async {
|
onTap: () async {
|
||||||
player.pause();
|
player.pause();
|
||||||
// 跳转到举报页面并等待返回结果
|
// 跳转到举报页面并等待返回结果
|
||||||
final result = await Get.toNamed(
|
final result = await Get.toNamed('/report', arguments: videoList[videoModuleController.videoPlayIndex.value]);
|
||||||
'/report',
|
|
||||||
arguments: videoList[videoModuleController
|
|
||||||
.videoPlayIndex.value]);
|
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
player.play();
|
player.play();
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -1312,8 +1315,9 @@ class _FriendModuleState extends State<FriendModule> {
|
|||||||
Text(
|
Text(
|
||||||
text,
|
text,
|
||||||
maxLines: videoList[videoModuleController.videoPlayIndex.value]['expanded'] ? null : 3,
|
maxLines: videoList[videoModuleController.videoPlayIndex.value]['expanded'] ? null : 3,
|
||||||
overflow:
|
overflow: videoList[videoModuleController.videoPlayIndex.value]['expanded']
|
||||||
videoList[videoModuleController.videoPlayIndex.value]['expanded'] ? TextOverflow.visible : TextOverflow.ellipsis,
|
? TextOverflow.visible
|
||||||
|
: TextOverflow.ellipsis,
|
||||||
style: const TextStyle(color: Colors.white, fontSize: 14.0),
|
style: const TextStyle(color: Colors.white, fontSize: 14.0),
|
||||||
),
|
),
|
||||||
if (isOverflow)
|
if (isOverflow)
|
||||||
|
@ -9,6 +9,7 @@ import 'package:flutter/services.dart';
|
|||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:loopin/IM/controller/chat_controller.dart';
|
import 'package:loopin/IM/controller/chat_controller.dart';
|
||||||
|
import 'package:loopin/IM/controller/im_user_info_controller.dart';
|
||||||
import 'package:loopin/IM/im_core.dart';
|
import 'package:loopin/IM/im_core.dart';
|
||||||
import 'package:loopin/IM/im_message.dart';
|
import 'package:loopin/IM/im_message.dart';
|
||||||
import 'package:loopin/IM/im_service.dart' hide logger;
|
import 'package:loopin/IM/im_service.dart' hide logger;
|
||||||
@ -16,12 +17,12 @@ import 'package:loopin/api/video_api.dart';
|
|||||||
import 'package:loopin/components/my_toast.dart';
|
import 'package:loopin/components/my_toast.dart';
|
||||||
import 'package:loopin/components/network_or_asset_image.dart';
|
import 'package:loopin/components/network_or_asset_image.dart';
|
||||||
import 'package:loopin/models/conversation_type.dart';
|
import 'package:loopin/models/conversation_type.dart';
|
||||||
|
import 'package:loopin/models/share_type.dart';
|
||||||
import 'package:loopin/models/summary_type.dart';
|
import 'package:loopin/models/summary_type.dart';
|
||||||
import 'package:loopin/service/http.dart';
|
import 'package:loopin/service/http.dart';
|
||||||
import 'package:loopin/utils/download_video.dart';
|
import 'package:loopin/utils/download_video.dart';
|
||||||
import 'package:loopin/utils/permissions.dart';
|
import 'package:loopin/utils/permissions.dart';
|
||||||
import 'package:loopin/utils/wxsdk.dart';
|
import 'package:loopin/utils/wxsdk.dart';
|
||||||
import 'package:loopin/models/share_type.dart';
|
|
||||||
import 'package:media_kit/media_kit.dart';
|
import 'package:media_kit/media_kit.dart';
|
||||||
import 'package:media_kit_video/media_kit_video.dart';
|
import 'package:media_kit_video/media_kit_video.dart';
|
||||||
import 'package:media_kit_video/media_kit_video_controls/src/controls/extensions/duration.dart';
|
import 'package:media_kit_video/media_kit_video_controls/src/controls/extensions/duration.dart';
|
||||||
@ -980,15 +981,20 @@ class _RecommendModuleState extends State<RecommendModule> {
|
|||||||
void handlCoverClick(V2TimConversation conv) async {
|
void handlCoverClick(V2TimConversation conv) async {
|
||||||
// 发送VideoMsg,获取当前视频信息
|
// 发送VideoMsg,获取当前视频信息
|
||||||
final userId = conv.userID;
|
final userId = conv.userID;
|
||||||
final String url = videoList[videoModuleController.videoPlayIndex.value]['url'];
|
final currentVideo = videoList[videoModuleController.videoPlayIndex.value];
|
||||||
final img = videoList[videoModuleController.videoPlayIndex.value]['firstFrameImg'];
|
logger.w(currentVideo);
|
||||||
final width = videoList[videoModuleController.videoPlayIndex.value]['width'];
|
final img = (currentVideo['cover'] != null && currentVideo['cover'].toString().isNotEmpty) ? currentVideo['cover'] : currentVideo['firstFrameImg'];
|
||||||
final height = videoList[videoModuleController.videoPlayIndex.value]['height'];
|
final url = currentVideo['url'];
|
||||||
|
final width = currentVideo['width'];
|
||||||
|
final height = currentVideo['height'];
|
||||||
|
final videoId = currentVideo['id'];
|
||||||
final makeJson = jsonEncode({
|
final makeJson = jsonEncode({
|
||||||
"width": width,
|
"width": width,
|
||||||
"height": height,
|
"height": height,
|
||||||
"imgUrl": img,
|
"imgUrl": img,
|
||||||
"videoUrl": url,
|
"videoUrl": url,
|
||||||
|
"videoId": videoId,
|
||||||
|
"userID": Get.find<ImUserInfoController>().userID.value,
|
||||||
});
|
});
|
||||||
final res = await IMMessage().createCustomMessage(
|
final res = await IMMessage().createCustomMessage(
|
||||||
data: makeJson,
|
data: makeJson,
|
||||||
|
@ -10,6 +10,7 @@ import 'package:loopin/pages/chat/chat_no_friend.dart';
|
|||||||
import 'package:loopin/pages/chat/notify/interaction.dart';
|
import 'package:loopin/pages/chat/notify/interaction.dart';
|
||||||
import 'package:loopin/pages/chat/notify/noFriend.dart';
|
import 'package:loopin/pages/chat/notify/noFriend.dart';
|
||||||
import 'package:loopin/pages/chat/notify/system.dart';
|
import 'package:loopin/pages/chat/notify/system.dart';
|
||||||
|
import 'package:loopin/pages/groupChat/groupList.dart';
|
||||||
import 'package:loopin/pages/groupChat/index.dart';
|
import 'package:loopin/pages/groupChat/index.dart';
|
||||||
import 'package:loopin/pages/my/des.dart';
|
import 'package:loopin/pages/my/des.dart';
|
||||||
import 'package:loopin/pages/my/fans.dart';
|
import 'package:loopin/pages/my/fans.dart';
|
||||||
@ -67,8 +68,8 @@ final Map<String, Widget> routes = {
|
|||||||
'/flow': const Flowing(),
|
'/flow': const Flowing(),
|
||||||
'/eachFlow': const MutualFollowers(),
|
'/eachFlow': const MutualFollowers(),
|
||||||
//群
|
//群
|
||||||
'/group': const StartGroupChatPage(),
|
'/group': const StartGroupChatPage(), // 创建群聊
|
||||||
//关系链
|
'/groupList': const Grouplist(), // 已入群列表
|
||||||
};
|
};
|
||||||
|
|
||||||
final List<GetPage> routeList = routes.entries
|
final List<GetPage> routeList = routes.entries
|
||||||
|
@ -51,10 +51,24 @@ String parseMessageSummary(V2TimMessage msg) {
|
|||||||
// groupNotifyLeaveUp, // 群通知->群升级为达人群通知
|
// groupNotifyLeaveUp, // 群通知->群升级为达人群通知
|
||||||
String _parseCustomMessage(V2TimMessage? msg) {
|
String _parseCustomMessage(V2TimMessage? msg) {
|
||||||
if (msg == null) return '[null]';
|
if (msg == null) return '[null]';
|
||||||
final sum = msg.cloudCustomData;
|
String sum;
|
||||||
|
// 客户端本地用的是字符串,服务端返回的是JSON(服务端改cloudCustomData的类型为String,或者客户端代码修改发送消息时携带的参数由string变为json)
|
||||||
|
final raw = msg.cloudCustomData;
|
||||||
|
if (raw == null) {
|
||||||
|
sum = '';
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
final decoded = jsonDecode(raw);
|
||||||
|
sum = decoded is String ? decoded : raw;
|
||||||
|
} catch (_) {
|
||||||
|
sum = raw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// final sum = jsonDecode(msg.cloudCustomData!);
|
||||||
final elment = msg.customElem; // 所有服务端发送的通知消息都走【自定义消息类型】
|
final elment = msg.customElem; // 所有服务端发送的通知消息都走【自定义消息类型】
|
||||||
logger.w('解析自定义消息:$sum,自定义属性:${msg.cloudCustomData}');
|
// logger.w('解析自定义消息:$sum,自定义属性:${msg.cloudCustomData}');
|
||||||
// logger.w('解析element:${elment?.desc ?? 'summary_error'}');
|
logger.w(sum);
|
||||||
|
logger.w('解析element:${elment?.desc ?? 'summary_error'}');
|
||||||
try {
|
try {
|
||||||
switch (sum) {
|
switch (sum) {
|
||||||
case SummaryType.hongbao:
|
case SummaryType.hongbao:
|
||||||
|
46
pubspec.lock
46
pubspec.lock
@ -249,6 +249,14 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.1"
|
||||||
|
dynamic_tabbar:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: dynamic_tabbar
|
||||||
|
sha256: "017be3705f70e353e579b7a7d56bb19b5c112072b4532bc8bd68767c4a6fc3fe"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.9"
|
||||||
easy_refresh:
|
easy_refresh:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -598,69 +606,69 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "4.5.4"
|
version: "4.5.4"
|
||||||
image_picker:
|
image_picker:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: image_picker
|
name: image_picker
|
||||||
sha256: "021834d9c0c3de46bf0fe40341fa07168407f694d9b2bb18d532dc1261867f7a"
|
sha256: "736eb56a911cf24d1859315ad09ddec0b66104bc41a7f8c5b96b4e2620cf5041"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.2"
|
version: "1.2.0"
|
||||||
image_picker_android:
|
image_picker_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: image_picker_android
|
name: image_picker_android
|
||||||
sha256: "6fae381e6af2bbe0365a5e4ce1db3959462fa0c4d234facf070746024bb80c8d"
|
sha256: "28f3987ca0ec702d346eae1d90eda59603a2101b52f1e234ded62cff1d5cfa6e"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.8.12+24"
|
version: "0.8.13+1"
|
||||||
image_picker_for_web:
|
image_picker_for_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: image_picker_for_web
|
name: image_picker_for_web
|
||||||
sha256: "717eb042ab08c40767684327be06a5d8dbb341fe791d514e4b92c7bbe1b7bb83"
|
sha256: "40c2a6a0da15556dc0f8e38a3246064a971a9f512386c3339b89f76db87269b6"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.6"
|
version: "3.1.0"
|
||||||
image_picker_ios:
|
image_picker_ios:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: image_picker_ios
|
name: image_picker_ios
|
||||||
sha256: "05da758e67bc7839e886b3959848aa6b44ff123ab4b28f67891008afe8ef9100"
|
sha256: eb06fe30bab4c4497bad449b66448f50edcc695f1c59408e78aa3a8059eb8f0e
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.8.12+2"
|
version: "0.8.13"
|
||||||
image_picker_linux:
|
image_picker_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: image_picker_linux
|
name: image_picker_linux
|
||||||
sha256: "34a65f6740df08bbbeb0a1abd8e6d32107941fd4868f67a507b25601651022c9"
|
sha256: "1f81c5f2046b9ab724f85523e4af65be1d47b038160a8c8deed909762c308ed4"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.1+2"
|
version: "0.2.2"
|
||||||
image_picker_macos:
|
image_picker_macos:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: image_picker_macos
|
name: image_picker_macos
|
||||||
sha256: "1b90ebbd9dcf98fb6c1d01427e49a55bd96b5d67b8c67cf955d60a5de74207c1"
|
sha256: d58cd9d67793d52beefd6585b12050af0a7663c0c2a6ece0fb110a35d6955e04
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.1+2"
|
version: "0.2.2"
|
||||||
image_picker_platform_interface:
|
image_picker_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: image_picker_platform_interface
|
name: image_picker_platform_interface
|
||||||
sha256: "886d57f0be73c4b140004e78b9f28a8914a09e50c2d816bdd0520051a71236a0"
|
sha256: "9f143b0dba3e459553209e20cc425c9801af48e6dfa4f01a0fcf927be3f41665"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.10.1"
|
version: "2.11.0"
|
||||||
image_picker_windows:
|
image_picker_windows:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: image_picker_windows
|
name: image_picker_windows
|
||||||
sha256: "6ad07afc4eb1bc25f3a01084d28520496c4a3bb0cb13685435838167c9dcedeb"
|
sha256: d248c86554a72b5495a31c56f060cf73a41c7ff541689327b1a7dbccc33adfae
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.1+1"
|
version: "0.2.2"
|
||||||
install_plugin:
|
install_plugin:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -846,7 +854,7 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "1.16.0"
|
version: "1.16.0"
|
||||||
mime:
|
mime:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: mime
|
name: mime
|
||||||
sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
|
sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
|
||||||
@ -1419,7 +1427,7 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.4"
|
version: "2.1.4"
|
||||||
video_player:
|
video_player:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: video_player
|
name: video_player
|
||||||
sha256: "0d55b1f1a31e5ad4c4967bfaa8ade0240b07d20ee4af1dfef5f531056512961a"
|
sha256: "0d55b1f1a31e5ad4c4967bfaa8ade0240b07d20ee4af1dfef5f531056512961a"
|
||||||
|
@ -61,6 +61,7 @@ dependencies:
|
|||||||
photo_view: ^0.15.0
|
photo_view: ^0.15.0
|
||||||
shirne_dialog: ^4.8.3
|
shirne_dialog: ^4.8.3
|
||||||
package_info_plus: ^8.3.0
|
package_info_plus: ^8.3.0
|
||||||
|
dynamic_tabbar: ^1.0.9 #tab
|
||||||
|
|
||||||
flutter_upgrader: ^1.1.20 #更新
|
flutter_upgrader: ^1.1.20 #更新
|
||||||
path_provider: ^2.1.2
|
path_provider: ^2.1.2
|
||||||
@ -88,6 +89,9 @@ dependencies:
|
|||||||
audioplayers: ^6.5.0 #音频播放
|
audioplayers: ^6.5.0 #音频播放
|
||||||
flutter_html: ^3.0.0
|
flutter_html: ^3.0.0
|
||||||
timer_count_down: ^2.2.2 #倒计时
|
timer_count_down: ^2.2.2 #倒计时
|
||||||
|
image_picker: ^1.2.0 #相机
|
||||||
|
video_player: ^2.10.0 #视频处理
|
||||||
|
mime: ^2.0.0 #文件类型推断
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_launcher_icons: ^0.13.1 # 使用最新版本
|
flutter_launcher_icons: ^0.13.1 # 使用最新版本
|
||||||
|
Loading…
x
Reference in New Issue
Block a user