diff --git a/lib/pages/common/play_all_btn_mixin.dart b/lib/pages/common/play_all_btn_mixin.dart new file mode 100644 index 000000000..a93c78a24 --- /dev/null +++ b/lib/pages/common/play_all_btn_mixin.dart @@ -0,0 +1,40 @@ +import 'package:PiliPlus/utils/storage.dart'; +import 'package:PiliPlus/utils/storage_key.dart'; +import 'package:PiliPlus/utils/storage_pref.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get_rx/get_rx.dart'; + +mixin PlayAllBtnMixin { + late double dx = 0; + late final RxBool isPlayAll = Pref.enablePlayAll.obs; + + void setIsPlayAll(bool isPlayAll) { + if (this.isPlayAll.value == isPlayAll) return; + this.isPlayAll.value = isPlayAll; + GStorage.setting.put(SettingBoxKey.enablePlayAll, isPlayAll); + } + + Widget playAllBtn(VoidCallback onPlayAll) { + return AnimatedSlide( + offset: isPlayAll.value ? Offset.zero : const Offset(0.75, 0), + duration: const Duration(milliseconds: 120), + child: GestureDetector( + onHorizontalDragDown: (details) => dx = details.localPosition.dx, + onHorizontalDragStart: (details) => setIsPlayAll( + details.localPosition.dx < dx, + ), + child: FloatingActionButton.extended( + onPressed: () { + if (isPlayAll.value) { + onPlayAll(); + } else { + setIsPlayAll(true); + } + }, + label: const Text('播放全部'), + icon: const Icon(Icons.playlist_play), + ), + ), + ); + } +} diff --git a/lib/pages/fav_detail/controller.dart b/lib/pages/fav_detail/controller.dart index 21e7af6d4..943848b60 100644 --- a/lib/pages/fav_detail/controller.dart +++ b/lib/pages/fav_detail/controller.dart @@ -12,13 +12,11 @@ import 'package:PiliPlus/models_new/video/video_detail/page.dart'; import 'package:PiliPlus/pages/common/common_list_controller.dart'; import 'package:PiliPlus/pages/common/multi_select/base.dart'; import 'package:PiliPlus/pages/common/multi_select/multi_select_controller.dart'; +import 'package:PiliPlus/pages/common/play_all_btn_mixin.dart'; import 'package:PiliPlus/pages/fav_sort/view.dart'; import 'package:PiliPlus/utils/accounts.dart'; import 'package:PiliPlus/utils/extension/scroll_controller_ext.dart'; import 'package:PiliPlus/utils/page_utils.dart'; -import 'package:PiliPlus/utils/storage.dart'; -import 'package:PiliPlus/utils/storage_key.dart'; -import 'package:PiliPlus/utils/storage_pref.dart'; import 'package:collection/collection.dart'; import 'package:flutter/widgets.dart' show Text, ValueChanged; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; @@ -79,7 +77,7 @@ mixin BaseFavController class FavDetailController extends MultiSelectController - with BaseFavController { + with BaseFavController, PlayAllBtnMixin { @override late int mediaId; final Rx folderInfo = FavFolderInfo().obs; @@ -91,15 +89,6 @@ class FavDetailController late final account = Accounts.main; - late double dx = 0; - late final RxBool isPlayAll = Pref.enablePlayAll.obs; - - void setIsPlayAll(bool isPlayAll) { - if (this.isPlayAll.value == isPlayAll) return; - this.isPlayAll.value = isPlayAll; - GStorage.setting.put(SettingBoxKey.enablePlayAll, isPlayAll); - } - @override void onInit() { super.onInit(); diff --git a/lib/pages/fav_detail/view.dart b/lib/pages/fav_detail/view.dart index 1ac9bf171..a39d891f4 100644 --- a/lib/pages/fav_detail/view.dart +++ b/lib/pages/fav_detail/view.dart @@ -93,32 +93,8 @@ class _FavDetailPageState extends State with GridMixin { padding: const .only(right: kFloatingActionButtonMargin), child: Obx( () => _favDetailController.folderInfo.value.mediaCount > 0 - ? AnimatedSlide( - offset: _favDetailController.isPlayAll.value - ? Offset.zero - : const Offset(0.75, 0), - duration: const Duration(milliseconds: 120), - child: GestureDetector( - onHorizontalDragDown: (details) => - _favDetailController.dx = - details.localPosition.dx, - onHorizontalDragStart: (details) => - _favDetailController.setIsPlayAll( - details.localPosition.dx < - _favDetailController.dx, - ), - child: FloatingActionButton.extended( - onPressed: () { - if (_favDetailController.isPlayAll.value) { - _favDetailController.toViewPlayAll(); - } else { - _favDetailController.setIsPlayAll(true); - } - }, - label: const Text('播放全部'), - icon: const Icon(Icons.playlist_play), - ), - ), + ? _favDetailController.playAllBtn( + _favDetailController.toViewPlayAll, ) : const SizedBox.shrink(), ), diff --git a/lib/pages/later/base_controller.dart b/lib/pages/later/base_controller.dart index d25285e2a..29547d70e 100644 --- a/lib/pages/later/base_controller.dart +++ b/lib/pages/later/base_controller.dart @@ -1,21 +1,10 @@ import 'package:PiliPlus/models/common/later_view_type.dart'; -import 'package:PiliPlus/utils/storage.dart'; -import 'package:PiliPlus/utils/storage_key.dart'; -import 'package:PiliPlus/utils/storage_pref.dart'; +import 'package:PiliPlus/pages/common/play_all_btn_mixin.dart'; import 'package:get/get.dart'; -class LaterBaseController extends GetxController { +class LaterBaseController extends GetxController with PlayAllBtnMixin { RxBool enableMultiSelect = false.obs; RxInt checkedCount = 0.obs; RxList counts = List.filled(LaterViewType.values.length, -1).obs; - - late double dx = 0; - late final RxBool isPlayAll = Pref.enablePlayAll.obs; - - void setIsPlayAll(bool isPlayAll) { - if (this.isPlayAll.value == isPlayAll) return; - this.isPlayAll.value = isPlayAll; - GStorage.setting.put(SettingBoxKey.enablePlayAll, isPlayAll); - } } diff --git a/lib/pages/later/view.dart b/lib/pages/later/view.dart index 02687b35f..a8b1cfc59 100644 --- a/lib/pages/later/view.dart +++ b/lib/pages/later/view.dart @@ -126,31 +126,7 @@ class _LaterPageState extends State padding: const .only(right: kFloatingActionButtonMargin), child: Obx( () => currCtr().loadingState.value.isSuccess - ? AnimatedSlide( - offset: _baseCtr.isPlayAll.value - ? Offset.zero - : const Offset(0.75, 0), - duration: const Duration(milliseconds: 120), - child: GestureDetector( - onHorizontalDragDown: (details) => - _baseCtr.dx = details.localPosition.dx, - onHorizontalDragStart: (details) => - _baseCtr.setIsPlayAll( - details.localPosition.dx < _baseCtr.dx, - ), - child: FloatingActionButton.extended( - onPressed: () { - if (_baseCtr.isPlayAll.value) { - currCtr().toViewPlayAll(); - } else { - _baseCtr.setIsPlayAll(true); - } - }, - label: const Text('播放全部'), - icon: const Icon(Icons.playlist_play), - ), - ), - ) + ? _baseCtr.playAllBtn(currCtr().toViewPlayAll) : const SizedBox.shrink(), ), ), diff --git a/lib/pages/popular_precious/view.dart b/lib/pages/popular_precious/view.dart index ad61bb5bc..f4236d9e8 100644 --- a/lib/pages/popular_precious/view.dart +++ b/lib/pages/popular_precious/view.dart @@ -6,6 +6,7 @@ import 'package:PiliPlus/common/widgets/view_sliver_safe_area.dart'; import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/models/common/video/source_type.dart'; import 'package:PiliPlus/models/model_hot_video_item.dart'; +import 'package:PiliPlus/pages/common/play_all_btn_mixin.dart'; import 'package:PiliPlus/pages/popular_precious/controller.dart'; import 'package:PiliPlus/utils/grid.dart'; import 'package:PiliPlus/utils/page_utils.dart'; @@ -20,27 +21,70 @@ class PopularPreciousPage extends StatefulWidget { } class _PopularPreciousPageState extends State - with GridMixin { + with GridMixin, PlayAllBtnMixin { final _controller = Get.put(PopularPreciousController()); @override Widget build(BuildContext context) { + final padding = MediaQuery.viewPaddingOf(context); return scaffold( appBar: AppBar(title: const Text('入站必刷')), - body: refreshIndicator( - onRefresh: _controller.onRefresh, - child: CustomScrollView( - physics: const AlwaysScrollableScrollPhysics(), - slivers: [ - ViewSliverSafeArea( - sliver: Obx(() => _buildBody(_controller.loadingState.value)), + body: Stack( + clipBehavior: .none, + children: [ + refreshIndicator( + onRefresh: _controller.onRefresh, + child: CustomScrollView( + physics: const AlwaysScrollableScrollPhysics(), + slivers: [ + ViewSliverSafeArea( + sliver: Obx(() => _buildBody(_controller.loadingState.value)), + ), + ], ), - ], - ), + ), + Positioned( + right: padding.right, + bottom: padding.bottom + kFloatingActionButtonMargin, + child: Padding( + padding: const .only(right: kFloatingActionButtonMargin), + child: Obx(() { + if (_controller.loadingState.value case Success( + :final response, + )) { + return playAllBtn(() => toVideoPage(response!.first)); + } + return const SizedBox.shrink(); + }), + ), + ), + ], ), ); } + void toVideoPage( + HotVideoItemModel item, { + int index = 0, + bool isPlayAll = true, + }) { + PageUtils.toVideoPage( + bvid: item.bvid, + cid: item.cid!, + dimension: item.dimension, + extraArguments: isPlayAll + ? { + 'sourceType': SourceType.playlist, + 'favTitle': '入站必刷', + 'mediaId': _controller.mediaId, + 'desc': true, + 'oid': item.aid, + 'isContinuePlaying': index != 0, + } + : null, + ); + } + Widget _buildBody(LoadingState?> value) { switch (value) { case Loading(): @@ -53,21 +97,11 @@ class _PopularPreciousPageState extends State final item = response[index]; return VideoCardH( videoItem: item, - onTap: () { - PageUtils.toVideoPage( - bvid: item.bvid, - cid: item.cid!, - dimension: item.dimension, - extraArguments: { - 'sourceType': SourceType.playlist, - 'favTitle': '入站必刷', - 'mediaId': _controller.mediaId, - 'desc': true, - 'oid': item.aid, - 'isContinuePlaying': index != 0, - }, - ); - }, + onTap: () => toVideoPage( + item, + index: index, + isPlayAll: isPlayAll.value, + ), ); }, ); diff --git a/lib/pages/popular_series/view.dart b/lib/pages/popular_series/view.dart index e7db49798..6c0b65772 100644 --- a/lib/pages/popular_series/view.dart +++ b/lib/pages/popular_series/view.dart @@ -11,6 +11,7 @@ import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/models/common/video/source_type.dart'; import 'package:PiliPlus/models/model_hot_video_item.dart'; import 'package:PiliPlus/models_new/popular/popular_series_one/config.dart'; +import 'package:PiliPlus/pages/common/play_all_btn_mixin.dart'; import 'package:PiliPlus/pages/popular_series/controller.dart'; import 'package:PiliPlus/utils/grid.dart'; import 'package:PiliPlus/utils/page_utils.dart'; @@ -24,11 +25,13 @@ class PopularSeriesPage extends StatefulWidget { State createState() => _PopularSeriesPageState(); } -class _PopularSeriesPageState extends State with GridMixin { +class _PopularSeriesPageState extends State + with GridMixin, PlayAllBtnMixin { final _controller = Get.put(PopularSeriesController()); @override Widget build(BuildContext context) { + final padding = MediaQuery.viewPaddingOf(context); return scaffold( appBar: AppBar( title: Obx(() { @@ -39,20 +42,63 @@ class _PopularSeriesPageState extends State with GridMixin { return const Text('每周必看'); }), ), - body: refreshIndicator( - onRefresh: _controller.onRefresh, - child: CustomScrollView( - physics: ReloadScrollPhysics(controller: _controller), - slivers: [ - ViewSliverSafeArea( - sliver: Obx(() => _buildBody(_controller.loadingState.value)), + body: Stack( + clipBehavior: .none, + children: [ + refreshIndicator( + onRefresh: _controller.onRefresh, + child: CustomScrollView( + physics: ReloadScrollPhysics(controller: _controller), + slivers: [ + ViewSliverSafeArea( + sliver: Obx(() => _buildBody(_controller.loadingState.value)), + ), + ], ), - ], - ), + ), + Positioned( + right: padding.right, + bottom: padding.bottom + kFloatingActionButtonMargin, + child: Padding( + padding: const .only(right: kFloatingActionButtonMargin), + child: Obx(() { + if (_controller.loadingState.value case Success( + :final response, + )) { + return playAllBtn(() => toVideoPage(response!.first)); + } + return const SizedBox.shrink(); + }), + ), + ), + ], ), ); } + void toVideoPage( + HotVideoItemModel item, { + int index = 0, + bool isPlayAll = true, + }) { + final config = _controller.config.value; + PageUtils.toVideoPage( + bvid: item.bvid, + cid: item.cid!, + dimension: item.dimension, + extraArguments: isPlayAll + ? { + 'sourceType': SourceType.playlist, + 'favTitle': '每周必看 ${config?.label ?? ''}', + 'mediaId': config?.mediaId, + 'desc': true, + 'oid': item.aid, + 'isContinuePlaying': index != 0, + } + : null, + ); + } + Widget _buildBody(LoadingState?> value) { switch (value) { case Loading(): @@ -67,22 +113,8 @@ class _PopularSeriesPageState extends State with GridMixin { final item = response[index]; return VideoCardH( videoItem: item, - onTap: () { - final config = _controller.config.value; - PageUtils.toVideoPage( - bvid: item.bvid, - cid: item.cid!, - dimension: item.dimension, - extraArguments: { - 'sourceType': SourceType.playlist, - 'favTitle': '每周必看 ${config?.label ?? ''}', - 'mediaId': config?.mediaId, - 'desc': true, - 'oid': item.aid, - 'isContinuePlaying': index != 0, - }, - ); - }, + onTap: () => + toVideoPage(item, index: index, isPlayAll: isPlayAll.value), ); }, );