diff --git a/lib/http/dynamics.dart b/lib/http/dynamics.dart index ac5effda..739f138b 100644 --- a/lib/http/dynamics.dart +++ b/lib/http/dynamics.dart @@ -1,9 +1,12 @@ +import 'package:PiliPalaX/http/loading_state.dart'; +import 'package:PiliPalaX/utils/extension.dart'; + import '../models/dynamics/result.dart'; import '../models/dynamics/up.dart'; import 'index.dart'; class DynamicsHttp { - static Future followDynamic({ + static Future followDynamic({ String? type, String? offset, int? mid, @@ -21,23 +24,17 @@ class DynamicsHttp { var res = await Request().get(Api.followDynamic, data: data); if (res.data['code'] == 0) { try { - return { - 'status': true, - 'data': DynamicsDataModel.fromJson(res.data['data']), - }; + DynamicsDataModel data = DynamicsDataModel.fromJson(res.data['data']); + if (!data.items.isNullOrEmpty) { + return LoadingState.success(data); + } else { + return LoadingState.empty(); + } } catch (err) { - return { - 'status': false, - 'data': [], - 'msg': err.toString(), - }; + return LoadingState.error(err.toString()); } } else { - return { - 'status': false, - 'data': [], - 'msg': res.data['message'], - }; + return LoadingState.error(res.data['message']); } } diff --git a/lib/models/common/dynamics_type.dart b/lib/models/common/dynamics_type.dart index 5e89213f..0f6ac1b7 100644 --- a/lib/models/common/dynamics_type.dart +++ b/lib/models/common/dynamics_type.dart @@ -17,40 +17,33 @@ extension BusinessTypeExtension on DynamicsType { List tabsConfig = [ { + 'tag': 'all', 'value': DynamicsType.all, 'label': '全部', 'enabled': true, - 'ctr': Get.put(DynamicsTabController(), tag: 'all'), - 'page': const DynamicsTabPage(dynamicsType: 'all'), }, { + 'tag': 'video', 'value': DynamicsType.video, 'label': '投稿', 'enabled': true, - 'ctr': - Get.put(DynamicsTabController(), tag: 'video'), - 'page': const DynamicsTabPage(dynamicsType: 'video'), }, { + 'tag': 'pgc', 'value': DynamicsType.pgc, 'label': '番剧', 'enabled': true, - 'ctr': Get.put(DynamicsTabController(), tag: 'pgc'), - 'page': const DynamicsTabPage(dynamicsType: 'pgc'), }, { + 'tag': 'article', 'value': DynamicsType.article, 'label': '专栏', 'enabled': true, - 'ctr': - Get.put(DynamicsTabController(), tag: 'article'), - 'page': const DynamicsTabPage(dynamicsType: 'article'), }, { + 'tag': 'up', 'value': DynamicsType.up, 'label': 'Up', 'enabled': true, - 'ctr': Get.put(DynamicsTabController(), tag: 'up'), - 'page': const DynamicsTabPage(dynamicsType: 'up'), }, ]; diff --git a/lib/pages/common/common_controller.dart b/lib/pages/common/common_controller.dart index 3ecd2a44..a6e40e20 100644 --- a/lib/pages/common/common_controller.dart +++ b/lib/pages/common/common_controller.dart @@ -63,7 +63,7 @@ abstract class CommonController extends GetxController { void onReload() { loadingState.value = LoadingState.loading(); - queryData(); + onRefresh(); } @override diff --git a/lib/pages/dynamics/controller.dart b/lib/pages/dynamics/controller.dart index bbd1ab63..5b1fbf24 100644 --- a/lib/pages/dynamics/controller.dart +++ b/lib/pages/dynamics/controller.dart @@ -1,6 +1,8 @@ // ignore_for_file: avoid_print import 'package:PiliPalaX/http/follow.dart'; +import 'package:PiliPalaX/pages/dynamics/tab/controller.dart'; +import 'package:PiliPalaX/pages/dynamics/tab/view.dart'; import 'package:PiliPalaX/utils/extension.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; @@ -36,7 +38,7 @@ class DynamicsController extends GetxController RxInt initialValue = 0.obs; Box userInfoCache = GStorage.userInfo; RxBool userLogin = false.obs; - var userInfo; + dynamic userInfo; RxBool isLoadingDynamic = false.obs; Box setting = GStorage.setting; List hasUpdatedUps = []; @@ -51,13 +53,13 @@ class DynamicsController extends GetxController super.onInit(); tabController = TabController( - length: tabsConfig.length, - vsync: this, - initialIndex: - setting.get(SettingBoxKey.defaultDynamicType, defaultValue: 0)); - tabsPageList = tabsConfig.map((e) { - return e['page'] as Widget; - }).toList(); + length: tabsConfig.length, + vsync: this, + initialIndex: + setting.get(SettingBoxKey.defaultDynamicType, defaultValue: 0), + ); + tabsPageList = + tabsConfig.map((e) => DynamicsTabPage(dynamicsType: e['tag'])).toList(); } void refreshNotifier() { @@ -304,17 +306,18 @@ class DynamicsController extends GetxController } onRefresh() async { - print('onRefresh'); - print(tabsConfig[tabController.index]['ctr']); await Future.wait([ queryFollowUp(), - tabsConfig[tabController.index]['ctr'].onRefresh() + Get.find( + tag: tabsConfig[tabController.index]['tag']) + .onRefresh() ]); } // 返回顶部并刷新 void animateToTop() async { - tabsConfig[tabController.index]['ctr'].animateToTop(); + Get.find(tag: tabsConfig[tabController.index]['tag']) + .animateToTop(); scrollController.animToTop(); } } diff --git a/lib/pages/dynamics/tab/controller.dart b/lib/pages/dynamics/tab/controller.dart index 5ef26c68..43d23333 100644 --- a/lib/pages/dynamics/tab/controller.dart +++ b/lib/pages/dynamics/tab/controller.dart @@ -1,71 +1,43 @@ -import 'package:PiliPalaX/utils/extension.dart'; -import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; -import 'package:get/get.dart'; -import 'package:flutter/material.dart'; -// import 'package:hive/hive.dart'; +import 'package:PiliPalaX/http/loading_state.dart'; +import 'package:PiliPalaX/pages/common/common_controller.dart'; import '../../../http/dynamics.dart'; -import '../../../models/dynamics/result.dart'; -// import '../../../utils/storage.dart'; -class DynamicsTabController extends GetxController { +class DynamicsTabController extends CommonController { + DynamicsTabController({required this.dynamicsType}); + final String dynamicsType; String offset = ''; - ScrollController scrollController = ScrollController(); - RxList dynamicsList = [].obs; - RxBool isLoadingMore = false.obs; - String dynamicsType = 'all'; - // Box userInfoCache = GStorage.userInfo; - // bool userLogin = false; int mid = -1; - Future queryFollowDynamic(String type, String dynamicsType, int? mid) async { - this.dynamicsType = dynamicsType; - if (mid != null) this.mid = mid; - if (type != 'onLoad') { - dynamicsList.clear(); - offset = ''; - } - // // 下拉刷新数据渲染时会触发onLoad - // if (type == 'onLoad' && page == 1) { - // return; - // } - isLoadingMore.value = true; - var res = await DynamicsHttp.followDynamic( - type: dynamicsType == "up" ? "all" : dynamicsType, - offset: offset, - mid: dynamicsType == "up" ? mid : -1, - ); - isLoadingMore.value = false; - if (res['status']) { - if (type == 'onLoad' && res['data'].items.isEmpty) { - SmartDialog.showToast('没有更多了'); - return; - } - if (type == 'onLoad') { - dynamicsList.addAll(res['data'].items); - } else { - dynamicsList.value = res['data'].items; - } - // print('dynamicsList: $dynamicsList'); - dynamicsList.refresh(); - offset = res['data'].offset; - // print("page: $page[dynamicsType]!"); - } - return res; + @override + void onInit() { + super.onInit(); + queryData(); } - // 下拉刷新 + @override Future onRefresh() async { - await queryFollowDynamic('onRefresh', dynamicsType, mid); + offset = ''; + print('11111111111111111111111111111111 $dynamicsType,,$mid'); + await queryData(); } - // 上拉加载 - Future onLoad() async { - await queryFollowDynamic('onLoad', dynamicsType, mid); + @override + bool customHandleResponse(Success response) { + offset = response.response.offset; + List currentList = loadingState.value is Success + ? (loadingState.value as Success).response + : []; + loadingState.value = offset == '' + ? LoadingState.success(response.response.items) + : LoadingState.success(currentList + response.response.items); + return true; } - // 返回顶部并刷新 - void animateToTop() { - scrollController.animToTop(); - } + @override + Future customGetData() => DynamicsHttp.followDynamic( + type: dynamicsType == "up" ? "all" : dynamicsType, + offset: offset, + mid: dynamicsType == "up" ? mid : -1, + ); } diff --git a/lib/pages/dynamics/tab/view.dart b/lib/pages/dynamics/tab/view.dart index 94531929..7a8287b5 100644 --- a/lib/pages/dynamics/tab/view.dart +++ b/lib/pages/dynamics/tab/view.dart @@ -1,7 +1,9 @@ import 'dart:async'; +import 'package:PiliPalaX/http/loading_state.dart'; +import 'package:PiliPalaX/pages/home/controller.dart'; +import 'package:PiliPalaX/pages/main/controller.dart'; import 'package:PiliPalaX/utils/storage.dart'; -import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:get/get.dart'; @@ -11,7 +13,6 @@ import 'package:PiliPalaX/common/widgets/no_data.dart'; import 'package:waterfall_flow/waterfall_flow.dart'; import '../../../common/skeleton/dynamic_card.dart'; -import '../../../models/dynamics/result.dart'; import '../../../utils/grid.dart'; import '../index.dart'; @@ -31,8 +32,6 @@ class DynamicsTabPage extends StatefulWidget { class _DynamicsTabPageState extends State with AutomaticKeepAliveClientMixin { late DynamicsTabController _dynamicsTabController; - late Future _futureBuilderFuture; - late ScrollController scrollController; late bool dynamicsWaterfallFlow; late final DynamicsController dynamicsController; @@ -42,31 +41,38 @@ class _DynamicsTabPageState extends State @override void initState() { super.initState(); - _dynamicsTabController = - Get.put(DynamicsTabController(), tag: widget.dynamicsType); + _dynamicsTabController = Get.put( + DynamicsTabController(dynamicsType: widget.dynamicsType), + tag: widget.dynamicsType, + ); dynamicsController = Get.find(); - _futureBuilderFuture = _dynamicsTabController.queryFollowDynamic( - 'init', widget.dynamicsType, dynamicsController.mid.value); - scrollController = _dynamicsTabController.scrollController - ..addListener(() { - if (scrollController.position.pixels >= - scrollController.position.maxScrollExtent - 200) { - if (!_dynamicsTabController.isLoadingMore.value) { - EasyThrottle.throttle('_dynamicsTabController_onLoad', - const Duration(milliseconds: 500), () { - _dynamicsTabController.isLoadingMore.value = true; - _dynamicsTabController.onLoad(); - _dynamicsTabController.isLoadingMore.value = false; - }); - } - } - }); + _dynamicsTabController.scrollController.addListener(() { + if (_dynamicsTabController.scrollController.position.pixels >= + _dynamicsTabController.scrollController.position.maxScrollExtent - + 200) { + _dynamicsTabController.onLoadMore(); + } + + StreamController mainStream = + Get.find().bottomBarStream; + StreamController searchBarStream = + Get.find().searchBarStream; + final ScrollDirection direction = + _dynamicsTabController.scrollController.position.userScrollDirection; + if (direction == ScrollDirection.forward) { + mainStream.add(true); + searchBarStream.add(true); + } else if (direction == ScrollDirection.reverse) { + mainStream.add(false); + searchBarStream.add(false); + } + }); dynamicsController.mid.listen((mid) { print('midListen: $mid'); - scrollController.jumpTo(0); - _futureBuilderFuture = _dynamicsTabController.queryFollowDynamic( - 'init', widget.dynamicsType, mid); + _dynamicsTabController.mid = mid; + _dynamicsTabController.scrollController.jumpTo(0); + _dynamicsTabController.onReload(); }); dynamicsWaterfallFlow = GStorage.setting .get(SettingBoxKey.dynamicsWaterfallFlow, defaultValue: true); @@ -74,7 +80,7 @@ class _DynamicsTabPageState extends State @override void dispose() { - scrollController.removeListener(() {}); + _dynamicsTabController.scrollController.removeListener(() {}); dynamicsController.mid.close(); super.dispose(); } @@ -84,147 +90,118 @@ class _DynamicsTabPageState extends State super.build(context); // print(widget.dynamicsType + widget.mid.value.toString()); return RefreshIndicator( - // key: - // ValueKey(widget.dynamicsType + widget.mid.value.toString()), - onRefresh: () async { - dynamicsWaterfallFlow = GStorage.setting - .get(SettingBoxKey.dynamicsWaterfallFlow, defaultValue: true); - await Future.wait([ - _dynamicsTabController.onRefresh(), - dynamicsController.queryFollowUp() - ]); - }, - child: CustomScrollView( - physics: const AlwaysScrollableScrollPhysics(), - controller: _dynamicsTabController.scrollController, - slivers: [ - FutureBuilder( - future: _futureBuilderFuture, - builder: (context, snapshot) { - // print(snapshot); - // print(widget.dynamicsType + "${widget.mid?.value}"); - if (snapshot.connectionState == ConnectionState.done) { - if (snapshot.data == null) { - return const NoData(); - } - Map data = snapshot.data; - // print('data: $data'); - if (data['status']) { - List list = - _dynamicsTabController.dynamicsList; - // print('list: $list'); - return Obx( - () { - if (list.isEmpty) { - if (_dynamicsTabController.isLoadingMore.value) { - return skeleton(); - } - return const NoData(); - } - if (!dynamicsWaterfallFlow) { - return SliverCrossAxisGroup( - slivers: [ - const SliverFillRemaining(), - SliverConstrainedCrossAxis( - maxExtent: Grid.maxRowWidth * 2, - sliver: SliverList( - delegate: SliverChildBuilderDelegate( - (context, index) { - if ((dynamicsController - .tabController.index == - 4 && - dynamicsController.mid.value != - -1) || - !dynamicsController.tempBannedList - .contains(list[index] - .modules - ?.moduleAuthor - ?.mid)) { - return DynamicPanel( - item: list[index]); - } - return const SizedBox(); - }, - childCount: list.length, - ), - )), - const SliverFillRemaining(), - ], - ); - } - return SliverWaterfallFlow.extent( - maxCrossAxisExtent: Grid.maxRowWidth * 2, - //cacheExtent: 0.0, - crossAxisSpacing: StyleString.cardSpace / 2, - mainAxisSpacing: StyleString.cardSpace / 2, - - lastChildLayoutTypeBuilder: (index) => - index == list.length - ? LastChildLayoutType.foot - : LastChildLayoutType.none, - children: [ - if (dynamicsController.tabController.index == 4 && - dynamicsController.mid.value != -1) ...[ - for (var i in list) DynamicPanel(item: i), - ] else ...[ - for (var i in list) - if (!dynamicsController.tempBannedList - .contains(i.modules?.moduleAuthor?.mid)) - DynamicPanel(item: i), - ] - ], - ); - }, - ); - } else { - return HttpError( - errMsg: data['msg'], - fn: () { - // setState(() { - _futureBuilderFuture = - _dynamicsTabController.queryFollowDynamic( - 'init', - widget.dynamicsType, - dynamicsController.mid.value); - // }); - dynamicsController.onRefresh(); - }, - ); - } - } else { - // 骨架屏 - return skeleton(); - } - }, - ) - ], - )); + // key: + // ValueKey(widget.dynamicsType + widget.mid.value.toString()), + onRefresh: () async { + dynamicsWaterfallFlow = GStorage.setting + .get(SettingBoxKey.dynamicsWaterfallFlow, defaultValue: true); + await _dynamicsTabController.onRefresh(); + await dynamicsController.queryFollowUp(); + }, + child: CustomScrollView( + physics: const AlwaysScrollableScrollPhysics(), + controller: _dynamicsTabController.scrollController, + slivers: [ + Obx(() => _buildBody(_dynamicsTabController.loadingState.value)), + ], + ), + ); } Widget skeleton() { if (!dynamicsWaterfallFlow) { - return SliverCrossAxisGroup(slivers: [ - const SliverFillRemaining(), - SliverConstrainedCrossAxis( - maxExtent: Grid.maxRowWidth * 2, - sliver: SliverList( - delegate: SliverChildBuilderDelegate((context, index) { - return const DynamicCardSkeleton(); - }, childCount: 10)), - ), - const SliverFillRemaining() - ]); + return SliverCrossAxisGroup( + slivers: [ + const SliverFillRemaining(), + SliverConstrainedCrossAxis( + maxExtent: Grid.maxRowWidth * 2, + sliver: SliverList( + delegate: SliverChildBuilderDelegate( + (context, index) { + return const DynamicCardSkeleton(); + }, + childCount: 10, + ), + ), + ), + const SliverFillRemaining() + ], + ); } return SliverGrid( gridDelegate: SliverGridDelegateWithExtentAndRatio( - crossAxisSpacing: StyleString.cardSpace / 2, - mainAxisSpacing: StyleString.cardSpace / 2, - maxCrossAxisExtent: Grid.maxRowWidth * 2, - childAspectRatio: StyleString.aspectRatio, - mainAxisExtent: 50), - delegate: SliverChildBuilderDelegate((context, index) { - return const DynamicCardSkeleton(); - }, childCount: 10), + crossAxisSpacing: StyleString.cardSpace / 2, + mainAxisSpacing: StyleString.cardSpace / 2, + maxCrossAxisExtent: Grid.maxRowWidth * 2, + childAspectRatio: StyleString.aspectRatio, + mainAxisExtent: 50, + ), + delegate: SliverChildBuilderDelegate( + (context, index) { + return const DynamicCardSkeleton(); + }, + childCount: 10, + ), ); } + + Widget _buildBody(LoadingState loadingState) { + return loadingState is Success + ? dynamicsWaterfallFlow + ? SliverWaterfallFlow.extent( + maxCrossAxisExtent: Grid.maxRowWidth * 2, + //cacheExtent: 0.0, + crossAxisSpacing: StyleString.cardSpace / 2, + mainAxisSpacing: StyleString.cardSpace / 2, + + lastChildLayoutTypeBuilder: (index) => + index == loadingState.response.length + ? LastChildLayoutType.foot + : LastChildLayoutType.none, + children: [ + if (dynamicsController.tabController.index == 4 && + dynamicsController.mid.value != -1) ...[ + for (var i in loadingState.response) DynamicPanel(item: i), + ] else ...[ + for (var i in loadingState.response) + if (!dynamicsController.tempBannedList + .contains(i.modules?.moduleAuthor?.mid)) + DynamicPanel(item: i), + ] + ], + ) + : SliverCrossAxisGroup( + slivers: [ + const SliverFillRemaining(), + SliverConstrainedCrossAxis( + maxExtent: Grid.maxRowWidth * 2, + sliver: SliverList( + delegate: SliverChildBuilderDelegate( + (context, index) { + if ((dynamicsController.tabController.index == 4 && + dynamicsController.mid.value != -1) || + !dynamicsController.tempBannedList.contains( + loadingState.response[index].modules + ?.moduleAuthor?.mid)) { + return DynamicPanel( + item: loadingState.response[index]); + } + return const SizedBox(); + }, + childCount: loadingState.response.length, + ), + ), + ), + const SliverFillRemaining(), + ], + ) + : loadingState is Empty + ? const NoData() + : loadingState is Error + ? HttpError( + errMsg: loadingState.errMsg, + fn: _dynamicsTabController.onReload, + ) + : skeleton(); + } } diff --git a/lib/pages/dynamics/view.dart b/lib/pages/dynamics/view.dart index 7a10e84b..b31332ee 100644 --- a/lib/pages/dynamics/view.dart +++ b/lib/pages/dynamics/view.dart @@ -4,7 +4,6 @@ import 'package:PiliPalaX/models/common/dynamics_type.dart'; import 'package:PiliPalaX/models/common/up_panel_position.dart'; import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart';