diff --git a/lib/models/common/tab_type.dart b/lib/models/common/tab_type.dart index 5c131eee..12dc4004 100644 --- a/lib/models/common/tab_type.dart +++ b/lib/models/common/tab_type.dart @@ -1,3 +1,4 @@ +import 'package:PiliPlus/pages/live/view.dart'; import 'package:PiliPlus/pages/rank/index.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -22,7 +23,7 @@ List get tabsConfig => [ 'label': '直播', 'type': TabType.live, 'ctr': Get.find, - 'page': const RcmdPage(tabType: TabType.live), + 'page': const LivePage(), }, { 'icon': const Icon( @@ -32,7 +33,7 @@ List get tabsConfig => [ 'label': '推荐', 'type': TabType.rcmd, 'ctr': Get.find, - 'page': const RcmdPage(tabType: TabType.rcmd), + 'page': const RcmdPage(), }, { 'icon': const Icon( diff --git a/lib/pages/live/view.dart b/lib/pages/live/view.dart new file mode 100644 index 00000000..d5838fb3 --- /dev/null +++ b/lib/pages/live/view.dart @@ -0,0 +1,129 @@ +import 'dart:async'; + +import 'package:PiliPlus/common/widgets/refresh_indicator.dart'; +import 'package:PiliPlus/http/loading_state.dart'; +import 'package:PiliPlus/pages/live/controller.dart'; +import 'package:PiliPlus/pages/live/widgets/live_item.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:get/get.dart'; +import 'package:PiliPlus/common/constants.dart'; +import 'package:PiliPlus/common/skeleton/video_card_v.dart'; +import 'package:PiliPlus/common/widgets/http_error.dart'; +import 'package:PiliPlus/pages/home/index.dart'; +import 'package:PiliPlus/pages/main/index.dart'; + +import '../../utils/grid.dart'; + +class LivePage extends StatefulWidget { + const LivePage({super.key}); + + @override + State createState() => _LivePageState(); +} + +class _LivePageState extends State + with AutomaticKeepAliveClientMixin { + late final _controller = Get.put(LiveController()); + + @override + bool get wantKeepAlive => true; + + @override + void initState() { + super.initState(); + StreamController mainStream = + Get.find().bottomBarStream; + StreamController searchBarStream = + Get.find().searchBarStream; + _controller.scrollController.addListener( + () { + final ScrollDirection direction = + _controller.scrollController.position.userScrollDirection; + if (direction == ScrollDirection.forward) { + mainStream.add(true); + searchBarStream.add(true); + } else if (direction == ScrollDirection.reverse) { + mainStream.add(false); + searchBarStream.add(false); + } + }, + ); + } + + @override + void dispose() { + _controller.scrollController.removeListener(() {}); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + super.build(context); + return Container( + clipBehavior: Clip.hardEdge, + margin: const EdgeInsets.only( + left: StyleString.safeSpace, right: StyleString.safeSpace), + decoration: const BoxDecoration( + borderRadius: BorderRadius.all(StyleString.imgRadius), + ), + child: refreshIndicator( + onRefresh: () async { + await _controller.onRefresh(); + }, + child: CustomScrollView( + controller: _controller.scrollController, + physics: const AlwaysScrollableScrollPhysics(), + slivers: [ + SliverPadding( + padding: EdgeInsets.only( + top: StyleString.cardSpace, + bottom: MediaQuery.paddingOf(context).bottom, + ), + sliver: Obx( + () => _controller.loadingState.value is Loading || + _controller.loadingState.value is Success + ? contentGrid(_controller.loadingState.value) + : HttpError( + errMsg: _controller.loadingState.value is Error + ? (_controller.loadingState.value as Error).errMsg + : '没有相关数据', + callback: _controller.onReload, + ), + ), + ), + ], + ), + ), + ); + } + + Widget contentGrid(LoadingState loadingState) { + return SliverGrid( + gridDelegate: SliverGridDelegateWithExtentAndRatio( + // 行间距 + mainAxisSpacing: StyleString.cardSpace, + // 列间距 + crossAxisSpacing: StyleString.cardSpace, + // 最大宽度 + maxCrossAxisExtent: Grid.maxRowWidth, + childAspectRatio: StyleString.aspectRatio, + mainAxisExtent: MediaQuery.textScalerOf(context).scale(90), + ), + delegate: SliverChildBuilderDelegate( + (BuildContext context, int index) { + if (loadingState is Success && + index == loadingState.response.length - 1) { + _controller.onLoadMore(); + } + return loadingState is Success + ? LiveCardV( + liveItem: loadingState.response[index], + ) + : const VideoCardVSkeleton(); + }, + childCount: loadingState is Success ? loadingState.response.length : 10, + ), + ); + } +} diff --git a/lib/pages/rcmd/view.dart b/lib/pages/rcmd/view.dart index 5c875546..4a430ca4 100644 --- a/lib/pages/rcmd/view.dart +++ b/lib/pages/rcmd/view.dart @@ -2,10 +2,6 @@ import 'dart:async'; import 'package:PiliPlus/common/widgets/refresh_indicator.dart'; import 'package:PiliPlus/http/loading_state.dart'; -import 'package:PiliPlus/models/common/tab_type.dart'; -import 'package:PiliPlus/pages/common/common_controller.dart'; -import 'package:PiliPlus/pages/live/controller.dart'; -import 'package:PiliPlus/pages/live/widgets/live_item.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:get/get.dart'; @@ -20,9 +16,7 @@ import '../../utils/grid.dart'; import 'controller.dart'; class RcmdPage extends StatefulWidget { - const RcmdPage({super.key, required this.tabType}); - - final TabType tabType; + const RcmdPage({super.key}); @override State createState() => _RcmdPageState(); @@ -30,9 +24,7 @@ class RcmdPage extends StatefulWidget { class _RcmdPageState extends State with AutomaticKeepAliveClientMixin { - late final CommonController _controller = widget.tabType == TabType.rcmd - ? Get.put(RcmdController()) - : Get.put(LiveController()); + late final _controller = Get.put(RcmdController()); @override bool get wantKeepAlive => true; @@ -124,70 +116,59 @@ class _RcmdPageState extends State index == loadingState.response.length - 1) { _controller.onLoadMore(); } - if (widget.tabType == TabType.rcmd) { - if (loadingState is Success) { - RcmdController ctr = _controller as RcmdController; - if (ctr.savedRcmdTip) { - if (ctr.lastRefreshAt == index) { - return GestureDetector( - onTap: () { - _controller.animateToTop(); - _controller.onRefresh(); - }, - child: Card( - child: Container( - alignment: Alignment.center, - padding: const EdgeInsets.symmetric(horizontal: 10), - child: Text( - '上次看到这里\n点击刷新', - textAlign: TextAlign.center, - style: TextStyle( - color: - Theme.of(context).colorScheme.onSurfaceVariant, - ), + if (loadingState is Success) { + if (_controller.lastRefreshAt != null) { + if (_controller.lastRefreshAt == index) { + return GestureDetector( + onTap: () { + _controller.animateToTop(); + _controller.onRefresh(); + }, + child: Card( + child: Container( + alignment: Alignment.center, + padding: const EdgeInsets.symmetric(horizontal: 10), + child: Text( + '上次看到这里\n点击刷新', + textAlign: TextAlign.center, + style: TextStyle( + color: Theme.of(context).colorScheme.onSurfaceVariant, ), ), ), - ); - } - int actualIndex = ctr.lastRefreshAt == null - ? index - : index > ctr.lastRefreshAt! - ? index - 1 - : index; - return VideoCardV( - videoItem: loadingState.response[actualIndex], - onRemove: () { - if (ctr.lastRefreshAt != null && - actualIndex < ctr.lastRefreshAt!) { - ctr.lastRefreshAt = ctr.lastRefreshAt! - 1; - } - _controller.loadingState.value = LoadingState.success( - (loadingState.response as List)..removeAt(actualIndex)); - }, - ); - } else { - return VideoCardV( - videoItem: loadingState.response[index], - onRemove: () { - _controller.loadingState.value = LoadingState.success( - (loadingState.response as List)..removeAt(index)); - }, + ), ); } + int actualIndex = _controller.lastRefreshAt == null + ? index + : index > _controller.lastRefreshAt! + ? index - 1 + : index; + return VideoCardV( + videoItem: loadingState.response[actualIndex], + onRemove: () { + if (_controller.lastRefreshAt != null && + actualIndex < _controller.lastRefreshAt!) { + _controller.lastRefreshAt = _controller.lastRefreshAt! - 1; + } + _controller.loadingState.value = LoadingState.success( + (loadingState.response as List)..removeAt(actualIndex)); + }, + ); + } else { + return VideoCardV( + videoItem: loadingState.response[index], + onRemove: () { + _controller.loadingState.value = LoadingState.success( + (loadingState.response as List)..removeAt(index)); + }, + ); } - return const VideoCardVSkeleton(); - } else { - return loadingState is Success - ? LiveCardV( - liveItem: loadingState.response[index], - ) - : const VideoCardVSkeleton(); } + return const VideoCardVSkeleton(); }, childCount: loadingState is Success - ? widget.tabType == TabType.rcmd && - (_controller as RcmdController).lastRefreshAt != null + ? _controller.lastRefreshAt != null ? loadingState.response.length + 1 : loadingState.response.length : 10,