chatapp3-flutter/lib/modules/search/sc_search_page.dart
2026-04-15 11:44:49 +08:00

759 lines
25 KiB
Dart

import 'dart:async';
import 'dart:convert';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:yumi/app_localizations.dart';
import 'package:yumi/ui_kit/theme/socialchat_theme.dart';
import 'package:yumi/app/constants/sc_global_config.dart';
import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
import 'package:yumi/shared/data_sources/sources/repositories/sc_room_repository_imp.dart';
import 'package:yumi/shared/data_sources/sources/repositories/sc_user_repository_impl.dart';
import 'package:yumi/shared/business_logic/models/res/room_res.dart';
import 'package:yumi/modules/index/main_route.dart';
import 'package:provider/provider.dart';
import 'package:yumi/ui_kit/components/socialchat_gradient_button.dart';
import 'package:yumi/ui_kit/components/sc_tts.dart';
import 'package:yumi/app/constants/sc_screen.dart';
import 'package:yumi/app/routes/sc_fluro_navigator.dart';
import 'package:yumi/shared/data_sources/sources/local/data_persistence.dart';
import 'package:yumi/shared/business_logic/models/res/login_res.dart';
import 'package:yumi/shared/business_logic/usecases/sc_fixed_width_tabIndicator.dart';
import 'package:yumi/services/audio/rtc_manager.dart';
import '../../services/general/sc_app_general_manager.dart';
import '../../ui_kit/components/sc_compontent.dart';
import '../../ui_kit/components/sc_debounce_widget.dart';
import '../../ui_kit/components/sc_page_list.dart';
import '../../ui_kit/components/text/sc_text.dart';
import '../../ui_kit/widgets/room/room_live_audio_indicator.dart';
///搜索房间
class SearchPage extends SCPageList {
@override
_SearchPageState createState() => _SearchPageState();
}
class _SearchPageState extends SCPageListState<dynamic, SearchPage>
with SingleTickerProviderStateMixin {
String search = '';
List<String> historys = [];
late TabController _tabController;
bool enablePullUp = false;
bool enablePullDown = false;
late TextEditingController _textEditingController;
@override
void dispose() {
super.dispose();
}
@override
void initState() {
super.initState();
_loadHistory();
_tabController = TabController(vsync: this, length: 2);
_textEditingController = TextEditingController();
_textEditingController.addListener(() {
if (_textEditingController.text.isEmpty) {
search = "";
_loadHistory();
}
});
}
@override
Widget build(BuildContext context) {
return Stack(
children: [
Image.asset(
"sc_images/person/sc_icon_edit_userinfo_bg.png",
width: ScreenUtil().screenWidth,
height: ScreenUtil().screenHeight,
fit: BoxFit.fill,
),
Scaffold(
backgroundColor:
SCGlobalConfig.businessLogicStrategy
.getSearchPageScaffoldBackgroundColor(),
body: Column(
children: [
Container(
height: kToolbarHeight + ScreenUtil().statusBarHeight,
padding: EdgeInsets.only(top: ScreenUtil().statusBarHeight),
alignment: AlignmentDirectional.centerStart,
child: Row(
children: <Widget>[
SizedBox(width: width(10)),
SCDebounceWidget(
child: Icon(
Icons.chevron_left,
color:
SCGlobalConfig.businessLogicStrategy
.getSearchPageBackIconColor(),
size: 25.w,
),
onTap: () {
SCNavigatorUtils.goBack(context);
},
),
Expanded(
child: searchWidget(
padding: EdgeInsets.symmetric(horizontal: 2.w),
hint:
SCAppLocalizations.of(context)!.pleaseEnterContent,
controller: _textEditingController,
borderColor:
SCGlobalConfig.businessLogicStrategy
.getSearchPageInputBorderColor(),
textColor:
SCGlobalConfig.businessLogicStrategy
.getSearchPageInputTextColor(),
),
),
socialchatGradientButton(
text: SCAppLocalizations.of(context)!.search,
radius: 25,
textSize: 14.sp,
textColor:
SCGlobalConfig.businessLogicStrategy
.getSearchPageButtonTextColor(),
gradient: LinearGradient(
colors:
SCGlobalConfig.businessLogicStrategy
.getSearchPageButtonGradient(),
begin: Alignment.centerLeft,
end: Alignment.centerRight,
),
onPress: () {
_reqSearch();
},
),
SizedBox(width: width(10)),
],
),
),
search.isEmpty || search == "" ? _history() : _result(),
],
),
),
],
);
}
_history() {
if (historys.isEmpty) {
return mainEmpty(msg: SCAppLocalizations.of(context)!.noData);
} else {
return Expanded(
child: Column(
children: <Widget>[
SizedBox(height: 20.w),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SizedBox(width: width(15)),
text(
SCAppLocalizations.of(context)!.history,
fontSize: 16.sp,
textColor:
SCGlobalConfig.businessLogicStrategy
.getSearchPageHistoryTitleTextColor(),
fontWeight: FontWeight.bold,
),
Expanded(child: SizedBox(width: width(1))),
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
setState(() {
historys.clear();
_saveHistory("");
});
},
child: Row(
children: <Widget>[
Image.asset(
'sc_images/general/sc_icon_delete.png',
width: 17.w,
),
//SizedBox(width: width(5),),
//Text("清空", style: TextStyle(fontSize: 13.sp, color: Color(0xffaaaaaa)),),
SizedBox(width: width(15)),
],
),
),
],
),
SizedBox(height: height(10)),
Expanded(
child: Column(
children: [
Row(
children: [
SizedBox(width: 12.w),
Expanded(
child: Wrap(
runSpacing: 10,
spacing: 10,
direction: Axis.horizontal,
children:
historys
.asMap()
.keys
.map((e) => _historyItem(historys[e]))
.toList(),
),
),
SizedBox(width: 12.w),
],
),
],
),
),
],
),
);
}
}
_saveHistory(String msg) async {
historys.removeWhere((history) => history == msg || history.isEmpty);
if (!historys.any((history) => history.startsWith(msg))) {
if (msg.isNotEmpty) {
historys.insert(0, msg);
}
}
setState(() {});
DataPersistence.setSearchHistroy(
AccountStorage().getCurrentUser()?.userProfile?.account ?? "",
jsonEncode(historys),
);
}
_loadHistory() async {
String json = DataPersistence.getSearchHistroy(
AccountStorage().getCurrentUser()?.userProfile?.account ?? "",
);
historys = List<String>.from(jsonDecode(json));
setState(() {});
}
Widget _historyItem(String history) {
return GestureDetector(
onTap: () {
_textEditingController.text = history;
_reqSearch();
},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 18.w, vertical: 6.w),
decoration: BoxDecoration(
color:
SCGlobalConfig.businessLogicStrategy
.getSearchPageHistoryItemBackgroundColor(),
borderRadius: BorderRadius.circular(52.w),
),
child: Text(
history,
style: TextStyle(
fontSize: sp(13),
color:
SCGlobalConfig.businessLogicStrategy
.getSearchPageHistoryItemTextColor(),
fontWeight: FontWeight.w400,
decoration: TextDecoration.none,
),
),
),
);
}
_result() {
return Expanded(
child: Column(
children: [
Theme(
data: ThemeData(
brightness: Brightness.light,
splashColor: Colors.transparent,
highlightColor: Colors.transparent,
),
child: Container(
padding: EdgeInsets.symmetric(vertical: 1.w),
decoration: BoxDecoration(
// color: Colors.white,
//border: Border(bottom: BorderSide(color: Color(0xffEEEEEE),width: 0.5.w))
),
child: TabBar(
tabs:
[
SCAppLocalizations.of(context)!.rooms,
SCAppLocalizations.of(context)!.users,
].map((e) => Tab(text: e)).toList(),
indicator: SCFixedWidthTabIndicator(
width: 15.w,
gradient: LinearGradient(
colors: [
SCGlobalConfig.businessLogicStrategy
.getSearchPageTabIndicatorGradientStartColor(),
SCGlobalConfig.businessLogicStrategy
.getSearchPageTabIndicatorGradientEndColor(),
],
),
),
indicatorSize: TabBarIndicatorSize.label,
// 控制指示器宽度
labelPadding: EdgeInsetsDirectional.only(start: 15, end: 15),
isScrollable: true,
tabAlignment: TabAlignment.start,
controller: _tabController,
labelColor:
SCGlobalConfig.businessLogicStrategy
.getSearchPageTabSelectedLabelColor(),
dividerColor:
SCGlobalConfig.businessLogicStrategy
.getSearchPageTabDividerColor(),
unselectedLabelColor:
SCGlobalConfig.businessLogicStrategy
.getSearchPageTabUnselectedLabelColor(),
unselectedLabelStyle:
SCGlobalConfig.businessLogicStrategy
.getSearchPageTabUnselectedLabelStyle(),
labelStyle:
SCGlobalConfig.businessLogicStrategy
.getSearchPageTabSelectedLabelStyle(),
),
),
),
Expanded(
child: TabBarView(
controller: _tabController,
children: [
SearchRoomList(search, (String text) {
_saveHistory(text);
}),
SearchUserList(search, (String text) {
_saveHistory(text);
}),
],
),
),
],
),
);
}
void _reqSearch() {
search = _textEditingController.text;
setState(() {});
if (search.trim().isNotEmpty) {
_saveHistory(search);
} else {
items.clear();
}
}
}
class SearchRoomList extends StatefulWidget {
final String text;
Function(String text) saveHistroy;
SearchRoomList(this.text, this.saveHistroy);
@override
_SearchRoomListState createState() => _SearchRoomListState(saveHistroy);
}
class _SearchRoomListState extends State<SearchRoomList> {
List<SocialChatRoomRes>? rooms;
Function(String text) saveHistroy;
Timer? _searchTimer;
_SearchRoomListState(this.saveHistroy);
@override
void initState() {
loadPage();
super.initState();
}
@override
void dispose() {
_searchTimer?.cancel();
super.dispose();
}
@override
void didUpdateWidget(SearchRoomList oldWidget) {
var oldText = oldWidget.text;
var text = widget.text;
if (oldText != text) {
_searchTimer?.cancel();
_searchTimer = Timer(Duration(milliseconds: 550), () {
loadPage();
});
}
super.didUpdateWidget(oldWidget);
}
@override
Widget build(BuildContext context) {
if (rooms == null) {
return Center(child: CupertinoActivityIndicator());
} else if (rooms!.length == 0) {
return empty();
}
return GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, // 每行2个
childAspectRatio: 1, // 宽高比
mainAxisSpacing: 10,
crossAxisSpacing: 10,
),
padding: EdgeInsets.all(10),
itemCount: rooms!.length,
itemBuilder: (context, index) {
return buildItem(rooms![index]);
},
);
}
///构建列表项
Widget buildItem(SocialChatRoomRes e) {
//return roomItem(room: e,context: context);
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () async {
if (context != null) {
Provider.of<RtcProvider>(
context,
listen: false,
).joinVoiceRoomSession(context, e.id ?? "");
}
},
child: Stack(
alignment: Alignment.bottomCenter,
children: [
Container(
padding: EdgeInsets.all(3.w),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("sc_images/index/sc_icon_room_bord.png"),
fit: BoxFit.fill,
),
),
child: netImage(
url: resolveRoomCoverUrl(e.id, e.roomCover),
defaultImg: kRoomCoverDefaultImg,
borderRadius: BorderRadius.circular(12.w),
width: 200.w,
height: 200.w,
),
),
Container(
padding: EdgeInsets.symmetric(vertical: 6.w),
margin: EdgeInsets.symmetric(horizontal: 1.w),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("sc_images/index/sc_icon_index_room_brd.png"),
fit: BoxFit.fill,
),
),
child: Row(
children: [
SizedBox(width: 10.w),
Consumer<SCAppGeneralManager>(
builder: (_, provider, __) {
return netImage(
url:
"${provider.findCountryByName(e.countryName ?? "")?.nationalFlag}",
width: 20.w,
height: 13.w,
borderRadius: BorderRadius.circular(2.w),
);
},
),
SizedBox(width: 5.w),
Expanded(
child: SizedBox(
height: 17.w,
child: Align(
alignment: Alignment.centerLeft,
child: Transform.translate(
offset: Offset(0, -0.6.w),
child: text(
e.roomName ?? "",
fontSize: 13.sp,
textColor: Color(0xffffffff),
fontWeight: FontWeight.w400,
lineHeight: 1,
),
),
),
// (roomRes.roomProfile?.roomName?.length ?? 0) > 10
// ? Marquee(
// text: roomRes.roomProfile?.roomName ?? "",
// style: TextStyle(
// fontSize: 15.sp,
// color: Color(0xffffffff),
// fontWeight: FontWeight.w400,
// decoration: TextDecoration.none,
// ),
// scrollAxis: Axis.horizontal,
// crossAxisAlignment: CrossAxisAlignment.start,
// blankSpace: 20.0,
// velocity: 40.0,
// pauseAfterRound: Duration(seconds: 1),
// accelerationDuration: Duration(seconds: 1),
// accelerationCurve: Curves.easeOut,
// decelerationDuration: Duration(
// milliseconds: 500,
// ),
// decelerationCurve: Curves.easeOut,
// )
// : Text(
// roomRes.roomProfile?.roomName ?? "",
// maxLines: 1,
// overflow: TextOverflow.ellipsis,
// style: TextStyle(
// fontSize: 15.sp,
// color: Color(0xffffffff),
// fontWeight: FontWeight.w400,
// decoration: TextDecoration.none,
// ),
// ),
),
),
SizedBox(width: 5.w),
(e.extValues?.roomSetting?.password?.isEmpty ?? false)
? SCRoomLiveAudioIndicator(width: 14.w, height: 14.w)
: Image.asset(
"sc_images/index/sc_icon_room_suo.png",
width: 20.w,
height: 20.w,
),
(e.extValues?.roomSetting?.password?.isEmpty ?? false)
? SizedBox(width: 3.w)
: Container(height: 10.w),
(e.extValues?.roomSetting?.password?.isEmpty ?? false)
? text(
e.extValues?.memberQuantity ?? "0",
fontSize: 10.sp,
lineHeight: 1,
)
: Container(height: 10.w),
SizedBox(width: 10.w),
],
),
),
(e.roomGameIcon?.isNotEmpty ?? false)
? PositionedDirectional(
top: 8.w,
end: 8.w,
child: netImage(
url: e.roomGameIcon ?? "",
width: 25.w,
height: 25.w,
borderRadius: BorderRadius.circular(4.w),
),
)
: Container(),
],
),
);
}
loadPage() async {
widget.saveHistroy(widget.text);
try {
rooms = await SCChatRoomRepository().searchRoom(widget.text);
} catch (e) {
} finally {
setState(() {});
}
}
empty() {
return mainEmpty(msg: SCAppLocalizations.of(context)!.noData);
}
}
class SearchUserList extends SCPageList {
final String text;
Function(String text) saveHistroy;
SearchUserList(this.text, this.saveHistroy);
@override
_SearchUserListState createState() => _SearchUserListState(saveHistroy);
}
class _SearchUserListState
extends SCPageListState<SocialChatUserProfile, SearchUserList> {
Function(String text) saveHistroy;
_SearchUserListState(this.saveHistroy);
Timer? _searchTimer;
@override
void initState() {
// TODO: implement initState
backgroundColor = Colors.transparent;
loadData(1);
super.initState();
}
@override
void dispose() {
_searchTimer?.cancel();
super.dispose();
}
@override
void didUpdateWidget(SCPageList oldWidget) {
// TODO: implement didUpdateWidget
var oldText = (oldWidget as SearchUserList).text;
var text = (widget as SearchUserList).text;
if (oldText != text) {
_searchTimer?.cancel();
_searchTimer = Timer(Duration(milliseconds: 550), () {
loadData(1);
});
}
super.didUpdateWidget(oldWidget);
}
@override
Widget build(BuildContext context) {
return buildList(context);
}
///构建列表项
Widget buildItem(SocialChatUserProfile data) {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
SCNavigatorUtils.push(
context,
"${SCMainRoute.person}?isMe=${AccountStorage().getCurrentUser()?.userProfile?.id == data.id}&tageId=${data.id}",
);
},
child: Container(
padding: EdgeInsets.symmetric(vertical: 5.w),
margin: EdgeInsets.symmetric(horizontal: 25.w),
child: Row(
children: [
head(url: data.userAvatar ?? "", width: 55.w),
SizedBox(width: 2.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: <Widget>[
xb(
data.userSex,
color:
data.userSex == 1
? Colors.blue
: Colors.purpleAccent,
),
SizedBox(width: 5.w),
// richText(
// txt: data.userNickname ?? "",
// key: (widget as SearchUserList).text,
// highlight: SocialChatTheme.primaryColor,
// defaultColor: Colors.black,
// ),
Expanded(
child: socialchatNickNameText(
textColor:
SCGlobalConfig.businessLogicStrategy
.getSearchPageHistoryTitleTextColor(),
data.userNickname ?? "",
fontSize: 15.sp,
type: data.getVIP()?.name ?? "",
needScroll:
(data.userNickname?.characters.length ?? 0) > 18,
),
),
SizedBox(width: 5.w),
// dj(data.level.toString()),
],
),
GestureDetector(
child: Container(
child: text(
"ID:${data.getID()}",
fontSize: 12.sp,
textColor:
SCGlobalConfig.businessLogicStrategy
.getSearchPageHistoryTitleTextColor(),
fontWeight: FontWeight.bold,
),
),
onTap: () {
Clipboard.setData(ClipboardData(text: data.getID()));
SCTts.show(
SCAppLocalizations.of(context)!.copiedToClipboard,
);
},
),
],
),
),
],
),
),
);
}
@override
loadPage({
required int page,
required Function(List<SocialChatUserProfile>) onSuccess,
Function? onErr,
}) async {
saveHistroy((widget as SearchUserList).text);
SCAccountRepository()
.searchUser((widget as SearchUserList).text)
.then((data) {
List<SocialChatUserProfile> users = [];
users.add(data);
onSuccess(users);
})
.catchError((e) {
if (onErr != null) {
onErr();
}
});
}
@override
empty() {
return mainEmpty(msg: SCAppLocalizations.of(context)!.noData);
}
@override
builderDivider() {
return Divider(
height: height(1),
color: SocialChatTheme.dividerColor,
indent: width(85),
endIndent: width(15),
);
}
}