mirror of
https://gitlab.com/TheOneWithTheBraid/dart_pkpass.git
synced 2025-07-06 05:18:47 +00:00
feat: implement web service
Signed-off-by: The one with the braid <info@braid.business>
This commit is contained in:
parent
44494eaa90
commit
6e7f19a764
26 changed files with 331 additions and 512 deletions
89
lib/pkpass/models/barcode.dart
Normal file
89
lib/pkpass/models/barcode.dart
Normal file
|
@ -0,0 +1,89 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:barcode/barcode.dart';
|
||||
|
||||
/// Information about a pass’s barcode.
|
||||
///
|
||||
/// Use with Flutter:
|
||||
///
|
||||
/// ```dart
|
||||
/// BarcodeWidget.fromBytes(
|
||||
/// barcode: Barcode.fromType(myPassBarcode.format),
|
||||
/// data: myPassBarcode.barcodeData,
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
/// Please not the spec requires you to display the [altText] next to the
|
||||
/// barcode in case available.
|
||||
class PassBarcode {
|
||||
/// the [Encoding] supported for [messageEncoding]. Can be expanded at runtime.
|
||||
///
|
||||
/// Default values are
|
||||
/// - iso-8859-1: [Latin1Codec]
|
||||
/// - utf-8: [Utf8Codec]
|
||||
static Map<String, Encoding> supportedCodecs = {
|
||||
'iso-8859-1': Latin1Codec(),
|
||||
'iso-8859': Latin1Codec(),
|
||||
'iso8859': Latin1Codec(),
|
||||
'utf-8': Utf8Codec(),
|
||||
'utf8': Utf8Codec(),
|
||||
};
|
||||
|
||||
static const _allowedFormats = {
|
||||
'PKBarcodeFormatQR': BarcodeType.QrCode,
|
||||
'PKBarcodeFormatPDF417': BarcodeType.PDF417,
|
||||
'PKBarcodeFormatAztec': BarcodeType.Aztec,
|
||||
'PKBarcodeFormatCode128': BarcodeType.Code128,
|
||||
};
|
||||
|
||||
/// Barcode format. For the barcode dictionary, you can use only the
|
||||
/// following values: PKBarcodeFormatQR, PKBarcodeFormatPDF417, or
|
||||
/// PKBarcodeFormatAztec. For dictionaries in the barcodes array, you may
|
||||
/// also use PKBarcodeFormatCode128.
|
||||
///
|
||||
/// The spec defined keys are converted into the corresponding [BarcodeType]
|
||||
/// representations.
|
||||
final BarcodeType format;
|
||||
|
||||
/// Message or payload to be displayed as a barcode.
|
||||
///
|
||||
/// Do not use directly, use the encoded [barcodeData] instead.
|
||||
final String message;
|
||||
|
||||
/// Text encoding that is used to convert the message from the string
|
||||
/// representation to a data representation to render the barcode.
|
||||
///
|
||||
/// The value is typically iso-8859-1, but you may use another encoding that
|
||||
/// is supported by your barcode scanning infrastructure.
|
||||
///
|
||||
/// Only supported values by this packages are:
|
||||
/// - iso-8859-1
|
||||
/// - utf-8
|
||||
///
|
||||
/// Custom codecs can be provided as [Codec] in [PassBarcode.supportedCodecs].
|
||||
final Encoding messageEncoding;
|
||||
|
||||
/// Text displayed near the barcode. For example, a human-readable version
|
||||
/// of the barcode data in case the barcode doesn’t scan.
|
||||
final String? altText;
|
||||
|
||||
const PassBarcode({
|
||||
required this.format,
|
||||
required this.message,
|
||||
required this.messageEncoding,
|
||||
required this.altText,
|
||||
});
|
||||
|
||||
factory PassBarcode.fromJson(Map<String, Object?> json) => PassBarcode(
|
||||
format: _allowedFormats[json['format']]!,
|
||||
message: json['message'] as String,
|
||||
messageEncoding:
|
||||
supportedCodecs[(json['messageEncoding'] as String).toLowerCase()]!,
|
||||
altText: json['altText'] as String?,
|
||||
);
|
||||
|
||||
/// Correctly encoded byte list to be displayed in the [barcode].
|
||||
Uint8List get barcodeData =>
|
||||
Uint8List.fromList(messageEncoding.encode(message));
|
||||
}
|
30
lib/pkpass/models/beacon.dart
Normal file
30
lib/pkpass/models/beacon.dart
Normal file
|
@ -0,0 +1,30 @@
|
|||
/// Information about a location beacon.
|
||||
class Beacon {
|
||||
/// Unique identifier of a Bluetooth Low Energy location beacon.
|
||||
final String proximityUUID;
|
||||
|
||||
/// Major identifier of a Bluetooth Low Energy location beacon.
|
||||
final double? major;
|
||||
|
||||
/// Minor identifier of a Bluetooth Low Energy location beacon.
|
||||
final double? minor;
|
||||
|
||||
/// Text displayed on the lock screen when the pass is currently relevant.
|
||||
/// For example, a description of the nearby location such as
|
||||
/// “Store nearby on 1st and Main.”
|
||||
final String? relevantText;
|
||||
|
||||
const Beacon({
|
||||
required this.proximityUUID,
|
||||
this.major,
|
||||
this.minor,
|
||||
this.relevantText,
|
||||
});
|
||||
|
||||
factory Beacon.fromJson(Map<String, Object?> json) => Beacon(
|
||||
proximityUUID: json['proximityUUID'] as String,
|
||||
major: json['major'] as double?,
|
||||
minor: json['minor'] as double?,
|
||||
relevantText: json['relevantText'] as String?,
|
||||
);
|
||||
}
|
30
lib/pkpass/models/location.dart
Normal file
30
lib/pkpass/models/location.dart
Normal file
|
@ -0,0 +1,30 @@
|
|||
/// Information about a location.
|
||||
class Location {
|
||||
/// Latitude, in degrees, of the location.
|
||||
final double latitude;
|
||||
|
||||
/// Longitude, in degrees, of the location.
|
||||
final double longitude;
|
||||
|
||||
/// Altitude, in meters, of the location.
|
||||
final double? altitude;
|
||||
|
||||
/// Text displayed on the lock screen when the pass is currently relevant.
|
||||
/// For example, a description of the nearby location such as
|
||||
/// “Store nearby on 1st and Main.”
|
||||
final String? relevantText;
|
||||
|
||||
const Location({
|
||||
required this.latitude,
|
||||
required this.longitude,
|
||||
this.altitude,
|
||||
this.relevantText,
|
||||
});
|
||||
|
||||
factory Location.fromJson(Map<String, Object?> json) => Location(
|
||||
latitude: json['latitude'] as double,
|
||||
longitude: json['longitude'] as double,
|
||||
altitude: json['altitude'] as double?,
|
||||
relevantText: json['relevantText'] as String?,
|
||||
);
|
||||
}
|
210
lib/pkpass/models/pass.dart
Normal file
210
lib/pkpass/models/pass.dart
Normal file
|
@ -0,0 +1,210 @@
|
|||
import 'package:intl/locale.dart';
|
||||
|
||||
import 'package:pkpass/pkpass.dart';
|
||||
import 'package:pkpass/pkpass/utils/mabe_decode.dart';
|
||||
|
||||
/// Information that is required for all passes.
|
||||
class PassMetadata {
|
||||
/// Brief description of the pass, used by accessibility technologies.
|
||||
///
|
||||
/// Don’t try to include all of the data on the pass in its description,
|
||||
/// just include enough detail to distinguish passes of the same type.
|
||||
final String description;
|
||||
|
||||
/// Version of the file format. The value must be 1.
|
||||
final int formatVersion;
|
||||
|
||||
/// Display name of the organization that originated and signed the pass.
|
||||
final String organizationName;
|
||||
|
||||
/// Pass type identifier, as issued by Apple. The value must correspond with
|
||||
/// your signing certificate.
|
||||
final String passTypeIdentifier;
|
||||
|
||||
/// Serial number that uniquely identifies the pass. No two passes with the
|
||||
/// same pass type identifier may have the same serial number.
|
||||
final String serialNumber;
|
||||
|
||||
/// Team identifier of the organization that originated and signed the pass,
|
||||
/// as issued by Apple.
|
||||
final String teamIdentifier;
|
||||
|
||||
/// A URL to be passed to the associated app when launching it.
|
||||
final String? appLaunchURL;
|
||||
|
||||
/// Date and time when the pass expires.
|
||||
final DateTime? expirationDate;
|
||||
|
||||
/// Indicates that the pass is void—for example, a one time use coupon that
|
||||
/// has been redeemed. The default value is false.
|
||||
final bool voided;
|
||||
|
||||
/// Beacons marking locations where the pass is relevant.
|
||||
final List<Beacon> beacons;
|
||||
|
||||
/// Locations where the pass is relevant. For example, the location of your store.
|
||||
final List<Location> locations;
|
||||
|
||||
/// Maximum distance in meters from a relevant latitude and longitude that
|
||||
/// the pass is relevant. This number is compared to the pass’s default
|
||||
/// distance and the smaller value is used.
|
||||
final int? maxDistance;
|
||||
|
||||
/// Recommended for event tickets and boarding passes; otherwise optional.
|
||||
/// Date and time when the pass becomes relevant. For example, the start
|
||||
/// time of a movie.
|
||||
final DateTime? relevantDate;
|
||||
|
||||
/// Information specific to a boarding pass.
|
||||
final PassStructureDictionary? boardingPass;
|
||||
|
||||
/// Information specific to a coupon.
|
||||
final PassStructureDictionary? coupon;
|
||||
|
||||
/// Information specific to an event ticket.
|
||||
final PassStructureDictionary? eventTicket;
|
||||
|
||||
/// Information specific to a generic pass.
|
||||
final PassStructureDictionary? generic;
|
||||
|
||||
/// Information specific to a store card.
|
||||
final PassStructureDictionary? storeCard;
|
||||
|
||||
/// Information specific to the pass’s barcode.
|
||||
/// The system uses the first valid barcode dictionary in the array.
|
||||
/// Additional dictionaries can be added as fallbacks.
|
||||
final List<PassBarcode> barcodes;
|
||||
|
||||
/// Background color of the pass.
|
||||
final int? backgroundColor;
|
||||
|
||||
/// Foreground color of the pass.
|
||||
final int? foregroundColor;
|
||||
|
||||
/// Optional for event tickets and boarding passes; otherwise not allowed.
|
||||
/// Identifier used to group related passes. If a grouping identifier is
|
||||
/// specified, passes with the same style, pass type identifier, and grouping
|
||||
/// identifier are displayed as a group. Otherwise, passes are grouped
|
||||
/// automatically.
|
||||
///
|
||||
/// Use this to group passes that are tightly related, such as the boarding
|
||||
/// passes for different connections of the same trip.
|
||||
final String? groupingIdentifier;
|
||||
|
||||
/// Color of the label text.
|
||||
final int? labelColor;
|
||||
|
||||
/// Text displayed next to the logo on the pass.
|
||||
final String? logoText;
|
||||
|
||||
/// Information used to update passes using the web service.
|
||||
final PassWebService? webService;
|
||||
|
||||
const PassMetadata({
|
||||
required this.description,
|
||||
required this.formatVersion,
|
||||
required this.organizationName,
|
||||
required this.passTypeIdentifier,
|
||||
required this.serialNumber,
|
||||
required this.teamIdentifier,
|
||||
this.appLaunchURL,
|
||||
this.expirationDate,
|
||||
this.voided = false,
|
||||
this.beacons = const [],
|
||||
this.locations = const [],
|
||||
this.maxDistance,
|
||||
this.relevantDate,
|
||||
this.boardingPass,
|
||||
this.coupon,
|
||||
this.eventTicket,
|
||||
this.generic,
|
||||
this.storeCard,
|
||||
this.barcodes = const [],
|
||||
this.backgroundColor,
|
||||
this.foregroundColor,
|
||||
this.groupingIdentifier,
|
||||
this.labelColor,
|
||||
this.logoText,
|
||||
this.webService,
|
||||
});
|
||||
|
||||
factory PassMetadata.fromJson(Map<String, Object?> json) => PassMetadata(
|
||||
description: json['description'] as String,
|
||||
formatVersion: json['formatVersion'] as int,
|
||||
organizationName: json['organizationName'] as String,
|
||||
passTypeIdentifier: json['passTypeIdentifier'] as String,
|
||||
serialNumber: json['serialNumber'] as String,
|
||||
teamIdentifier: json['teamIdentifier'] as String,
|
||||
boardingPass: json['boardingPass'] == null
|
||||
? null
|
||||
: PassStructureDictionary.fromJson(
|
||||
(json['boardingPass'] as Map).cast<String, Object?>(),
|
||||
),
|
||||
coupon: json['coupon'] == null
|
||||
? null
|
||||
: PassStructureDictionary.fromJson(
|
||||
(json['coupon'] as Map).cast<String, Object?>(),
|
||||
),
|
||||
eventTicket: json['eventTicket'] == null
|
||||
? null
|
||||
: PassStructureDictionary.fromJson(
|
||||
(json['eventTicket'] as Map).cast<String, Object?>(),
|
||||
),
|
||||
generic: json['generic'] == null
|
||||
? null
|
||||
: PassStructureDictionary.fromJson(
|
||||
(json['generic'] as Map).cast<String, Object?>(),
|
||||
),
|
||||
storeCard: json['storeCard'] == null
|
||||
? null
|
||||
: PassStructureDictionary.fromJson(
|
||||
(json['storeCard'] as Map).cast<String, Object?>(),
|
||||
),
|
||||
barcodes: (json['barcodes'] as List? ??
|
||||
[if (json['barcode'] != null) json['barcode']])
|
||||
.map((i) => PassBarcode.fromJson(i))
|
||||
.toList(),
|
||||
locations: (json['locations'] as List? ?? [])
|
||||
.map((i) => Location.fromJson(i))
|
||||
.toList(),
|
||||
appLaunchURL: json['appLaunchURL'] as String?,
|
||||
expirationDate:
|
||||
MaybeDecode.maybeDateTime(json['expirationDate'] as String?),
|
||||
voided: json['voided'] as bool? ?? false,
|
||||
beacons: (json['beacons'] as List? ?? [])
|
||||
.map((i) => Beacon.fromJson(i))
|
||||
.toList(),
|
||||
maxDistance: json['maxDistance'] as int?,
|
||||
relevantDate:
|
||||
MaybeDecode.maybeDateTime(json['relevantDate'] as String?),
|
||||
backgroundColor:
|
||||
MaybeDecode.maybeColor(json['backgroundColor'] as String?),
|
||||
foregroundColor:
|
||||
MaybeDecode.maybeColor(json['foregroundColor'] as String?),
|
||||
groupingIdentifier: json['locoText'] as String?,
|
||||
labelColor: MaybeDecode.maybeColor(json['labelColor'] as String?),
|
||||
logoText: json['locoText'] as String?,
|
||||
webService: PassWebService.maybe(
|
||||
authenticationToken: json['authenticationToken'] as String?,
|
||||
webServiceURL: json['webServiceURL'] as String?,
|
||||
),
|
||||
);
|
||||
|
||||
/// Localized version of [description] based on given [locale] and [pass].
|
||||
String getLocalizedDescription(PassFile pass, Locale? locale) {
|
||||
final localizations = pass.getLocalizations(locale);
|
||||
return localizations?[description] ?? description;
|
||||
}
|
||||
|
||||
/// Localized version of [organizationName] based on given [locale] and [pass].
|
||||
String getLocalizedOrganizationName(PassFile pass, Locale? locale) {
|
||||
final localizations = pass.getLocalizations(locale);
|
||||
return localizations?[organizationName] ?? organizationName;
|
||||
}
|
||||
|
||||
/// Localized version of [logoText] based on given [locale] and [pass].
|
||||
String? getLocalizedLogoText(PassFile pass, Locale? locale) {
|
||||
final localizations = pass.getLocalizations(locale);
|
||||
return localizations?[logoText] ?? logoText;
|
||||
}
|
||||
}
|
235
lib/pkpass/models/pass_structure_dictionary.dart
Normal file
235
lib/pkpass/models/pass_structure_dictionary.dart
Normal file
|
@ -0,0 +1,235 @@
|
|||
import 'package:intl/locale.dart';
|
||||
|
||||
import 'package:pkpass/pkpass.dart';
|
||||
import 'package:pkpass/pkpass/utils/mabe_decode.dart';
|
||||
|
||||
/// Keys that define the structure of the pass.
|
||||
///
|
||||
/// These keys are used for all pass styles and partition the fields into the various parts of the pass.
|
||||
class PassStructureDictionary {
|
||||
/// Fields to be displayed in the header on the front of the pass.
|
||||
///
|
||||
/// Use header fields sparingly; unlike all other fields, they remain visible when a stack of passes are displayed.
|
||||
final List<DictionaryField> headerFields;
|
||||
|
||||
/// Fields to be displayed prominently on the front of the pass.
|
||||
final List<DictionaryField> primaryFields;
|
||||
|
||||
/// Fields to be displayed on the front of the pass.
|
||||
final List<DictionaryField> secondaryFields;
|
||||
|
||||
/// Fields to be on the back of the pass.
|
||||
final List<DictionaryField> backFields;
|
||||
|
||||
/// Additional fields to be displayed on the front of the pass.
|
||||
final List<DictionaryField> auxiliaryFields;
|
||||
|
||||
/// Required for boarding passes; otherwise not allowed. Type of transit.
|
||||
final TransitType? transitType;
|
||||
|
||||
const PassStructureDictionary({
|
||||
this.headerFields = const [],
|
||||
this.primaryFields = const [],
|
||||
this.secondaryFields = const [],
|
||||
this.backFields = const [],
|
||||
this.auxiliaryFields = const [],
|
||||
this.transitType,
|
||||
});
|
||||
|
||||
factory PassStructureDictionary.fromJson(Map<String, Object?> json) =>
|
||||
PassStructureDictionary(
|
||||
headerFields: (json['headerFields'] as List?)
|
||||
?.map((i) => DictionaryField.fromJson(i))
|
||||
.toList() ??
|
||||
[],
|
||||
primaryFields: (json['primaryFields'] as List?)
|
||||
?.map((i) => DictionaryField.fromJson(i))
|
||||
.toList() ??
|
||||
[],
|
||||
secondaryFields: (json['secondaryFields'] as List?)
|
||||
?.map((i) => DictionaryField.fromJson(i))
|
||||
.toList() ??
|
||||
[],
|
||||
backFields: (json['backFields'] as List?)
|
||||
?.map((i) => DictionaryField.fromJson(i))
|
||||
.toList() ??
|
||||
[],
|
||||
auxiliaryFields: (json['auxiliaryFields'] as List?)
|
||||
?.map((i) => DictionaryField.fromJson(i))
|
||||
.toList() ??
|
||||
[],
|
||||
transitType: _TarnsitType.parse(json['transitType'] as String?),
|
||||
);
|
||||
}
|
||||
|
||||
/// Information about a field.
|
||||
class DictionaryField {
|
||||
/// The key must be unique within the scope of the entire pass. For example, “departure-gate.”
|
||||
final String key;
|
||||
|
||||
/// Value of the field, for example, 42.
|
||||
final DictionaryValue value;
|
||||
|
||||
/// Label text for the field.
|
||||
final String? label;
|
||||
|
||||
/// Format string for the alert text that is displayed when the pass is updated. The format string must contain the escape %@, which is replaced with the field’s new value. For example, “Gate changed to %@.”
|
||||
///
|
||||
/// If you don’t specify a change message, the user isn’t notified when the field changes.
|
||||
final String? changeMessage;
|
||||
|
||||
/// Alignment for the field’s contents.
|
||||
final PassTextAlign? textAlignment;
|
||||
|
||||
/// Attributed value of the field.
|
||||
///
|
||||
/// The value may contain HTML markup for links. Only the <a> tag and its href attribute are supported. For example, the following is key-value pair specifies a link with the text “Edit my profile”:
|
||||
///
|
||||
/// "attributedValue": "<a href='http://example.com/customers/123'>Edit my profile</a>"
|
||||
///
|
||||
/// This key’s value overrides the text specified by the value key.
|
||||
final DictionaryValue? attributedValue;
|
||||
|
||||
const DictionaryField({
|
||||
required this.key,
|
||||
required this.value,
|
||||
this.label,
|
||||
this.changeMessage,
|
||||
this.textAlignment,
|
||||
this.attributedValue,
|
||||
});
|
||||
|
||||
factory DictionaryField.fromJson(Map<String, Object?> json) =>
|
||||
DictionaryField(
|
||||
key: json['key'] as String,
|
||||
value: DictionaryValue.parse(json['value'] as String),
|
||||
label: json['label'] as String?,
|
||||
changeMessage: json['changeMessage'] as String?,
|
||||
textAlignment:
|
||||
MaybeDecode.maybeTextAlign(json['textAlignment'] as String?),
|
||||
attributedValue: json['attributedValue'] == null
|
||||
? null
|
||||
: DictionaryValue.parse(json['attributedValue'] as String),
|
||||
);
|
||||
|
||||
/// Localized version of [label] based on given [locale] and [pass].
|
||||
String? getLocalizedLabel(PassFile pass, Locale? locale) {
|
||||
final localizations = pass.getLocalizations(locale);
|
||||
return localizations?[label] ?? label;
|
||||
}
|
||||
|
||||
/// Localized version of [changeMessage] based on given [locale] and [pass].
|
||||
String? getLocalizedChangeMessage(PassFile pass, Locale? locale) {
|
||||
final localizations = pass.getLocalizations(locale);
|
||||
return localizations?[changeMessage] ?? changeMessage;
|
||||
}
|
||||
}
|
||||
|
||||
/// represents the possible values of a [DictionaryField].
|
||||
abstract class DictionaryValue {
|
||||
const DictionaryValue();
|
||||
|
||||
/// parses the correct [DictionaryValue] implementor based on a given [value].
|
||||
factory DictionaryValue.parse(String value) {
|
||||
final number = int.tryParse(value);
|
||||
if (number != null) return NumberDictionaryValue(number);
|
||||
|
||||
final dateTime = DateTime.tryParse(value);
|
||||
if (dateTime != null) return DateTimeDictionaryValue(dateTime);
|
||||
|
||||
return StringDictionaryValue(value);
|
||||
}
|
||||
|
||||
/// Localized value based on given [locale] and [pass].
|
||||
DictionaryValue getLocalizedValue(PassFile pass, Locale? locale);
|
||||
}
|
||||
|
||||
/// [String] content of a [DictionaryField].
|
||||
class StringDictionaryValue extends DictionaryValue {
|
||||
final String string;
|
||||
|
||||
const StringDictionaryValue(this.string);
|
||||
|
||||
@override
|
||||
|
||||
/// Localized value based on given [locale] and [pass].
|
||||
DictionaryValue getLocalizedValue(PassFile pass, Locale? locale) {
|
||||
final localizations = pass.getLocalizations(locale);
|
||||
return StringDictionaryValue(localizations?[string] ?? string);
|
||||
}
|
||||
}
|
||||
|
||||
/// [DateTime] content of a [DictionaryField].
|
||||
class DateTimeDictionaryValue extends DictionaryValue {
|
||||
final DateTime dateTime;
|
||||
|
||||
const DateTimeDictionaryValue(this.dateTime);
|
||||
|
||||
@override
|
||||
|
||||
/// Localized value based on given [locale] and [pass]. Same as [dateTime].
|
||||
DictionaryValue getLocalizedValue(PassFile pass, Locale? locale) => this;
|
||||
}
|
||||
|
||||
/// [int] content of a [DictionaryField].
|
||||
class NumberDictionaryValue extends DictionaryValue {
|
||||
final int number;
|
||||
|
||||
const NumberDictionaryValue(this.number);
|
||||
|
||||
@override
|
||||
|
||||
/// Localized value based on given [locale] and [pass]. Same as [number].
|
||||
DictionaryValue getLocalizedValue(PassFile pass, Locale? locale) => this;
|
||||
}
|
||||
|
||||
/// Possible types of [PassStructureDictionary.transitType].
|
||||
enum TransitType {
|
||||
/// PKTransitTypeAir
|
||||
air,
|
||||
|
||||
/// PKTransitTypeBoat
|
||||
boat,
|
||||
|
||||
/// PKTransitTypeBus
|
||||
bus,
|
||||
|
||||
/// PKTransitTypeGeneric
|
||||
generic,
|
||||
|
||||
/// PKTransitTypeTrain
|
||||
train,
|
||||
}
|
||||
|
||||
/// Possible types of [DictionaryField.textAlignment].
|
||||
enum PassTextAlign {
|
||||
/// PKTextAlignmentLeft, corresponds `dart:ui` [TextAlign.left]
|
||||
left,
|
||||
|
||||
/// PKTextAlignmentCenter, corresponds `dart:ui` [TextAlign.center]
|
||||
center,
|
||||
|
||||
/// PKTextAlignmentRight, corresponds `dart:ui` [TextAlign.left]
|
||||
right,
|
||||
|
||||
/// PKTextAlignmentNatural, corresponds `dart:ui` [TextAlign.start]
|
||||
natural,
|
||||
}
|
||||
|
||||
extension _TarnsitType on TransitType {
|
||||
static TransitType? parse(String? type) {
|
||||
if (type == null) return null;
|
||||
switch (type) {
|
||||
case 'PKTransitTypeAir':
|
||||
return TransitType.air;
|
||||
case 'PKTransitTypeBoat':
|
||||
return TransitType.boat;
|
||||
case 'PKTransitTypeBus':
|
||||
return TransitType.bus;
|
||||
case 'PKTransitTypeTrain':
|
||||
return TransitType.train;
|
||||
default:
|
||||
return TransitType.generic;
|
||||
}
|
||||
}
|
||||
}
|
31
lib/pkpass/models/pass_web_service.dart
Normal file
31
lib/pkpass/models/pass_web_service.dart
Normal file
|
@ -0,0 +1,31 @@
|
|||
/// Metadata required for Pass Web Service
|
||||
///
|
||||
/// https://developer.apple.com/library/archive/documentation/PassKit/Reference/PassKit_WebService/WebService.html#//apple_ref/doc/uid/TP40011988
|
||||
class PassWebService {
|
||||
/// The authentication token to use with the web service.
|
||||
/// The token must be 16 characters or longer.
|
||||
final String authenticationToken;
|
||||
|
||||
/// The URL of a web service that conforms to the API described in PassKit Web Service Reference.
|
||||
final Uri webServiceURL;
|
||||
|
||||
const PassWebService({
|
||||
required this.authenticationToken,
|
||||
required this.webServiceURL,
|
||||
});
|
||||
|
||||
/// returns a [PassWebService] in case [authenticationToken] and
|
||||
/// [webServiceURL] are both valid values.
|
||||
static PassWebService? maybe({
|
||||
String? authenticationToken,
|
||||
String? webServiceURL,
|
||||
}) {
|
||||
if (authenticationToken == null || webServiceURL == null) return null;
|
||||
final uri = Uri.tryParse(webServiceURL);
|
||||
if (uri == null || uri.scheme != 'https') return null;
|
||||
return PassWebService(
|
||||
authenticationToken: authenticationToken,
|
||||
webServiceURL: uri,
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue