mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-04-20 03:06:59 +08:00
Compare commits
2 Commits
ed66a4655b
...
d6579b29ae
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d6579b29ae | ||
|
|
8a8aa6c1e0 |
@@ -2,10 +2,12 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' hide PopScope;
|
||||
import 'package:get/get_core/src/get_main.dart';
|
||||
import 'package:get/get_navigation/src/extension_navigation.dart';
|
||||
|
||||
abstract class PopScopeState<T extends StatefulWidget> extends State<T>
|
||||
implements PopEntry<T> {
|
||||
implements PopEntry<Object> {
|
||||
ModalRoute<dynamic>? _route;
|
||||
|
||||
@override
|
||||
@@ -14,31 +16,60 @@ abstract class PopScopeState<T extends StatefulWidget> extends State<T>
|
||||
@override
|
||||
late final ValueNotifier<bool> canPopNotifier;
|
||||
|
||||
void initCanPopNotifier() {
|
||||
canPopNotifier = ValueNotifier<bool>(false);
|
||||
}
|
||||
bool get initCanPop => true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
initCanPopNotifier();
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
final ModalRoute<dynamic>? nextRoute = ModalRoute.of(context);
|
||||
if (nextRoute != _route) {
|
||||
_route?.unregisterPopEntry(this);
|
||||
_route = nextRoute;
|
||||
_route?.registerPopEntry(this);
|
||||
}
|
||||
canPopNotifier = ValueNotifier<bool>(initCanPop);
|
||||
_route = (Get.routing.route as ModalRoute)..registerPopEntry(this);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_route?.unregisterPopEntry(this);
|
||||
_route = null;
|
||||
canPopNotifier.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
// ignore: camel_case_types
|
||||
typedef popScope = PopScope;
|
||||
|
||||
class PopScope extends StatefulWidget {
|
||||
const PopScope({
|
||||
super.key,
|
||||
required this.child,
|
||||
this.canPop = true,
|
||||
required this.onPopInvokedWithResult,
|
||||
});
|
||||
|
||||
final Widget child;
|
||||
|
||||
final PopInvokedWithResultCallback<Object> onPopInvokedWithResult;
|
||||
|
||||
final bool canPop;
|
||||
|
||||
@override
|
||||
State<PopScope> createState() => _PopScopeState();
|
||||
}
|
||||
|
||||
class _PopScopeState<T extends PopScope> extends PopScopeState<T> {
|
||||
@override
|
||||
bool get initCanPop => widget.canPop;
|
||||
|
||||
@override
|
||||
void onPopInvokedWithResult(bool didPop, Object? result) {
|
||||
widget.onPopInvokedWithResult(didPop, result);
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(T oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
canPopNotifier.value = widget.canPop;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => widget.child;
|
||||
}
|
||||
|
||||
@@ -108,6 +108,7 @@ class ImageGridView extends StatelessWidget {
|
||||
PageUtils.imageView(
|
||||
initialPage: index,
|
||||
imgList: imgList,
|
||||
tag: hashCode.toString(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -255,7 +256,7 @@ class ImageGridView extends StatelessWidget {
|
||||
);
|
||||
if (!item.isLongPic) {
|
||||
child = Hero(
|
||||
tag: item.url,
|
||||
tag: '${item.url}$hashCode',
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ class GalleryViewer extends StatefulWidget {
|
||||
required this.sources,
|
||||
this.initIndex = 0,
|
||||
this.onPageChanged,
|
||||
this.tag = '',
|
||||
});
|
||||
|
||||
final double minScale;
|
||||
@@ -63,6 +64,7 @@ class GalleryViewer extends StatefulWidget {
|
||||
final List<SourceModel> sources;
|
||||
final int initIndex;
|
||||
final ValueChanged<int>? onPageChanged;
|
||||
final String tag;
|
||||
|
||||
@override
|
||||
State<GalleryViewer> createState() => _GalleryViewerState();
|
||||
@@ -472,7 +474,7 @@ class _GalleryViewerState extends State<GalleryViewer>
|
||||
: const SizedBox.shrink(),
|
||||
);
|
||||
}
|
||||
return Hero(tag: item.url, child: child);
|
||||
return Hero(tag: '${item.url}${widget.tag}', child: child);
|
||||
}
|
||||
|
||||
void _onTap() {
|
||||
|
||||
@@ -3,11 +3,11 @@ import 'package:flutter/material.dart';
|
||||
class KeepAliveWrapper extends StatefulWidget {
|
||||
const KeepAliveWrapper({
|
||||
super.key,
|
||||
required this.builder,
|
||||
required this.child,
|
||||
this.wantKeepAlive = true,
|
||||
});
|
||||
|
||||
final WidgetBuilder builder;
|
||||
final Widget child;
|
||||
final bool wantKeepAlive;
|
||||
|
||||
@override
|
||||
@@ -19,7 +19,7 @@ class _KeepAliveWrapperState extends State<KeepAliveWrapper>
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
return widget.builder(context);
|
||||
return widget.child;
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
21
lib/common/widgets/route_aware_mixin.dart
Normal file
21
lib/common/widgets/route_aware_mixin.dart
Normal file
@@ -0,0 +1,21 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:get/get_core/src/get_main.dart';
|
||||
import 'package:get/get_navigation/src/extension_navigation.dart';
|
||||
import 'package:get/get_navigation/src/routes/default_route.dart'
|
||||
show GetPageRoute;
|
||||
|
||||
final routeObserver = RouteObserver<GetPageRoute>();
|
||||
|
||||
mixin RouteAwareMixin<T extends StatefulWidget> on State<T>, RouteAware {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
routeObserver.subscribe(this, Get.routing.route as GetPageRoute);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
routeObserver.unsubscribe(this);
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import 'package:PiliPlus/build_config.dart';
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/back_detector.dart';
|
||||
import 'package:PiliPlus/common/widgets/custom_toast.dart';
|
||||
import 'package:PiliPlus/common/widgets/route_aware_mixin.dart';
|
||||
import 'package:PiliPlus/common/widgets/scale_app.dart';
|
||||
import 'package:PiliPlus/common/widgets/scroll_behavior.dart';
|
||||
import 'package:PiliPlus/http/init.dart';
|
||||
@@ -18,7 +19,6 @@ import 'package:PiliPlus/utils/date_utils.dart';
|
||||
import 'package:PiliPlus/utils/extension/iterable_ext.dart';
|
||||
import 'package:PiliPlus/utils/extension/theme_ext.dart';
|
||||
import 'package:PiliPlus/utils/json_file_handler.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/path_utils.dart';
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:PiliPlus/utils/request_utils.dart';
|
||||
@@ -296,7 +296,7 @@ class MyApp extends StatelessWidget {
|
||||
builder: _builder,
|
||||
),
|
||||
navigatorObservers: [
|
||||
PageUtils.routeObserver,
|
||||
routeObserver,
|
||||
FlutterSmartDialog.observer,
|
||||
],
|
||||
scrollBehavior: PlatformUtils.isDesktop
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:PiliPlus/common/widgets/appbar/appbar.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/pop_scope.dart';
|
||||
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
|
||||
import 'package:PiliPlus/common/widgets/view_sliver_safe_area.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
@@ -20,7 +21,7 @@ abstract class CommonSearchPageState<S extends StatefulWidget, R, T>
|
||||
if (controller case final MultiSelectBase multiCtr) {
|
||||
return Obx(() {
|
||||
final enableMultiSelect = multiCtr.enableMultiSelect.value;
|
||||
return PopScope(
|
||||
return popScope(
|
||||
canPop: !enableMultiSelect,
|
||||
onPopInvokedWithResult: (didPop, result) {
|
||||
if (enableMultiSelect) {
|
||||
|
||||
@@ -64,7 +64,7 @@ class _DanmakuBlockPageState extends State<DanmakuBlockPage> {
|
||||
children: DmBlockType.values
|
||||
.map(
|
||||
(e) => KeepAliveWrapper(
|
||||
builder: (context) => Obx(
|
||||
child: Obx(
|
||||
() => tabViewBuilder(e.index, _controller.rules[e.index]),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'dart:async';
|
||||
|
||||
import 'package:PiliPlus/common/widgets/appbar/appbar.dart';
|
||||
import 'package:PiliPlus/common/widgets/dialog/dialog.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/pop_scope.dart';
|
||||
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
|
||||
import 'package:PiliPlus/common/widgets/view_sliver_safe_area.dart';
|
||||
import 'package:PiliPlus/models_new/download/bili_download_entry_info.dart';
|
||||
@@ -85,7 +86,7 @@ class _DownloadDetailPageState extends State<DownloadDetailPage>
|
||||
final colorScheme = ColorScheme.of(context);
|
||||
return Obx(() {
|
||||
final enableMultiSelect = this.enableMultiSelect.value;
|
||||
return PopScope(
|
||||
return popScope(
|
||||
canPop: !enableMultiSelect,
|
||||
onPopInvokedWithResult: (didPop, result) {
|
||||
if (enableMultiSelect) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:PiliPlus/common/widgets/appbar/appbar.dart';
|
||||
import 'package:PiliPlus/common/widgets/dialog/dialog.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/pop_scope.dart';
|
||||
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
|
||||
import 'package:PiliPlus/common/widgets/view_sliver_safe_area.dart';
|
||||
import 'package:PiliPlus/models_new/download/bili_download_entry_info.dart';
|
||||
@@ -33,7 +34,7 @@ class _DownloadingPageState extends State<DownloadingPage>
|
||||
Widget build(BuildContext context) {
|
||||
return Obx(() {
|
||||
final enableMultiSelect = this.enableMultiSelect.value;
|
||||
return PopScope(
|
||||
return popScope(
|
||||
canPop: !enableMultiSelect,
|
||||
onPopInvokedWithResult: (didPop, result) {
|
||||
if (enableMultiSelect) {
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:PiliPlus/common/widgets/appbar/appbar.dart';
|
||||
import 'package:PiliPlus/common/widgets/badge.dart';
|
||||
import 'package:PiliPlus/common/widgets/dialog/dialog.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/layout_builder.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/pop_scope.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
|
||||
import 'package:PiliPlus/common/widgets/select_mask.dart';
|
||||
@@ -49,7 +50,7 @@ class _DownloadPageState extends State<DownloadPage> {
|
||||
final padding = MediaQuery.viewPaddingOf(context);
|
||||
return Obx(() {
|
||||
final enableMultiSelect = _controller.enableMultiSelect.value;
|
||||
return PopScope(
|
||||
return popScope(
|
||||
canPop: !enableMultiSelect,
|
||||
onPopInvokedWithResult: (didPop, result) {
|
||||
if (enableMultiSelect) {
|
||||
|
||||
@@ -90,6 +90,7 @@ Widget content(
|
||||
),
|
||||
if (pics != null && pics.isNotEmpty)
|
||||
ImageGridView(
|
||||
fullScreen: true,
|
||||
picArr: pics
|
||||
.map(
|
||||
(item) => ImageModel(
|
||||
@@ -100,7 +101,6 @@ Widget content(
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
fullScreen: true,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -279,7 +279,7 @@ class _EpisodePanelState extends State<EpisodePanel>
|
||||
) {
|
||||
final isCurrTab = tabIndex == widget.initialTabIndex;
|
||||
return KeepAliveWrapper(
|
||||
builder: (context) => CustomScrollView(
|
||||
child: CustomScrollView(
|
||||
reverse: _isReversed[tabIndex],
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
controller: _itemScrollController[tabIndex],
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:PiliPlus/common/widgets/button/icon_button.dart';
|
||||
import 'package:PiliPlus/common/widgets/dialog/dialog.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/pop_scope.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/refresh_indicator.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
|
||||
@@ -51,7 +52,7 @@ class _FavDetailPageState extends State<FavDetailPage> with GridMixin {
|
||||
return Obx(
|
||||
() {
|
||||
final enableMultiSelect = _favDetailController.enableMultiSelect.value;
|
||||
return PopScope(
|
||||
return popScope(
|
||||
canPop: !enableMultiSelect,
|
||||
onPopInvokedWithResult: (didPop, result) {
|
||||
if (enableMultiSelect) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:PiliPlus/common/widgets/appbar/appbar.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/page/tabs.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/pop_scope.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/refresh_indicator.dart';
|
||||
import 'package:PiliPlus/common/widgets/gesture/horizontal_drag_gesture_recognizer.dart';
|
||||
import 'package:PiliPlus/common/widgets/keep_alive_wrapper.dart';
|
||||
@@ -84,7 +85,7 @@ class _HistoryPageState extends State<HistoryPage>
|
||||
() {
|
||||
final enableMultiSelect =
|
||||
_historyController.baseCtr.enableMultiSelect.value;
|
||||
return PopScope(
|
||||
return popScope(
|
||||
canPop: !enableMultiSelect,
|
||||
onPopInvokedWithResult: (didPop, result) {
|
||||
if (enableMultiSelect) {
|
||||
@@ -141,7 +142,7 @@ class _HistoryPageState extends State<HistoryPage>
|
||||
horizontalDragGestureRecognizer:
|
||||
CustomHorizontalDragGestureRecognizer.new,
|
||||
children: [
|
||||
KeepAliveWrapper(builder: (context) => child),
|
||||
KeepAliveWrapper(child: child),
|
||||
..._historyController.tabs.map(
|
||||
(item) => HistoryPage(type: item.type),
|
||||
),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:PiliPlus/common/widgets/appbar/appbar.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/page/tabs.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/pop_scope.dart';
|
||||
import 'package:PiliPlus/common/widgets/gesture/horizontal_drag_gesture_recognizer.dart';
|
||||
import 'package:PiliPlus/common/widgets/scroll_physics.dart';
|
||||
import 'package:PiliPlus/common/widgets/view_safe_area.dart';
|
||||
@@ -65,7 +66,7 @@ class _LaterPageState extends State<LaterPage>
|
||||
return Obx(
|
||||
() {
|
||||
final enableMultiSelect = _baseCtr.enableMultiSelect.value;
|
||||
return PopScope(
|
||||
return popScope(
|
||||
canPop: !enableMultiSelect,
|
||||
onPopInvokedWithResult: (didPop, result) {
|
||||
if (enableMultiSelect) {
|
||||
|
||||
@@ -95,57 +95,57 @@ class _LiveAreaPageState extends State<LiveAreaPage> {
|
||||
children: response
|
||||
.map(
|
||||
(e) => KeepAliveWrapper(
|
||||
builder: (context) {
|
||||
if (e.areaList.isNullOrEmpty) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return GridView.builder(
|
||||
padding: EdgeInsets.only(
|
||||
top: 12,
|
||||
bottom: bottom + 100,
|
||||
),
|
||||
gridDelegate:
|
||||
const SliverGridDelegateWithMaxCrossAxisExtent(
|
||||
maxCrossAxisExtent: 100,
|
||||
mainAxisSpacing: 10,
|
||||
crossAxisSpacing: 10,
|
||||
mainAxisExtent: 80,
|
||||
child: e.areaList.isNullOrEmpty
|
||||
? const SizedBox.shrink()
|
||||
: GridView.builder(
|
||||
padding: EdgeInsets.only(
|
||||
top: 12,
|
||||
bottom: bottom + 100,
|
||||
),
|
||||
itemCount: e.areaList!.length,
|
||||
itemBuilder: (context, index) {
|
||||
final item = e.areaList![index];
|
||||
return _tagItem(
|
||||
theme: theme,
|
||||
item: item,
|
||||
onPressed: () {
|
||||
// success
|
||||
bool? isFav =
|
||||
_controller.favInfo[item.id];
|
||||
if (isFav == true) {
|
||||
_controller.favInfo[item.id] = false;
|
||||
_controller.favState
|
||||
..value.data.remove(item)
|
||||
..refresh();
|
||||
(context as Element).markNeedsBuild();
|
||||
} else {
|
||||
// check
|
||||
if (_controller
|
||||
.favState
|
||||
.value
|
||||
.isSuccess) {
|
||||
_controller.favInfo[item.id] = true;
|
||||
_controller.favState
|
||||
..value.data.add(item)
|
||||
..refresh();
|
||||
(context as Element)
|
||||
.markNeedsBuild();
|
||||
}
|
||||
}
|
||||
gridDelegate:
|
||||
const SliverGridDelegateWithMaxCrossAxisExtent(
|
||||
maxCrossAxisExtent: 100,
|
||||
mainAxisSpacing: 10,
|
||||
crossAxisSpacing: 10,
|
||||
mainAxisExtent: 80,
|
||||
),
|
||||
itemCount: e.areaList!.length,
|
||||
itemBuilder: (context, index) {
|
||||
final item = e.areaList![index];
|
||||
return _tagItem(
|
||||
theme: theme,
|
||||
item: item,
|
||||
onPressed: () {
|
||||
// success
|
||||
bool? isFav =
|
||||
_controller.favInfo[item.id];
|
||||
if (isFav == true) {
|
||||
_controller.favInfo[item.id] =
|
||||
false;
|
||||
_controller.favState
|
||||
..value.data.remove(item)
|
||||
..refresh();
|
||||
(context as Element)
|
||||
.markNeedsBuild();
|
||||
} else {
|
||||
// check
|
||||
if (_controller
|
||||
.favState
|
||||
.value
|
||||
.isSuccess) {
|
||||
_controller.favInfo[item.id] =
|
||||
true;
|
||||
_controller.favState
|
||||
..value.data.add(item)
|
||||
..refresh();
|
||||
(context as Element)
|
||||
.markNeedsBuild();
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
|
||||
@@ -47,12 +47,10 @@ class _LiveDmBlockPageState extends State<LiveDmBlockPage> {
|
||||
controller: _controller.tabController,
|
||||
children: [
|
||||
KeepAliveWrapper(
|
||||
builder: (context) =>
|
||||
Obx(() => _buildKeyword(_controller.keywordList)),
|
||||
child: Obx(() => _buildKeyword(_controller.keywordList)),
|
||||
),
|
||||
KeepAliveWrapper(
|
||||
builder: (context) =>
|
||||
Obx(() => _buildKeyword(_controller.shieldUserList)),
|
||||
child: Obx(() => _buildKeyword(_controller.shieldUserList)),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
@@ -6,10 +6,12 @@ import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/button/icon_button.dart';
|
||||
import 'package:PiliPlus/common/widgets/custom_icon.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/page/page_view.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/pop_scope.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/text_field/controller.dart';
|
||||
import 'package:PiliPlus/common/widgets/gesture/horizontal_drag_gesture_recognizer.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/common/widgets/keep_alive_wrapper.dart';
|
||||
import 'package:PiliPlus/common/widgets/route_aware_mixin.dart';
|
||||
import 'package:PiliPlus/common/widgets/scroll_physics.dart';
|
||||
import 'package:PiliPlus/models/common/image_type.dart';
|
||||
import 'package:PiliPlus/models/common/live/live_contribution_rank_type.dart';
|
||||
@@ -56,7 +58,7 @@ class LiveRoomPage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _LiveRoomPageState extends State<LiveRoomPage>
|
||||
with WidgetsBindingObserver, RouteAware {
|
||||
with WidgetsBindingObserver, RouteAware, RouteAwareMixin {
|
||||
final String heroTag = Utils.generateRandomString(6);
|
||||
late final LiveRoomController _liveRoomController;
|
||||
late final PlPlayerController plPlayerController;
|
||||
@@ -83,10 +85,6 @@ class _LiveRoomPageState extends State<LiveRoomPage>
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
PageUtils.routeObserver.subscribe(
|
||||
this,
|
||||
ModalRoute.of(context)! as PageRoute,
|
||||
);
|
||||
padding = MediaQuery.viewPaddingOf(context);
|
||||
final size = MediaQuery.sizeOf(context);
|
||||
maxWidth = size.width;
|
||||
@@ -159,7 +157,6 @@ class _LiveRoomPageState extends State<LiveRoomPage>
|
||||
plPlayerController
|
||||
..removeStatusLister(playerListener)
|
||||
..dispose();
|
||||
PageUtils.routeObserver.unsubscribe(this);
|
||||
for (final e in LiveContributionRankType.values) {
|
||||
Get.delete<ContributionRankController>(
|
||||
tag: '${_liveRoomController.roomId}${e.name}',
|
||||
@@ -352,7 +349,7 @@ class _LiveRoomPageState extends State<LiveRoomPage>
|
||||
],
|
||||
);
|
||||
}
|
||||
return PopScope(
|
||||
return popScope(
|
||||
canPop: !isFullScreen && !plPlayerController.isDesktopPip,
|
||||
onPopInvokedWithResult: plPlayerController.onPopInvokedWithResult,
|
||||
child: player,
|
||||
@@ -731,7 +728,7 @@ class _LiveRoomPageState extends State<LiveRoomPage>
|
||||
horizontalDragGestureRecognizer:
|
||||
CustomHorizontalDragGestureRecognizer.new,
|
||||
children: [
|
||||
KeepAliveWrapper(builder: (context) => chat()),
|
||||
KeepAliveWrapper(child: chat()),
|
||||
SuperChatPanel(
|
||||
key: scKey,
|
||||
controller: _liveRoomController,
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/pop_scope.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/tabs.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/common/widgets/route_aware_mixin.dart';
|
||||
import 'package:PiliPlus/models/common/nav_bar_config.dart';
|
||||
import 'package:PiliPlus/pages/home/view.dart';
|
||||
import 'package:PiliPlus/pages/main/controller.dart';
|
||||
@@ -13,7 +14,6 @@ import 'package:PiliPlus/utils/app_scheme.dart';
|
||||
import 'package:PiliPlus/utils/extension/context_ext.dart';
|
||||
import 'package:PiliPlus/utils/extension/size_ext.dart';
|
||||
import 'package:PiliPlus/utils/extension/theme_ext.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
import 'package:PiliPlus/utils/storage_key.dart';
|
||||
@@ -32,11 +32,19 @@ class MainApp extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _MainAppState extends PopScopeState<MainApp>
|
||||
with RouteAware, WidgetsBindingObserver, WindowListener, TrayListener {
|
||||
with
|
||||
RouteAware,
|
||||
RouteAwareMixin,
|
||||
WidgetsBindingObserver,
|
||||
WindowListener,
|
||||
TrayListener {
|
||||
final _mainController = Get.put(MainController());
|
||||
late final _setting = GStorage.setting;
|
||||
late EdgeInsets _padding;
|
||||
|
||||
@override
|
||||
bool get initCanPop => false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@@ -65,10 +73,6 @@ class _MainAppState extends PopScopeState<MainApp>
|
||||
if (PlatformUtils.isDesktop) {
|
||||
windowManager.setBrightness(brightness);
|
||||
}
|
||||
PageUtils.routeObserver.subscribe(
|
||||
this,
|
||||
ModalRoute.of(context) as PageRoute,
|
||||
);
|
||||
if (!_mainController.useSideBar) {
|
||||
_mainController.useBottomNav = MediaQuery.sizeOf(context).isPortrait;
|
||||
}
|
||||
@@ -106,7 +110,6 @@ class _MainAppState extends PopScopeState<MainApp>
|
||||
trayManager.removeListener(this);
|
||||
windowManager.removeListener(this);
|
||||
}
|
||||
PageUtils.routeObserver.unsubscribe(this);
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
PiliScheme.listener?.cancel();
|
||||
GStorage.close();
|
||||
|
||||
@@ -429,7 +429,7 @@ class UserInfoCard extends StatelessWidget {
|
||||
);
|
||||
|
||||
Widget _buildAvatar(bool hasPendant) => fromHero(
|
||||
tag: card.face ?? '',
|
||||
tag: '${card.face}$hashCode',
|
||||
child: PendantAvatar(
|
||||
avatar: card.face,
|
||||
size: hasPendant ? kPendantAvatarSize : kAvatarSize,
|
||||
@@ -440,6 +440,7 @@ class UserInfoCard extends StatelessWidget {
|
||||
garbPendantImage: card.pendant?.image,
|
||||
roomId: live?.liveStatus == 1 ? live!.roomid : null,
|
||||
onTap: () => PageUtils.imageView(
|
||||
tag: hashCode.toString(),
|
||||
imgList: [SourceModel(url: card.face.http2https)],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -136,9 +136,7 @@ class _UpowerRankPageState extends State<UpowerRankPage>
|
||||
Expanded(
|
||||
child: tabBarView(
|
||||
children: [
|
||||
KeepAliveWrapper(
|
||||
builder: (context) => child,
|
||||
),
|
||||
KeepAliveWrapper(child: child),
|
||||
...tabs
|
||||
.skip(1)
|
||||
.map(
|
||||
|
||||
@@ -108,7 +108,7 @@ class VideoDetailController extends GetxController
|
||||
|
||||
// 请求返回的视频信息
|
||||
late PlayUrlModel data;
|
||||
final Rx<LoadingState> videoState = LoadingState.loading().obs;
|
||||
final RxBool videoState = false.obs;
|
||||
|
||||
/// 播放器配置 画质 音质 解码格式
|
||||
final Rxn<VideoQuality> currentVideoQa = Rxn<VideoQuality>();
|
||||
@@ -269,8 +269,6 @@ class VideoDetailController extends GetxController
|
||||
}
|
||||
}
|
||||
|
||||
bool imageview = false;
|
||||
|
||||
final isLoginVideo = Accounts.get(AccountType.video).isLogin;
|
||||
|
||||
late final watchProgress = GStorage.watchProgress;
|
||||
@@ -711,9 +709,7 @@ class VideoDetailController extends GetxController
|
||||
pgcType: isUgc ? null : pgcType,
|
||||
videoType: videoType,
|
||||
onInit: () {
|
||||
if (videoState.value is! Success) {
|
||||
videoState.value = const Success(null);
|
||||
}
|
||||
videoState.value = true;
|
||||
setSubtitle(vttSubtitlesIndex.value);
|
||||
},
|
||||
width: firstVideo.width,
|
||||
@@ -845,7 +841,7 @@ class VideoDetailController extends GetxController
|
||||
if (data.dash == null) {
|
||||
SmartDialog.showToast('视频资源不存在');
|
||||
_autoPlay.value = false;
|
||||
videoState.value = const Error('视频资源不存在');
|
||||
videoState.value = false;
|
||||
if (plPlayerController.isFullScreen.value) {
|
||||
plPlayerController.toggleFullScreen(false);
|
||||
}
|
||||
@@ -940,7 +936,7 @@ class VideoDetailController extends GetxController
|
||||
await _initPlayerIfNeeded(autoFullScreenFlag);
|
||||
} else {
|
||||
_autoPlay.value = false;
|
||||
videoState.value = result..toast();
|
||||
videoState.value = false;
|
||||
if (plPlayerController.isFullScreen.value) {
|
||||
plPlayerController.toggleFullScreen(false);
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ class _IntroDetailState extends State<PgcIntroPanel>
|
||||
horizontalDragGestureRecognizer: () =>
|
||||
TabBarDragGestureRecognizer(isDxAllowed: isDxAllowed),
|
||||
children: [
|
||||
KeepAliveWrapper(builder: (context) => _buildInfo(theme)),
|
||||
KeepAliveWrapper(child: _buildInfo(theme)),
|
||||
PgcReviewPage(
|
||||
name: widget.item.title!,
|
||||
mediaId: widget.item.mediaId,
|
||||
|
||||
@@ -5,12 +5,12 @@ import 'dart:ui';
|
||||
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/custom_icon.dart';
|
||||
import 'package:PiliPlus/common/widgets/flutter/pop_scope.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/common/widgets/image_viewer/hero_dialog_route.dart';
|
||||
import 'package:PiliPlus/common/widgets/keep_alive_wrapper.dart';
|
||||
import 'package:PiliPlus/common/widgets/route_aware_mixin.dart';
|
||||
import 'package:PiliPlus/common/widgets/scroll_physics.dart';
|
||||
import 'package:PiliPlus/common/widgets/sliver/sliver_pinned_dynamic_header.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/main.dart';
|
||||
import 'package:PiliPlus/models/common/episode_panel_type.dart';
|
||||
import 'package:PiliPlus/models_new/pgc/pgc_info_model/result.dart';
|
||||
@@ -78,7 +78,11 @@ class VideoDetailPageV extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
with TickerProviderStateMixin, RouteAware, WidgetsBindingObserver {
|
||||
with
|
||||
TickerProviderStateMixin,
|
||||
RouteAware,
|
||||
RouteAwareMixin,
|
||||
WidgetsBindingObserver {
|
||||
final heroTag = Get.arguments['heroTag'];
|
||||
|
||||
late final VideoDetailController videoDetailController;
|
||||
@@ -368,7 +372,6 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
PlPlayerController.updatePlayCount();
|
||||
}
|
||||
}
|
||||
PageUtils.routeObserver.unsubscribe(this);
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
if (PlatformUtils.isMobile) {
|
||||
showStatusBar();
|
||||
@@ -379,10 +382,8 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
@override
|
||||
// 离开当前页面时
|
||||
void didPushNext() {
|
||||
if (Get.routing.route is HeroDialogRoute) {
|
||||
videoDetailController.imageview = true;
|
||||
return;
|
||||
}
|
||||
super.didPushNext();
|
||||
isShowing = false;
|
||||
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
|
||||
@@ -390,11 +391,11 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
ScreenBrightnessPlatform.instance.resetApplicationScreenBrightness();
|
||||
}
|
||||
|
||||
videoDetailController.cancelBlockListener();
|
||||
|
||||
introController.cancelTimer();
|
||||
|
||||
videoDetailController
|
||||
..videoState.value = false
|
||||
..cancelBlockListener()
|
||||
..playerStatus = plPlayerController?.playerStatus.value
|
||||
..brightness = plPlayerController?.brightness.value;
|
||||
if (plPlayerController != null) {
|
||||
@@ -404,21 +405,18 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
..removePositionListener(positionListener)
|
||||
..pause();
|
||||
}
|
||||
isShowing = false;
|
||||
super.didPushNext();
|
||||
}
|
||||
|
||||
@override
|
||||
// 返回当前页面时
|
||||
void didPopNext() {
|
||||
if (videoDetailController.imageview) {
|
||||
videoDetailController.imageview = false;
|
||||
super.didPopNext();
|
||||
|
||||
if (plPlayerController?.isCloseAll ?? false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (plPlayerController?.isCloseAll == true) {
|
||||
return;
|
||||
}
|
||||
isShowing = true;
|
||||
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
|
||||
@@ -428,7 +426,6 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
videoDetailController.plPlayerController.pause();
|
||||
}
|
||||
|
||||
isShowing = true;
|
||||
PlPlayerController.setPlayCallBack(playCallBack);
|
||||
|
||||
introController.startTimer();
|
||||
@@ -460,21 +457,14 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
);
|
||||
} else if (videoDetailController.plPlayerController.preInitPlayer &&
|
||||
!videoDetailController.isQuerying &&
|
||||
videoDetailController.videoState.value is! Error) {
|
||||
videoDetailController.videoUrl != null) {
|
||||
videoDetailController.playerInit();
|
||||
}
|
||||
|
||||
super.didPopNext();
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
PageUtils.routeObserver.subscribe(
|
||||
this,
|
||||
ModalRoute.of(context)! as PageRoute,
|
||||
);
|
||||
|
||||
padding = MediaQuery.viewPaddingOf(context);
|
||||
|
||||
final size = MediaQuery.sizeOf(context);
|
||||
@@ -538,9 +528,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
..addListener(animListener);
|
||||
if (PlatformUtils.isMobile && mounted && isShowing && !isFullScreen) {
|
||||
if (isPortrait) {
|
||||
if (!videoDetailController.imageview) {
|
||||
showStatusBar();
|
||||
}
|
||||
showStatusBar();
|
||||
} else if (!videoDetailController.horizontalScreen) {
|
||||
hideStatusBar();
|
||||
}
|
||||
@@ -1085,7 +1073,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
localIntroPanel()
|
||||
else if (showIntro)
|
||||
KeepAliveWrapper(
|
||||
builder: (context) => CustomScrollView(
|
||||
child: CustomScrollView(
|
||||
key: const PageStorageKey(CommonIntroController),
|
||||
controller:
|
||||
videoDetailController.effectiveIntroScrollCtr,
|
||||
@@ -1342,7 +1330,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
required double width,
|
||||
required double height,
|
||||
bool isPipMode = false,
|
||||
}) => PopScope(
|
||||
}) => popScope(
|
||||
key: videoDetailController.videoPlayerKey,
|
||||
canPop:
|
||||
!isFullScreen &&
|
||||
@@ -1351,7 +1339,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
onPopInvokedWithResult: _onPopInvokedWithResult,
|
||||
child: Obx(
|
||||
() =>
|
||||
videoDetailController.videoState.value is! Success ||
|
||||
!videoDetailController.videoState.value ||
|
||||
!videoDetailController.autoPlay ||
|
||||
plPlayerController?.videoController == null
|
||||
? const SizedBox.shrink()
|
||||
@@ -1589,7 +1577,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
children: [
|
||||
const Positioned.fill(child: ColoredBox(color: Colors.black)),
|
||||
|
||||
if (isShowing) plPlayer(width: width, height: height),
|
||||
plPlayer(width: width, height: height),
|
||||
|
||||
Obx(() {
|
||||
if (!videoDetailController.autoPlay) {
|
||||
@@ -1773,75 +1761,76 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
if (videoDetailController.isFileSource) {
|
||||
return localIntroPanel(needCtr: needCtr);
|
||||
}
|
||||
Widget introPanel() => KeepAliveWrapper(
|
||||
builder: (context) {
|
||||
final child = CustomScrollView(
|
||||
key: const PageStorageKey(CommonIntroController),
|
||||
controller: needCtr
|
||||
? videoDetailController.effectiveIntroScrollCtr
|
||||
: null,
|
||||
physics: !needCtr
|
||||
? const AlwaysScrollableScrollPhysics(
|
||||
parent: ClampingScrollPhysics(),
|
||||
)
|
||||
: null,
|
||||
slivers: [
|
||||
if (videoDetailController.isUgc) ...[
|
||||
UgcIntroPanel(
|
||||
key: videoIntroKey,
|
||||
heroTag: heroTag,
|
||||
showAiBottomSheet: showAiBottomSheet,
|
||||
showEpisodes: showEpisodes,
|
||||
onShowMemberPage: onShowMemberPage,
|
||||
isPortrait: isPortrait,
|
||||
isHorizontal: isHorizontal ?? width! / height! >= kScreenRatio,
|
||||
),
|
||||
if (needRelated && videoDetailController.showRelatedVideo) ...[
|
||||
SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: StyleString.safeSpace),
|
||||
child: Divider(
|
||||
height: 1,
|
||||
indent: 12,
|
||||
endIndent: 12,
|
||||
color: themeData.colorScheme.outline.withValues(
|
||||
alpha: 0.08,
|
||||
),
|
||||
Widget introPanel() {
|
||||
Widget child = CustomScrollView(
|
||||
key: const PageStorageKey(CommonIntroController),
|
||||
controller: needCtr
|
||||
? videoDetailController.effectiveIntroScrollCtr
|
||||
: null,
|
||||
physics: !needCtr
|
||||
? const AlwaysScrollableScrollPhysics(
|
||||
parent: ClampingScrollPhysics(),
|
||||
)
|
||||
: null,
|
||||
slivers: [
|
||||
if (videoDetailController.isUgc) ...[
|
||||
UgcIntroPanel(
|
||||
key: videoIntroKey,
|
||||
heroTag: heroTag,
|
||||
showAiBottomSheet: showAiBottomSheet,
|
||||
showEpisodes: showEpisodes,
|
||||
onShowMemberPage: onShowMemberPage,
|
||||
isPortrait: isPortrait,
|
||||
isHorizontal: isHorizontal ?? width! / height! >= kScreenRatio,
|
||||
),
|
||||
if (needRelated && videoDetailController.showRelatedVideo) ...[
|
||||
SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: StyleString.safeSpace,
|
||||
),
|
||||
child: Divider(
|
||||
height: 1,
|
||||
indent: 12,
|
||||
endIndent: 12,
|
||||
color: themeData.colorScheme.outline.withValues(
|
||||
alpha: 0.08,
|
||||
),
|
||||
),
|
||||
),
|
||||
RelatedVideoPanel(key: videoRelatedKey, heroTag: heroTag),
|
||||
],
|
||||
] else
|
||||
PgcIntroPage(
|
||||
key: videoIntroKey,
|
||||
heroTag: heroTag,
|
||||
cid: videoDetailController.cid.value,
|
||||
showEpisodes: showEpisodes,
|
||||
showIntroDetail: showIntroDetail,
|
||||
maxWidth: width ?? maxWidth,
|
||||
isLandscape: !isPortrait,
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: SizedBox(
|
||||
height:
|
||||
(videoDetailController.isPlayAll && !isPortrait
|
||||
? 80
|
||||
: StyleString.safeSpace) +
|
||||
padding.bottom,
|
||||
),
|
||||
RelatedVideoPanel(key: videoRelatedKey, heroTag: heroTag),
|
||||
],
|
||||
] else
|
||||
PgcIntroPage(
|
||||
key: videoIntroKey,
|
||||
heroTag: heroTag,
|
||||
cid: videoDetailController.cid.value,
|
||||
showEpisodes: showEpisodes,
|
||||
showIntroDetail: showIntroDetail,
|
||||
maxWidth: width ?? maxWidth,
|
||||
isLandscape: !isPortrait,
|
||||
),
|
||||
],
|
||||
SliverToBoxAdapter(
|
||||
child: SizedBox(
|
||||
height:
|
||||
(videoDetailController.isPlayAll && !isPortrait
|
||||
? 80
|
||||
: StyleString.safeSpace) +
|
||||
padding.bottom,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
if (isNested) {
|
||||
child = ExtendedVisibilityDetector(
|
||||
uniqueKey: const Key('intro-panel'),
|
||||
child: child,
|
||||
);
|
||||
if (isNested) {
|
||||
return ExtendedVisibilityDetector(
|
||||
uniqueKey: const Key('intro-panel'),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
return child;
|
||||
},
|
||||
);
|
||||
}
|
||||
return KeepAliveWrapper(child: child);
|
||||
}
|
||||
|
||||
if (videoDetailController.isPlayAll) {
|
||||
return Stack(
|
||||
clipBehavior: Clip.none,
|
||||
@@ -1894,7 +1883,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
|
||||
Widget get seasonPanel {
|
||||
final videoDetail = ugcIntroController.videoDetail.value;
|
||||
return KeepAliveWrapper(
|
||||
builder: (context) => Column(
|
||||
child: Column(
|
||||
children: [
|
||||
if ((videoDetail.pages?.length ?? 0) > 1)
|
||||
if (videoDetail.ugcSeason != null)
|
||||
|
||||
@@ -1683,8 +1683,11 @@ class PlPlayerController with BlockConfigMixin {
|
||||
}
|
||||
|
||||
bool onPopInvokedWithResult(bool didPop, Object? result) {
|
||||
if (Platform.isAndroid && didPop) {
|
||||
_disableAutoEnterPipIfNeeded();
|
||||
if (didPop) {
|
||||
if (Platform.isAndroid) {
|
||||
_disableAutoEnterPipIfNeeded();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (controlsLock.value) {
|
||||
onLockControl(false);
|
||||
|
||||
@@ -37,9 +37,6 @@ import 'package:get/get.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
abstract final class PageUtils {
|
||||
static final RouteObserver<PageRoute> routeObserver =
|
||||
RouteObserver<PageRoute>();
|
||||
|
||||
static RelativeRect menuPosition(Offset offset) {
|
||||
return .fromLTRB(offset.dx, offset.dy, offset.dx, 0);
|
||||
}
|
||||
@@ -49,6 +46,7 @@ abstract final class PageUtils {
|
||||
required List<SourceModel> imgList,
|
||||
int? quality,
|
||||
ValueChanged<int>? onPageChanged,
|
||||
String tag = '',
|
||||
}) {
|
||||
return Get.key.currentState!.push<void>(
|
||||
HeroDialogRoute(
|
||||
@@ -57,6 +55,7 @@ abstract final class PageUtils {
|
||||
initIndex: initialPage,
|
||||
quality: quality ?? GlobalData().imgQuality,
|
||||
onPageChanged: onPageChanged,
|
||||
tag: tag,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user