From 867efecc54d9f4b5601f6bc02d83b8084704d064 Mon Sep 17 00:00:00 2001 From: bggRGjQaUbCoE Date: Tue, 6 May 2025 20:00:09 +0800 Subject: [PATCH] refa: member search Signed-off-by: bggRGjQaUbCoE --- lib/http/member.dart | 71 +++++++ lib/models/common/member/search_type.dart | 1 + lib/pages/member_search/child/controller.dart | 68 +++++++ lib/pages/member_search/child/view.dart | 180 ++++++++++++++++++ lib/pages/member_search/controller.dart | 166 +++------------- lib/pages/member_search/search_archive.dart | 100 ---------- lib/pages/member_search/search_dynamic.dart | 157 --------------- lib/pages/member_search/view.dart | 81 +++++--- 8 files changed, 394 insertions(+), 430 deletions(-) create mode 100644 lib/models/common/member/search_type.dart create mode 100644 lib/pages/member_search/child/controller.dart create mode 100644 lib/pages/member_search/child/view.dart delete mode 100644 lib/pages/member_search/search_archive.dart delete mode 100644 lib/pages/member_search/search_dynamic.dart diff --git a/lib/http/member.dart b/lib/http/member.dart index 74de7e2f..b81e1835 100644 --- a/lib/http/member.dart +++ b/lib/http/member.dart @@ -383,6 +383,53 @@ class MemberHttp { } } + static Future memberArchiveNew({ + required mid, + int ps = 25, + int tid = 0, + int? pn, + String? keyword, + String order = 'pubdate', + bool orderAvoided = true, + }) async { + String dmImgStr = Utils.base64EncodeRandomString(16, 64); + String dmCoverImgStr = Utils.base64EncodeRandomString(32, 128); + Map params = await WbiSign.makSign({ + 'mid': mid, + 'ps': ps, + 'tid': tid, + 'pn': pn, + if (keyword != null) 'keyword': keyword, + 'order': order, + 'platform': 'web', + 'web_location': '333.1387', + 'order_avoided': orderAvoided, + 'dm_img_list': '[]', + 'dm_img_str': dmImgStr, + 'dm_cover_img_str': dmCoverImgStr, + 'dm_img_inter': '{"ds":[],"wh":[0,0,0],"of":[0,0,0]}', + }); + var res = await Request().get( + Api.memberArchive, + queryParameters: params, + options: Options(headers: { + HttpHeaders.userAgentHeader: Request.headerUa(type: 'pc'), + HttpHeaders.refererHeader: '${HttpString.spaceBaseUrl}/$mid', + 'origin': HttpString.spaceBaseUrl, + }), + ); + if (res.data['code'] == 0) { + return LoadingState.success( + MemberArchiveDataModel.fromJson(res.data['data'])); + } else { + Map errMap = { + -352: '风控校验失败,请检查登录状态', + }; + return LoadingState.error( + errMap[res.data['code']] ?? res.data['message']); + } + } + // 用户动态 static Future> memberDynamic({ String? offset, @@ -455,6 +502,30 @@ class MemberHttp { } } + static Future memberDynamicSearchNew({ + required int pn, + required dynamic mid, + required dynamic offset, + required String keyword, + }) async { + var res = await Request().get( + Api.memberDynamicSearch, + queryParameters: { + 'host_mid': mid, + 'page': pn, + 'offset': offset, + 'keyword': keyword, + 'features': 'itemOpusStyle,listOnlyfans', + 'web_location': 333.1387, + }, + ); + if (res.data['code'] == 0) { + return LoadingState.success(DynamicsDataModel.fromJson(res.data['data'])); + } else { + return LoadingState.error(res.data['message']); + } + } + // 查询分组 static Future followUpTags() async { var res = await Request().get(Api.followUpTag); diff --git a/lib/models/common/member/search_type.dart b/lib/models/common/member/search_type.dart new file mode 100644 index 00000000..aac58c00 --- /dev/null +++ b/lib/models/common/member/search_type.dart @@ -0,0 +1 @@ +enum MemberSearchType { archive, dynamic } diff --git a/lib/pages/member_search/child/controller.dart b/lib/pages/member_search/child/controller.dart new file mode 100644 index 00000000..2910b88a --- /dev/null +++ b/lib/pages/member_search/child/controller.dart @@ -0,0 +1,68 @@ +import 'package:PiliPlus/http/loading_state.dart'; +import 'package:PiliPlus/http/member.dart'; +import 'package:PiliPlus/models/common/member/search_type.dart'; +import 'package:PiliPlus/pages/common/common_list_controller.dart'; +import 'package:PiliPlus/pages/member_search/controller.dart'; + +class MemberSearchChildController extends CommonListController { + MemberSearchChildController(this.controller, this.searchType); + + final MemberSearchController controller; + final MemberSearchType searchType; + + dynamic offset; + + @override + void checkIsEnd(int length) { + switch (searchType) { + case MemberSearchType.archive: + if (controller.counts.first != -1 && + length >= controller.counts.first) { + isEnd = true; + } + break; + case MemberSearchType.dynamic: + if (controller.counts[1] != -1 && length >= controller.counts[1]) { + isEnd = true; + } + break; + } + } + + @override + List? getDataList(response) { + switch (searchType) { + case MemberSearchType.archive: + controller.counts[searchType.index] = response.page?['count'] ?? 0; + return response.list?.vlist; + case MemberSearchType.dynamic: + offset = response.offset; + controller.counts[searchType.index] = response.total ?? 0; + return response.items; + } + } + + @override + Future onRefresh() { + offset = null; + return super.onRefresh(); + } + + @override + Future customGetData() { + return switch (searchType) { + MemberSearchType.archive => MemberHttp.memberArchiveNew( + mid: controller.mid, + pn: currentPage, + keyword: controller.editingController.text, + order: 'pubdate', + ), + MemberSearchType.dynamic => MemberHttp.memberDynamicSearchNew( + mid: controller.mid, + pn: currentPage, + offset: offset ?? '', + keyword: controller.editingController.text, + ), + }; + } +} diff --git a/lib/pages/member_search/child/view.dart b/lib/pages/member_search/child/view.dart new file mode 100644 index 00000000..8b0d3d23 --- /dev/null +++ b/lib/pages/member_search/child/view.dart @@ -0,0 +1,180 @@ +import 'package:PiliPlus/common/constants.dart'; +import 'package:PiliPlus/common/skeleton/dynamic_card.dart'; +import 'package:PiliPlus/common/skeleton/video_card_h.dart'; +import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart'; +import 'package:PiliPlus/common/widgets/refresh_indicator.dart'; +import 'package:PiliPlus/common/widgets/video_card/video_card_h.dart'; +import 'package:PiliPlus/http/loading_state.dart'; +import 'package:PiliPlus/models/common/member/search_type.dart'; +import 'package:PiliPlus/pages/dynamics/widgets/dynamic_panel.dart'; +import 'package:PiliPlus/pages/member_search/child/controller.dart'; +import 'package:PiliPlus/utils/grid.dart'; +import 'package:PiliPlus/utils/storage.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:waterfall_flow/waterfall_flow.dart'; + +class MemberSearchChildPage extends StatefulWidget { + const MemberSearchChildPage({ + super.key, + required this.controller, + required this.searchType, + }); + + final MemberSearchChildController controller; + final MemberSearchType searchType; + + @override + State createState() => _MemberSearchChildPageState(); +} + +class _MemberSearchChildPageState extends State + with AutomaticKeepAliveClientMixin { + late final bool dynamicsWaterfallFlow = GStorage.setting + .get(SettingBoxKey.dynamicsWaterfallFlow, defaultValue: true); + MemberSearchChildController get _controller => widget.controller; + + @override + Widget build(BuildContext context) { + super.build(context); + return refreshIndicator( + onRefresh: _controller.onRefresh, + child: CustomScrollView( + slivers: [ + SliverPadding( + padding: EdgeInsets.only( + top: widget.searchType == MemberSearchType.archive ? 7 : 0, + bottom: MediaQuery.paddingOf(context).bottom + 80, + ), + sliver: Obx(() => _buildBody(_controller.loadingState.value)), + ), + ], + ), + ); + } + + Widget get _buildLoading { + return switch (widget.searchType) { + MemberSearchType.archive => SliverGrid( + gridDelegate: Grid.videoCardHDelegate(context), + delegate: SliverChildBuilderDelegate( + (context, index) { + return const VideoCardHSkeleton(); + }, + childCount: 10, + ), + ), + MemberSearchType.dynamic => dynSkeleton(), + }; + } + + Widget dynSkeleton() { + if (!dynamicsWaterfallFlow) { + return SliverCrossAxisGroup( + slivers: [ + const SliverFillRemaining(), + SliverConstrainedCrossAxis( + maxExtent: Grid.smallCardWidth * 2, + sliver: SliverList.builder( + itemBuilder: (context, index) { + return const DynamicCardSkeleton(); + }, + itemCount: 10, + ), + ), + const SliverFillRemaining() + ], + ); + } + return SliverGrid( + gridDelegate: SliverGridDelegateWithExtentAndRatio( + crossAxisSpacing: StyleString.cardSpace / 2, + mainAxisSpacing: StyleString.cardSpace / 2, + maxCrossAxisExtent: Grid.smallCardWidth * 2, + childAspectRatio: StyleString.aspectRatio, + mainAxisExtent: 50, + ), + delegate: SliverChildBuilderDelegate( + (context, index) { + return const DynamicCardSkeleton(); + }, + childCount: 10, + ), + ); + } + + Widget _buildBody(LoadingState loadingState) { + return switch (loadingState) { + Loading() => _buildLoading, + Success() => loadingState.response?.isNotEmpty == true + ? Builder( + builder: (context) { + return switch (widget.searchType) { + MemberSearchType.archive => SliverGrid( + gridDelegate: Grid.videoCardHDelegate(context), + delegate: SliverChildBuilderDelegate( + (context, index) { + if (index == loadingState.response!.length - 1) { + _controller.onLoadMore(); + } + return VideoCardH( + videoItem: loadingState.response![index], + ); + }, + childCount: loadingState.response!.length, + ), + ), + MemberSearchType.dynamic => dynamicsWaterfallFlow + ? SliverWaterfallFlow.extent( + maxCrossAxisExtent: Grid.smallCardWidth * 2, + crossAxisSpacing: StyleString.safeSpace, + mainAxisSpacing: StyleString.safeSpace, + lastChildLayoutTypeBuilder: (index) { + if (index == loadingState.response!.length - 1) { + _controller.onLoadMore(); + } + return index == loadingState.response!.length + ? LastChildLayoutType.foot + : LastChildLayoutType.none; + }, + children: loadingState.response! + .map((item) => DynamicPanel(item: item)) + .toList(), + ) + : SliverCrossAxisGroup( + slivers: [ + const SliverFillRemaining(), + SliverConstrainedCrossAxis( + maxExtent: Grid.smallCardWidth * 2, + sliver: SliverList.builder( + itemBuilder: (context, index) { + if (index == + loadingState.response!.length - 1) { + _controller.onLoadMore(); + } + return DynamicPanel( + item: loadingState.response![index], + ); + }, + itemCount: loadingState.response!.length, + ), + ), + const SliverFillRemaining(), + ], + ), + }; + }, + ) + : HttpError( + onReload: _controller.onReload, + ), + Error() => HttpError( + errMsg: loadingState.errMsg, + onReload: _controller.onReload, + ), + }; + } + + @override + bool get wantKeepAlive => true; +} diff --git a/lib/pages/member_search/controller.dart b/lib/pages/member_search/controller.dart index dbda3581..6982bfad 100644 --- a/lib/pages/member_search/controller.dart +++ b/lib/pages/member_search/controller.dart @@ -1,169 +1,51 @@ -import 'package:PiliPlus/http/loading_state.dart'; -import 'package:PiliPlus/http/member.dart'; -import 'package:PiliPlus/models/dynamics/result.dart'; -import 'package:PiliPlus/models/member/archive.dart'; -import 'package:PiliPlus/utils/extension.dart'; +import 'package:PiliPlus/models/common/member/search_type.dart'; +import 'package:PiliPlus/pages/member_search/child/controller.dart'; +import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; class MemberSearchController extends GetxController with GetSingleTickerProviderStateMixin { - final scrollController = ScrollController(); late final tabController = TabController(vsync: this, length: 2); - final textEditingController = TextEditingController(); - final searchFocusNode = FocusNode(); + final editingController = TextEditingController(); + final focusNode = FocusNode(); - RxBool hasData = false.obs; + final mid = Get.parameters['mid']; + final uname = Get.parameters['uname']; - late int mid; - RxString uname = ''.obs; + final RxBool hasData = false.obs; + final RxList counts = [-1, -1].obs; - int archivePn = 1; - RxInt archiveCount = (-1).obs; - bool isEndArchive = false; - Rx?>> archiveState = - LoadingState?>.loading().obs; + late final arcCtr = Get.put( + MemberSearchChildController(this, MemberSearchType.archive), + tag: Utils.generateRandomString(8)); + late final dynCtr = Get.put( + MemberSearchChildController(this, MemberSearchType.dynamic), + tag: Utils.generateRandomString(8)); - String offset = ''; - int dynamicPn = 1; - RxInt dynamicCount = (-1).obs; - bool isEndDynamic = false; - Rx?>> dynamicState = - LoadingState?>.loading().obs; - - @override - void onInit() { - super.onInit(); - mid = int.parse(Get.parameters['mid']!); - uname.value = Get.parameters['uname']!; - } - - // 清空搜索 void onClear() { - if (textEditingController.value.text.isNotEmpty) { - textEditingController.clear(); + if (editingController.value.text.isNotEmpty) { + editingController.clear(); + counts.value = [-1, -1]; hasData.value = false; - searchFocusNode.requestFocus(); + focusNode.requestFocus(); } else { Get.back(); } } - // 提交搜索内容 void submit() { - if (textEditingController.text.isNotEmpty) { + if (editingController.text.isNotEmpty) { hasData.value = true; - - dynamicCount.value = -1; - dynamicState.value = LoadingState.loading(); - refreshArchive(); - - archiveCount.value = -1; - archiveState.value = LoadingState.loading(); - refreshDynamic(); - } - } - - Future refreshDynamic() async { - offset = ''; - dynamicPn = 1; - isEndDynamic = false; - await searchDynamic(); - } - - Future refreshArchive() async { - archivePn = 1; - isEndArchive = false; - await searchArchives(); - } - - Future searchDynamic([bool isRefresh = true]) async { - if (isRefresh.not && isEndDynamic) return; - dynamic res = await MemberHttp.memberDynamicSearch( - mid: mid, - pn: dynamicPn, - offset: offset, - keyword: textEditingController.text, - ); - if (res['status']) { - DynamicsDataModel data = res['data']; - List? items = data.items; - dynamicCount.value = data.total ?? 0; - offset = data.offset ?? ''; - - if (data.hasMore == false || items.isNullOrEmpty) { - isEndDynamic = true; - if (isRefresh) { - dynamicState.value = LoadingState.success(items); - } - return; - } - - if (isRefresh) { - if (items!.length >= dynamicCount.value) { - isEndDynamic = true; - } - dynamicState.value = LoadingState.success(items); - } else if (dynamicState.value is Success) { - List currentList = dynamicState.value.data! - ..addAll(items!); - if (currentList.length >= dynamicCount.value) { - isEndDynamic = true; - } - dynamicState.refresh(); - } - dynamicPn++; - } else if (isRefresh) { - dynamicState.value = LoadingState.error(res['msg']); - } - } - - // 搜索视频 - Future searchArchives([bool isRefresh = true]) async { - if (isRefresh.not && isEndArchive) return; - dynamic res = await MemberHttp.memberArchive( - mid: mid, - pn: archivePn, - keyword: textEditingController.text, - order: 'pubdate', - ); - if (res['status']) { - MemberArchiveDataModel data = res['data']; - List? vlist = data.list?.vlist; - archiveCount.value = data.page?['count'] ?? 0; - - if (vlist.isNullOrEmpty) { - isEndArchive = true; - if (isRefresh) { - archiveState.value = LoadingState.success(vlist); - } - return; - } - - if (isRefresh) { - if (vlist!.length >= archiveCount.value) { - isEndArchive = true; - } - archiveState.value = LoadingState.success(vlist); - } else if (archiveState.value is Success) { - List currentList = archiveState.value.data! - ..addAll(vlist!); - if (currentList.length >= archiveCount.value) { - isEndArchive = true; - } - archiveState.refresh(); - } - archivePn++; - } else if (isRefresh) { - archiveState.value = LoadingState.error(res['msg']); + arcCtr.onReload(); + dynCtr.onReload(); } } @override void onClose() { - textEditingController.dispose(); - searchFocusNode.dispose(); - scrollController.dispose(); + editingController.dispose(); + focusNode.dispose(); tabController.dispose(); super.onClose(); } diff --git a/lib/pages/member_search/search_archive.dart b/lib/pages/member_search/search_archive.dart deleted file mode 100644 index 7e30bfcc..00000000 --- a/lib/pages/member_search/search_archive.dart +++ /dev/null @@ -1,100 +0,0 @@ -import 'package:PiliPlus/common/constants.dart'; -import 'package:PiliPlus/common/skeleton/video_card_h.dart'; -import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart'; -import 'package:PiliPlus/common/widgets/refresh_indicator.dart'; -import 'package:PiliPlus/common/widgets/video_card/video_card_h.dart'; -import 'package:PiliPlus/http/loading_state.dart'; -import 'package:PiliPlus/models/member/archive.dart'; -import 'package:PiliPlus/pages/member_search/controller.dart'; -import 'package:PiliPlus/utils/grid.dart'; -import 'package:easy_debounce/easy_throttle.dart'; -import 'package:flutter/material.dart'; -import 'package:get/get_state_manager/src/rx_flutter/rx_obx_widget.dart'; - -class SearchArchive extends StatefulWidget { - const SearchArchive({ - super.key, - required this.ctr, - }); - - final MemberSearchController ctr; - - @override - State createState() => _SearchArchiveState(); -} - -class _SearchArchiveState extends State - with AutomaticKeepAliveClientMixin { - @override - Widget build(BuildContext context) { - super.build(context); - return refreshIndicator( - onRefresh: () async { - await widget.ctr.refreshArchive(); - }, - child: CustomScrollView( - physics: const AlwaysScrollableScrollPhysics(), - slivers: [ - SliverPadding( - padding: EdgeInsets.only( - top: StyleString.safeSpace - 5, - bottom: MediaQuery.paddingOf(context).bottom + 80, - ), - sliver: - Obx(() => _buildBody(context, widget.ctr.archiveState.value)), - ) - ], - ), - ); - } - - Widget _buildBody( - BuildContext context, LoadingState?> loadingState) { - return switch (loadingState) { - Loading() => SliverGrid( - gridDelegate: Grid.videoCardHDelegate(context), - delegate: SliverChildBuilderDelegate( - (context, index) { - return const VideoCardHSkeleton(); - }, - childCount: 10, - ), - ), - Success() => loadingState.response?.isNotEmpty == true - ? SliverGrid( - gridDelegate: Grid.videoCardHDelegate(context), - delegate: SliverChildBuilderDelegate( - (context, index) { - if (index == loadingState.response!.length - 1) { - EasyThrottle.throttle( - 'searchArchives', const Duration(milliseconds: 500), - () { - widget.ctr.searchArchives(false); - }); - } - return VideoCardH( - videoItem: loadingState.response![index], - ); - }, - childCount: loadingState.response!.length, - ), - ) - : HttpError( - onReload: () { - widget.ctr.archiveState.value = LoadingState.loading(); - widget.ctr.refreshArchive(); - }, - ), - Error() => HttpError( - errMsg: loadingState.errMsg, - onReload: () { - widget.ctr.archiveState.value = LoadingState.loading(); - widget.ctr.refreshArchive(); - }, - ), - }; - } - - @override - bool get wantKeepAlive => true; -} diff --git a/lib/pages/member_search/search_dynamic.dart b/lib/pages/member_search/search_dynamic.dart deleted file mode 100644 index b6ae97c0..00000000 --- a/lib/pages/member_search/search_dynamic.dart +++ /dev/null @@ -1,157 +0,0 @@ -import 'package:PiliPlus/common/constants.dart'; -import 'package:PiliPlus/common/skeleton/dynamic_card.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/dynamics/result.dart'; -import 'package:PiliPlus/pages/dynamics/widgets/dynamic_panel.dart'; -import 'package:PiliPlus/pages/member_search/controller.dart'; -import 'package:PiliPlus/utils/grid.dart'; -import 'package:PiliPlus/utils/storage.dart'; -import 'package:easy_debounce/easy_throttle.dart'; -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; -import 'package:waterfall_flow/waterfall_flow.dart'; - -class SearchDynamic extends StatefulWidget { - const SearchDynamic({ - super.key, - required this.ctr, - }); - - final MemberSearchController ctr; - - @override - State createState() => _SearchDynamicState(); -} - -class _SearchDynamicState extends State - with AutomaticKeepAliveClientMixin { - @override - Widget build(BuildContext context) { - super.build(context); - return refreshIndicator( - onRefresh: () async { - await widget.ctr.refreshDynamic(); - }, - child: CustomScrollView( - physics: const AlwaysScrollableScrollPhysics(), - slivers: [ - SliverPadding( - padding: EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom + 80, - ), - sliver: - Obx(() => _buildBody(context, widget.ctr.dynamicState.value)), - ) - ], - ), - ); - } - - late final bool dynamicsWaterfallFlow = GStorage.setting - .get(SettingBoxKey.dynamicsWaterfallFlow, defaultValue: true); - - Widget skeleton() { - if (!dynamicsWaterfallFlow) { - return SliverCrossAxisGroup( - slivers: [ - const SliverFillRemaining(), - SliverConstrainedCrossAxis( - maxExtent: Grid.smallCardWidth * 2, - sliver: SliverList.builder( - itemBuilder: (context, index) { - return const DynamicCardSkeleton(); - }, - itemCount: 10, - ), - ), - const SliverFillRemaining() - ], - ); - } - return SliverGrid( - gridDelegate: SliverGridDelegateWithExtentAndRatio( - crossAxisSpacing: StyleString.cardSpace / 2, - mainAxisSpacing: StyleString.cardSpace / 2, - maxCrossAxisExtent: Grid.smallCardWidth * 2, - childAspectRatio: StyleString.aspectRatio, - mainAxisExtent: 50, - ), - delegate: SliverChildBuilderDelegate( - (context, index) { - return const DynamicCardSkeleton(); - }, - childCount: 10, - ), - ); - } - - Widget _buildBody(BuildContext context, - LoadingState?> loadingState) { - return switch (loadingState) { - Loading() => skeleton(), - Success() => loadingState.response?.isNotEmpty == true - ? dynamicsWaterfallFlow - ? SliverWaterfallFlow.extent( - maxCrossAxisExtent: Grid.smallCardWidth * 2, - crossAxisSpacing: StyleString.safeSpace, - mainAxisSpacing: StyleString.safeSpace, - lastChildLayoutTypeBuilder: (index) { - if (index == loadingState.response!.length - 1) { - EasyThrottle.throttle( - 'member_dynamics', const Duration(milliseconds: 1000), - () { - widget.ctr.searchDynamic(false); - }); - } - return index == loadingState.response!.length - ? LastChildLayoutType.foot - : LastChildLayoutType.none; - }, - children: loadingState.response! - .map((item) => DynamicPanel(item: item)) - .toList(), - ) - : SliverCrossAxisGroup( - slivers: [ - const SliverFillRemaining(), - SliverConstrainedCrossAxis( - maxExtent: Grid.smallCardWidth * 2, - sliver: SliverList.builder( - itemBuilder: (context, index) { - if (index == loadingState.response!.length - 1) { - EasyThrottle.throttle('member_dynamics', - const Duration(milliseconds: 1000), () { - widget.ctr.searchDynamic(false); - }); - } - return DynamicPanel( - item: loadingState.response![index], - ); - }, - itemCount: loadingState.response!.length, - ), - ), - const SliverFillRemaining(), - ], - ) - : HttpError( - onReload: () { - widget.ctr.dynamicState.value = LoadingState.loading(); - widget.ctr.refreshDynamic(); - }, - ), - Error() => HttpError( - errMsg: loadingState.errMsg, - onReload: () { - widget.ctr.dynamicState.value = LoadingState.loading(); - widget.ctr.refreshDynamic(); - }, - ), - }; - } - - @override - bool get wantKeepAlive => true; -} diff --git a/lib/pages/member_search/view.dart b/lib/pages/member_search/view.dart index 6bf00cba..1f96c47a 100644 --- a/lib/pages/member_search/view.dart +++ b/lib/pages/member_search/view.dart @@ -1,7 +1,8 @@ import 'package:PiliPlus/common/widgets/scroll_physics.dart'; +import 'package:PiliPlus/models/common/member/search_type.dart'; +import 'package:PiliPlus/pages/member_search/child/view.dart'; import 'package:PiliPlus/pages/member_search/controller.dart'; -import 'package:PiliPlus/pages/member_search/search_archive.dart'; -import 'package:PiliPlus/pages/member_search/search_dynamic.dart'; +import 'package:PiliPlus/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -13,7 +14,8 @@ class MemberSearchPage extends StatefulWidget { } class _MemberSearchPageState extends State { - final _memberSearchCtr = Get.put(MemberSearchController()); + final _controller = + Get.put(MemberSearchController(), tag: Utils.generateRandomString(8)); @override Widget build(BuildContext context) { @@ -23,15 +25,15 @@ class _MemberSearchPageState extends State { actions: [ IconButton( tooltip: '搜索', - onPressed: _memberSearchCtr.submit, + onPressed: _controller.submit, icon: const Icon(Icons.search, size: 22), ), const SizedBox(width: 10) ], title: TextField( autofocus: true, - focusNode: _memberSearchCtr.searchFocusNode, - controller: _memberSearchCtr.textEditingController, + focusNode: _controller.focusNode, + controller: _controller.editingController, textInputAction: TextInputAction.search, textAlignVertical: TextAlignVertical.center, decoration: InputDecoration( @@ -40,63 +42,80 @@ class _MemberSearchPageState extends State { suffixIcon: IconButton( tooltip: '清空', icon: const Icon(Icons.clear, size: 22), - onPressed: _memberSearchCtr.onClear, + onPressed: _controller.onClear, ), ), - onSubmitted: (value) => _memberSearchCtr.submit(), + onSubmitted: (value) => _controller.submit(), onChanged: (value) { if (value.isEmpty) { - _memberSearchCtr.hasData.value = false; + _controller.hasData.value = false; } }, ), ), - body: Obx( - () => _memberSearchCtr.hasData.value - ? SafeArea( - top: false, - bottom: false, + body: SafeArea( + top: false, + bottom: false, + child: Stack( + clipBehavior: Clip.none, + children: [ + Obx(() { + return Opacity( + opacity: _controller.hasData.value ? 1 : 0, child: Column( children: [ TabBar( - controller: _memberSearchCtr.tabController, + controller: _controller.tabController, tabs: [ Obx( () => Tab( text: - '视频 ${_memberSearchCtr.archiveCount.value != -1 ? '${_memberSearchCtr.archiveCount.value}' : ''}', + '视频 ${_controller.counts[0] != -1 ? _controller.counts[0] : ''}', ), ), Obx( () => Tab( text: - '动态 ${_memberSearchCtr.dynamicCount.value != -1 ? '${_memberSearchCtr.dynamicCount.value}' : ''}', + '动态 ${_controller.counts[1] != -1 ? _controller.counts[1] : ''}', ), ), ], ), Expanded( child: tabBarView( - controller: _memberSearchCtr.tabController, + controller: _controller.tabController, children: [ - SearchArchive(ctr: _memberSearchCtr), - SearchDynamic(ctr: _memberSearchCtr), + MemberSearchChildPage( + controller: _controller.arcCtr, + searchType: MemberSearchType.archive, + ), + MemberSearchChildPage( + controller: _controller.dynCtr, + searchType: MemberSearchType.dynamic, + ), ], ), ), ], ), - ) - : FractionallySizedBox( - heightFactor: 0.5, - widthFactor: 1.0, - child: Center( - child: Text( - '搜索「${_memberSearchCtr.uname.value}」的动态、视频', - textAlign: TextAlign.center, - ), - ), - ), + ); + }), + Obx( + () => _controller.hasData.value + ? const SizedBox.shrink() + : FractionallySizedBox( + heightFactor: 0.5, + widthFactor: 1.0, + child: Center( + child: Text( + '搜索「${_controller.uname}」的动态、视频', + textAlign: TextAlign.center, + ), + ), + ), + ), + ], + ), ), ); }