diff --git a/CHANGELOG.md b/CHANGELOG.md index eaad34da..a370fb19 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 (aligns with Flutter's own iOS 13.0+ requirement) +- 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/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 { 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..3e03fe3b 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,16 @@ public void onInitiallyRendered(int pages) { } }) .load(); + + Float maxZoom = getFloat(params, "maxZoom"); + Float minZoom = getFloat(params, "minZoom"); + 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/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; 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/FlutterPDFView.m b/ios/flutter_pdfview/Sources/flutter_pdfview/FlutterPDFView.m index 3f393ae0..39dbbefb 100644 --- a/ios/flutter_pdfview/Sources/flutter_pdfview/FlutterPDFView.m +++ b/ios/flutter_pdfview/Sources/flutter_pdfview/FlutterPDFView.m @@ -105,6 +105,9 @@ @implementation FLTPDFView { BOOL _defaultPageSet; BOOL _isIPad; BOOL _isScrolling; + CGFloat _maxScaleFactor; + CGFloat _minScaleFactor; + BOOL _hasSentInitialPage; } - (instancetype)initWithFrame:(CGRect)frame @@ -140,7 +143,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 +171,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,16 +265,29 @@ - (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; + 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); } 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 + + + diff --git a/lib/flutter_pdfview.dart b/lib/flutter_pdfview.dart index 3f6d580d..e428e174 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, }; } @@ -330,15 +350,21 @@ 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; } } 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 +422,7 @@ class PDFViewController { Future setPage(int page) async { final bool? isSet = - await _channel.invokeMethod('setPage', { + await _channel.invokeMethod('setPage', { 'page': page, }); return isSet; 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: