import 'dart:math'; import 'package:flutter/material.dart'; import 'package:pdfrx/pdfrx.dart'; import '../../../core/models/config.dart'; /// Displays PDF pages with optional two-page mode. class PdfPageDisplay extends StatelessWidget { final PdfDocument document; final int numPages; final int currentPageNumber; final Config config; const PdfPageDisplay({ super.key, required this.document, required this.numPages, required this.currentPageNumber, required this.config, }); /// Whether two-page mode is active and we have enough pages. bool get _showTwoPages => config.twoPageMode && numPages >= 2; @override Widget build(BuildContext context) { return Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [_buildLeftPage(), if (_showTwoPages) _buildRightPage()], ); } Widget _buildLeftPage() { return Expanded( child: Stack( children: [ PdfPageView( key: ValueKey(currentPageNumber), document: document, pageNumber: currentPageNumber, maximumDpi: 300, alignment: _showTwoPages ? Alignment.centerRight : Alignment.center, ), _buildPageIndicator(currentPageNumber), ], ), ); } Widget _buildRightPage() { final rightPageNumber = currentPageNumber + 1; return Expanded( child: Stack( children: [ PdfPageView( key: ValueKey(rightPageNumber), document: document, pageNumber: rightPageNumber, maximumDpi: 300, alignment: Alignment.centerLeft, ), _buildPageIndicator(rightPageNumber), ], ), ); } Widget _buildPageIndicator(int pageNumber) { return Positioned.fill( child: Container( alignment: Alignment.bottomCenter, padding: const EdgeInsets.only(bottom: 5), child: Text('$pageNumber / $numPages'), ), ); } // --------------------------------------------------------------------------- // Page Size Calculations // --------------------------------------------------------------------------- /// Calculates scaled page sizes for the current view. /// /// Returns a tuple of (leftPageSize, rightPageSize). /// rightPageSize is null when not in two-page mode. (Size, Size?) calculateScaledPageSizes(Size parentSize) { if (config.twoPageMode) { return _calculateTwoPageSizes(parentSize); } return (_calculateSinglePageSize(parentSize), null); } (Size, Size?) _calculateTwoPageSizes(Size parentSize) { final leftSize = _getUnscaledPageSize(currentPageNumber); final rightSize = numPages > currentPageNumber ? _getUnscaledPageSize(currentPageNumber + 1) : leftSize; // Combine pages for scaling calculation final combinedSize = Size( leftSize.width + rightSize.width, max(leftSize.height, rightSize.height), ); final scaledCombined = _scaleToFit(parentSize, combinedSize); final scaleFactor = scaledCombined.width / combinedSize.width; return (leftSize * scaleFactor, rightSize * scaleFactor); } Size _calculateSinglePageSize(Size parentSize) { return _scaleToFit(parentSize, _getUnscaledPageSize(currentPageNumber)); } Size _getUnscaledPageSize(int pageNumber) { return document.pages.elementAt(pageNumber - 1).size; } /// Scales a page size to fit within parent bounds while maintaining aspect ratio. Size _scaleToFit(Size parentSize, Size pageSize) { // Determine if height or width is the limiting factor if (parentSize.aspectRatio > pageSize.aspectRatio) { // Constrained by height final height = parentSize.height; final width = height * pageSize.aspectRatio; return Size(width, height); } else { // Constrained by width final width = parentSize.width; final height = width / pageSize.aspectRatio; return Size(width, height); } } }