worldhopper/lib/providers/page_dimensions_provider.dart

58 lines
1.9 KiB
Dart

import 'package:flutter/widgets.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'page_dimensions_provider.g.dart';
/// Threshold: images with aspect ratio >= this are considered double-page spreads.
const double doublePageAspectRatio = 1.0;
/// Stores resolved dimensions for a single page image.
class PageDimension {
final int width;
final int height;
const PageDimension({required this.width, required this.height});
double get aspectRatio => height > 0 ? width / height : 0;
bool get isDoublePage => aspectRatio >= doublePageAspectRatio;
}
/// Provider that caches page dimensions for a publication.
/// Parameterized by publication ID so each publication has its own cache.
@riverpod
class PageDimensionsNotifier extends _$PageDimensionsNotifier {
@override
Map<int, PageDimension> build(String publicationId) {
return {};
}
/// Resolve dimensions for a page from its [imageProvider].
/// Returns immediately if already cached. Otherwise resolves asynchronously
/// and updates state when done.
void resolvePage(int pageIndex, ImageProvider imageProvider) {
if (state.containsKey(pageIndex)) return;
final stream = imageProvider.resolve(ImageConfiguration.empty);
late ImageStreamListener listener;
listener = ImageStreamListener(
(ImageInfo info, bool synchronousCall) {
final image = info.image;
final dim = PageDimension(
width: image.width,
height: image.height,
);
state = {...state, pageIndex: dim};
image.dispose();
stream.removeListener(listener);
},
onError: (exception, stackTrace) {
// Failed to resolve — leave uncached, will be treated as portrait
stream.removeListener(listener);
},
);
stream.addListener(listener);
}
/// Check if a page's dimensions have been resolved.
bool hasPage(int pageIndex) => state.containsKey(pageIndex);
}