Browse Source

优化样式,增加消息页搜索,对接直播间搜索,对接定位与同城

dev-2.0
王子贤 3 weeks ago
parent
commit
d5593bbf1c
16 changed files with 833 additions and 214 deletions
  1. 2
      lib/components/home_appbar.dart
  2. 60
      lib/controller/discover/search_page_controller.dart
  3. 232
      lib/controller/home/home_controller.dart
  4. 28
      lib/controller/message/conversation_controller.dart
  5. 108
      lib/model/discover/matchmaker_live_data.dart
  6. 3
      lib/network/api_urls.dart
  7. 8
      lib/network/rtc_api.dart
  8. 35
      lib/network/rtc_api.g.dart
  9. 41
      lib/pages/discover/discover_page.dart
  10. 289
      lib/pages/discover/search_page.dart
  11. 11
      lib/pages/home/home_page.dart
  12. 13
      lib/pages/home/nearby_tab.dart
  13. 63
      lib/pages/home/send_timeline.dart
  14. 28
      lib/pages/main/main_page.dart
  15. 100
      lib/pages/message/message_page.dart
  16. 26
      lib/rtc/rtc_manager.dart

2
lib/components/home_appbar.dart

@ -34,7 +34,7 @@ class _HomeAppbarState extends State<HomeAppbar> {
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: [
...widget.topNav.asMap().entries.map((entry){
return InkWell(

60
lib/controller/discover/search_page_controller.dart

@ -0,0 +1,60 @@
import 'dart:async';
import 'package:dating_touchme_app/network/network_service.dart';
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import '../../model/discover/matchmaker_live_data.dart';
class SearchPageController extends GetxController {
SearchPageController({NetworkService? networkService})
: _networkService = networkService ?? Get.find<NetworkService>();
final NetworkService _networkService;
final FocusNode blankFocusNode = FocusNode();
final searchController = TextEditingController().obs;
final name = "".obs;
final matchmakerList = <Records>[].obs;
Timer? _debounce;
void onTextChanged(String text) {
//
_debounce?.cancel();
_debounce = Timer(const Duration(milliseconds: 500), () {
name.value = text;
searchData();
});
}
searchData() async {
try{
final response = await _networkService.rtcApi.userPageLiveMatchmaker(
searchKey: name.value
);
if (response.data.isSuccess && response.data.data != null) {
final data = response.data.data?.records ?? [];
matchmakerList.value = data.toList();
} else {
//
throw Exception(response.data.message ?? '获取数据失败');
}
} catch(e) {
print('红娘搜索失败: $e');
SmartDialog.showToast('红娘搜索失败');
rethrow;
}
}
}

232
lib/controller/home/home_controller.dart

@ -2,9 +2,11 @@ import 'dart:async';
import 'package:dating_touchme_app/generated/assets.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:location_plugin/location_plugin.dart';
import '../../network/home_api.dart';
import '../../model/home/marriage_data.dart';
@ -35,6 +37,14 @@ class HomeController extends GetxController {
final timelineTab = 0.obs;
final RxnInt cityCode = RxnInt();
final _locationPlugin = LocationPlugin();
String _status = 'Idle';
String _cityStatus = 'Idle';
Map<String, double>? _location;
CityInfo? _cityInfo;
@override
void onInit() {
super.onInit();
@ -43,142 +53,72 @@ class HomeController extends GetxController {
//
loadInitialData();
// Timer.periodic(const Duration(seconds: 10), (t) {
// SmartDialog.show(
// alignment: Alignment.center,
// maskColor: Colors.black.withOpacity(0.5),
// onDismiss: () {
//
// },
// builder: (context) {
// // return LiveRoomGuestListDialog(
// // initialTab: isMaleSeat ? 1 : 0, // 0: , 1:
// // );
// return ClipRRect(
// borderRadius: BorderRadius.all(Radius.circular(16.w)),
// child: Material(
// child: Stack(
//
// children: [
// Container(
// width: 311.w,
// height: 210.w,
// color: Colors.white,
// padding: EdgeInsets.only(
// top: 53.w,
// left: 23.w,
// right: 23.w,
// bottom: 20.w
// ),
// child: Column(
// children: [
// Text(
// "主持人邀请您视频连麦",
// style: TextStyle(
// fontSize: 21.w,
// color: const Color.fromRGBO(117, 98, 249, 1),
// fontWeight: FontWeight.w500
// ),
// ),
// SizedBox(height: 15.w,),
// Text(
// "有相亲卡的用户免费",
// style: TextStyle(
// fontSize: 12.w,
// color: const Color.fromRGBO(87, 87, 87, 1),
// fontWeight: FontWeight.w500
// ),
// ),
// SizedBox(height: 28.w,),
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// Container(
// width: 128.w,
// height: 40.w,
// decoration: BoxDecoration(
// borderRadius: BorderRadius.all(Radius.circular(12.w)),
// color: const Color.fromRGBO(237, 237, 237, 1)
// ),
// child: Row(
// mainAxisAlignment: MainAxisAlignment.center,
// children: [
// Image.asset(
// Assets.imagesHangUpIcon,
// width: 26.w,
// height: 26.w,
// ),
// SizedBox(width: 5.w,),
// Text(
// "拒绝",
// style: TextStyle(
// fontSize: 15.w
// ),
// )
// ],
// ),
// ),
// Container(
// width: 128.w,
// height: 40.w,
// decoration: BoxDecoration(
// borderRadius: BorderRadius.all(Radius.circular(12.w)),
// color: const Color.fromRGBO(117, 98, 249, 1)
// ),
// child: Row(
// mainAxisAlignment: MainAxisAlignment.center,
// children: [
// Image.asset(
// Assets.imagesAnswerIcon,
// width: 18.w,
// height: 13.w,
// ),
// SizedBox(width: 5.w,),
// Text(
// "拒绝",
// style: TextStyle(
// fontSize: 15.w,
// color: Colors.white
// ),
// )
// ],
// ),
// ),
// ],
// )
// ],
// ),
// ),
// Positioned(
// top: 0,
// left: 0,
// child: Container(
// padding: EdgeInsets.symmetric(vertical: 5.w, horizontal: 15.w),
// decoration: BoxDecoration(
// borderRadius: BorderRadius.only(
// topLeft: Radius.circular(16.w),
// bottomRight: Radius.circular(16.w),
// ),
// color: const Color.fromRGBO(117, 98, 249, 1)
// ),
// child: Text(
// "20玫瑰",
// style: TextStyle(
// fontSize: 14.w,
// color: Colors.white,
// fontWeight: FontWeight.w500
// ),
// ),
// ),
// )
// ],
// ),
// ),
// );
// },
// );
// //
// });
}
Future<void> _fetchLocation() async {
_status = 'Fetching location...';
try {
final location = await _locationPlugin.getCurrentLocation();
if (location == null) {
_status = 'Location unavailable';
_cityStatus = 'Skip city lookup';
_cityInfo = null;
return;
}
_location = location;
_status = 'Success';
_cityStatus = 'Fetching city info...';
await _fetchCityInfo(location['latitude']!, location['longitude']!);
} on PlatformException catch (e) {
_status = 'Failed: ${e.code} ${e.message ?? ''}'.trim();
_cityStatus = 'Skip city lookup';
_cityInfo = null;
} catch (e) {
_status = 'Failed: $e';
_cityStatus = 'Skip city lookup';
_cityInfo = null;
}
}
Future<void> _fetchCityInfo(double latitude, double longitude) async {
try {
final info = await _locationPlugin.getCityInfo(latitude: latitude, longitude: longitude);
print(info);
_cityInfo = info;
_cityStatus = info == null ? 'No city info' : 'Success';
cityCode.value = ((_cityInfo?.code ?? 0) ~/ 100) * 100;
} on CityInfoLookupException catch (e) {
_cityInfo = null;
_cityStatus = 'Failed: ${e.code}';
} catch (e) {
_cityInfo = null;
_cityStatus = 'Failed: $e';
}
}
///
@ -187,7 +127,6 @@ class HomeController extends GetxController {
print(12121);
await Future.wait([
loadRecommendInitialData(),
loadNearbyInitialData(),
]);
}
@ -225,6 +164,11 @@ class HomeController extends GetxController {
///
Future<void> loadNearbyInitialData() async {
if(cityCode.value == null || cityCode.value == 0){
return;
}
if (nearbyIsLoading.value) return;
try {
@ -236,6 +180,7 @@ class HomeController extends GetxController {
final result = await _fetchMarriageData(
pageNum: 1,
type: 1,
code: cityCode.value
);
//
@ -311,11 +256,11 @@ class HomeController extends GetxController {
// final nextPage = nearbyPage.value + 1;
final nextPage = nearbyPage.value;
print('同城列表加载更多 - 当前页: ${nearbyPage.value}, 下一页: $nextPage');
// (type=1)
final result = await _fetchMarriageData(
pageNum: nextPage,
type: 1,
code: cityCode.value
);
//
@ -394,6 +339,7 @@ class HomeController extends GetxController {
final result = await _fetchMarriageData(
pageNum: 1,
type: 1,
code: cityCode.value
);
//
@ -413,11 +359,17 @@ class HomeController extends GetxController {
}
///
void setSelectedTabIndex(int index) {
void setSelectedTabIndex(int index) async {
print('Setting selected tab index to: $index');
selectedTabIndex.value = index;
// UI能够更新
update();
if(index == 2 && nearbyFeed.isEmpty){
if(cityCode.value == null || cityCode.value == 0){
await _fetchLocation();
}
loadNearbyInitialData();
}
}
///
@ -430,6 +382,7 @@ class HomeController extends GetxController {
Future<Map<String, dynamic>> _fetchMarriageData({
required int pageNum,
required int type,
int? code
}) async {
try {
print('_fetchMarriageData - pageNum: $pageNum, pageSize: $pageSize, type: $type');
@ -438,6 +391,7 @@ class HomeController extends GetxController {
pageNum: pageNum,
pageSize: pageSize,
type: type,
cityCode: type == 1 ? code : null
);
if (response.data.isSuccess) {

28
lib/controller/message/conversation_controller.dart

@ -1,4 +1,5 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:im_flutter_sdk/im_flutter_sdk.dart';
import 'package:app_badge_plus/app_badge_plus.dart';
@ -65,6 +66,33 @@ class ConversationController extends GetxController {
//
Timer? _refreshDebounceTimer;
final FocusNode blankFocusNode = FocusNode();
final searchController = TextEditingController().obs;
final name = "".obs;
final showSearch = false.obs;
void onTextChanged(String text) {
name.value = text;
}
search(){
if(name.value == ""){
_checkAndLoadConversations();
} else {
List<ExtendedUserInfo> list = _userInfoCache.values.where((e) => e.nickName!.contains(name.value)).toList();
List<EMConversation> result = conversations.where((a) {
return list.any((b) => b.userId == a.id);
}).toList();
conversations.value = result;
}
}
/// ChatController
void cacheUserInfo(String userId, ExtendedUserInfo userInfo) {

108
lib/model/discover/matchmaker_live_data.dart

@ -0,0 +1,108 @@
class MatchmakerLiveData {
List<Records>? records;
int? total;
int? size;
int? current;
int? pages;
MatchmakerLiveData(
{this.records, this.total, this.size, this.current, this.pages});
MatchmakerLiveData.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? userId;
String? miId;
String? nickName;
String? profilePhoto;
int? genderCode;
String? birthYear;
String? birthDate;
int? age;
int? provinceCode;
String? provinceName;
int? cityCode;
String? cityName;
Null? isLiveChannelId;
bool? liveStreaming;
int? uid;
Records(
{this.userId,
this.miId,
this.nickName,
this.profilePhoto,
this.genderCode,
this.birthYear,
this.birthDate,
this.age,
this.provinceCode,
this.provinceName,
this.cityCode,
this.cityName,
this.isLiveChannelId,
this.liveStreaming,
this.uid});
Records.fromJson(Map<String, dynamic> json) {
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'];
isLiveChannelId = json['isLiveChannelId'];
liveStreaming = json['liveStreaming'];
uid = json['uid'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
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;
data['isLiveChannelId'] = this.isLiveChannelId;
data['liveStreaming'] = this.liveStreaming;
data['uid'] = this.uid;
return data;
}
}

3
lib/network/api_urls.dart

@ -229,4 +229,7 @@ class ApiUrls {
static const String userPageDongwoMatchmakerMarriageInformation =
'dating-agency-service/user/page/dongwo/matchmaker-marriage-information';
static const String userPageLiveMatchmaker =
'dating-agency-chat-audio/user/page/live-matchmaker';
}

8
lib/network/rtc_api.dart

@ -1,5 +1,6 @@
import 'package:dating_touchme_app/model/discover/audience_list_data.dart';
import 'package:dating_touchme_app/model/discover/live_income_data.dart';
import 'package:dating_touchme_app/model/discover/matchmaker_live_data.dart';
import 'package:dating_touchme_app/model/discover/rtc_channel_model.dart';
import 'package:dating_touchme_app/model/discover/task_template_data.dart';
import 'package:dating_touchme_app/model/live/gift_product_model.dart';
@ -187,4 +188,11 @@ abstract class RtcApi {
@Query('id') required String id,
}
);
///
@GET(ApiUrls.userPageLiveMatchmaker)
Future<HttpResponse<BaseResponse<MatchmakerLiveData>>> userPageLiveMatchmaker(
{
@Query('searchKey') required String searchKey,
}
);
}

35
lib/network/rtc_api.g.dart

@ -980,6 +980,41 @@ class _RtcApi implements RtcApi {
return httpResponse;
}
@override
Future<HttpResponse<BaseResponse<MatchmakerLiveData>>>
userPageLiveMatchmaker({required String searchKey}) async {
final _extra = <String, dynamic>{};
final queryParameters = <String, dynamic>{r'searchKey': searchKey};
final _headers = <String, dynamic>{};
const Map<String, dynamic>? _data = null;
final _options =
_setStreamType<HttpResponse<BaseResponse<MatchmakerLiveData>>>(
Options(method: 'GET', headers: _headers, extra: _extra)
.compose(
_dio.options,
'dating-agency-chat-audio/user/page/live-matchmaker',
queryParameters: queryParameters,
data: _data,
)
.copyWith(
baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl),
),
);
final _result = await _dio.fetch<Map<String, dynamic>>(_options);
late BaseResponse<MatchmakerLiveData> _value;
try {
_value = BaseResponse<MatchmakerLiveData>.fromJson(
_result.data!,
(json) => MatchmakerLiveData.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 ||

41
lib/pages/discover/discover_page.dart

@ -1,8 +1,10 @@
import 'package:dating_touchme_app/components/home_appbar.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/pages/discover/dating_page.dart';
import 'package:dating_touchme_app/pages/discover/party_page.dart';
import 'package:dating_touchme_app/pages/discover/search_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
@ -78,19 +80,32 @@ class _DiscoverPageState extends State<DiscoverPage>
topNav: topNav,
changeNav: changeNav,
activeIndex: active,
right: TDButton(
text: roomController.matchmakerFlag.value ? '开始直播' : '申请红娘',
width: 60.w,
height: 24.h,
padding: EdgeInsetsGeometry.symmetric(horizontal: 4.w),
size: TDButtonSize.small,
type: TDButtonType.fill,
shape: TDButtonShape.round,
theme: roomController.matchmakerFlag.value ? TDButtonTheme.danger : TDButtonTheme.primary,
textStyle: TextStyle(fontSize: 10.w),
onTap: () async {
roomController.registerMatch(roomController.matchmakerFlag.value ? 2 : 1);
},
right: Row(
children: [
Icon(
Icons.search,
size: 30,
).onTap((){
Get.to(() => SearchPage());
}),
SizedBox(width: 10,),
TDButton(
text: roomController.matchmakerFlag.value ? '开始直播' : '申请红娘',
width: 60.w,
height: 24.h,
padding: EdgeInsetsGeometry.symmetric(horizontal: 4.w),
size: TDButtonSize.small,
type: TDButtonType.fill,
shape: TDButtonShape.round,
theme: roomController.matchmakerFlag.value ? TDButtonTheme.danger : TDButtonTheme.primary,
textStyle: TextStyle(fontSize: 10.w),
onTap: () async {
roomController.registerMatch(roomController.matchmakerFlag.value ? 2 : 1);
},
)
],
),
);
}),

289
lib/pages/discover/search_page.dart

@ -0,0 +1,289 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:dating_touchme_app/components/page_appbar.dart';
import 'package:dating_touchme_app/controller/discover/room_controller.dart';
import 'package:dating_touchme_app/controller/discover/search_page_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/matchmaker_live_data.dart';
import 'package:dating_touchme_app/model/home/marriage_data.dart';
import 'package:dating_touchme_app/model/home/user_info_data.dart';
import 'package:dating_touchme_app/network/user_api.dart';
import 'package:dating_touchme_app/pages/home/user_information_page.dart';
import 'package:dating_touchme_app/pages/message/chat_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
class SearchPage extends StatelessWidget {
const SearchPage({super.key});
@override
Widget build(BuildContext context) {
return GetX<SearchPageController>(
init: SearchPageController(),
builder: (controller){
return Scaffold(
appBar: PageAppbar(title: "搜索"),
body: Column(
children: [
Container(
padding: EdgeInsets.symmetric(horizontal: 15.w),
margin: EdgeInsets.only(bottom: 10.w),
child: Container(
height: 30.w,
padding: EdgeInsets.symmetric(horizontal: 10),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(30.w)),
color: const Color.fromRGBO(200, 200, 200, 1.0)
),
child: Row(
children: [
Icon(
Icons.search,
size: 20,
color: Colors.grey,
),
Expanded(
child: TextField(
focusNode: controller.blankFocusNode,
onTapOutside: (e){
controller.blankFocusNode.unfocus();
},
controller: controller.searchController.value,
textAlignVertical: TextAlignVertical.center,
style: TextStyle(
fontSize: ScreenUtil().setWidth(13),
height: 1
),
decoration: InputDecoration(
isDense: true,
contentPadding: EdgeInsets.only(
left: 0,
right: -8
),
hintText: "输入趣恋恋ID/昵称进行搜索",
border: const OutlineInputBorder(
borderSide: BorderSide.none, // //
),
// focusedBorder enabledBorder
focusedBorder: const OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.all(Radius.circular(4.0)),
),
enabledBorder: const OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.all(Radius.circular(4.0)),
),
),
onChanged: controller.onTextChanged,
),
),
],
),
),
),
Expanded(
child: SingleChildScrollView(
child: Container(
padding: EdgeInsets.symmetric(horizontal: 15.w),
child: Column(
children: [
if(controller.matchmakerList.isEmpty) Container(
padding: EdgeInsets.only(top: 45.w),
child: Column(
children: [
Text(
"仅支持搜索",
style: TextStyle(
color: const Color.fromRGBO(121, 121, 121, 1)
),
),
Text(
"趣恋恋主持人"
)
],
),
),
...controller.matchmakerList.map((e){
return SearchItem(item: e,);
}),
],
),
),
),
)
],
),
);
},
);
}
}
class SearchItem extends StatefulWidget {
final Records item;
const SearchItem({super.key, required this.item});
@override
State<SearchItem> createState() => _SearchItemState();
}
class _SearchItemState extends State<SearchItem> {
@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.only(bottom: 10.w),
child: Row(
children: [
CachedNetworkImage(
imageUrl: '${widget.item.profilePhoto}?x-oss-process=image/format,webp/resize,w_320',
width: 60,
height: 60,
fit: BoxFit.cover,
imageBuilder: (context, imageProvider) => Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(60),
image: DecorationImage(
image: imageProvider,
fit: BoxFit.cover,
),
),
),
errorWidget: (context, url, error) => Image.asset(
Assets.imagesUserAvatar,
width: 60,
height: 60,
fit: BoxFit.cover,
),
),
SizedBox(width: 15.w,),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Flexible(
child: Text(
widget.item.nickName ?? '用户',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Color.fromRGBO(51, 51, 51, 1),
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
SizedBox(width: 10.w,),
if(widget.item.liveStreaming ?? false)Container(
width: 33.w,
height: 13.w,
margin: EdgeInsets.only(left: 6.w),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(13.w)),
color: const Color.fromRGBO(234, 255, 219, 1)
),
child: Center(
child: Text(
"在线",
style: TextStyle(
fontSize: 11.w,
color: const Color.fromRGBO(38, 199, 124, 1)
),
),
),
)
],
),
const SizedBox(height: 6),
SizedBox(
height: 16,
child: Text(
'${widget.item.age ?? 0}${(widget.item.cityName ?? '') != "" ? "· " : ""}${widget.item.cityName ?? ''}',
style: TextStyle(
fontSize: 12,
color: Color.fromRGBO(51, 51, 51, 1),
),
overflow: TextOverflow.ellipsis,
),
),
],
),
),
SizedBox(width: 15.w,),
Container(
width: 60.w,
height: 30.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(30.w)),
color: const Color.fromRGBO(117, 98, 249, 1)
),
child: Center(
child: Text(
"发消息",
style: TextStyle(
fontSize: 13.w,
color: Colors.white
),
),
),
).onTap(() async {
if (widget.item.userId?.isEmpty ?? false) {
SmartDialog.showToast('用户ID不存在,无法发送消息');
return;
}
try {
final _userApi = Get.find<UserApi>();
final response = await _userApi.getDongwoMarriageInformationDetail(miId: widget.item.miId ?? "");
if (response.data.isSuccess && response.data.data != null) {
// 使 UserInfoData MarriageData
final marriageData = MarriageData.fromUserInfoData(response.data.data ?? UserInfoData());
await Get.to(() => ChatPage(
userId: widget.item.userId ?? "",
userData: marriageData,
));
} else {
//
final errorMsg = response.data.message ?? '获取数据失败';
SmartDialog.showToast(errorMsg);
}
} catch(e, stackTrace){
print('获取用户信息失败: $e');
print('堆栈跟踪: $stackTrace');
SmartDialog.showToast('获取用户信息失败,请稍后重试');
// rethrow退
}
})
],
),
).onTap(() async {
if (widget.item.liveStreaming ?? false) {
late final RoomController roomController;
if (Get.isRegistered<RoomController>()) {
roomController = Get.find<RoomController>();
} else {
roomController = Get.put(RoomController());
}
await roomController.joinChannel(widget.item.isLiveChannelId ?? "");
} else {
Get.to(() => UserInformationPage(miId: widget.item.miId ?? ""));
}
});
}
}

11
lib/pages/home/home_page.dart

@ -61,10 +61,11 @@ class _HomePageState extends State<HomePage>
return IndexedStack(
index: controller.selectedTabIndex.value,
children: const [
//
RecommendTab(),
//
AllTimeline(),
//
RecommendTab(),
NearbyTab()
],
);
}),
@ -86,9 +87,11 @@ class _HomePageState extends State<HomePage>
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
_buildTabButton(title: '推荐', index: 0, controller: controller),
_buildTabButton(title: '广场', index: 0, controller: controller),
const SizedBox(width: 28),
_buildTabButton(title: '推荐', index: 1, controller: controller),
const SizedBox(width: 28),
_buildTabButton(title: '广场', index: 1, controller: controller),
_buildTabButton(title: '同城', index: 2, controller: controller),
],
),
bottom: const PreferredSize(

13
lib/pages/home/nearby_tab.dart

@ -96,6 +96,19 @@ class _NearbyTabState extends State<NearbyTab> with AutomaticKeepAliveClientMixi
// padding AppBar
padding: EdgeInsets.only(left: 12, right: 12),
itemBuilder: (context, index) {
//
if (controller.nearbyFeed.isEmpty && (controller.cityCode.value == null || controller.cityCode.value == 0) && index == 0) {
// 使
return SizedBox(
height: MediaQuery.of(context).size.height - totalBottomPadding,
child: const Center(
child: Text(
"暂未获取到定位,请稍后重试",
style: TextStyle(fontSize: 14, color: Color(0xFF999999)),
),
),
);
}
//
if (controller.nearbyFeed.isEmpty && index == 0) {
// 使

63
lib/pages/home/send_timeline.dart

@ -10,8 +10,10 @@ import 'package:dating_touchme_app/network/home_api.dart';
import 'package:dating_touchme_app/oss/oss_manager.dart';
import 'package:dating_touchme_app/widget/emoji_panel.dart';
import 'package:dating_touchme_app/widget/emoji_panel.dart';
import 'package:extended_text_field/extended_text_field.dart';
import 'package:flustars/flustars.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
@ -19,6 +21,8 @@ import 'package:image_picker/image_picker.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:tdesign_flutter/tdesign_flutter.dart';
import '../../widget/message/chat_input_bar.dart';
class SendTimeline extends StatefulWidget {
const SendTimeline({super.key});
@ -499,58 +503,35 @@ class _SendTimelineState extends State<SendTimeline> {
Expanded(
child: Stack(
children: [
TextField(
ExtendedTextField(
controller: messageController,
focusNode: focusNode,
minLines: 1,
maxLines: null, //
style: TextStyle(
fontSize: 14.sp,
color: messageController.text.contains('[emoji:')
? Colors.transparent
: Colors.black,
),
decoration: InputDecoration(
contentPadding: EdgeInsets.symmetric(
vertical: 0,
horizontal: 0
),
hintText: "勇敢表达吧,你的有趣,总有人懂。",
border: InputBorder.none,
hintText: "请输入聊天内容~",
hintStyle: TextStyle(
fontSize: 14.sp,
color: Colors.grey,
),
border: const OutlineInputBorder(
borderSide: BorderSide.none, // //
),
// focusedBorder enabledBorder
focusedBorder: const OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.all(Radius.circular(8.0)),
),
enabledBorder: const OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.all(Radius.circular(8.0)),
contentPadding: EdgeInsets.only(
bottom: 10
),
),
onChanged: (value){
setState(() {
});
},
),
if (messageController.text.contains('[emoji:'))
Positioned.fill(
child: IgnorePointer(
child: SingleChildScrollView(
child: Wrap(
crossAxisAlignment: WrapCrossAlignment.center,
children: buildInputContentWidgets(),
),
),
),
inputFormatters: [
// Unicode的规则
FilteringTextInputFormatter.deny(RegExp(r'[\u200B]')), //
],
specialTextSpanBuilder: MySpecialTextSpanBuilder(),
style: TextStyle(
fontSize: 14.sp,
color: Colors.black, // specialTextSpanBuilder
),
onChanged: (value) {
setState(() {}); //
},
)
],
),
),

28
lib/pages/main/main_page.dart

@ -1,4 +1,6 @@
import 'package:dating_touchme_app/extension/ex_widget.dart';
import 'package:dating_touchme_app/generated/assets.dart';
import 'package:dating_touchme_app/pages/home/send_timeline.dart';
import 'package:dating_touchme_app/pages/main/main_controller.dart';
import 'package:dating_touchme_app/pages/message/message_page.dart';
import 'package:dating_touchme_app/pages/mine/mine_page.dart';
@ -89,7 +91,23 @@ class _MainPageState extends State<MainPage> {
minePage,
],
),
bottomNavigationBar: _buildBottomNavigationBar(),
bottomNavigationBar: Stack(
clipBehavior: Clip.none,
children: [
_buildBottomNavigationBar(),
Positioned(
top: -20,
left: 150,
child: Image.asset(
Assets.imagesPublish,
width: 60,
height: 60,
).onTap((){
Get.to(() => SendTimeline());
}),
)
],
),
),
);
}
@ -115,6 +133,14 @@ class _MainPageState extends State<MainPage> {
navigationTabs: [
tabItem('首页', Assets.imagesHomePre, Assets.imagesHomeNol, 0, 0),
tabItem('找对象', Assets.imagesDiscoverPre, Assets.imagesDiscoverNol, 1, 0),
TDBottomTabBarTabConfig(
tabText: "",
selectedIcon: Image.asset(Assets.imagesHomePre, width: 30, height: 30, fit: BoxFit.cover, color: Colors.transparent,),
unselectedIcon: Image.asset(Assets.imagesHomeNol, width: 30, height: 30, fit: BoxFit.cover, color: Colors.transparent,),
onTap: (){
Get.to(() => SendTimeline());
}
),
tabItem('消息', Assets.imagesMessagePre, Assets.imagesMessageNol, 2, unreadCount),
tabItem('我的', Assets.imagesMinePre, Assets.imagesMineNol, 3, 0),
]

100
lib/pages/message/message_page.dart

@ -1,5 +1,7 @@
import 'package:dating_touchme_app/extension/ex_widget.dart';
import 'package:flutter/material.dart';
import 'package:dating_touchme_app/generated/assets.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'conversation_tab.dart';
import 'friend_tab.dart';
@ -16,12 +18,14 @@ class _MessagePageState extends State<MessagePage> with AutomaticKeepAliveClient
late TabController _tabController;
final GlobalKey _filterButtonKey = GlobalKey();
late ConversationController controller;
@override
void initState() {
super.initState();
_tabController = TabController(length: 2, vsync: this);
// ConversationController
Get.put(ConversationController());
controller = Get.put(ConversationController());
}
@override
@ -56,7 +60,6 @@ class _MessagePageState extends State<MessagePage> with AutomaticKeepAliveClient
backgroundColor: Colors.transparent,
elevation: 0,
automaticallyImplyLeading: false,
centerTitle: true,
title: Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
@ -67,6 +70,99 @@ class _MessagePageState extends State<MessagePage> with AutomaticKeepAliveClient
],
),
actions: [
Container(
width: 243,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
if(controller.showSearch.value) Expanded(
child: Container(
height: 30.w,
padding: EdgeInsets.symmetric(horizontal: 10),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(30.w)),
color: const Color.fromRGBO(200, 200, 200, 1.0)
),
child: Row(
children: [
Icon(
Icons.search,
size: 20,
color: Colors.grey,
),
Expanded(
child: TextField(
focusNode: controller.blankFocusNode,
onTapOutside: (e){
controller.blankFocusNode.unfocus();
},
controller: controller.searchController.value,
textAlignVertical: TextAlignVertical.center,
style: TextStyle(
fontSize: ScreenUtil().setWidth(13),
height: 1
),
decoration: InputDecoration(
isDense: true,
contentPadding: EdgeInsets.only(
left: 0,
right: -8
),
hintText: "搜索昵称",
border: const OutlineInputBorder(
borderSide: BorderSide.none, // //
),
// focusedBorder enabledBorder
focusedBorder: const OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.all(Radius.circular(4.0)),
),
enabledBorder: const OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.all(Radius.circular(4.0)),
),
),
onChanged: controller.onTextChanged,
),
),
SizedBox(width: 10,),
Row(
children: [
Container(
height: 15,
width: 1,
color: const Color.fromRGBO(51, 51, 51, 1),
),
SizedBox(width: 10,),
Text(
"搜索",
style: TextStyle(
fontSize: 12
),
)
],
).onTap((){
controller.search();
})
],
),
),
),
SizedBox(width: 10,),
Icon(
Icons.search,
size: 30,
).onTap((){
controller.showSearch.value = !controller.showSearch.value;
setState(() {
});
})
],
),
),
SizedBox(width: 10,),
//
GestureDetector(
key: _filterButtonKey,

26
lib/rtc/rtc_manager.dart

@ -587,20 +587,20 @@ class RTCManager {
),
);
print('正在加入频道:$channelId,UID:$uid,类型:$rtcType');
await _engine?.enableContentInspect(
enabled: true,
config: ContentInspectConfig(
modules: [
ContentInspectModule(
type: ContentInspectType.contentInspectImageModeration,
interval: 10
)
],
moduleCount: 1
)
);
if (role == ClientRoleType.clientRoleBroadcaster) {
await _engine?.enableContentInspect(
enabled: true,
config: ContentInspectConfig(
modules: [
ContentInspectModule(
type: ContentInspectType.contentInspectImageModeration,
interval: 10
)
],
moduleCount: 1
)
);
await _engine?.enableFaceDetection(true);
}
}

Loading…
Cancel
Save