feat: Add native TTS snapshot reconciliation and lifecycle management

This commit is contained in:
2026-04-27 00:58:51 +07:00
parent c3e6d66f43
commit 977c8306b1
2 changed files with 47 additions and 1 deletions
@@ -26,7 +26,8 @@ class ReaderScreen extends ConsumerStatefulWidget {
ConsumerState<ReaderScreen> createState() => _ReaderScreenState(); ConsumerState<ReaderScreen> createState() => _ReaderScreenState();
} }
class _ReaderScreenState extends ConsumerState<ReaderScreen> { class _ReaderScreenState extends ConsumerState<ReaderScreen>
with WidgetsBindingObserver {
static const List<Color> _backgroundColorChoices = [ static const List<Color> _backgroundColorChoices = [
Color(0xFFFFFEF8), Color(0xFFFFFEF8),
Color(0xFFF6EAD7), Color(0xFFF6EAD7),
@@ -260,11 +261,39 @@ class _ReaderScreenState extends ConsumerState<ReaderScreen> {
return partiallyVisibleIndex ?? 0; return partiallyVisibleIndex ?? 0;
} }
Future<void> _reconcileChapterWithNativeTts() async {
if (defaultTargetPlatform != TargetPlatform.android) return;
final notifier = ref.read(ttsProvider.notifier);
await notifier.refreshNativeSnapshot();
if (!mounted) return;
final tts = ref.read(ttsProvider);
final targetChapterId = tts.contentKey;
if (targetChapterId == null || targetChapterId.isEmpty) return;
if (targetChapterId == widget.chapterId) return;
if (tts.status != TtsStatus.playing && tts.status != TtsStatus.paused) return;
context.pushReplacement(RouteNames.readerChapter(targetChapterId));
}
@override @override
void initState() { void initState() {
super.initState(); super.initState();
WidgetsBinding.instance.addObserver(this);
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
_scrollCtrl.addListener(_onScroll); _scrollCtrl.addListener(_onScroll);
WidgetsBinding.instance.addPostFrameCallback((_) {
if (!mounted) return;
unawaited(_reconcileChapterWithNativeTts());
});
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
unawaited(_reconcileChapterWithNativeTts());
}
} }
/// Handle TTS state transitions that require navigation or restarts. /// Handle TTS state transitions that require navigation or restarts.
@@ -336,6 +365,7 @@ class _ReaderScreenState extends ConsumerState<ReaderScreen> {
@override @override
void dispose() { void dispose() {
WidgetsBinding.instance.removeObserver(this);
_uiAutoHideTimer?.cancel(); _uiAutoHideTimer?.cancel();
_scrollCtrl.removeListener(_onScroll); _scrollCtrl.removeListener(_onScroll);
_scrollCtrl.dispose(); _scrollCtrl.dispose();
+16
View File
@@ -261,6 +261,22 @@ class TtsNotifier extends StateNotifier<TtsState> {
await _mediaChannel.invokeMethod<void>('openNotificationSettings'); await _mediaChannel.invokeMethod<void>('openNotificationSettings');
} }
Future<void> refreshNativeSnapshot() async {
if (!_useNativeAndroidMediaService) return;
if (!_initialized) {
await (_initFuture ?? _init());
return;
}
try {
final snapshot = await _mediaChannel.invokeMethod<dynamic>('getSnapshot');
_applyAndroidSnapshot(snapshot);
} catch (_) {
// Ignore snapshot pull errors; event stream updates will continue.
}
}
Future<void> _configureVietnameseVoiceWithFlutterTts() async { Future<void> _configureVietnameseVoiceWithFlutterTts() async {
final dynamic voicesRaw = await _tts.getVoices; final dynamic voicesRaw = await _tts.getVoices;