Skip to content

Commit

Permalink
feat(passkit_ui): Export rendered PkPass as image (#79)
Browse files Browse the repository at this point in the history
  • Loading branch information
ueman authored Aug 30, 2024
1 parent a51e2e8 commit 8c4fdc7
Show file tree
Hide file tree
Showing 9 changed files with 119 additions and 3 deletions.
23 changes: 23 additions & 0 deletions app/lib/pass_backside/pass_backside_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_linkify/flutter_linkify.dart';
import 'package:geocoding/geocoding.dart' as geocoding;
import 'package:passkit/passkit.dart';
import 'package:passkit_ui/passkit_ui.dart';
import 'package:path_provider/path_provider.dart';
import 'package:share_plus/share_plus.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:path/path.dart' as p;

class PassBackSidePageArgs {
PassBackSidePageArgs(this.pass, this.showDelete);
Expand Down Expand Up @@ -131,6 +134,10 @@ class _PassBacksidePageState extends State<PassBacksidePage> {
icon: Icon(Icons.adaptive.share),
onPressed: _sharePass,
),
IconButton(
icon: const Icon(Icons.image),
onPressed: _sharePassAsImage,
),
],
),
body: ListView(
Expand Down Expand Up @@ -234,6 +241,22 @@ class _PassBacksidePageState extends State<PassBacksidePage> {
Share.shareXFiles([XFile.fromData(data)]);
}

void _sharePassAsImage() async {
final name = widget.pass.pass.serialNumber;
final imageData = await exportPassAsImage(widget.pass);
if (Platform.isMacOS || Platform.isLinux || Platform.isWindows) {
final dir = await getApplicationDocumentsDirectory();

await File(p.join(dir.path, '$name.png')).writeAsBytes(imageData!);
} else {
if (imageData != null) {
await Share.shareXFiles(
[XFile.fromData(imageData, name: 'pass.png', mimeType: 'image/png')],
);
}
}
}

void _onAppClick(Uri url) {
launchUrl(url);
}
Expand Down
4 changes: 4 additions & 0 deletions app/macos/Runner/Release.entitlements
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.files.downloads.read-write</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
</dict>
Expand Down
2 changes: 2 additions & 0 deletions passkit_ui/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## 0.0.5

- Add ability to create an image from a PkPass

## 0.0.4

- Improved fidelity in the visual representation of all pass types
Expand Down
6 changes: 5 additions & 1 deletion passkit_ui/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -73,5 +73,9 @@ Future<PkPass> loadPass(String path) async {
passData.offsetInBytes,
passData.lengthInBytes,
);
return PkPass.fromBytes(list);
return PkPass.fromBytes(
list,
skipChecksumVerification: true,
skipSignatureVerification: true,
);
}
2 changes: 1 addition & 1 deletion passkit_ui/example/macos/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0920;
LastUpgradeCheck = 1300;
LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
331C80D4294CF70F00263BE5 = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1300"
LastUpgradeVersion = "1510"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
1 change: 1 addition & 0 deletions passkit_ui/lib/passkit_ui.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export 'src/widgets/widgets.dart';
export 'src/extensions/extensions.dart';
export 'src/pk_pass_widget.dart';
export 'src/export_pass_image.dart';
81 changes: 81 additions & 0 deletions passkit_ui/lib/src/export_pass_image.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import 'dart:typed_data';

import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:meta/meta.dart';
import 'dart:ui' as ui;

import 'package:passkit/passkit.dart';
import 'package:passkit_ui/passkit_ui.dart';

/// Creates a PNG from the given [pass]
///
/// Remarks:
/// - Sometimes, images aren't correctly rendered on a pass. Calling this method
/// a second time with the same pass resolves that issue.
@experimental
Future<Uint8List?> exportPassAsImage(
PkPass pass, {
Size logicalSize = const Size(320, 460),
double pixelRatio = 3,
}) async {
final repaintBoundary = RenderRepaintBoundary();
final pipelineOwner = PipelineOwner();
final buildOwner = BuildOwner(focusManager: FocusManager());

try {
final renderView = RenderView(
view: ui.PlatformDispatcher.instance.implicitView!,
child: RenderPositionedBox(
alignment: Alignment.center,
child: repaintBoundary,
),
configuration: ViewConfiguration(
logicalConstraints: BoxConstraints.tight(logicalSize),
devicePixelRatio: pixelRatio,
),
);

pipelineOwner.rootNode = renderView;
renderView.prepareInitialFrame();

final widget = MediaQuery(
data: MediaQueryData(devicePixelRatio: pixelRatio),
child: Directionality(
textDirection: TextDirection.ltr,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Material(
color: Colors.transparent,
child: PkPassWidget(pass: pass),
),
],
),
),
);

final rootElement = RenderObjectToWidgetAdapter<RenderBox>(
container: repaintBoundary,
child: widget,
).attachToRenderTree(buildOwner);

buildOwner.buildScope(rootElement);
buildOwner.buildScope(rootElement);
buildOwner.finalizeTree();
pipelineOwner.flushLayout();
pipelineOwner.flushCompositingBits();
pipelineOwner.flushPaint();

final image = await repaintBoundary.toImage(pixelRatio: pixelRatio);

final byteData = await image.toByteData(format: ui.ImageByteFormat.png);

return byteData?.buffer.asUint8List(
byteData.offsetInBytes,
byteData.lengthInBytes,
);
} catch (e) {
throw Exception('Failed to render the widget: $e');
}
}
1 change: 1 addition & 0 deletions passkit_ui/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ dependencies:
csslib: ^1.0.0
flutter:
sdk: flutter
meta: any
passkit: ^0.0.5

dev_dependencies:
Expand Down

0 comments on commit 8c4fdc7

Please sign in to comment.