fix(player): 修正飘心Overlay生命周期
This commit is contained in:
1 parent
a7d01e0306
commit
c0f65042e6
4 files changed
+184
-28
No files matched your search
@@ -39,6 +39,7 @@ import 'widgets/exit_clear_screen_button.dart';
|
||||
import 'widgets/feed_pull_refresh.dart';
|
||||
import 'widgets/player_center_toggle.dart';
|
||||
import 'widgets/player_loading_overlay.dart';
|
||||
import 'widgets/player_heart_overlay_scope.dart';
|
||||
import 'widgets/player_more_sheet.dart';
|
||||
import 'widgets/player_overlay.dart';
|
||||
import 'widgets/player_overlay_models.dart';
|
||||
@@ -147,6 +148,8 @@ class _PlayerPageState extends State<PlayerPage>
|
||||
bool _loadingRemoved = true;
|
||||
bool _loadingFadeScheduled = false;
|
||||
bool _loggedLoadingUiVisible = false;
|
||||
final PlayerHeartOverlayController _heartOverlayController =
|
||||
PlayerHeartOverlayController();
|
||||
final Set<String> _precachedPosterUrls = {};
|
||||
|
||||
/// 收藏切换后的本地增量:基准是详情快照,切换成功按服务端状态 ±1。
|
||||
@@ -306,6 +309,7 @@ class _PlayerPageState extends State<PlayerPage>
|
||||
void dispose() {
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
appRouteObserver.unsubscribe(this);
|
||||
_heartOverlayController.dispose();
|
||||
_spriteAxisService
|
||||
..removeListener(_handleSpriteFrameChanged)
|
||||
..dispose();
|
||||
@@ -701,6 +705,10 @@ class _PlayerPageState extends State<PlayerPage>
|
||||
),
|
||||
],
|
||||
);
|
||||
final scopedBody = PlayerHeartOverlayScope(
|
||||
controller: _heartOverlayController,
|
||||
child: body,
|
||||
);
|
||||
final player = AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
key: const Key('player-system-overlay'),
|
||||
value: SystemUiOverlayStyle.light.copyWith(
|
||||
@@ -713,12 +721,12 @@ class _PlayerPageState extends State<PlayerPage>
|
||||
child: Theme(
|
||||
data: AppTheme.dark(),
|
||||
child: widget.embedded
|
||||
? body
|
||||
? scopedBody
|
||||
: Scaffold(
|
||||
key: const Key('player-scaffold'),
|
||||
backgroundColor: AppColors.transparent,
|
||||
resizeToAvoidBottomInset: false,
|
||||
body: body,
|
||||
body: scopedBody,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
// Author: fruit.com.cn
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
/// Tracks heart overlays for the lifetime of one player page.
|
||||
class PlayerHeartOverlayController {
|
||||
final Map<int, OverlayEntry> _entries = {};
|
||||
|
||||
void track(int id, OverlayEntry entry) {
|
||||
_entries[id] = entry;
|
||||
}
|
||||
|
||||
void untrack(int id) {
|
||||
_entries.remove(id);
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
for (final entry in _entries.entries.toList()) {
|
||||
if (entry.value.mounted) entry.value.remove();
|
||||
entry.value.dispose();
|
||||
if (kDebugMode) {
|
||||
debugPrint(
|
||||
'[player] phase=heart-burst-entry-removed '
|
||||
'id=${entry.key} reason=page-dispose',
|
||||
);
|
||||
}
|
||||
}
|
||||
_entries.clear();
|
||||
}
|
||||
}
|
||||
|
||||
class PlayerHeartOverlayScope extends InheritedWidget {
|
||||
const PlayerHeartOverlayScope({
|
||||
super.key,
|
||||
required this.controller,
|
||||
required super.child,
|
||||
});
|
||||
|
||||
final PlayerHeartOverlayController controller;
|
||||
|
||||
static PlayerHeartOverlayController? maybeOf(BuildContext context) {
|
||||
return context
|
||||
.dependOnInheritedWidgetOfExactType<PlayerHeartOverlayScope>()
|
||||
?.controller;
|
||||
}
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(PlayerHeartOverlayScope oldWidget) {
|
||||
return controller != oldWidget.controller;
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'player_double_tap_hearts.dart';
|
||||
import 'player_heart_overlay_scope.dart';
|
||||
|
||||
typedef PlayerLongPressStartCallback =
|
||||
void Function(LongPressStartDetails details, double width);
|
||||
@@ -48,18 +49,11 @@ class _PlayerTapGestureRegionState extends State<PlayerTapGestureRegion> {
|
||||
int _downSerial = 0;
|
||||
int _tapSeq = 0;
|
||||
int _nextHeartBurstId = 0;
|
||||
final Map<int, OverlayEntry> _heartOverlayEntries = {};
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_tapSeq++;
|
||||
_singleTapTimer?.cancel();
|
||||
for (final entry in _heartOverlayEntries.values.toList()) {
|
||||
entry
|
||||
..remove()
|
||||
..dispose();
|
||||
}
|
||||
_heartOverlayEntries.clear();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -161,7 +155,20 @@ class _PlayerTapGestureRegionState extends State<PlayerTapGestureRegion> {
|
||||
? overlayBox.globalToLocal(globalPosition)
|
||||
: globalPosition;
|
||||
final id = _nextHeartBurstId++;
|
||||
final controller = PlayerHeartOverlayScope.maybeOf(context);
|
||||
var removed = false;
|
||||
late final OverlayEntry entry;
|
||||
void removeEntry() {
|
||||
if (removed) return;
|
||||
removed = true;
|
||||
controller?.untrack(id);
|
||||
if (entry.mounted) entry.remove();
|
||||
entry.dispose();
|
||||
if (kDebugMode) {
|
||||
debugPrint('[player] phase=heart-burst-entry-removed id=$id');
|
||||
}
|
||||
}
|
||||
|
||||
entry = OverlayEntry(
|
||||
builder: (context) => Positioned.fill(
|
||||
child: IgnorePointer(
|
||||
@@ -169,26 +176,15 @@ class _PlayerTapGestureRegionState extends State<PlayerTapGestureRegion> {
|
||||
key: ValueKey('heart-overlay-$id'),
|
||||
burstId: id,
|
||||
origin: origin,
|
||||
onCompleted: () => _removeHeartOverlay(id),
|
||||
onCompleted: removeEntry,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
_heartOverlayEntries[id] = entry;
|
||||
controller?.track(id, entry);
|
||||
overlay.insert(entry);
|
||||
}
|
||||
|
||||
void _removeHeartOverlay(int id) {
|
||||
final entry = _heartOverlayEntries.remove(id);
|
||||
if (entry == null) return;
|
||||
entry
|
||||
..remove()
|
||||
..dispose();
|
||||
if (kDebugMode) {
|
||||
debugPrint('[player] phase=heart-burst-entry-removed id=$id');
|
||||
}
|
||||
}
|
||||
|
||||
void _handleLongPressStart(LongPressStartDetails details, double width) {
|
||||
_tapSeq++;
|
||||
_cancelPendingSingleTap();
|
||||
|
||||
Reference in new issue
Block a user