通讯录部分页面

This commit is contained in:
2022-10-20 14:41:49 +08:00
parent 36b860752a
commit e38a5aeeb7
12 changed files with 1360 additions and 5 deletions

View File

@@ -14,6 +14,8 @@ PODS:
- Flutter
- path_provider_ios (0.0.1):
- Flutter
- permission_handler_apple (9.0.4):
- Flutter
- photo_manager (2.0.0):
- Flutter
- FlutterMacOS
@@ -35,6 +37,8 @@ PODS:
- Flutter
- video_player_avfoundation (0.0.1):
- Flutter
- wakelock (0.0.1):
- Flutter
DEPENDENCIES:
- Flutter (from `Flutter`)
@@ -42,6 +46,7 @@ DEPENDENCIES:
- image_cropper (from `.symlinks/plugins/image_cropper/ios`)
- open_file (from `.symlinks/plugins/open_file/ios`)
- path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`)
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
- photo_manager (from `.symlinks/plugins/photo_manager/ios`)
- scan (from `.symlinks/plugins/scan/ios`)
- smart_auth (from `.symlinks/plugins/smart_auth/ios`)
@@ -49,6 +54,7 @@ DEPENDENCIES:
- tencent_im_sdk_plugin (from `.symlinks/plugins/tencent_im_sdk_plugin/ios`)
- vibration (from `.symlinks/plugins/vibration/ios`)
- video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/ios`)
- wakelock (from `.symlinks/plugins/wakelock/ios`)
SPEC REPOS:
trunk:
@@ -69,6 +75,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/open_file/ios"
path_provider_ios:
:path: ".symlinks/plugins/path_provider_ios/ios"
permission_handler_apple:
:path: ".symlinks/plugins/permission_handler_apple/ios"
photo_manager:
:path: ".symlinks/plugins/photo_manager/ios"
scan:
@@ -83,6 +91,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/vibration/ios"
video_player_avfoundation:
:path: ".symlinks/plugins/video_player_avfoundation/ios"
wakelock:
:path: ".symlinks/plugins/wakelock/ios"
SPEC CHECKSUMS:
Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
@@ -92,6 +102,7 @@ SPEC CHECKSUMS:
image_cropper: 60c2789d1f1a78c873235d4319ca0c34a69f2d98
open_file: 02eb5cb6b21264bd3a696876f5afbfb7ca4f4b7d
path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02
permission_handler_apple: 44366e37eaf29454a1e7b1b7d736c2cceaeb17ce
photo_manager: 4f6810b7dfc4feb03b461ac1a70dacf91fba7604
scan: aea35bb4aa59ccc8839c576a18cd57c7d492cc86
smart_auth: 4bedbc118723912d0e45a07e8ab34039c19e04f2
@@ -102,6 +113,7 @@ SPEC CHECKSUMS:
TXIMSDK_Plus_iOS: 5412f55a77f058b2b5a8575900334daccbae3b08
vibration: 7d883d141656a1c1a6d8d238616b2042a51a1241
video_player_avfoundation: e489aac24ef5cf7af82702979ed16f2a5ef84cff
wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f
PODFILE CHECKSUM: a75497545d4391e2d394c3668e20cfb1c2bbd4aa

View File

@@ -13,6 +13,10 @@ abstract class ContactRoutes {
static const String friend = '/contact/friend';
static const String friendSearch = '/contact/friend/search';
static const String friendProfile = '/contact/friend/profile';
static const String friendProfileMore = '/contact/friend/profile';
static const String friendRemark = '/contact/friend/profile';
static const String friendApply = '/contact/friend/profile';
static const String friendRecommend = '/contact/friend/profile';
static const String group = '/contact/group';
static const String groupQrCode = '/contact/group/qrCode';

View File

@@ -0,0 +1,147 @@
import 'package:chat/configs/app_colors.dart';
import 'package:chat/controllers/private_controller.dart';
import 'package:chat/routes/contact_routes.dart';
import 'package:chat/routes/conversation_routes.dart';
import 'package:chat/routes/moments_routes.dart';
import 'package:chat/services/tim/conversation_service.dart';
import 'package:chat/utils/im_tools.dart';
import 'package:chat/views/home/widgets/action_button.dart';
import 'package:chat/views/home/widgets/action_item.dart';
import 'package:chat/widgets/custom_avatar.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class ImFriendProfilePage extends StatelessWidget {
const ImFriendProfilePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return GetX<PrivateController>(builder: (_) {
return Scaffold(
appBar: AppBar(
backgroundColor: AppColors.white,
actions: [
Visibility(
visible: _.currentFriend.value.isFriend,
child: IconButton(
onPressed: () {
Get.toNamed(
ContactRoutes.friendProfileMore,
);
},
icon: const Icon(
Icons.more_horiz,
),
),
),
],
),
body: Column(
children: [
Container(
padding: const EdgeInsets.all(16),
color: AppColors.white,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CustomAvatar(
_.currentFriend.value.userProfile?.faceUrl,
size: 54,
),
const SizedBox(width: 16),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
_.currentFriend.value.conversation!.showName!,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w400,
),
),
Text('昵称:${_.currentFriend.value.userProfile?.nickName}'),
// Text('区块链地址:$friendId'),
],
),
],
),
),
const Divider(height: 0),
ActionItem(
'设置备注',
extend: _.currentFriend.value.friendRemark,
onTap: () {
Get.toNamed(
ContactRoutes.friendRemark,
);
},
),
const SizedBox(height: 8),
ActionItem(
'他的动态',
onTap: () {
Get.toNamed(
MomentsRoutes.user,
arguments: {
'userId': _.currentFriend.value.userID,
},
);
},
),
const SizedBox(height: 8),
Visibility(
visible: !_.currentFriend.value.isFriend,
child: ActionButton(
'添加到通讯录',
onTap: () {
Get.toNamed(
ContactRoutes.friendApply,
arguments: {
'userID': _.currentFriend.value.userID,
},
);
},
),
),
Visibility(
visible: _.currentFriend.value.isFriend,
child: ActionButton(
'发消息',
color: AppColors.primary,
onTap: () async {
var result = await TimConversationService
.to.conversationManager
.getConversation(
conversationID: 'c2c_' + _.currentFriend.value.userID,
);
if (result.code == 0) {
Get.toNamed(
ConversationRoutes.index,
arguments: {
'conversation': result.data,
},
);
}
},
),
),
Visibility(
visible: _.currentFriend.value.isFriend,
child: const Divider(height: 0),
),
Visibility(
visible: _.currentFriend.value.isFriend,
child: ActionButton(
'音视频通话',
color: AppColors.primary,
onTap: () {
ImTools.showTrtcMessage(_.currentFriend.value.userID);
},
),
),
],
),
);
});
}
}

View File

@@ -0,0 +1,105 @@
import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:chat/controllers/private_controller.dart';
import 'package:chat/routes/contact_routes.dart';
import 'package:chat/services/tim/friend_service.dart';
import 'package:chat/utils/ui_tools.dart';
import 'package:chat/views/home/widgets/action_button.dart';
import 'package:chat/views/home/widgets/action_item.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class ImFriendProfileMorePage extends StatelessWidget {
const ImFriendProfileMorePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return GetX<PrivateController>(builder: (_) {
return Scaffold(
appBar: AppBar(
title: const Text('资料设置'),
),
body: Column(
children: [
ActionItem(
'设置备注',
extend: _.currentFriend.value.friendRemark,
onTap: () {
Get.toNamed(
ContactRoutes.friendRemark,
);
},
),
const SizedBox(height: 8),
ActionItem(
'把他推荐给朋友',
onTap: () {
Get.toNamed(
ContactRoutes.friendRecommend,
);
},
),
const SizedBox(height: 8),
ActionItem(
'加入黑名单',
rightWidget: SizedBox(
height: 24,
child: Switch(
value: false,
onChanged: (e) async {
OkCancelResult result = await showOkCancelAlertDialog(
style: AdaptiveStyle.iOS,
context: Get.context!,
title: '加入黑名单',
message: '加入黑名单将会解除好友关系,不再接收他的消息',
okLabel: '确定',
cancelLabel: '取消',
defaultType: OkCancelAlertDefaultType.ok,
);
if (result == OkCancelResult.ok) {
var res = await TimBlockService.to.add(
_.currentFriend.value.userID,
);
if (res) {
UiTools.toast('加入黑名单成功');
Navigator.popUntil(context, (route) => route.isFirst);
}
}
},
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
),
),
const SizedBox(height: 8),
ActionButton(
'删除',
onTap: () async {
OkCancelResult result = await showOkCancelAlertDialog(
style: AdaptiveStyle.iOS,
context: Get.context!,
title: '删除好友',
message: '确定要删除该好友么?',
okLabel: '确定',
cancelLabel: '取消',
defaultType: OkCancelAlertDefaultType.ok,
);
if (result == OkCancelResult.ok) {
var res = await TimFriendService.to.delete(
_.currentFriend.value.userID,
);
if (res) {
UiTools.toast('好友删除成功');
/// 关闭所有页面
Navigator.popUntil(context, (route) => route.isFirst);
}
}
},
),
],
),
);
});
}
}

