mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
feat: live search
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
@@ -819,4 +819,7 @@ class Api {
|
|||||||
|
|
||||||
static const String setLiveFavTag =
|
static const String setLiveFavTag =
|
||||||
'${HttpString.liveBaseUrl}/xlive/app-interface/v2/second/set_fav_tag';
|
'${HttpString.liveBaseUrl}/xlive/app-interface/v2/second/set_fav_tag';
|
||||||
|
|
||||||
|
static const String liveSearch =
|
||||||
|
'${HttpString.liveBaseUrl}/xlive/app-interface/v2/search_live';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'package:PiliPlus/common/constants.dart';
|
|||||||
import 'package:PiliPlus/http/api.dart';
|
import 'package:PiliPlus/http/api.dart';
|
||||||
import 'package:PiliPlus/http/init.dart';
|
import 'package:PiliPlus/http/init.dart';
|
||||||
import 'package:PiliPlus/http/loading_state.dart';
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
|
import 'package:PiliPlus/models/common/live_search_type.dart';
|
||||||
import 'package:PiliPlus/models/live/live_area_list/area_item.dart';
|
import 'package:PiliPlus/models/live/live_area_list/area_item.dart';
|
||||||
import 'package:PiliPlus/models/live/live_area_list/area_list.dart';
|
import 'package:PiliPlus/models/live/live_area_list/area_list.dart';
|
||||||
import 'package:PiliPlus/models/live/live_emoticons/data.dart';
|
import 'package:PiliPlus/models/live/live_emoticons/data.dart';
|
||||||
@@ -12,6 +13,7 @@ import 'package:PiliPlus/models/live/live_room/danmu_info.dart';
|
|||||||
import 'package:PiliPlus/models/live/live_room/item.dart';
|
import 'package:PiliPlus/models/live/live_room/item.dart';
|
||||||
import 'package:PiliPlus/models/live/live_room/room_info.dart';
|
import 'package:PiliPlus/models/live/live_room/room_info.dart';
|
||||||
import 'package:PiliPlus/models/live/live_room/room_info_h5.dart';
|
import 'package:PiliPlus/models/live/live_room/room_info_h5.dart';
|
||||||
|
import 'package:PiliPlus/models/live/live_search/data.dart';
|
||||||
import 'package:PiliPlus/models/live/live_second_list/data.dart';
|
import 'package:PiliPlus/models/live/live_second_list/data.dart';
|
||||||
import 'package:PiliPlus/utils/storage.dart';
|
import 'package:PiliPlus/utils/storage.dart';
|
||||||
import 'package:PiliPlus/utils/utils.dart';
|
import 'package:PiliPlus/utils/utils.dart';
|
||||||
@@ -432,4 +434,44 @@ class LiveHttp {
|
|||||||
return LoadingState.error(res.data['message']);
|
return LoadingState.error(res.data['message']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future<LoadingState<LiveSearchData>> liveSearch({
|
||||||
|
required bool isLogin,
|
||||||
|
required int page,
|
||||||
|
required String keyword,
|
||||||
|
required LiveSearchType type,
|
||||||
|
}) async {
|
||||||
|
final params = {
|
||||||
|
if (isLogin) 'access_key': Accounts.main.accessKey,
|
||||||
|
'appkey': Constants.appKey,
|
||||||
|
'actionKey': 'appkey',
|
||||||
|
'build': '8350200',
|
||||||
|
'c_locale': 'zh_CN',
|
||||||
|
'device': 'pad',
|
||||||
|
'page': page,
|
||||||
|
'pagesize': 30,
|
||||||
|
'keyword': keyword,
|
||||||
|
'disable_rcmd': '0',
|
||||||
|
'mobi_app': 'android_hd',
|
||||||
|
'platform': 'android',
|
||||||
|
's_locale': 'zh_CN',
|
||||||
|
'statistics': Constants.statistics,
|
||||||
|
'ts': DateTime.now().millisecondsSinceEpoch ~/ 1000,
|
||||||
|
'type': type.name,
|
||||||
|
};
|
||||||
|
Utils.appSign(
|
||||||
|
params,
|
||||||
|
Constants.appKey,
|
||||||
|
Constants.appSec,
|
||||||
|
);
|
||||||
|
var res = await Request().get(
|
||||||
|
Api.liveSearch,
|
||||||
|
queryParameters: params,
|
||||||
|
);
|
||||||
|
if (res.data['code'] == 0) {
|
||||||
|
return LoadingState.success(LiveSearchData.fromJson(res.data['data']));
|
||||||
|
} else {
|
||||||
|
return LoadingState.error(res.data['message']);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1
lib/models/common/live_search_type.dart
Normal file
1
lib/models/common/live_search_type.dart
Normal file
@@ -0,0 +1 @@
|
|||||||
|
enum LiveSearchType { room, user }
|
||||||
39
lib/models/live/live_search/data.dart
Normal file
39
lib/models/live/live_search/data.dart
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import 'room.dart';
|
||||||
|
import 'user.dart';
|
||||||
|
|
||||||
|
class LiveSearchData {
|
||||||
|
String? type;
|
||||||
|
int? page;
|
||||||
|
int? pagesize;
|
||||||
|
Room? room;
|
||||||
|
User? user;
|
||||||
|
String? trackId;
|
||||||
|
String? abtestId;
|
||||||
|
String? query;
|
||||||
|
|
||||||
|
LiveSearchData({
|
||||||
|
this.type,
|
||||||
|
this.page,
|
||||||
|
this.pagesize,
|
||||||
|
this.room,
|
||||||
|
this.user,
|
||||||
|
this.trackId,
|
||||||
|
this.abtestId,
|
||||||
|
this.query,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory LiveSearchData.fromJson(Map<String, dynamic> json) => LiveSearchData(
|
||||||
|
type: json['type'] as String?,
|
||||||
|
page: json['page'] as int?,
|
||||||
|
pagesize: json['pagesize'] as int?,
|
||||||
|
room: json['room'] == null
|
||||||
|
? null
|
||||||
|
: Room.fromJson(json['room'] as Map<String, dynamic>),
|
||||||
|
user: json['user'] == null
|
||||||
|
? null
|
||||||
|
: User.fromJson(json['user'] as Map<String, dynamic>),
|
||||||
|
trackId: json['track_id'] as String?,
|
||||||
|
abtestId: json['abtest_id'] as String?,
|
||||||
|
query: json['query'] as String?,
|
||||||
|
);
|
||||||
|
}
|
||||||
19
lib/models/live/live_search/live_search.dart
Normal file
19
lib/models/live/live_search/live_search.dart
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import 'data.dart';
|
||||||
|
|
||||||
|
class LiveSearch {
|
||||||
|
int? code;
|
||||||
|
String? message;
|
||||||
|
int? ttl;
|
||||||
|
LiveSearchData? data;
|
||||||
|
|
||||||
|
LiveSearch({this.code, this.message, this.ttl, this.data});
|
||||||
|
|
||||||
|
factory LiveSearch.fromJson(Map<String, dynamic> json) => LiveSearch(
|
||||||
|
code: json['code'] as int?,
|
||||||
|
message: json['message'] as String?,
|
||||||
|
ttl: json['ttl'] as int?,
|
||||||
|
data: json['data'] == null
|
||||||
|
? null
|
||||||
|
: LiveSearchData.fromJson(json['data'] as Map<String, dynamic>),
|
||||||
|
);
|
||||||
|
}
|
||||||
18
lib/models/live/live_search/room.dart
Normal file
18
lib/models/live/live_search/room.dart
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import 'room_item.dart';
|
||||||
|
|
||||||
|
class Room {
|
||||||
|
List<LiveSearchRoomItemModel>? list;
|
||||||
|
int? totalRoom;
|
||||||
|
int? totalPage;
|
||||||
|
|
||||||
|
Room({this.list, this.totalRoom, this.totalPage});
|
||||||
|
|
||||||
|
factory Room.fromJson(Map<String, dynamic> json) => Room(
|
||||||
|
list: (json['list'] as List<dynamic>?)
|
||||||
|
?.map((e) =>
|
||||||
|
LiveSearchRoomItemModel.fromJson(e as Map<String, dynamic>))
|
||||||
|
.toList(),
|
||||||
|
totalRoom: json['total_room'] as int?,
|
||||||
|
totalPage: json['total_page'] as int?,
|
||||||
|
);
|
||||||
|
}
|
||||||
38
lib/models/live/live_search/room_item.dart
Normal file
38
lib/models/live/live_search/room_item.dart
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import 'watched_show.dart';
|
||||||
|
|
||||||
|
class LiveSearchRoomItemModel {
|
||||||
|
int? roomid;
|
||||||
|
String? cover;
|
||||||
|
String? title;
|
||||||
|
String? name;
|
||||||
|
String? face;
|
||||||
|
int? online;
|
||||||
|
String? link;
|
||||||
|
WatchedShow? watchedShow;
|
||||||
|
|
||||||
|
LiveSearchRoomItemModel({
|
||||||
|
this.roomid,
|
||||||
|
this.cover,
|
||||||
|
this.title,
|
||||||
|
this.name,
|
||||||
|
this.face,
|
||||||
|
this.online,
|
||||||
|
this.link,
|
||||||
|
this.watchedShow,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory LiveSearchRoomItemModel.fromJson(Map<String, dynamic> json) =>
|
||||||
|
LiveSearchRoomItemModel(
|
||||||
|
roomid: json['roomid'] as int?,
|
||||||
|
cover: json['cover'] as String?,
|
||||||
|
title: json['title'] as String?,
|
||||||
|
name: json['name'] as String?,
|
||||||
|
face: json['face'] as String?,
|
||||||
|
online: json['online'] as int?,
|
||||||
|
link: json['link'] as String?,
|
||||||
|
watchedShow: json['watched_show'] == null
|
||||||
|
? null
|
||||||
|
: WatchedShow.fromJson(
|
||||||
|
json['watched_show'] as Map<String, dynamic>),
|
||||||
|
);
|
||||||
|
}
|
||||||
17
lib/models/live/live_search/user.dart
Normal file
17
lib/models/live/live_search/user.dart
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import 'package:PiliPlus/models/live/live_search/user_item.dart';
|
||||||
|
|
||||||
|
class User {
|
||||||
|
List<LiveSearchUserItemModel>? list;
|
||||||
|
int? totalUser;
|
||||||
|
int? totalPage;
|
||||||
|
|
||||||
|
User({this.list, this.totalUser, this.totalPage});
|
||||||
|
|
||||||
|
factory User.fromJson(Map<String, dynamic> json) => User(
|
||||||
|
list: (json['list'] as List<dynamic>?)
|
||||||
|
?.map((e) => LiveSearchUserItemModel.fromJson(e))
|
||||||
|
.toList(),
|
||||||
|
totalUser: json['total_user'] as int?,
|
||||||
|
totalPage: json['total_page'] as int?,
|
||||||
|
);
|
||||||
|
}
|
||||||
33
lib/models/live/live_search/user_item.dart
Normal file
33
lib/models/live/live_search/user_item.dart
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
class LiveSearchUserItemModel {
|
||||||
|
String? face;
|
||||||
|
String? name;
|
||||||
|
int? liveStatus;
|
||||||
|
String? areaName;
|
||||||
|
int? fansNum;
|
||||||
|
List? roomTags;
|
||||||
|
int? roomid;
|
||||||
|
String? link;
|
||||||
|
|
||||||
|
LiveSearchUserItemModel({
|
||||||
|
this.face,
|
||||||
|
this.name,
|
||||||
|
this.liveStatus,
|
||||||
|
this.areaName,
|
||||||
|
this.fansNum,
|
||||||
|
this.roomTags,
|
||||||
|
this.roomid,
|
||||||
|
this.link,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory LiveSearchUserItemModel.fromJson(Map<String, dynamic> json) =>
|
||||||
|
LiveSearchUserItemModel(
|
||||||
|
face: json['face'] as String?,
|
||||||
|
name: json['name'] as String?,
|
||||||
|
liveStatus: json['live_status'] as int?,
|
||||||
|
areaName: json['areaName'] as String?,
|
||||||
|
fansNum: json['fansNum'] as int?,
|
||||||
|
roomTags: json['roomTags'],
|
||||||
|
roomid: json['roomid'] as int?,
|
||||||
|
link: json['link'] as String?,
|
||||||
|
);
|
||||||
|
}
|
||||||
26
lib/models/live/live_search/watched_show.dart
Normal file
26
lib/models/live/live_search/watched_show.dart
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
class WatchedShow {
|
||||||
|
int? num;
|
||||||
|
String? textSmall;
|
||||||
|
String? textLarge;
|
||||||
|
String? icon;
|
||||||
|
int? iconLocation;
|
||||||
|
String? iconWeb;
|
||||||
|
|
||||||
|
WatchedShow({
|
||||||
|
this.num,
|
||||||
|
this.textSmall,
|
||||||
|
this.textLarge,
|
||||||
|
this.icon,
|
||||||
|
this.iconLocation,
|
||||||
|
this.iconWeb,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory WatchedShow.fromJson(Map<String, dynamic> json) => WatchedShow(
|
||||||
|
num: json['num'] as int?,
|
||||||
|
textSmall: json['text_small'] as String?,
|
||||||
|
textLarge: json['text_large'] as String?,
|
||||||
|
icon: json['icon'] as String?,
|
||||||
|
iconLocation: json['icon_location'] as int?,
|
||||||
|
iconWeb: json['icon_web'] as String?,
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -45,33 +45,21 @@ class BangumiCardVMemberHome extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
bangumiContent(bangumiItem)
|
Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(4, 5, 0, 3),
|
||||||
|
child: Text(
|
||||||
|
bangumiItem.title,
|
||||||
|
textAlign: TextAlign.start,
|
||||||
|
style: const TextStyle(
|
||||||
|
letterSpacing: 0.3,
|
||||||
|
),
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget bangumiContent(SpaceArchiveItem bangumiItem) {
|
|
||||||
return Expanded(
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.fromLTRB(4, 5, 0, 3),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
bangumiItem.title,
|
|
||||||
textAlign: TextAlign.start,
|
|
||||||
style: const TextStyle(
|
|
||||||
letterSpacing: 0.3,
|
|
||||||
),
|
|
||||||
maxLines: 2,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 1),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -52,29 +52,18 @@ class BangumiCardVSearch extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
bagumiContent(context)
|
Padding(
|
||||||
],
|
padding: const EdgeInsets.fromLTRB(4, 5, 0, 3),
|
||||||
),
|
child: Text(
|
||||||
),
|
item.title!.map((e) => e['text']).join(),
|
||||||
);
|
textAlign: TextAlign.start,
|
||||||
}
|
style: const TextStyle(
|
||||||
|
letterSpacing: 0.3,
|
||||||
Widget bagumiContent(context) {
|
),
|
||||||
return Expanded(
|
maxLines: 2,
|
||||||
child: Padding(
|
overflow: TextOverflow.ellipsis,
|
||||||
padding: const EdgeInsets.fromLTRB(4, 5, 0, 3),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
item.title!.map((e) => e['text']).join(),
|
|
||||||
textAlign: TextAlign.start,
|
|
||||||
style: const TextStyle(
|
|
||||||
letterSpacing: 0.3,
|
|
||||||
),
|
),
|
||||||
maxLines: 2,
|
)
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ class _LivePageState extends CommonPageState<LivePage, LiveController>
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
super.build(context);
|
super.build(context);
|
||||||
|
final ThemeData theme = Theme.of(context);
|
||||||
return Container(
|
return Container(
|
||||||
clipBehavior: Clip.hardEdge,
|
clipBehavior: Clip.hardEdge,
|
||||||
margin: const EdgeInsets.only(
|
margin: const EdgeInsets.only(
|
||||||
@@ -58,8 +59,8 @@ class _LivePageState extends CommonPageState<LivePage, LiveController>
|
|||||||
bottom: MediaQuery.paddingOf(context).bottom + 80,
|
bottom: MediaQuery.paddingOf(context).bottom + 80,
|
||||||
),
|
),
|
||||||
sliver: SliverMainAxisGroup(slivers: [
|
sliver: SliverMainAxisGroup(slivers: [
|
||||||
Obx(() => _buildTop(controller.topState.value)),
|
Obx(() => _buildTop(theme, controller.topState.value)),
|
||||||
Obx(() => _buildBody(controller.loadingState.value)),
|
Obx(() => _buildBody(theme, controller.loadingState.value)),
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -68,11 +69,11 @@ class _LivePageState extends CommonPageState<LivePage, LiveController>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildTop(Pair<LiveCardList?, LiveCardList?> data) {
|
Widget _buildTop(ThemeData theme, Pair<LiveCardList?, LiveCardList?> data) {
|
||||||
return SliverMainAxisGroup(
|
return SliverMainAxisGroup(
|
||||||
slivers: [
|
slivers: [
|
||||||
if (data.first != null)
|
if (data.first != null)
|
||||||
SliverToBoxAdapter(child: _buildFollowList(data.first!)),
|
SliverToBoxAdapter(child: _buildFollowList(theme, data.first!)),
|
||||||
if (data.second?.cardData?.areaEntranceV3?.list?.isNotEmpty == true)
|
if (data.second?.cardData?.areaEntranceV3?.list?.isNotEmpty == true)
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: Row(
|
child: Row(
|
||||||
@@ -94,12 +95,10 @@ class _LivePageState extends CommonPageState<LivePage, LiveController>
|
|||||||
),
|
),
|
||||||
text: index == 0 ? '推荐' : '${item.title}',
|
text: index == 0 ? '推荐' : '${item.title}',
|
||||||
bgColor: index == controller.areaIndex.value
|
bgColor: index == controller.areaIndex.value
|
||||||
? Theme.of(context).colorScheme.secondaryContainer
|
? theme.colorScheme.secondaryContainer
|
||||||
: Colors.transparent,
|
: Colors.transparent,
|
||||||
textColor: index == controller.areaIndex.value
|
textColor: index == controller.areaIndex.value
|
||||||
? Theme.of(context)
|
? theme.colorScheme.onSecondaryContainer
|
||||||
.colorScheme
|
|
||||||
.onSecondaryContainer
|
|
||||||
: null,
|
: null,
|
||||||
onTap: (value) {
|
onTap: (value) {
|
||||||
controller.onSelectArea(
|
controller.onSelectArea(
|
||||||
@@ -133,7 +132,7 @@ class _LivePageState extends CommonPageState<LivePage, LiveController>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildBody(LoadingState<List?> loadingState) {
|
Widget _buildBody(ThemeData theme, LoadingState<List?> loadingState) {
|
||||||
return switch (loadingState) {
|
return switch (loadingState) {
|
||||||
Loading() => SliverGrid(
|
Loading() => SliverGrid(
|
||||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||||
@@ -169,14 +168,10 @@ class _LivePageState extends CommonPageState<LivePage, LiveController>
|
|||||||
),
|
),
|
||||||
text: '${item.name}',
|
text: '${item.name}',
|
||||||
bgColor: index == controller.tagIndex.value
|
bgColor: index == controller.tagIndex.value
|
||||||
? Theme.of(context)
|
? theme.colorScheme.secondaryContainer
|
||||||
.colorScheme
|
|
||||||
.secondaryContainer
|
|
||||||
: Colors.transparent,
|
: Colors.transparent,
|
||||||
textColor: index == controller.tagIndex.value
|
textColor: index == controller.tagIndex.value
|
||||||
? Theme.of(context)
|
? theme.colorScheme.onSecondaryContainer
|
||||||
.colorScheme
|
|
||||||
.onSecondaryContainer
|
|
||||||
: null,
|
: null,
|
||||||
onTap: (value) {
|
onTap: (value) {
|
||||||
controller.onSelectTag(
|
controller.onSelectTag(
|
||||||
@@ -224,8 +219,7 @@ class _LivePageState extends CommonPageState<LivePage, LiveController>
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildFollowList(LiveCardList item) {
|
Widget _buildFollowList(ThemeData theme, LiveCardList item) {
|
||||||
final theme = Theme.of(context);
|
|
||||||
return Column(
|
return Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
|||||||
@@ -316,8 +316,8 @@ class _LiveAreaPageState extends State<LiveAreaPage> {
|
|||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: theme.colorScheme.outline,
|
color: theme.colorScheme.outline,
|
||||||
),
|
),
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(4)),
|
|
||||||
color: theme.colorScheme.surface,
|
color: theme.colorScheme.surface,
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(4)),
|
||||||
),
|
),
|
||||||
child: SearchText(
|
child: SearchText(
|
||||||
text: item.name!,
|
text: item.name!,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import 'package:PiliPlus/http/loading_state.dart';
|
|||||||
import 'package:PiliPlus/models/live/live_area_list/area_item.dart';
|
import 'package:PiliPlus/models/live/live_area_list/area_item.dart';
|
||||||
import 'package:PiliPlus/pages/live_area_detail/child/view.dart';
|
import 'package:PiliPlus/pages/live_area_detail/child/view.dart';
|
||||||
import 'package:PiliPlus/pages/live_area_detail/controller.dart';
|
import 'package:PiliPlus/pages/live_area_detail/controller.dart';
|
||||||
|
import 'package:PiliPlus/pages/live_search/view.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
@@ -35,7 +36,7 @@ class _LiveAreaDetailPageState extends State<LiveAreaDetailPage> {
|
|||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// TODO: search
|
Get.to(const LiveSearchPage());
|
||||||
},
|
},
|
||||||
icon: const Icon(Icons.search),
|
icon: const Icon(Icons.search),
|
||||||
),
|
),
|
||||||
|
|||||||
55
lib/pages/live_search/child/controller.dart
Normal file
55
lib/pages/live_search/child/controller.dart
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import 'package:PiliPlus/http/live.dart';
|
||||||
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
|
import 'package:PiliPlus/models/common/live_search_type.dart';
|
||||||
|
import 'package:PiliPlus/models/live/live_search/data.dart';
|
||||||
|
import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
||||||
|
import 'package:PiliPlus/pages/live_search/controller.dart';
|
||||||
|
import 'package:PiliPlus/utils/storage.dart';
|
||||||
|
|
||||||
|
class LiveSearchChildController
|
||||||
|
extends CommonListController<LiveSearchData, dynamic> {
|
||||||
|
LiveSearchChildController(this.controller, this.searchType);
|
||||||
|
|
||||||
|
final isLogin = Accounts.main.isLogin;
|
||||||
|
final LiveSearchController controller;
|
||||||
|
final LiveSearchType searchType;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void checkIsEnd(int length) {
|
||||||
|
switch (searchType) {
|
||||||
|
case LiveSearchType.room:
|
||||||
|
if (controller.counts.first != -1 &&
|
||||||
|
length >= controller.counts.first) {
|
||||||
|
isEnd = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case LiveSearchType.user:
|
||||||
|
if (controller.counts[1] != -1 && length >= controller.counts[1]) {
|
||||||
|
isEnd = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List? getDataList(response) {
|
||||||
|
switch (searchType) {
|
||||||
|
case LiveSearchType.room:
|
||||||
|
controller.counts[searchType.index] = response.room?.totalRoom ?? 0;
|
||||||
|
return response.room?.list;
|
||||||
|
case LiveSearchType.user:
|
||||||
|
controller.counts[searchType.index] = response.user?.totalUser ?? 0;
|
||||||
|
return response.user?.list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<LoadingState<LiveSearchData>> customGetData() {
|
||||||
|
return LiveHttp.liveSearch(
|
||||||
|
isLogin: isLogin,
|
||||||
|
page: currentPage,
|
||||||
|
keyword: controller.editingController.text,
|
||||||
|
type: searchType,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
151
lib/pages/live_search/child/view.dart
Normal file
151
lib/pages/live_search/child/view.dart
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
import 'package:PiliPlus/common/constants.dart';
|
||||||
|
import 'package:PiliPlus/common/skeleton/msg_feed_top.dart';
|
||||||
|
import 'package:PiliPlus/common/skeleton/video_card_v.dart';
|
||||||
|
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
|
||||||
|
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
||||||
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
|
import 'package:PiliPlus/models/common/live_search_type.dart';
|
||||||
|
import 'package:PiliPlus/pages/live_search/child/controller.dart';
|
||||||
|
import 'package:PiliPlus/pages/live_search/widgets/live_search_room.dart';
|
||||||
|
import 'package:PiliPlus/pages/live_search/widgets/live_search_user.dart';
|
||||||
|
import 'package:PiliPlus/utils/grid.dart';
|
||||||
|
import 'package:PiliPlus/utils/storage.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
class LiveSearchChildPage extends StatefulWidget {
|
||||||
|
const LiveSearchChildPage({
|
||||||
|
super.key,
|
||||||
|
required this.controller,
|
||||||
|
required this.searchType,
|
||||||
|
});
|
||||||
|
|
||||||
|
final LiveSearchChildController controller;
|
||||||
|
final LiveSearchType searchType;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<LiveSearchChildPage> createState() => _LiveSearchChildPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _LiveSearchChildPageState extends State<LiveSearchChildPage>
|
||||||
|
with AutomaticKeepAliveClientMixin {
|
||||||
|
late final bool dynamicsWaterfallFlow = GStorage.setting
|
||||||
|
.get(SettingBoxKey.dynamicsWaterfallFlow, defaultValue: true);
|
||||||
|
LiveSearchChildController get _controller => widget.controller;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
super.build(context);
|
||||||
|
double padding = widget.searchType == LiveSearchType.room ? 12 : 0;
|
||||||
|
return refreshIndicator(
|
||||||
|
onRefresh: _controller.onRefresh,
|
||||||
|
child: CustomScrollView(
|
||||||
|
controller: _controller.scrollController,
|
||||||
|
slivers: [
|
||||||
|
SliverPadding(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
top: padding,
|
||||||
|
left: padding,
|
||||||
|
right: padding,
|
||||||
|
bottom: MediaQuery.paddingOf(context).bottom + 80,
|
||||||
|
),
|
||||||
|
sliver: Obx(() => _buildBody(_controller.loadingState.value)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget get _buildLoading {
|
||||||
|
return switch (widget.searchType) {
|
||||||
|
LiveSearchType.room => SliverGrid(
|
||||||
|
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||||
|
mainAxisSpacing: StyleString.cardSpace,
|
||||||
|
crossAxisSpacing: StyleString.cardSpace,
|
||||||
|
maxCrossAxisExtent: Grid.smallCardWidth,
|
||||||
|
childAspectRatio: StyleString.aspectRatio,
|
||||||
|
mainAxisExtent: MediaQuery.textScalerOf(context).scale(90),
|
||||||
|
),
|
||||||
|
delegate: SliverChildBuilderDelegate(
|
||||||
|
(context, index) {
|
||||||
|
return const VideoCardVSkeleton();
|
||||||
|
},
|
||||||
|
childCount: 10,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
LiveSearchType.user => SliverGrid(
|
||||||
|
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
|
||||||
|
maxCrossAxisExtent: Grid.smallCardWidth * 2,
|
||||||
|
mainAxisExtent: 60,
|
||||||
|
),
|
||||||
|
delegate: SliverChildBuilderDelegate(
|
||||||
|
(BuildContext context, int index) {
|
||||||
|
return const MsgFeedTopSkeleton();
|
||||||
|
},
|
||||||
|
childCount: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildBody(LoadingState<List?> loadingState) {
|
||||||
|
return switch (loadingState) {
|
||||||
|
Loading() => _buildLoading,
|
||||||
|
Success() => loadingState.response?.isNotEmpty == true
|
||||||
|
? Builder(
|
||||||
|
builder: (context) {
|
||||||
|
return switch (widget.searchType) {
|
||||||
|
LiveSearchType.room => SliverGrid(
|
||||||
|
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||||
|
mainAxisSpacing: StyleString.cardSpace,
|
||||||
|
crossAxisSpacing: StyleString.cardSpace,
|
||||||
|
maxCrossAxisExtent: Grid.smallCardWidth,
|
||||||
|
childAspectRatio: StyleString.aspectRatio,
|
||||||
|
mainAxisExtent:
|
||||||
|
MediaQuery.textScalerOf(context).scale(60),
|
||||||
|
),
|
||||||
|
delegate: SliverChildBuilderDelegate(
|
||||||
|
(context, index) {
|
||||||
|
if (index == loadingState.response!.length - 1) {
|
||||||
|
_controller.onLoadMore();
|
||||||
|
}
|
||||||
|
return LiveCardVSearch(
|
||||||
|
item: loadingState.response![index],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
childCount: loadingState.response!.length,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
LiveSearchType.user => SliverGrid(
|
||||||
|
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
|
||||||
|
maxCrossAxisExtent: Grid.smallCardWidth * 2,
|
||||||
|
mainAxisExtent: 60,
|
||||||
|
),
|
||||||
|
delegate: SliverChildBuilderDelegate(
|
||||||
|
(BuildContext context, int index) {
|
||||||
|
if (index == loadingState.response!.length - 1) {
|
||||||
|
_controller.onLoadMore();
|
||||||
|
}
|
||||||
|
return LiveSearchUserItem(
|
||||||
|
item: loadingState.response![index],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
childCount: loadingState.response!.length,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: HttpError(
|
||||||
|
onReload: _controller.onReload,
|
||||||
|
),
|
||||||
|
Error() => HttpError(
|
||||||
|
errMsg: loadingState.errMsg,
|
||||||
|
onReload: _controller.onReload,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get wantKeepAlive => true;
|
||||||
|
}
|
||||||
63
lib/pages/live_search/controller.dart
Normal file
63
lib/pages/live_search/controller.dart
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import 'package:PiliPlus/models/common/live_search_type.dart';
|
||||||
|
import 'package:PiliPlus/pages/live_search/child/controller.dart';
|
||||||
|
import 'package:PiliPlus/utils/extension.dart';
|
||||||
|
import 'package:PiliPlus/utils/utils.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
class LiveSearchController extends GetxController
|
||||||
|
with GetSingleTickerProviderStateMixin {
|
||||||
|
late final tabController = TabController(vsync: this, length: 2);
|
||||||
|
final editingController = TextEditingController();
|
||||||
|
final focusNode = FocusNode();
|
||||||
|
|
||||||
|
final mid = Get.parameters['mid'];
|
||||||
|
final uname = Get.parameters['uname'];
|
||||||
|
|
||||||
|
final RxBool hasData = false.obs;
|
||||||
|
final RxList<int> counts = <int>[-1, -1].obs;
|
||||||
|
|
||||||
|
late final roomCtr = Get.put(
|
||||||
|
LiveSearchChildController(this, LiveSearchType.room),
|
||||||
|
tag: Utils.generateRandomString(8));
|
||||||
|
late final userCtr = Get.put(
|
||||||
|
LiveSearchChildController(this, LiveSearchType.user),
|
||||||
|
tag: Utils.generateRandomString(8));
|
||||||
|
|
||||||
|
void onClear() {
|
||||||
|
if (editingController.value.text.isNotEmpty) {
|
||||||
|
editingController.clear();
|
||||||
|
counts.value = <int>[-1, -1];
|
||||||
|
hasData.value = false;
|
||||||
|
focusNode.requestFocus();
|
||||||
|
} else {
|
||||||
|
Get.back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
late final regex = RegExp(r'^\d+$');
|
||||||
|
|
||||||
|
void submit() {
|
||||||
|
if (editingController.text.isNotEmpty) {
|
||||||
|
if (regex.hasMatch(editingController.text)) {
|
||||||
|
Get.toNamed('/liveRoom?roomid=${editingController.text}');
|
||||||
|
} else {
|
||||||
|
hasData.value = true;
|
||||||
|
roomCtr
|
||||||
|
..scrollController.jumpToTop()
|
||||||
|
..onReload();
|
||||||
|
userCtr
|
||||||
|
..scrollController.jumpToTop()
|
||||||
|
..onReload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onClose() {
|
||||||
|
editingController.dispose();
|
||||||
|
focusNode.dispose();
|
||||||
|
tabController.dispose();
|
||||||
|
super.onClose();
|
||||||
|
}
|
||||||
|
}
|
||||||
103
lib/pages/live_search/view.dart
Normal file
103
lib/pages/live_search/view.dart
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
import 'package:PiliPlus/common/widgets/scroll_physics.dart';
|
||||||
|
import 'package:PiliPlus/models/common/live_search_type.dart';
|
||||||
|
import 'package:PiliPlus/pages/live_search/child/view.dart';
|
||||||
|
import 'package:PiliPlus/pages/live_search/controller.dart';
|
||||||
|
import 'package:PiliPlus/utils/utils.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
class LiveSearchPage extends StatefulWidget {
|
||||||
|
const LiveSearchPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<LiveSearchPage> createState() => _LiveSearchPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _LiveSearchPageState extends State<LiveSearchPage> {
|
||||||
|
final _controller =
|
||||||
|
Get.put(LiveSearchController(), tag: Utils.generateRandomString(8));
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
resizeToAvoidBottomInset: false,
|
||||||
|
appBar: AppBar(
|
||||||
|
actions: [
|
||||||
|
IconButton(
|
||||||
|
tooltip: '搜索',
|
||||||
|
onPressed: _controller.submit,
|
||||||
|
icon: const Icon(Icons.search, size: 22),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10)
|
||||||
|
],
|
||||||
|
title: TextField(
|
||||||
|
autofocus: true,
|
||||||
|
focusNode: _controller.focusNode,
|
||||||
|
controller: _controller.editingController,
|
||||||
|
textInputAction: TextInputAction.search,
|
||||||
|
textAlignVertical: TextAlignVertical.center,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: '搜索房间或主播',
|
||||||
|
border: InputBorder.none,
|
||||||
|
suffixIcon: IconButton(
|
||||||
|
tooltip: '清空',
|
||||||
|
icon: const Icon(Icons.clear, size: 22),
|
||||||
|
onPressed: _controller.onClear,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onSubmitted: (value) => _controller.submit(),
|
||||||
|
onChanged: (value) {
|
||||||
|
if (value.isEmpty) {
|
||||||
|
_controller.hasData.value = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
body: SafeArea(
|
||||||
|
top: false,
|
||||||
|
bottom: false,
|
||||||
|
child: Obx(() {
|
||||||
|
return Opacity(
|
||||||
|
opacity: _controller.hasData.value ? 1 : 0,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
TabBar(
|
||||||
|
controller: _controller.tabController,
|
||||||
|
tabs: [
|
||||||
|
Obx(
|
||||||
|
() => Tab(
|
||||||
|
text:
|
||||||
|
'正在直播 ${_controller.counts[0] != -1 ? _controller.counts[0] : ''}',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Obx(
|
||||||
|
() => Tab(
|
||||||
|
text:
|
||||||
|
'主播 ${_controller.counts[1] != -1 ? _controller.counts[1] : ''}',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: tabBarView(
|
||||||
|
controller: _controller.tabController,
|
||||||
|
children: [
|
||||||
|
LiveSearchChildPage(
|
||||||
|
controller: _controller.roomCtr,
|
||||||
|
searchType: LiveSearchType.room,
|
||||||
|
),
|
||||||
|
LiveSearchChildPage(
|
||||||
|
controller: _controller.userCtr,
|
||||||
|
searchType: LiveSearchType.user,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
114
lib/pages/live_search/widgets/live_search_room.dart
Normal file
114
lib/pages/live_search/widgets/live_search_room.dart
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
import 'package:PiliPlus/common/constants.dart';
|
||||||
|
import 'package:PiliPlus/common/widgets/image/image_save.dart';
|
||||||
|
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||||
|
import 'package:PiliPlus/models/live/live_search/room_item.dart';
|
||||||
|
import 'package:PiliPlus/utils/utils.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
// 视频卡片 - 垂直布局
|
||||||
|
class LiveCardVSearch extends StatelessWidget {
|
||||||
|
final LiveSearchRoomItemModel item;
|
||||||
|
|
||||||
|
const LiveCardVSearch({
|
||||||
|
super.key,
|
||||||
|
required this.item,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
String heroTag = Utils.makeHeroTag(item.roomid);
|
||||||
|
return Card(
|
||||||
|
clipBehavior: Clip.hardEdge,
|
||||||
|
margin: EdgeInsets.zero,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
Get.toNamed('/liveRoom?roomid=${item.roomid}');
|
||||||
|
},
|
||||||
|
onLongPress: () => imageSaveDialog(
|
||||||
|
title: item.title,
|
||||||
|
cover: item.cover,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
AspectRatio(
|
||||||
|
aspectRatio: StyleString.aspectRatio,
|
||||||
|
child: LayoutBuilder(builder: (context, boxConstraints) {
|
||||||
|
double maxWidth = boxConstraints.maxWidth;
|
||||||
|
double maxHeight = boxConstraints.maxHeight;
|
||||||
|
return Stack(
|
||||||
|
clipBehavior: Clip.none,
|
||||||
|
children: [
|
||||||
|
Hero(
|
||||||
|
tag: heroTag,
|
||||||
|
child: NetworkImgLayer(
|
||||||
|
src: item.cover!,
|
||||||
|
width: maxWidth,
|
||||||
|
height: maxHeight,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
child: AnimatedOpacity(
|
||||||
|
opacity: 1,
|
||||||
|
duration: const Duration(milliseconds: 200),
|
||||||
|
child: videoStat(context),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(5, 8, 5, 4),
|
||||||
|
child: Text(
|
||||||
|
'${item.title}',
|
||||||
|
textAlign: TextAlign.start,
|
||||||
|
style: const TextStyle(
|
||||||
|
letterSpacing: 0.3,
|
||||||
|
),
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget videoStat(context) {
|
||||||
|
return Container(
|
||||||
|
height: 50,
|
||||||
|
padding: const EdgeInsets.only(top: 26, left: 10, right: 10),
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
gradient: LinearGradient(
|
||||||
|
begin: Alignment.topCenter,
|
||||||
|
end: Alignment.bottomCenter,
|
||||||
|
colors: <Color>[
|
||||||
|
Colors.transparent,
|
||||||
|
Colors.black54,
|
||||||
|
],
|
||||||
|
tileMode: TileMode.mirror,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'${item.name}',
|
||||||
|
style: const TextStyle(fontSize: 11, color: Colors.white),
|
||||||
|
),
|
||||||
|
if (item.watchedShow?.textSmall != null)
|
||||||
|
Text(
|
||||||
|
'${Utils.numFormat(item.watchedShow!.textSmall)}围观',
|
||||||
|
style: const TextStyle(fontSize: 11, color: Colors.white),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
66
lib/pages/live_search/widgets/live_search_user.dart
Normal file
66
lib/pages/live_search/widgets/live_search_user.dart
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||||
|
import 'package:PiliPlus/models/live/live_search/user_item.dart';
|
||||||
|
import 'package:PiliPlus/utils/utils.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
class LiveSearchUserItem extends StatelessWidget {
|
||||||
|
const LiveSearchUserItem({
|
||||||
|
super.key,
|
||||||
|
required this.item,
|
||||||
|
});
|
||||||
|
|
||||||
|
final LiveSearchUserItemModel item;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final theme = Theme.of(context);
|
||||||
|
final style = TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
color: theme.colorScheme.outline,
|
||||||
|
);
|
||||||
|
return InkWell(
|
||||||
|
onTap: () => Get.toNamed(
|
||||||
|
'/liveRoom?roomid=${item.roomid}',
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
const SizedBox(width: 15),
|
||||||
|
NetworkImgLayer(
|
||||||
|
src: item.face,
|
||||||
|
width: 42,
|
||||||
|
height: 42,
|
||||||
|
type: 'avatar',
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Column(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
item.name!,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (item.liveStatus == 1) ...[
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Image.asset(height: 14, 'assets/images/live/live.gif'),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 2),
|
||||||
|
Text(
|
||||||
|
'分区: ${item.areaName ?? ''} 关注数: ${Utils.numFormat(item.fansNum ?? 0)}',
|
||||||
|
style: style,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -50,7 +50,9 @@ class _MemberArticleState extends State<MemberArticle>
|
|||||||
slivers: [
|
slivers: [
|
||||||
SliverPadding(
|
SliverPadding(
|
||||||
padding: EdgeInsets.only(
|
padding: EdgeInsets.only(
|
||||||
bottom: MediaQuery.paddingOf(context).bottom + 80),
|
top: 7,
|
||||||
|
bottom: MediaQuery.paddingOf(context).bottom + 80,
|
||||||
|
),
|
||||||
sliver: SliverGrid(
|
sliver: SliverGrid(
|
||||||
gridDelegate: Grid.videoCardHDelegate(context),
|
gridDelegate: Grid.videoCardHDelegate(context),
|
||||||
delegate: SliverChildBuilderDelegate(
|
delegate: SliverChildBuilderDelegate(
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:PiliPlus/models/common/member/search_type.dart';
|
import 'package:PiliPlus/models/common/member/search_type.dart';
|
||||||
import 'package:PiliPlus/pages/member_search/child/controller.dart';
|
import 'package:PiliPlus/pages/member_search/child/controller.dart';
|
||||||
|
import 'package:PiliPlus/utils/extension.dart';
|
||||||
import 'package:PiliPlus/utils/utils.dart';
|
import 'package:PiliPlus/utils/utils.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
@@ -37,8 +38,12 @@ class MemberSearchController extends GetxController
|
|||||||
void submit() {
|
void submit() {
|
||||||
if (editingController.text.isNotEmpty) {
|
if (editingController.text.isNotEmpty) {
|
||||||
hasData.value = true;
|
hasData.value = true;
|
||||||
arcCtr.onReload();
|
arcCtr
|
||||||
dynCtr.onReload();
|
..scrollController.jumpToTop()
|
||||||
|
..onReload();
|
||||||
|
dynCtr
|
||||||
|
..scrollController.jumpToTop()
|
||||||
|
..onReload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user