Compare commits

...

2 Commits

Author SHA1 Message Date
dom
d6579b29ae reduce rebuild
Signed-off-by: dom <githubaccount56556@proton.me>
2026-03-17 14:38:10 +08:00
dom
8a8aa6c1e0 unique image tag
Signed-off-by: dom <githubaccount56556@proton.me>
2026-03-17 14:37:53 +08:00
27 changed files with 264 additions and 218 deletions

View File

@@ -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;
}

View File

@@ -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,
);
}

View File

@@ -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() {

View File

@@ -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

View 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();
}
}

View File

@@ -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

View File

@@ -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) {

View File

@@ -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]),
),
),

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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,
),
],
),

View File

@@ -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],

View File

@@ -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) {

View File

@@ -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),
),

View File

@@ -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) {

View File

@@ -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(),

View File

@@ -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)),
),
],
);

View File

@@ -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,

View File

@@ -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();

View File

@@ -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)],
),
),

View File

@@ -136,9 +136,7 @@ class _UpowerRankPageState extends State<UpowerRankPage>
Expanded(
child: tabBarView(
children: [
KeepAliveWrapper(
builder: (context) => child,
),
KeepAliveWrapper(child: child),
...tabs
.skip(1)
.map(

View File

@@ -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);
}

View File

@@ -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,

View File

@@ -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)

View File

@@ -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);

View File

@@ -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,
),
),
);