View File

@@ -0,0 +1,164 @@
import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:azlistview/azlistview.dart';
import 'package:chat/configs/app_colors.dart';
import 'package:chat/controllers/private_controller.dart';
import 'package:chat/models/im/contact_info_model.dart';
import 'package:chat/models/im/name_card_model.dart';
import 'package:chat/models/im/private_conversation_model.dart';
import 'package:chat/services/tim/conversation_service.dart';
import 'package:chat/services/tim/friend_service.dart';
import 'package:chat/utils/im_tools.dart';
import 'package:chat/widgets/custom_avatar.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:tencent_im_sdk_plugin/models/v2_tim_conversation.dart';
class ImFriendRecommendFriendsPage extends StatelessWidget {
const ImFriendRecommendFriendsPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
PrivateConversationModel card = PrivateController.to.currentFriend.value;
return Scaffold(
appBar: AppBar(
title: const Text('选择联系人'),
),
body: Column(
children: [
Container(
color: AppColors.page,
padding: const EdgeInsets.only(
left: 16,
right: 16,
bottom: 8,
),
child: Container(
constraints: const BoxConstraints(
maxHeight: 32,
),
child: ClipRRect(
borderRadius: BorderRadius.circular(32),
child: TextField(
onChanged: (e) async {},
decoration: const InputDecoration(
hintText: '搜索',
hintStyle: TextStyle(
fontSize: 14,
color: AppColors.unactive,
),
border: InputBorder.none,
focusedBorder: InputBorder.none,
fillColor: AppColors.white,
filled: true,
contentPadding: EdgeInsets.only(
bottom: 14,
left: 16,
),
),
cursorColor: AppColors.primary,
),
),
),
),
InkWell(
onTap: () {
FocusScope.of(Get.context!).requestFocus(FocusNode());
// Get.offNamed(ImRoutes.friendRecommendGroups);
},
child: Container(
decoration: const BoxDecoration(
color: AppColors.white,
),
width: double.infinity,
padding: const EdgeInsets.only(
left: 16,
top: 12,
bottom: 12,
),
child: const Text(
'选择一个群',
style: TextStyle(
fontSize: 16,
),
),
),
),
Expanded(
child: GetX<TimFriendService>(builder: (_) {
return AzListView(
physics: const ClampingScrollPhysics(),
data: _.contacts,
itemCount: _.contacts.length,
itemBuilder: (__, index) {
ContactInfoModel info = _.contacts[index];
return Column(
children: [
ListTile(
onTap: () async {
FocusScope.of(Get.context!).requestFocus(FocusNode());
V2TimConversation conversation =
await TimConversationService.to.getById(
'c2c_' + info.userID,
);
OkCancelResult result = await showOkCancelAlertDialog(
style: AdaptiveStyle.iOS,
context: context,
title: '发送名片',
message: '确定要发送【个人名片】至${conversation.showName}',
okLabel: '确定',
cancelLabel: '取消',
defaultType: OkCancelAlertDefaultType.cancel,
);
if (result == OkCancelResult.ok) {
var model = NameCardModel(
avatar: card.userProfile?.faceUrl ?? '',
userID: card.userID,
userName: card.userProfile?.nickName ?? '',
);
TimConversationService.to.sendCustomMessage(
conversation,
model,
'NAME_CARD',
);
Get.back();
}
},
tileColor: AppColors.white,
leading: CustomAvatar(
info.friendInfo!.userProfile!.faceUrl,
size: 40,
),
title: Text(info.name),
),
const Divider(
height: 0,
indent: 72,
),
],
);
},
susItemBuilder: (__, index) {
ContactInfoModel model = _.contacts[index];
if ('' == model.getSuspensionTag()) {
return Container();
}
return ImTools.susItem(context, model.getSuspensionTag());
},
indexBarData:
SuspensionUtil.getTagIndexList(_.contacts).toList(),
indexBarOptions: ImTools.indexBarOptions,
);
}),
),
],
),
);
}
}

