Browse Source

对接接口,优化列表

dev-2.0
王子贤 2 months ago
parent
commit
325a4dcf8c
8 changed files with 555 additions and 97 deletions
  1. 142
      lib/controller/discover/room_controller.dart
  2. 104
      lib/model/discover/audience_list_data.dart
  3. 7
      lib/network/api_urls.dart
  4. 21
      lib/network/rtc_api.dart
  5. 86
      lib/network/rtc_api.g.dart
  6. 8
      lib/rtc/rtc_manager.dart
  7. 33
      lib/widget/live/live_room_anchor_showcase.dart
  8. 251
      lib/widget/live/live_room_invitation_list.dart

142
lib/controller/discover/room_controller.dart

@ -12,12 +12,14 @@ import 'package:dating_touchme_app/pages/mine/edit_info_page.dart';
import 'package:dating_touchme_app/rtc/rtc_manager.dart';
import 'package:dating_touchme_app/rtc/rtm_manager.dart';
import 'package:dating_touchme_app/service/live_chat_message_service.dart';
import 'package:easy_refresh/easy_refresh.dart';
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:tdesign_flutter/tdesign_flutter.dart';
import '../../model/discover/audience_list_data.dart';
import '../../model/live/live_chat_message.dart';
import '../../pages/discover/live_end_page.dart';
import '../../pages/mine/real_name_page.dart';
@ -78,6 +80,10 @@ class RoomController extends GetxController with WidgetsBindingObserver {
_registerMessageListener();
//
loadGiftProducts();
listRefreshController = EasyRefreshController(
controlFinishRefresh: true,
controlFinishLoad: true,
);
}
@override
@ -97,6 +103,142 @@ class RoomController extends GetxController with WidgetsBindingObserver {
chatMessages.clear();
}
final tab = 0.obs;
changeTab(int i) async {
tab.value = i;
if(tab.value == 0){
page.value = 1;
audienceList.clear();
await getAudienceList();
} else if(tab.value == 1) {
page.value = 1;
audienceList.clear();
await getOnMicList();
} else if(tab.value == 2) {
page.value = 1;
audienceList.clear();
await getFriendList();
}
listRefreshController.finishRefresh(IndicatorResult.success);
listRefreshController.finishLoad(IndicatorResult.none);
}
final page = 1.obs;
final audienceList = <Records>[].obs;
late final EasyRefreshController listRefreshController;
getAudienceList() async {
try{
final channelId = RTCManager.instance.currentChannelId;
final response = await _networkService.rtcApi.userPageRtcChannelAudience(
pageNum: page.value,
pageSize: 10,
channelId: channelId ?? ""
);
if (response.data.isSuccess && response.data.data != null) {
final data = response.data.data?.records ?? [];
audienceList.addAll(data.toList());
if((data.length ?? 0) == 10){
listRefreshController.finishLoad(IndicatorResult.success);
} else {
listRefreshController.finishLoad(IndicatorResult.noMore);
}
} else {
//
throw Exception(response.data.message ?? '获取数据失败');
}
} catch(e) {
print('提现记录获取失败: $e');
SmartDialog.showToast('提现记录获取失败');
rethrow;
}
}
getOnMicList() async {
try{
final channelId = RTCManager.instance.currentChannelId;
final response = await _networkService.rtcApi.userPageMicJoinRtcChannelUser(
pageNum: page.value,
pageSize: 10,
channelId: channelId ?? ""
);
if (response.data.isSuccess && response.data.data != null) {
final data = response.data.data?.records ?? [];
audienceList.addAll(data.toList());
if((data.length ?? 0) == 10){
listRefreshController.finishLoad(IndicatorResult.success);
} else {
listRefreshController.finishLoad(IndicatorResult.noMore);
}
} else {
//
throw Exception(response.data.message ?? '获取数据失败');
}
} catch(e) {
print('提现记录获取失败: $e');
SmartDialog.showToast('提现记录获取失败');
rethrow;
}
}
getFriendList() async {
try{
final response = await _networkService.userApi.userPageFriendRelation(
pageNum: page.value,
pageSize: 10,
);
if (response.data.isSuccess && response.data.data != null) {
final data = response.data.data?.records ?? [];
final List<Records> b = data.map((e){
return Records(
id: e.id,
userManagementId: null,
userId: e.userId,
miId: e.miId,
nickName: e.nickName,
profilePhoto: e.profilePhoto,
genderCode: e.genderCode,
birthYear: e.birthYear,
birthDate: e.birthDate,
age: e.age,
provinceCode: e.provinceCode,
provinceName: e.provinceName,
cityCode: e.cityCode,
cityName: e.cityName,
);
}).toList();
audienceList.addAll(b);
if((data.length ?? 0) == 10){
listRefreshController.finishLoad(IndicatorResult.success);
} else {
listRefreshController.finishLoad(IndicatorResult.noMore);
}
} else {
//
throw Exception(response.data.message ?? '获取数据失败');
}
} catch(e) {
print('好友列表获取失败: $e');
SmartDialog.showToast('好友列表获取失败');
rethrow;
}
}
void setDialogDismiss(bool flag){
isDialogShowing.value = flag;
}

