From 27dbf21cd3fb94b1e19f6eb276f6765e9be92152 Mon Sep 17 00:00:00 2001 From: endigo Date: Wed, 11 Mar 2026 21:46:06 +0800 Subject: [PATCH 1/7] fix(android): upgrade java compatibility from 1.8 to 17 Closes #334 Co-Authored-By: Claude Opus 4.6 --- android/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 7e1fc99c..113ee33e 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -30,8 +30,8 @@ android { compileSdk = 35 compileOptions { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } defaultConfig { From 22dd25a68e5eba7518fcc262d2e3c48f3e37028f Mon Sep 17 00:00:00 2001 From: endigo Date: Wed, 11 Mar 2026 21:46:18 +0800 Subject: [PATCH 2/7] feat(ios): add privacy manifest and update deployment target to 13.0 Closes #271 Co-Authored-By: Claude Opus 4.6 --- ios/flutter_pdfview.podspec | 3 ++- ios/flutter_pdfview/Package.swift | 6 ++++-- .../Sources/flutter_pdfview/PrivacyInfo.xcprivacy | 14 ++++++++++++++ 3 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 ios/flutter_pdfview/Sources/flutter_pdfview/PrivacyInfo.xcprivacy diff --git a/ios/flutter_pdfview.podspec b/ios/flutter_pdfview.podspec index 95d6c63d..74418664 100644 --- a/ios/flutter_pdfview.podspec +++ b/ios/flutter_pdfview.podspec @@ -18,7 +18,8 @@ Pod::Spec.new do |s| s.public_header_files = 'flutter_pdfview/Sources/flutter_pdfview/include/**/*.h' s.dependency 'Flutter' - s.ios.deployment_target = '11.0' + s.ios.deployment_target = '13.0' s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS' => 'armv7 arm64 x86_64' } + s.resource_bundles = { 'flutter_pdfview_privacy' => ['flutter_pdfview/Sources/flutter_pdfview/PrivacyInfo.xcprivacy'] } end diff --git a/ios/flutter_pdfview/Package.swift b/ios/flutter_pdfview/Package.swift index 684c5afd..87129a8d 100644 --- a/ios/flutter_pdfview/Package.swift +++ b/ios/flutter_pdfview/Package.swift @@ -6,7 +6,7 @@ import PackageDescription let package = Package( name: "flutter_pdfview", platforms: [ - .iOS("12.0"), + .iOS("13.0"), ], products: [ .library(name: "flutter-pdfview", targets: ["flutter_pdfview"]) @@ -16,7 +16,9 @@ let package = Package( .target( name: "flutter_pdfview", dependencies: [], - resources: [], + resources: [ + .process("PrivacyInfo.xcprivacy"), + ], cSettings: [ .headerSearchPath("include/flutter_pdfview") ] diff --git a/ios/flutter_pdfview/Sources/flutter_pdfview/PrivacyInfo.xcprivacy b/ios/flutter_pdfview/Sources/flutter_pdfview/PrivacyInfo.xcprivacy new file mode 100644 index 00000000..8c2bd468 --- /dev/null +++ b/ios/flutter_pdfview/Sources/flutter_pdfview/PrivacyInfo.xcprivacy @@ -0,0 +1,14 @@ + + + + + NSPrivacyCollectedDataTypes + + NSPrivacyTracking + + NSPrivacyTrackingDomains + + NSPrivacyAccessedAPITypes + + + From fb26b370cc44093b50dacb80dbc4eeedcaafa7ce Mon Sep 17 00:00:00 2001 From: endigo Date: Wed, 11 Mar 2026 21:46:35 +0800 Subject: [PATCH 3/7] fix(ios): onError not firing, initial onPageChanged, landscape zoom - Dispatch onError asynchronously so Dart handler is ready (#211) - Fire onPageChanged after initial render completes (#66) - Preserve user-configured maxScaleFactor in layoutSubviews (#247) - Add minScaleFactor support from Dart parameters Closes #211 Closes #66 Closes #247 Co-Authored-By: Claude Opus 4.6 --- .../Sources/flutter_pdfview/FlutterPDFView.m | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/ios/flutter_pdfview/Sources/flutter_pdfview/FlutterPDFView.m b/ios/flutter_pdfview/Sources/flutter_pdfview/FlutterPDFView.m index 3f393ae0..2b112edd 100644 --- a/ios/flutter_pdfview/Sources/flutter_pdfview/FlutterPDFView.m +++ b/ios/flutter_pdfview/Sources/flutter_pdfview/FlutterPDFView.m @@ -105,6 +105,8 @@ @implementation FLTPDFView { BOOL _defaultPageSet; BOOL _isIPad; BOOL _isScrolling; + CGFloat _maxScaleFactor; + CGFloat _minScaleFactor; } - (instancetype)initWithFrame:(CGRect)frame @@ -140,7 +142,10 @@ - (instancetype)initWithFrame:(CGRect)frame if (document == nil) { - [_controller invokeChannelMethod:@"onError" arguments:@{@"error" : @"cannot create document: File not in PDF format or corrupted."}]; + __weak __typeof__(self) weakSelf = self; + dispatch_async(dispatch_get_main_queue(), ^{ + [weakSelf->_controller invokeChannelMethod:@"onError" arguments:@{@"error" : @"cannot create document: File not in PDF format or corrupted."}]; + }); } else { _pdfView.autoresizesSubviews = YES; _pdfView.autoresizingMask = UIViewAutoresizingFlexibleWidth; @@ -165,7 +170,13 @@ - (instancetype)initWithFrame:(CGRect)frame } _pdfView.document = document; - _pdfView.maxScaleFactor = [args[@"maxZoom"] doubleValue]; + _maxScaleFactor = [args[@"maxZoom"] doubleValue]; + if (_maxScaleFactor <= 0) { + _maxScaleFactor = 4.0; + } + _minScaleFactor = [args[@"minZoom"] doubleValue]; + + _pdfView.maxScaleFactor = _maxScaleFactor; _pdfView.minScaleFactor = _pdfView.scaleFactorForSizeToFit; NSString* password = args[@"password"]; @@ -253,8 +264,9 @@ - (void)layoutSubviews { // Wrap layout updates in try-catch for safety @try { _pdfView.frame = self.frame; - _pdfView.minScaleFactor = _pdfView.scaleFactorForSizeToFit; - _pdfView.maxScaleFactor = 4.0; + CGFloat fitScale = _pdfView.scaleFactorForSizeToFit; + _pdfView.minScaleFactor = (_minScaleFactor > 0) ? fmin(fitScale, _minScaleFactor) : fitScale; + _pdfView.maxScaleFactor = _maxScaleFactor; if (_autoSpacing) { _pdfView.scaleFactor = _pdfView.scaleFactorForSizeToFit; } @@ -301,6 +313,14 @@ -(void)handlePageChanged:(NSNotification*)notification { -(void)handleRenderCompleted: (NSNumber*)pages { [_controller invokeChannelMethod:@"onRender" arguments:@{@"pages" : pages}]; + + if (_pdfView.document != nil && _pdfView.currentPage != nil) { + NSUInteger currentPageIndex = [_pdfView.document indexForPage:_pdfView.currentPage]; + [_controller invokeChannelMethod:@"onPageChanged" arguments:@{ + @"page" : [NSNumber numberWithUnsignedLong:currentPageIndex], + @"total" : pages + }]; + } } - (void)PDFViewWillClickOnLink:(PDFView *)sender From aeff587b8590cce7fa6161bb3e2553cf90e07d0a Mon Sep 17 00:00:00 2001 From: endigo Date: Wed, 11 Mar 2026 21:46:48 +0800 Subject: [PATCH 4/7] feat: add configurable maxZoom and minZoom parameters Closes #296 Co-Authored-By: Claude Opus 4.6 --- .../pdfviewflutter/FlutterPDFView.java | 8 ++++ lib/flutter_pdfview.dart | 38 ++++++++++++++----- 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/android/src/main/java/io/endigo/plugins/pdfviewflutter/FlutterPDFView.java b/android/src/main/java/io/endigo/plugins/pdfviewflutter/FlutterPDFView.java index 79fa32d5..f7d1266b 100644 --- a/android/src/main/java/io/endigo/plugins/pdfviewflutter/FlutterPDFView.java +++ b/android/src/main/java/io/endigo/plugins/pdfviewflutter/FlutterPDFView.java @@ -25,6 +25,9 @@ import com.github.barteksc.pdfviewer.link.LinkHandler; public class FlutterPDFView implements PlatformView, MethodCallHandler { + private static final float DEFAULT_MAX_ZOOM = 4.0f; + private static final float DEFAULT_MIN_ZOOM = 1.0f; + private final PDFView pdfView; private final MethodChannel methodChannel; private final LinkHandler linkHandler; @@ -113,6 +116,11 @@ public void onInitiallyRendered(int pages) { } }) .load(); + + Float maxZoom = getFloat(params, "maxZoom"); + Float minZoom = getFloat(params, "minZoom"); + pdfView.setMaxZoom(maxZoom != null ? maxZoom : DEFAULT_MAX_ZOOM); + pdfView.setMinZoom(minZoom != null ? minZoom : DEFAULT_MIN_ZOOM); } } diff --git a/lib/flutter_pdfview.dart b/lib/flutter_pdfview.dart index 3f6d580d..eec7f133 100644 --- a/lib/flutter_pdfview.dart +++ b/lib/flutter_pdfview.dart @@ -43,7 +43,12 @@ class PDFView extends StatefulWidget { this.fitPolicy = FitPolicy.WIDTH, this.preventLinkNavigation = false, this.backgroundColor, + this.maxZoom = 4.0, + this.minZoom = 1.0, }) : assert(filePath != null || pdfData != null), + assert(maxZoom > 0, 'maxZoom must be greater than 0'), + assert(minZoom > 0, 'minZoom must be greater than 0'), + assert(maxZoom >= minZoom, 'maxZoom must be >= minZoom'), super(key: key); @override @@ -135,11 +140,17 @@ class PDFView extends StatefulWidget { /// Use to change the background color. ex : "#FF0000" => red final Color? backgroundColor; + + /// Maximum zoom level. Defaults to 4.0. + final double maxZoom; + + /// Minimum zoom level. Defaults to 1.0 (fit to page). + final double minZoom; } class _PDFViewState extends State { final Completer _controller = - Completer(); + Completer(); @override Widget build(BuildContext context) { @@ -147,9 +158,9 @@ class _PDFViewState extends State { return PlatformViewLink( viewType: 'plugins.endigo.io/pdfview', surfaceFactory: ( - BuildContext context, - PlatformViewController controller, - ) { + BuildContext context, + PlatformViewController controller, + ) { return AndroidViewSurface( controller: controller as AndroidViewController, gestureRecognizers: widget.gestureRecognizers ?? @@ -197,7 +208,7 @@ class _PDFViewState extends State { void didUpdateWidget(PDFView oldWidget) { super.didUpdateWidget(oldWidget); _controller.future.then( - (PDFViewController controller) => controller._updateWidget(widget)); + (PDFViewController controller) => controller._updateWidget(widget)); } @override @@ -257,6 +268,8 @@ class _PDFViewSettings { this.fitPolicy, this.preventLinkNavigation, this.backgroundColor, + this.maxZoom, + this.minZoom, }); static _PDFViewSettings fromWidget(PDFView widget) { @@ -276,6 +289,8 @@ class _PDFViewSettings { fitPolicy: widget.fitPolicy, preventLinkNavigation: widget.preventLinkNavigation, backgroundColor: widget.backgroundColor, + maxZoom: widget.maxZoom, + minZoom: widget.minZoom, ); } @@ -296,6 +311,9 @@ class _PDFViewSettings { final Color? backgroundColor; + final double? maxZoom; + final double? minZoom; + Map toMap() { return { 'enableSwipe': enableSwipe, @@ -313,6 +331,8 @@ class _PDFViewSettings { 'fitPolicy': fitPolicy.toString(), 'preventLinkNavigation': preventLinkNavigation, 'backgroundColor': backgroundColor?.value, + 'maxZoom': maxZoom, + 'minZoom': minZoom, }; } @@ -336,9 +356,9 @@ class _PDFViewSettings { class PDFViewController { PDFViewController._( - int id, - PDFView widget, - ) : _channel = MethodChannel('plugins.endigo.io/pdfview_$id'), + int id, + PDFView widget, + ) : _channel = MethodChannel('plugins.endigo.io/pdfview_$id'), _widget = widget { _settings = _PDFViewSettings.fromWidget(widget); _channel.setMethodCallHandler(_onMethodCall); @@ -396,7 +416,7 @@ class PDFViewController { Future setPage(int page) async { final bool? isSet = - await _channel.invokeMethod('setPage', { + await _channel.invokeMethod('setPage', { 'page': page, }); return isSet; From 4f68547974961d46f5b10a19870ee03fca9b1b59 Mon Sep 17 00:00:00 2001 From: endigo Date: Wed, 11 Mar 2026 21:47:05 +0800 Subject: [PATCH 5/7] fix(example): remove undefined nightModeBackgroundColor parameter Co-Authored-By: Claude Opus 4.6 --- example/lib/main.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index a80d1c7d..64b83528 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -231,7 +231,6 @@ class _PDFScreenState extends State with WidgetsBindingObserver { false, // if set to true the link is handled in flutter backgroundColor: Color(0xFFFEF7FF), nightMode: true, - nightModeBackgroundColor: Colors.amber, onRender: (_pages) { setState(() { pages = _pages; From 0e885f943d078de33dbcc9b996e5a9423c5911f8 Mon Sep 17 00:00:00 2001 From: endigo Date: Wed, 11 Mar 2026 21:47:18 +0800 Subject: [PATCH 6/7] chore: bump version to 1.4.5 Co-Authored-By: Claude Opus 4.6 --- CHANGELOG.md | 11 +++++++++++ pubspec.yaml | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eaad34da..bfbf46cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +## 1.4.5 + +- Upgrade Java source/target compatibility from 1.8 to 17 [#334](https://github.com/endigo/flutter_pdfview/issues/334) +- Add iOS Privacy Manifest (PrivacyInfo.xcprivacy) [#271](https://github.com/endigo/flutter_pdfview/issues/271) +- Fix iOS `onError` callback not firing for invalid documents [#211](https://github.com/endigo/flutter_pdfview/issues/211) +- Fix iOS `onPageChanged` not triggered on first load [#66](https://github.com/endigo/flutter_pdfview/issues/66) +- Fix iOS landscape PDF wrong initial zoom by preserving user-configured max scale factor [#247](https://github.com/endigo/flutter_pdfview/issues/247) +- Add configurable `maxZoom` and `minZoom` parameters [#296](https://github.com/endigo/flutter_pdfview/issues/296) +- Update iOS deployment target to 13.0 in podspec and Package.swift +- Fix example app build error with undefined `nightModeBackgroundColor` parameter + ## 1.4.4 - Fixes an Android rendering [#330](https://github.com/endigo/flutter_pdfview/pull/330) @TimelessLin diff --git a/pubspec.yaml b/pubspec.yaml index 7c2cf021..101829b7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_pdfview description: A Flutter plugin that provides a PDFView widget on Android and iOS. -version: 1.4.4 +version: 1.4.5 homepage: https://github.com/endigo/flutter_pdfview environment: From 07b070ec9f20d26e9356100c22536f1a8164fd04 Mon Sep 17 00:00:00 2001 From: endigo Date: Wed, 11 Mar 2026 22:02:35 +0800 Subject: [PATCH 7/7] fix: address code review feedback - iOS: use user's minScaleFactor directly instead of fmin with fitScale - iOS: defer initial onPageChanged until layoutSubviews applies defaultPage - iOS: guard against double-fire with _hasSentInitialPage flag - Android: validate maxZoom >= minZoom with defensive clamping - Dart: propagate maxZoom/minZoom in updatesMap for widget rebuilds Co-Authored-By: Claude Opus 4.6 --- CHANGELOG.md | 2 +- .../pdfviewflutter/FlutterPDFView.java | 9 +++++-- .../Sources/flutter_pdfview/FlutterPDFView.m | 27 +++++++++++-------- lib/flutter_pdfview.dart | 6 +++++ 4 files changed, 30 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bfbf46cd..a370fb19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ - Fix iOS `onPageChanged` not triggered on first load [#66](https://github.com/endigo/flutter_pdfview/issues/66) - Fix iOS landscape PDF wrong initial zoom by preserving user-configured max scale factor [#247](https://github.com/endigo/flutter_pdfview/issues/247) - Add configurable `maxZoom` and `minZoom` parameters [#296](https://github.com/endigo/flutter_pdfview/issues/296) -- Update iOS deployment target to 13.0 in podspec and Package.swift +- Update iOS deployment target to 13.0 in podspec and Package.swift (aligns with Flutter's own iOS 13.0+ requirement) - Fix example app build error with undefined `nightModeBackgroundColor` parameter ## 1.4.4 diff --git a/android/src/main/java/io/endigo/plugins/pdfviewflutter/FlutterPDFView.java b/android/src/main/java/io/endigo/plugins/pdfviewflutter/FlutterPDFView.java index f7d1266b..3e03fe3b 100644 --- a/android/src/main/java/io/endigo/plugins/pdfviewflutter/FlutterPDFView.java +++ b/android/src/main/java/io/endigo/plugins/pdfviewflutter/FlutterPDFView.java @@ -119,8 +119,13 @@ public void onInitiallyRendered(int pages) { Float maxZoom = getFloat(params, "maxZoom"); Float minZoom = getFloat(params, "minZoom"); - pdfView.setMaxZoom(maxZoom != null ? maxZoom : DEFAULT_MAX_ZOOM); - pdfView.setMinZoom(minZoom != null ? minZoom : DEFAULT_MIN_ZOOM); + float effectiveMax = maxZoom != null ? maxZoom : DEFAULT_MAX_ZOOM; + float effectiveMin = minZoom != null ? minZoom : DEFAULT_MIN_ZOOM; + if (effectiveMin > effectiveMax) { + effectiveMin = effectiveMax; + } + pdfView.setMaxZoom(effectiveMax); + pdfView.setMinZoom(effectiveMin); } } diff --git a/ios/flutter_pdfview/Sources/flutter_pdfview/FlutterPDFView.m b/ios/flutter_pdfview/Sources/flutter_pdfview/FlutterPDFView.m index 2b112edd..39dbbefb 100644 --- a/ios/flutter_pdfview/Sources/flutter_pdfview/FlutterPDFView.m +++ b/ios/flutter_pdfview/Sources/flutter_pdfview/FlutterPDFView.m @@ -107,6 +107,7 @@ @implementation FLTPDFView { BOOL _isScrolling; CGFloat _maxScaleFactor; CGFloat _minScaleFactor; + BOOL _hasSentInitialPage; } - (instancetype)initWithFrame:(CGRect)frame @@ -265,16 +266,28 @@ - (void)layoutSubviews { @try { _pdfView.frame = self.frame; CGFloat fitScale = _pdfView.scaleFactorForSizeToFit; - _pdfView.minScaleFactor = (_minScaleFactor > 0) ? fmin(fitScale, _minScaleFactor) : fitScale; - _pdfView.maxScaleFactor = _maxScaleFactor; + CGFloat minScale = (_minScaleFactor > 0) ? _minScaleFactor : fitScale; + _pdfView.minScaleFactor = minScale; + _pdfView.maxScaleFactor = fmax(_maxScaleFactor, minScale); if (_autoSpacing) { - _pdfView.scaleFactor = _pdfView.scaleFactorForSizeToFit; + CGFloat clampedScale = fmin(fmax(fitScale, _pdfView.minScaleFactor), _pdfView.maxScaleFactor); + _pdfView.scaleFactor = clampedScale; } if (!_defaultPageSet && _defaultPage != nil) { [_pdfView goToPage: _defaultPage]; _defaultPageSet = true; } + + if (!_hasSentInitialPage && _defaultPageSet && _pdfView.document != nil && _pdfView.currentPage != nil) { + _hasSentInitialPage = YES; + NSUInteger currentPageIndex = [_pdfView.document indexForPage:_pdfView.currentPage]; + NSUInteger pageCount = [_pdfView.document pageCount]; + [_controller invokeChannelMethod:@"onPageChanged" arguments:@{ + @"page" : [NSNumber numberWithUnsignedLong:currentPageIndex], + @"total" : [NSNumber numberWithUnsignedLong:pageCount] + }]; + } } @catch (NSException *exception) { NSLog(@"Warning: Layout update failed: %@", exception.reason); } @@ -313,14 +326,6 @@ -(void)handlePageChanged:(NSNotification*)notification { -(void)handleRenderCompleted: (NSNumber*)pages { [_controller invokeChannelMethod:@"onRender" arguments:@{@"pages" : pages}]; - - if (_pdfView.document != nil && _pdfView.currentPage != nil) { - NSUInteger currentPageIndex = [_pdfView.document indexForPage:_pdfView.currentPage]; - [_controller invokeChannelMethod:@"onPageChanged" arguments:@{ - @"page" : [NSNumber numberWithUnsignedLong:currentPageIndex], - @"total" : pages - }]; - } } - (void)PDFViewWillClickOnLink:(PDFView *)sender diff --git a/lib/flutter_pdfview.dart b/lib/flutter_pdfview.dart index eec7f133..e428e174 100644 --- a/lib/flutter_pdfview.dart +++ b/lib/flutter_pdfview.dart @@ -350,6 +350,12 @@ class _PDFViewSettings { if (preventLinkNavigation != newSettings.preventLinkNavigation) { updates['preventLinkNavigation'] = newSettings.preventLinkNavigation; } + if (maxZoom != newSettings.maxZoom) { + updates['maxZoom'] = newSettings.maxZoom; + } + if (minZoom != newSettings.minZoom) { + updates['minZoom'] = newSettings.minZoom; + } return updates; } }