View File

@@ -0,0 +1,126 @@
import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:chat/configs/app_colors.dart';
import 'package:chat/controllers/private_controller.dart';
import 'package:chat/models/im/name_card_model.dart';
import 'package:chat/models/im/private_conversation_model.dart';
import 'package:chat/services/tim/conversation_service.dart';
import 'package:chat/services/tim/group_service.dart';
import 'package:chat/views/home/widgets/group_avatar.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:tencent_im_sdk_plugin/models/v2_tim_conversation.dart';
import 'package:tencent_im_sdk_plugin/models/v2_tim_group_info.dart';
class ImFriendRecommendGroupsPage extends StatelessWidget {
const ImFriendRecommendGroupsPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
PrivateConversationModel card = PrivateController.to.currentFriend.value;
return Scaffold(
appBar: AppBar(
title: const Text('选择一个群'),
),
body: Column(
children: [
Container(
color: AppColors.page,
padding: const EdgeInsets.only(
left: 16,
right: 16,
bottom: 8,
),
child: Container(
constraints: const BoxConstraints(
maxHeight: 32,
),
child: ClipRRect(
borderRadius: BorderRadius.circular(32),
child: TextField(
onChanged: (e) async {},
decoration: const InputDecoration(
hintText: '搜索',
hintStyle: TextStyle(
fontSize: 14,
color: AppColors.unactive,
),
border: InputBorder.none,
focusedBorder: InputBorder.none,
fillColor: AppColors.white,
filled: true,
contentPadding: EdgeInsets.only(
bottom: 14,
left: 16,
),
),
cursorColor: AppColors.primary,
),
),
),
),
Expanded(
child: GetX<TimGroupService>(
builder: (_) {
return ListView.separated(
shrinkWrap: true,
physics: const ClampingScrollPhysics(),
itemBuilder: (context, index) {
V2TimGroupInfo group = _.groups[index];
return ListTile(
onTap: () async {
FocusScope.of(Get.context!).requestFocus(FocusNode());
V2TimConversation conversation =
await TimConversationService.to.getById(
'group_' + group.groupID,
);
OkCancelResult result = await showOkCancelAlertDialog(
style: AdaptiveStyle.iOS,
context: context,
title: '发送名片',
message: '确定要发送【个人名片】至${conversation.showName}',
okLabel: '确定',
cancelLabel: '取消',
defaultType: OkCancelAlertDefaultType.cancel,
);
if (result == OkCancelResult.ok) {
var model = NameCardModel(
avatar: card.userProfile?.faceUrl ?? '',
userID: card.userID,
userName: card.userProfile?.nickName ?? '',
);
TimConversationService.to.sendCustomMessage(
conversation,
model,
'NAME_CARD',
);
Get.back();
}
},
leading: GroupAvatar(group.groupID),
tileColor: AppColors.white,
title: Text(group.groupName!),
subtitle: Text('成员数: ${group.memberCount}'),
);
},
separatorBuilder: (context, index) {
return const Divider(
height: 0,
indent: 72,
);
},
itemCount: _.groups.length,
);
},
),
),
],
),
);
}
}

View File

