fix: ensure epub content does not go behind in-screen cameras #3

Merged
fmartingr merged 2 commits from epub-safearea into master 2026-02-12 22:46:09 +01:00

View file

@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:wakelock_plus/wakelock_plus.dart'; import 'package:wakelock_plus/wakelock_plus.dart';
import 'package:flutter_epub_viewer/flutter_epub_viewer.dart'; import 'package:flutter_epub_viewer/flutter_epub_viewer.dart';
@ -74,6 +75,9 @@ class _EpubReaderScreenState extends ConsumerState<EpubReaderScreen> {
// Keep screen awake while reading // Keep screen awake while reading
WakelockPlus.enable(); WakelockPlus.enable();
// Enter immersive mode to hide system bars (matching image reader behavior)
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky);
} }
Future<void> _loadEpub() async { Future<void> _loadEpub() async {
@ -476,22 +480,24 @@ class _EpubReaderScreenState extends ConsumerState<EpubReaderScreen> {
const minWidthForDualPage = 768.0; const minWidthForDualPage = 768.0;
final shouldUseDualPage = screenWidth >= minWidthForDualPage; final shouldUseDualPage = screenWidth >= minWidthForDualPage;
return EpubViewer( return SafeArea(
key: ValueKey( child: EpubViewer(
'$_viewerKey-${shouldUseDualPage ? "dual" : "single"}'), // Include spread in key to force recreation key: ValueKey(
epubController: _epubController!, '$_viewerKey-${shouldUseDualPage ? "dual" : "single"}'), // Include spread in key to force recreation
epubSource: EpubSource.fromFile(_epubFile!), epubController: _epubController!,
initialCfi: _initialCfi, epubSource: EpubSource.fromFile(_epubFile!),
displaySettings: _buildDisplaySettings(orientation), initialCfi: _initialCfi,
onEpubLoaded: () { displaySettings: _buildDisplaySettings(orientation),
debugPrint('EPUB loaded successfully'); onEpubLoaded: () {
}, debugPrint('EPUB loaded successfully');
onChaptersLoaded: _handleChaptersLoaded, },
onRelocated: _handleRelocated, onChaptersLoaded: _handleChaptersLoaded,
onTextSelected: (selection) { onRelocated: _handleRelocated,
// Future: Handle text selection for highlights/notes onTextSelected: (selection) {
debugPrint('Selected: ${selection.selectedText}'); // Future: Handle text selection for highlights/notes
}, debugPrint('Selected: ${selection.selectedText}');
},
),
); );
}, },
), ),
@ -646,8 +652,10 @@ class _EpubReaderScreenState extends ConsumerState<EpubReaderScreen> {
}); });
if (_showAppBar) { if (_showAppBar) {
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
_startAutoHideTimer(); _startAutoHideTimer();
} else { } else {
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky);
_cancelAutoHideTimer(); _cancelAutoHideTimer();
} }
} }
@ -660,6 +668,7 @@ class _EpubReaderScreenState extends ConsumerState<EpubReaderScreen> {
setState(() { setState(() {
_showAppBar = false; _showAppBar = false;
}); });
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky);
} }
}); });
} }
@ -676,6 +685,7 @@ class _EpubReaderScreenState extends ConsumerState<EpubReaderScreen> {
setState(() { setState(() {
_showAppBar = false; _showAppBar = false;
}); });
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky);
} }
/// Build custom app bar overlay /// Build custom app bar overlay
@ -796,6 +806,9 @@ class _EpubReaderScreenState extends ConsumerState<EpubReaderScreen> {
// Allow screen to sleep again // Allow screen to sleep again
WakelockPlus.disable(); WakelockPlus.disable();
// Restore system UI bars when leaving the reader
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
// Clean up WebView resources to prevent OOM crashes // Clean up WebView resources to prevent OOM crashes
// The EpubController holds a reference to the InAppWebViewController // The EpubController holds a reference to the InAppWebViewController
// which needs to be properly disposed // which needs to be properly disposed