104
lib/model/discover/audience_list_data.dart

@ -0,0 +1,104 @@
class AudienceListData {
List<Records>? records;
int? total;
int? size;
int? current;
int? pages;
AudienceListData(
{this.records, this.total, this.size, this.current, this.pages});
AudienceListData.fromJson(Map<String, dynamic> json) {
if (json['records'] != null) {
records = <Records>[];
json['records'].forEach((v) {
records!.add(new Records.fromJson(v));
});
}
total = json['total'];
size = json['size'];
current = json['current'];
pages = json['pages'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
if (this.records != null) {
data['records'] = this.records!.map((v) => v.toJson()).toList();
}
data['total'] = this.total;
data['size'] = this.size;
data['current'] = this.current;
data['pages'] = this.pages;
return data;
}
}
class Records {
String? id;
int? userManagementId;
String? userId;
String? miId;
String? nickName;
String? profilePhoto;
int? genderCode;
String? birthYear;
String? birthDate;
int? age;
int? provinceCode;
String? provinceName;
int? cityCode;
String? cityName;
Records(
{this.id,
this.userManagementId,
this.userId,
this.miId,
this.nickName,
this.profilePhoto,
this.genderCode,
this.birthYear,
this.birthDate,
this.age,
this.provinceCode,
this.provinceName,
this.cityCode,
this.cityName});
Records.fromJson(Map<String, dynamic> json) {
id = json['id'];
userManagementId = json['userManagementId'];
userId = json['userId'];
miId = json['miId'];
nickName = json['nickName'];
profilePhoto = json['profilePhoto'];
genderCode = json['genderCode'];
birthYear = json['birthYear'];
birthDate = json['birthDate'];
age = json['age'];
provinceCode = json['provinceCode'];
provinceName = json['provinceName'];
cityCode = json['cityCode'];
cityName = json['cityName'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['userManagementId'] = this.userManagementId;
data['userId'] = this.userId;
data['miId'] = this.miId;
data['nickName'] = this.nickName;
data['profilePhoto'] = this.profilePhoto;
data['genderCode'] = this.genderCode;
data['birthYear'] = this.birthYear;
data['birthDate'] = this.birthDate;
data['age'] = this.age;
data['provinceCode'] = this.provinceCode;
data['provinceName'] = this.provinceName;
data['cityCode'] = this.cityCode;
data['cityName'] = this.cityName;
return data;
}
}

7
lib/network/api_urls.dart

@ -152,4 +152,11 @@ class ApiUrls {
static const String consumeOneOnOneRtcChannel =
'dating-agency-chat-audio/user/consume/one-on-one/rtc-channel';
//
static const String userPageRtcChannelAudience =
'dating-agency-chat-audio/user/page/rtc-channel-audience';
static const String userPageMicJoinRtcChannelUser =
'dating-agency-chat-audio/user/page/mic-join/rtc-channel-user';
}

21
lib/network/rtc_api.dart

@ -1,3 +1,4 @@
import 'package:dating_touchme_app/model/discover/audience_list_data.dart';
import 'package:dating_touchme_app/model/discover/rtc_channel_model.dart';
import 'package:dating_touchme_app/model/live/gift_product_model.dart';
import 'package:dating_touchme_app/model/rtc/chat_audio_product_model.dart';
@ -137,4 +138,24 @@ abstract class RtcApi {
Future<HttpResponse<BaseResponse<ConsumeRtcChannelResponse>>> consumeOneOnOneRtcChannel(
@Body() Map<String, dynamic> data,
);
@GET(ApiUrls.userPageRtcChannelAudience)
Future<HttpResponse<BaseResponse<AudienceListData>>> userPageRtcChannelAudience(
{
@Query('pageNum') required int pageNum,
@Query('pageSize') required int pageSize,
@Query('channelId') required String channelId,
}
);
@GET(ApiUrls.userPageMicJoinRtcChannelUser)
Future<HttpResponse<BaseResponse<AudienceListData>>> userPageMicJoinRtcChannelUser(
{
@Query('pageNum') required int pageNum,
@Query('pageSize') required int pageSize,
@Query('channelId') required String channelId,
}
);
}

86
lib/network/rtc_api.g.dart

@ -786,6 +786,92 @@ class _RtcApi implements RtcApi {
return httpResponse;
}
@override
Future<HttpResponse<BaseResponse<AudienceListData>>>
userPageRtcChannelAudience({
required int pageNum,
required int pageSize,
required String channelId,
}) async {
final _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{
r'pageNum': pageNum,
r'pageSize': pageSize,
r'channelId': channelId,
};
final _headers = <String, dynamic>{};
const Map<String, dynamic>? _data = null;
final _options =
_setStreamType<HttpResponse<BaseResponse<AudienceListData>>>(
Options(method: 'GET', headers: _headers, extra: _extra)
.compose(
_dio.options,
'dating-agency-chat-audio/user/page/rtc-channel-audience',
queryParameters: queryParameters,
data: _data,
)
.copyWith(
baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl),
),
);
final _result = await _dio.fetch<Map<String, dynamic>>(_options);
late BaseResponse<AudienceListData> _value;
try {
_value = BaseResponse<AudienceListData>.fromJson(
_result.data!,
(json) => AudienceListData.fromJson(json as Map<String, dynamic>),
);
} on Object catch (e, s) {
errorLogger?.logError(e, s, _options);
rethrow;
}
final httpResponse = HttpResponse(_value, _result);
return httpResponse;
}
@override
Future<HttpResponse<BaseResponse<AudienceListData>>>
userPageMicJoinRtcChannelUser({
required int pageNum,
required int pageSize,
required String channelId,
}) async {
final _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{
r'pageNum': pageNum,
r'pageSize': pageSize,
r'channelId': channelId,
};
final _headers = <String, dynamic>{};
const Map<String, dynamic>? _data = null;
final _options =
_setStreamType<HttpResponse<BaseResponse<AudienceListData>>>(
Options(method: 'GET', headers: _headers, extra: _extra)
.compose(
_dio.options,
'dating-agency-chat-audio/user/page/mic-join/rtc-channel-user',
queryParameters: queryParameters,
data: _data,
)
.copyWith(
baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl),
),
);
final _result = await _dio.fetch<Map<String, dynamic>>(_options);
late BaseResponse<AudienceListData> _value;
try {
_value = BaseResponse<AudienceListData>.fromJson(
_result.data!,
(json) => AudienceListData.fromJson(json as Map<String, dynamic>),
);
} on Object catch (e, s) {
errorLogger?.logError(e, s, _options);
rethrow;
}
final httpResponse = HttpResponse(_value, _result);
return httpResponse;
}
RequestOptions _setStreamType<T>(RequestOptions requestOptions) {
if (T != dynamic &&
!(requestOptions.responseType == ResponseType.bytes ||

8
lib/rtc/rtc_manager.dart

@ -105,6 +105,7 @@ class RTCManager {
));
await _engine?.enableVideo();
// await _engine?.startPreview();
await _engine?.enableFaceDetection(true);
//
_registerEventHandlers();
@ -326,6 +327,13 @@ class RTCManager {
);
_engine?.renewToken(rtcToken);
},
onFacePositionChanged: (int imageWidth,
int imageHeight,
List<Rectangle> vecRectangle,
List<int> vecDistance,
int numFaces){
print("当前人脸数:$numFaces");
},
onError: (ErrorCodeType err, String msg) {
print('RTC Engine 错误:$err,消息:$msg');
if (onError != null) {

33
lib/widget/live/live_room_anchor_showcase.dart

@ -130,6 +130,35 @@ class _LiveRoomAnchorShowcaseState extends State<LiveRoomAnchorShowcase> {
),
);
}),
Positioned(
left: 5.w,
bottom: 5.w,
child: Row(
children: [
Container(
width: 20.w,
height: 20.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(
Radius.circular(4.w),
),
color: const Color.fromRGBO(0, 0, 0, .65),
),
child: Center(
child: Image.asset(
(_roomController
.rtcChannelDetail
.value?.anchorInfo?.isMicrophoneOn ?? false)
? Assets.imagesMicOpen
: Assets.imagesMicClose,
width: 10.w,
height: 11.w,
),
),
),
],
),
),
],
),
SizedBox(height: 5.w),
@ -379,7 +408,9 @@ class _LiveRoomAnchorShowcaseState extends State<LiveRoomAnchorShowcase> {
);
}
void _showGuestListDialog(BuildContext context, bool isMaleSeat) {
void _showGuestListDialog(BuildContext context, bool isMaleSeat) async {
_roomController.audienceList.clear();
await _roomController.getAudienceList();
_roomController.setDialogDismiss(true);
SmartDialog.show(
alignment: Alignment.bottomCenter,

251
lib/widget/live/live_room_invitation_list.dart

@ -1,6 +1,9 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:dating_touchme_app/controller/discover/room_controller.dart';
import 'package:dating_touchme_app/extension/ex_widget.dart';
import 'package:dating_touchme_app/generated/assets.dart';
import 'package:dating_touchme_app/model/discover/audience_list_data.dart';
import 'package:easy_refresh/easy_refresh.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
@ -51,7 +54,7 @@ class _LiveRoomInvitationListState extends State<LiveRoomInvitationList> with Ti
controller: _tabController,
showIndicator: true,
onTap: (int i) async {
_roomController.changeTab(i);
},
),
),
@ -61,102 +64,85 @@ class _LiveRoomInvitationListState extends State<LiveRoomInvitationList> with Ti
child: Column(
children: [
Expanded(
child: SingleChildScrollView(
child: Column(
children: [
Container(
margin: EdgeInsets.symmetric(vertical: 10.w),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Image.asset(
Assets.imagesUserAvatar,
width: 40.w,
height: 40.w,
),
SizedBox(width: 5.w,),
Column(
children: [
Text(
"开心",
style: TextStyle(
fontSize: 12.w
),
),
Text(
"22岁",
style: TextStyle(
fontSize: 12.w,
color: const Color.fromRGBO(121, 121, 121, 1)
),
),
],
)
],
),
Checkbox(
value: false,
onChanged: (value) {
},
activeColor: const Color.fromRGBO(117, 98, 249, 1),
side: const BorderSide(color: Colors.grey),
shape: const CircleBorder(),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
],
),
),
Container(
margin: EdgeInsets.symmetric(vertical: 10.w),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Image.asset(
Assets.imagesUserAvatar,
width: 40.w,
height: 40.w,
),
SizedBox(width: 5.w,),
Column(
children: [
Text(
"开心",
style: TextStyle(
fontSize: 12.w
),
),
Text(
"22岁",
style: TextStyle(
fontSize: 12.w,
color: const Color.fromRGBO(121, 121, 121, 1)
),
),
],
)
],
),
Checkbox(
value: false,
onChanged: (value) {
child: EasyRefresh(
},
activeColor: const Color.fromRGBO(117, 98, 249, 1),
side: const BorderSide(color: Colors.grey),
shape: const CircleBorder(),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
],
),
),
],
controller: _roomController.listRefreshController,
header: const ClassicHeader(
dragText: '下拉刷新',
armedText: '释放刷新',
readyText: '刷新中...',
processingText: '刷新中...',
processedText: '刷新完成',
failedText: '刷新失败',
noMoreText: '没有更多数据',
showMessage: false
),
footer: ClassicFooter(
dragText: '上拉加载',
armedText: '释放加载',
readyText: '加载中...',
processingText: '加载中...',
processedText: '加载完成',
failedText: '加载失败',
noMoreText: '没有更多数据',
showMessage: false
),
//
onRefresh: () async {
print('推荐列表下拉刷新被触发');
_roomController.page.value = 1;
_roomController.audienceList.clear();
if(_roomController.tab.value == 0){
await _roomController.getAudienceList();
} else if(_roomController.tab.value == 1){
await _roomController.getOnMicList();
} else if(_roomController.tab.value == 2){
await _roomController.getFriendList();
}
_roomController.listRefreshController.finishRefresh(IndicatorResult.success);
_roomController.listRefreshController.finishLoad(IndicatorResult.none);
},
//
onLoad: () async {
print('推荐列表上拉加载被触发, hasMore: ');
_roomController.page.value += 1;
if(_roomController.tab.value == 0){
await _roomController.getAudienceList();
} else if(_roomController.tab.value == 1){
await _roomController.getOnMicList();
} else if(_roomController.tab.value == 2){
await _roomController.getFriendList();
}
},
child: ListView.separated(
// 使
// padding AppBar
padding: EdgeInsets.only(left: 12, right: 12),
itemBuilder: (context, index) {
//
if (_roomController.audienceList.isEmpty && index == 0) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('暂无数据'),
],
),
);
}
//
final item = _roomController.audienceList[index];
return AudienceItem(item: item);
},
separatorBuilder: (context, index) {
//
if (_roomController.audienceList.isEmpty) {
return const SizedBox.shrink();
}
return const SizedBox(height: 12);
},
// item
itemCount: _roomController.audienceList.isEmpty ? 1 : _roomController.audienceList.length,
),
),
)
@ -347,3 +333,76 @@ class _LiveRoomInvitationListState extends State<LiveRoomInvitationList> with Ti
}
}
class AudienceItem extends StatefulWidget {
final Records item;
const AudienceItem({super.key, required this.item});
@override
State<AudienceItem> createState() => _AudienceItemState();
}
class _AudienceItemState extends State<AudienceItem> {
@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.symmetric(vertical: 10.w),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(40.w)),
child: CachedNetworkImage(
imageUrl: "${widget.item.profilePhoto ?? ""}?x-oss-process=image/format,webp/resize,w_120",
width: 40.w,
height: 40.w,
imageBuilder: (context, imageProvider) => Container(
decoration: BoxDecoration(
image: DecorationImage(
image: imageProvider,
fit: BoxFit.cover,
),
),
),
),
),
SizedBox(width: 5.w,),
Column(
children: [
Text(
widget.item.nickName ?? "",
style: TextStyle(
fontSize: 12.w
),
),
Text(
"${widget.item.age ?? ""}",
style: TextStyle(
fontSize: 12.w,
color: const Color.fromRGBO(121, 121, 121, 1)
),
),
],
)
],
),
Checkbox(
value: false,
onChanged: (value) {
},
activeColor: const Color.fromRGBO(117, 98, 249, 1),
side: const BorderSide(color: Colors.grey),
shape: const CircleBorder(),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
],
),
);
}
}
Loading…
Cancel
Save