@@ -0,0 +1,179 @@
import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:chat/configs/app_colors.dart';
import 'package:chat/configs/app_size.dart';
import 'package:chat/controllers/private_controller.dart';
import 'package:chat/models/im/name_card_model.dart';
import 'package:chat/models/im/private_conversation_model.dart';
import 'package:chat/services/tim/conversation_service.dart';
import 'package:chat/views/home/widgets/group_avatar.dart';
import 'package:chat/widgets/custom_avatar.dart';
import 'package:chat/widgets/custom_easy_refresh.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:tencent_im_sdk_plugin/enum/conversation_type.dart';
class ImFriendRecommendPage extends StatelessWidget {
const ImFriendRecommendPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
PrivateConversationModel info = PrivateController.to.currentFriend.value;
return GestureDetector(
onTap: () {
FocusScope.of(Get.context!).requestFocus(FocusNode());
},
child: Scaffold(
appBar: AppBar(
title: const Text(
'选择一个聊天',
),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(
left: 16,
right: 16,
bottom: 8,
),
child: Container(
constraints: const BoxConstraints(
maxHeight: 32,
),
child: ClipRRect(
borderRadius: BorderRadius.circular(32),
child: TextField(
onChanged: (e) async {},
decoration: const InputDecoration(
hintText: '搜索',
hintStyle: TextStyle(
fontSize: 14,
color: AppColors.unactive,
),
border: InputBorder.none,
focusedBorder: InputBorder.none,
fillColor: AppColors.white,
filled: true,
contentPadding: EdgeInsets.only(
bottom: 14,
left: 16,
),
),
cursorColor: AppColors.primary,
),
),
),
),
InkWell(
onTap: () {
FocusScope.of(Get.context!).requestFocus(FocusNode());
// Get.offNamed(ImRoutes.friendRecommendFriends);
},
child: Container(
decoration: const BoxDecoration(
color: AppColors.white,
),
width: double.infinity,
padding: const EdgeInsets.only(
left: 16,
top: 12,
bottom: 12,
),
child: const Text(
'更多联系人',
style: TextStyle(
fontSize: 16,
),
),
),
),
Container(
padding: const EdgeInsets.only(
left: 16,
top: 6,
bottom: 6,
),
child: const Text(
'最近联系人',
style: TextStyle(
color: AppColors.unactive,
fontSize: AppSize.smallFontSize,
),
),
),
Expanded(
child: GetX<TimConversationService>(
builder: (_) {
return _.conversationList.isEmpty
? CustomEasyRefresh.empty()
: ListView.separated(
shrinkWrap: true,
physics: const ClampingScrollPhysics(),
itemCount: _.conversationList.length,
itemBuilder: (context, index) {
var conversation = _.conversationList[index]!;
return ListTile(
tileColor: AppColors.white,
onTap: () async {
FocusScope.of(Get.context!)
.requestFocus(FocusNode());
OkCancelResult result =
await showOkCancelAlertDialog(
style: AdaptiveStyle.iOS,
context: context,
title: '发送名片',
message:
'确定要发送【个人名片】至${conversation.showName}',
okLabel: '确定',
cancelLabel: '取消',
defaultType: OkCancelAlertDefaultType.cancel,
);
if (result == OkCancelResult.ok) {
var model = NameCardModel(
avatar: info.userProfile?.faceUrl ?? '',
userID: info.userID,
userName: info.userProfile?.nickName ?? '',
);
TimConversationService.to.sendCustomMessage(
conversation,
model,
'NAME_CARD',
);
Get.back();
}
},
leading: conversation.type ==
ConversationType.V2TIM_C2C
? CustomAvatar(
conversation.faceUrl,
)
: GroupAvatar(conversation.groupID!),
title: Text(
conversation.showName!,
style: const TextStyle(
fontSize: 16,
),
),
);
},
separatorBuilder: (context, index) {
return const Divider(
height: 0,
indent: 72,
);
},
);
},
),
),
],
),
),
);
}
}

View File

