mangayomi-mirror/lib/modules/manga/reader/services/page_navigation_service.dart
Moustapha Kodjo Amadou f1dfbbaefc Fix #480/#165 #479 #443 #541 #554 #452: reader and download fixes
- #480/#165: Fix double page mode chapter navigation index conversion
- #479: Fix page jump when switching single/double page mode
- #443: Clear zoom cache on page change for swipe-back
- #541: Add shift-click range selection for chapters (desktop)
- #554: Auto-clean orphaned download records, cascade delete
- #452: Add horizontal continuous RTL reader mode
2026-03-18 09:47:41 +01:00

139 lines
3.8 KiB
Dart

import 'package:extended_image/extended_image.dart';
import 'package:flutter/material.dart';
import 'package:mangayomi/models/settings.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
/// Service for handling page navigation in the manga reader.
///
/// Abstracts the complexity of navigating between different reader modes:
/// - Paged modes (vertical, LTR, RTL)
/// - Continuous modes (vertical continuous, webtoon, horizontal continuous)
class PageNavigationService {
final ItemScrollController itemScrollController;
final ExtendedPageController extendedController;
const PageNavigationService({
required this.itemScrollController,
required this.extendedController,
});
/// Navigates to a specific page index.
///
/// Parameters:
/// - [index]: The target page index
/// - [readerMode]: Current reader mode
/// - [animate]: Whether to animate the transition
void navigateToPage({
required int index,
required ReaderMode readerMode,
required bool animate,
}) {
if (index < 0) return;
if (_isContinuousMode(readerMode)) {
_navigateContinuous(index, animate);
} else {
_navigatePaged(index, animate);
}
}
/// Navigates to next page.
void nextPage({
required ReaderMode readerMode,
required int currentIndex,
required int maxPages,
required bool animate,
}) {
if (currentIndex >= maxPages - 1) return;
navigateToPage(
index: currentIndex + 1,
readerMode: readerMode,
animate: animate,
);
}
/// Navigates to previous page.
void previousPage({
required ReaderMode readerMode,
required int currentIndex,
required bool animate,
}) {
if (currentIndex <= 0) return;
navigateToPage(
index: currentIndex - 1,
readerMode: readerMode,
animate: animate,
);
}
/// Jumps to a page without animation (for slider).
void jumpToPage({required int index, required ReaderMode readerMode}) {
if (index < 0) return;
if (_isContinuousMode(readerMode)) {
itemScrollController.jumpTo(index: index);
} else {
if (extendedController.hasClients) {
extendedController.jumpToPage(index);
}
}
}
void _navigateContinuous(int index, bool animate) {
if (animate) {
itemScrollController.scrollTo(
curve: Curves.ease,
index: index,
duration: const Duration(milliseconds: 150),
);
} else {
itemScrollController.jumpTo(index: index);
}
}
void _navigatePaged(int index, bool animate) {
if (!extendedController.hasClients) return;
if (animate) {
extendedController.animateToPage(
index,
duration: const Duration(milliseconds: 150),
curve: Curves.ease,
);
} else {
extendedController.jumpToPage(index);
}
}
bool _isContinuousMode(ReaderMode mode) {
return mode == ReaderMode.verticalContinuous ||
mode == ReaderMode.webtoon ||
mode == ReaderMode.horizontalContinuous ||
mode == ReaderMode.horizontalContinuousRTL;
}
}
/// Mixin to add page navigation capabilities to reader state.
mixin PageNavigationMixin<T extends StatefulWidget> on State<T> {
PageNavigationService? _navigationService;
/// Initializes the navigation service with the required controllers.
void initPageNavigation({
required ItemScrollController itemScrollController,
required ExtendedPageController extendedController,
}) {
_navigationService = PageNavigationService(
itemScrollController: itemScrollController,
extendedController: extendedController,
);
}
/// Gets the navigation service.
PageNavigationService get navigationService {
assert(
_navigationService != null,
'PageNavigationService not initialized. Call initPageNavigation first.',
);
return _navigationService!;
}
}