mirror of
https://github.com/R0m1k3/xtremobile.git
synced 2026-10-11 17:29:26 +02:00
perf(critical): Implement P0 optimization fixes - TiviMate parity
CRITICAL FIXES: - P0-1: Fix EPG N+1 query storm with batch loading * Implement XtreamServiceMobile with getBatchEPG() method * Batch load EPG data instead of per-channel requests * Reduce 50-200 concurrent requests to 1 request per category * Add EPG cache with 1-hour TTL to prevent redundant calls - P0-2: Remove destructive cache clearing on startup * Implement TTL-based cache invalidation * Preserve cache between app launches (6h channels, 1h EPG, 30m search) * Add HiveService.invalidateExpiredCache() method * Expected: -80% startup time on cold launches - P0-3: Preserve tab state with IndexedStack * Replace switch statement with IndexedStack * Keep all tabs alive in widget tree * Preserve scroll position, search state, filters across tab switches * Expected: instant tab switching (<100ms) HIGH PRIORITY FIXES: - P1-1: Debounce search input (500ms) * Prevent per-keystroke full rebuilds * Add search debounce timer - P1-3: Fix double FocusNode disposal crash * Remove duplicate focus node disposal in native_player_screen.dart * Prevents StateError on player exit Expected impact: -75-80% perceived latency, instant tab switching, no OOM on startup. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
70c2241527
commit
93b8e5b337
8 files changed
+791
-41
No files matched your search
@@ -28,25 +28,10 @@ class _MobileDashboardScreenState extends ConsumerState<MobileDashboardScreen> {
|
||||
Widget build(BuildContext context) {
|
||||
final currentIndex = ref.watch(mobileDashboardIndexProvider);
|
||||
|
||||
// Determine which tab to show
|
||||
Widget currentTab;
|
||||
switch (currentIndex) {
|
||||
case 0:
|
||||
currentTab = MobileLiveTVTab(playlist: widget.playlist);
|
||||
break;
|
||||
case 1:
|
||||
currentTab = MobileMoviesTab(playlist: widget.playlist);
|
||||
break;
|
||||
case 2:
|
||||
currentTab = MobileSeriesTab(playlist: widget.playlist);
|
||||
break;
|
||||
case 3:
|
||||
currentTab = const MobileSettingsTab();
|
||||
break;
|
||||
default:
|
||||
currentTab = MobileLiveTVTab(playlist: widget.playlist);
|
||||
}
|
||||
|
||||
// [P0-3 FIX] Use IndexedStack to preserve tab state and scroll position
|
||||
// Instead of destroying/recreating tabs on each switch, keep all tabs alive in memory
|
||||
// This enables instant tab switching and preserves user scroll position, search state, etc.
|
||||
// The AutomaticKeepAliveClientMixin in each tab will keep them in the widget tree
|
||||
return Theme(
|
||||
data: MobileTheme.darkTheme,
|
||||
child: MobileScaffold(
|
||||
@@ -54,7 +39,30 @@ class _MobileDashboardScreenState extends ConsumerState<MobileDashboardScreen> {
|
||||
onIndexChanged: (index) {
|
||||
ref.read(mobileDashboardIndexProvider.notifier).state = index;
|
||||
},
|
||||
child: currentTab,
|
||||
child: IndexedStack(
|
||||
index: currentIndex,
|
||||
children: [
|
||||
// Tab 0: Live TV - stays alive to preserve channel list scroll position
|
||||
MobileLiveTVTab(
|
||||
key: const PageStorageKey('live_tv_tab'),
|
||||
playlist: widget.playlist,
|
||||
),
|
||||
// Tab 1: Movies - stays alive to preserve search and filter state
|
||||
MobileMoviesTab(
|
||||
key: const PageStorageKey('movies_tab'),
|
||||
playlist: widget.playlist,
|
||||
),
|
||||
// Tab 2: Series - stays alive to preserve series browsing state
|
||||
MobileSeriesTab(
|
||||
key: const PageStorageKey('series_tab'),
|
||||
playlist: widget.playlist,
|
||||
),
|
||||
// Tab 3: Settings - stays alive to preserve settings scroll position
|
||||
const MobileSettingsTab(
|
||||
key: PageStorageKey('settings_tab'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -459,6 +459,8 @@ class _NativePlayerScreenState extends ConsumerState<NativePlayerScreen>
|
||||
_player.stop();
|
||||
_player.dispose();
|
||||
_xtreamService?.dispose();
|
||||
|
||||
// [P1-3 FIX] Dispose all focus nodes ONCE (was being disposed twice, causing crash)
|
||||
_playPauseFocusNode.dispose();
|
||||
_prevFocusNode.dispose();
|
||||
_nextFocusNode.dispose();
|
||||
@@ -475,12 +477,6 @@ class _NativePlayerScreenState extends ConsumerState<NativePlayerScreen>
|
||||
]);
|
||||
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
|
||||
|
||||
// Dispose focus nodes
|
||||
_playPauseFocusNode.dispose();
|
||||
_prevFocusNode.dispose();
|
||||
_nextFocusNode.dispose();
|
||||
_backFocusNode.dispose();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user