@@ -0,0 +1,82 @@
import 'package:chat/configs/app_colors.dart';
import 'package:chat/controllers/private_controller.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class ImFriendRemarkPage extends StatefulWidget {
const ImFriendRemarkPage({Key? key}) : super(key: key);
@override
State<ImFriendRemarkPage> createState() => _ImFriendRemarkPageState();
}
class _ImFriendRemarkPageState extends State<ImFriendRemarkPage> {
final TextEditingController _controller = TextEditingController();
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return GetX<PrivateController>(builder: (_) {
_controller.text = _.currentFriend.value.friendRemark;
return GestureDetector(
onTap: () {
FocusScope.of(Get.context!).requestFocus(FocusNode());
},
child: Scaffold(
appBar: AppBar(
actions: [
TextButton(
child: const Text('保存'),
onPressed: () async {
_.setRemark(_controller.text);
},
)
],
),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
const Text(
'设置备注',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 32),
ClipRRect(
borderRadius: BorderRadius.circular(8),
child: TextField(
controller: _controller,
autofocus: true,
decoration: const InputDecoration(
labelText: '好友备注',
hintText: '',
hintStyle: TextStyle(
fontSize: 14,
color: AppColors.unactive,
),
border: InputBorder.none,
focusedBorder: InputBorder.none,
fillColor: AppColors.white,
filled: true,
),
cursorColor: AppColors.primary,
),
),
],
),
),
),
),
);
});
}
}

View File

@@ -0,0 +1,118 @@
import 'package:chat/configs/app_colors.dart';
import 'package:chat/services/tim/friend_service.dart';
import 'package:chat/utils/ui_tools.dart';
import 'package:chat/widgets/custom_primary_button.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class ImFriendRequestApplyPage extends StatefulWidget {
const ImFriendRequestApplyPage({Key? key}) : super(key: key);
@override
State<ImFriendRequestApplyPage> createState() =>
_ImFriendRequestApplyPageState();
}
class _ImFriendRequestApplyPageState extends State<ImFriendRequestApplyPage> {
late final String userID;
String _remark = '';
String _wording = '';
final TextEditingController _wordingController = TextEditingController();
final TextEditingController _remarkController = TextEditingController();
@override
void initState() {
super.initState();
userID = Get.arguments['userID'];
_wordingController.text = '我是 ';
}
@override
void dispose() {
_wordingController.dispose();
_remarkController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('好友申请'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'发送添加朋友申请',
style: TextStyle(
color: AppColors.unactive,
),
),
const SizedBox(height: 4),
TextField(
controller: _wordingController,
style: const TextStyle(
fontSize: 14,
),
decoration: InputDecoration(
border: InputBorder.none,
fillColor: AppColors.unactive.withOpacity(0.1),
filled: true,
),
onChanged: (e) {
setState(() {
_wording = e;
});
},
),
const SizedBox(height: 32),
const Text(
'设置备注',
style: TextStyle(
color: AppColors.unactive,
),
),
const SizedBox(height: 4),
TextField(
controller: _remarkController,
style: const TextStyle(
fontSize: 14,
),
decoration: InputDecoration(
hintText: '',
border: InputBorder.none,
fillColor: AppColors.unactive.withOpacity(0.1),
filled: true,
),
onChanged: (e) {
setState(() {
_remark = e;
});
},
),
const SizedBox(height: 32),
CustomPrimaryButton(
text: '发送',
onPressed: () async {
var res = await TimFriendService.to.add(
userID,
remark: _remark,
addWording: _wording,
);
if (res) {
UiTools.toast('申请成功');
Get.back();
}
},
),
],
),
),
);
}
}

View File

@@ -0,0 +1,293 @@
import 'package:chat/configs/app_colors.dart';
import 'package:chat/controllers/private_controller.dart';
import 'package:chat/models/im/search_user_model.dart';
import 'package:chat/routes/contact_routes.dart';
import 'package:chat/services/tim/apply_service.dart';
import 'package:chat/services/tim/friend_service.dart';
import 'package:chat/widgets/custom_avatar.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:tencent_im_sdk_plugin/enum/friend_type.dart';
import 'package:tencent_im_sdk_plugin/models/v2_tim_friend_application.dart';
import 'package:tencent_im_sdk_plugin/models/v2_tim_friend_check_result.dart';
class ImFriendRequestPage extends StatefulWidget {
const ImFriendRequestPage({Key? key}) : super(key: key);
@override
State<ImFriendRequestPage> createState() => _ImFriendRequestState();
}
class _ImFriendRequestState extends State<ImFriendRequestPage> {
List<SearchUserModel>? searchList;
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('新的朋友'),
bottom: _Search(
onChanged: (String e) async {
if (e.length < 3) {
setState(() {
searchList = null;
});
} else {
var result = await TimFriendService.to.searchUser(e);
setState(() {
searchList = result;
});
}
},
),
),
body: GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(FocusNode());
},
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ListView.separated(
shrinkWrap: true,
physics: const ClampingScrollPhysics(),
itemCount: searchList?.length ?? 0,
itemBuilder: (context, index) {
var user = searchList![index];
return FutureBuilder<V2TimFriendCheckResult?>(
future: TimFriendService.to.check(user.userID),
builder: (context, snapshot) {
V2TimFriendCheckResult? result = snapshot.data;
return ListTile(
leading: CustomAvatar(user.avatar),
title: Text(user.nickname),
onTap: () {
if (result?.resultType == 3) {
PrivateController.to.setCurrentFriend(user.userID);
Get.toNamed(
ContactRoutes.friendProfile,
);
}
Get.toNamed(
ContactRoutes.friendApply,
arguments: {
'userID': user.userID,
},
);
},
trailing: _buildTrailing(result),
);
},
);
},
separatorBuilder: (context, index) {
return const Divider(
height: 0,
indent: 72,
);
},
),
const Divider(height: 0),
const Padding(
padding: EdgeInsets.only(
left: 16,
top: 4,
),
child: Text(
'好友申请',
style: TextStyle(
color: AppColors.unactive,
),
),
),
GetX<TimApplyService>(
builder: (_) {
return ListView.separated(
shrinkWrap: true,
physics: const ClampingScrollPhysics(),
itemBuilder: (context, index) {
V2TimFriendApplication apply = _.applies[index]!;
return ListTile(
leading: CustomAvatar(apply.faceUrl),
title: Text(apply.nickname!),
subtitle: Text(apply.addWording ?? ''),
trailing: apply.type ==
FriendApplicationType
.V2TIM_FRIEND_APPLICATION_COME_IN
? InkWell(
onTap: () {
TimApplyService.to.accept(apply.userID);
},
child: Container(
padding: const EdgeInsets.symmetric(
vertical: 4,
horizontal: 12,
),
decoration: BoxDecoration(
color: AppColors.primary,
borderRadius: BorderRadius.circular(16),
),
child: const Text(
'通过请求',
style: TextStyle(
color: AppColors.white,
fontSize: 12,
),
),
),
)
: const Text('已发送'),
onTap: () {
PrivateController.to.setCurrentFriend(apply.userID);
Get.toNamed(
ContactRoutes.friendProfile,
);
},
);
},
separatorBuilder: (context, index) {
return const Divider(
height: 0,
);
},
itemCount: _.applies.length,
);
},
)
],
),
),
),
);
}
Widget _buildTrailing(V2TimFriendCheckResult? result) {
if (result == null) {
return const Text('');
}
if (result.resultType == 0) {
return Container(
padding: const EdgeInsets.symmetric(
vertical: 4,
horizontal: 8,
),
decoration: BoxDecoration(
color: AppColors.primary,
borderRadius: BorderRadius.circular(16),
),
child: const Text(
'添加好友',
style: TextStyle(
color: AppColors.white,
fontSize: 12,
),
),
);
}
if (result.resultType == 2) {
return const Text(
'已申请',
style: TextStyle(
color: AppColors.unactive,
),
);
}
if (result.resultType == 3) {
return const Text('已是好友');
}
return Text(result.resultType.toString());
}
}
class _Search extends StatelessWidget implements PreferredSizeWidget {
final Function(String e) onChanged;
const _Search({
Key? key,
required this.onChanged,
}) : super(key: key);
@override
Size get preferredSize => const Size.fromHeight(64);
@override
Widget build(BuildContext context) {
return Column(
children: [
Container(
constraints: const BoxConstraints(
maxHeight: 32,
),
padding: const EdgeInsets.symmetric(horizontal: 16),
child: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: TextField(
keyboardType: TextInputType.phone,
decoration: InputDecoration(
hintText: '请输入对方手机号',
hintStyle: const TextStyle(
fontSize: 14,
color: AppColors.unactive,
),
border: InputBorder.none,
focusedBorder: InputBorder.none,
fillColor: AppColors.unactive.withOpacity(0.1),
filled: true,
prefixIcon: const Icon(
Icons.search,
color: AppColors.unactive,
size: 18,
),
contentPadding: const EdgeInsets.only(
bottom: 14,
),
),
cursorColor: AppColors.primary,
onChanged: (e) async {
onChanged.call(e);
},
),
),
),
Container(
padding: const EdgeInsets.only(
top: 8,
bottom: 16,
left: 16,
right: 16,
),
child: InkWell(
onTap: () {
Get.toNamed(ContactRoutes.friendProfile);
},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Text(
'我的二维码: ',
style: TextStyle(
color: AppColors.unactive,
),
),
SizedBox(width: 8),
Icon(
Icons.qr_code,
size: 18,
color: AppColors.unactive,
),
],
),
),
),
const Divider(height: 0),
],
);
}
}

View File

@@ -0,0 +1,57 @@
import 'package:chat/configs/app_colors.dart';
import 'package:flutter/material.dart';
class ImFriendSearchPage extends StatefulWidget {
const ImFriendSearchPage({Key? key}) : super(key: key);
@override
State<ImFriendSearchPage> createState() => _ImFriendSearchPageState();
}
class _ImFriendSearchPageState extends State<ImFriendSearchPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.white,
appBar: AppBar(
title: Container(
constraints: const BoxConstraints(
maxHeight: 32,
),
child: ClipRRect(
borderRadius: BorderRadius.circular(32),
child: TextField(
onChanged: (e) async {},
autofocus: true,
decoration: const InputDecoration(
hintText: '请输入搜索内容',
hintStyle: TextStyle(
fontSize: 14,
color: AppColors.unactive,
),
border: InputBorder.none,
focusedBorder: InputBorder.none,
fillColor: AppColors.white,
filled: true,
contentPadding: EdgeInsets.only(
bottom: 14,
left: 16,
),
),
cursorColor: AppColors.primary,
),
),
),
actions: <Widget>[
IconButton(
icon: const Icon(
Icons.search,
color: AppColors.black,
),
onPressed: () {},
),
],
),
);
}
}

View File

@@ -1,19 +1,87 @@
import 'package:azlistview/azlistview.dart';
import 'package:chat/configs/app_colors.dart';
import 'package:chat/controllers/private_controller.dart';
import 'package:chat/models/im/contact_info_model.dart';
import 'package:chat/routes/contact_routes.dart';
import 'package:chat/services/tim/friend_service.dart';
import 'package:chat/utils/im_tools.dart';
import 'package:chat/widgets/custom_avatar.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class ContactPage extends StatefulWidget {
const ContactPage({Key? key}) : super(key: key);
@override
_ContactPageState createState() => _ContactPageState();
State<ContactPage> createState() => _ContactPageState();
}
class _ContactPageState extends State<ContactPage> {
@override
void initState() {
super.initState();
TimFriendService.to.fetchList();
}
@override
Widget build(BuildContext context) {
return GetX<TimFriendService>(
builder: (_) {
return Scaffold(
appBar: AppBar(
title: const Text('通讯录'),
title: Text('我的好友(${_.friends.length})'),
actions: [
IconButton(
onPressed: () {
// Get.toNamed(ImRoutes.friendSearch);
},
icon: const Icon(Icons.search_outlined),
),
],
),
body: AzListView(
physics: const ClampingScrollPhysics(),
data: _.contacts,
itemCount: _.contacts.length,
itemBuilder: (__, index) {
ContactInfoModel info = _.contacts[index];
return Column(
children: [
ListTile(
onTap: () async {
await PrivateController.to.setCurrentFriend(
info.userID,
);
Get.toNamed(
ContactRoutes.friendProfile,
);
},
tileColor: AppColors.white,
leading: CustomAvatar(
info.friendInfo!.userProfile!.faceUrl,
size: 40,
),
title: Text(info.name),
),
const Divider(
height: 0,
indent: 72,
),
],
);
},
susItemBuilder: (__, index) {
ContactInfoModel model = _.contacts[index];
if ('' == model.getSuspensionTag()) {
return Container();
}
return ImTools.susItem(context, model.getSuspensionTag());
},
indexBarData: SuspensionUtil.getTagIndexList(_.contacts).toList(),
indexBarOptions: ImTools.indexBarOptions,
),
);
},
);
}
}