checkout_sheet_kit_flutter 0.0.2
checkout_sheet_kit_flutter: ^0.0.2 copied to clipboard
Flutter plugin for Shopify Checkout Sheet Kit - provides seamless checkout experiences using Shopify's native SDKs for Android and iOS.
Shopify Checkout Sheet Kit for Flutter #
Shopify's Checkout Kit for Flutter is a plugin that enables Flutter apps to provide the world's highest converting, customizable, one-page checkout within an app. The presented experience is a fully-featured checkout that preserves all of the store customizations: Checkout UI extensions, Functions, Web Pixels, and more. It also provides idiomatic defaults such as support for light and dark mode, and convenient developer APIs to embed, customize, and follow the lifecycle of the checkout experience.
This plugin wraps the native SDKs:
- Android: checkout-sheet-kit-android
- iOS: checkout-sheet-kit-swift
Table of Contents #
- Requirements
- Getting Started
- Basic Usage
- Configuration
- Preloading
- Monitoring the Lifecycle of a Checkout Session
- Error Handling
- Integrating with Web Pixels
- Integrating Identity & Customer Accounts
- Contributing
- License
Requirements #
| Platform | Minimum Version |
|---|---|
| Flutter | 3.3.0+ |
| Dart | 3.0.0+ |
| Android | API 23+ (Android 6.0+) |
| iOS | 13.0+ |
Getting Started #
Installation #
Add this to your pubspec.yaml:
dependencies:
checkout_sheet_kit_flutter: ^0.0.2
Then run:
flutter pub get
Android Setup #
The plugin automatically includes the Shopify Checkout SDK via Gradle. Ensure your app's minSdkVersion is at least 23 in android/app/build.gradle:
android {
defaultConfig {
minSdkVersion 23
}
}
iOS Setup #
The plugin uses CocoaPods and will automatically include the ShopifyCheckoutSheetKit dependency. Ensure your iOS deployment target is at least 13.0 in ios/Podfile:
platform :ios, '13.0'
Then run:
cd ios && pod install
Basic Usage #
Obtaining a Checkout URL #
To present a checkout to the buyer, your application must first obtain a checkout URL. The most common way is to use the Storefront GraphQL API to assemble a cart (via cartCreate and related update mutations) and load the checkoutUrl.
Alternatively, a cart permalink can be provided.
// Example using a GraphQL client to get checkout URL
final cartQuery = '''
query GetCart(\$id: ID!) {
cart(id: \$id) {
checkoutUrl
}
}
''';
final result = await graphqlClient.query(cartQuery, variables: {'id': cartId});
final checkoutUrl = result['cart']['checkoutUrl'];
Presenting Checkout #
Once you have a checkout URL, present the checkout sheet:
import 'package:checkout_sheet_kit_flutter/checkout_sheet_kit_flutter.dart';
// Configure the SDK (typically done once at app startup)
await ShopifyCheckoutSheetKit.configure(
Configuration(
colorScheme: CheckoutColorScheme.automatic,
preloading: Preloading(enabled: true),
),
);
// Present checkout
final result = await ShopifyCheckoutSheetKit.present(
url: checkoutUrl,
eventHandler: CheckoutEventHandler(
onCheckoutCompleted: (event) {
print('Order completed: ${event.orderDetails.id}');
},
onCheckoutCanceled: () {
print('Checkout canceled');
},
onCheckoutFailed: (error) {
print('Checkout failed: ${error.message}');
},
),
);
// Handle result using pattern matching
switch (result) {
case CheckoutCompletedResult(:final event):
navigateToOrderConfirmation(event.orderDetails.id);
case CheckoutCanceledResult():
showMessage('Checkout canceled');
case CheckoutFailedResult(:final error):
showError(error.message);
}
💡 Tip: To help optimize and deliver the best experience, the SDK also provides a preloading API that can be used to initialize the checkout session ahead of time.
Configuration #
The SDK provides a way to customize the presented checkout experience via the ShopifyCheckoutSheetKit.configure function.
Color Scheme #
By default, the SDK will match the user's device color appearance. This behavior can be customized via the colorScheme property:
await ShopifyCheckoutSheetKit.configure(
Configuration(
// [Default] Automatically toggle light/dark themes based on device preference
colorScheme: CheckoutColorScheme.automatic,
// Force light mode
// colorScheme: CheckoutColorScheme.light,
// Force dark mode
// colorScheme: CheckoutColorScheme.dark,
// Use web theme as rendered by a mobile browser
// colorScheme: CheckoutColorScheme.web,
),
);
| Value | Description |
|---|---|
automatic |
Matches system appearance (light/dark mode) |
light |
Forces light mode |
dark |
Forces dark mode |
web |
Uses the web checkout theme colors |
iOS-Specific Options #
iOS provides additional customization options:
await ShopifyCheckoutSheetKit.configure(
Configuration(
colorScheme: CheckoutColorScheme.automatic,
// iOS-specific options
title: 'Checkout', // Navigation bar title
tintColor: Color.fromRGBA(0, 122, 255), // Tint color for UI elements
backgroundColor: Color.fromRGBA(255, 255, 255), // Background color
),
);
| Option | Type | Description |
|---|---|---|
title |
String |
Navigation bar title |
tintColor |
Color |
Tint color for UI elements |
backgroundColor |
Color |
Background color |
Preloading #
Initializing a checkout session requires communicating with Shopify servers, which depending on network quality can result in undesirable waiting time for the buyer. To help optimize and deliver the best experience, the SDK provides a preloading "hint" that allows developers to signal that the checkout session should be initialized in the background, ahead of time.
// Enable preloading (enabled by default)
await ShopifyCheckoutSheetKit.configure(
Configuration(
preloading: Preloading(enabled: true),
),
);
// Preload a checkout URL
await ShopifyCheckoutSheetKit.preload(url: checkoutUrl);
// Later, present the preloaded checkout (will be faster)
await ShopifyCheckoutSheetKit.present(url: checkoutUrl);
Setting enabled to false will cause all calls to the preload function to be ignored:
await ShopifyCheckoutSheetKit.configure(
Configuration(
preloading: Preloading(enabled: false),
),
);
await ShopifyCheckoutSheetKit.preload(url: checkoutUrl); // no-op
Important Considerations #
-
Resource usage: Initiating preload results in background network requests and additional CPU/memory utilization for the client, and should be used when there is a high likelihood that the buyer will soon request to checkout.
-
Cart state: A preloaded checkout session reflects the cart contents at the time when
preloadis called. If the cart is updated afterpreloadis called, the application needs to callpreloadagain to reflect the updated checkout session. -
Not guaranteed: Calling
preload()is a hint, not a guarantee: the library may debounce or ignore calls depending on various conditions; the preload may not complete beforepresent()is called, in which case the buyer may still see a loading indicator.
Flash Sales #
During Flash Sales or periods of high traffic, buyers may be entered into a queue system.
Calls to preload which result in a buyer being enqueued will be rejected. This means that a buyer will never enter the queue without their knowledge.
When to Preload #
Calling preload() each time an item is added to a buyer's cart can put significant strain on Shopify systems, which can result in rejected requests. Instead, call preload() when you have a strong signal that the buyer intends to check out—for example, when the buyer navigates to a "cart" screen.
Cache Invalidation #
Should you wish to manually clear the preload cache, use the invalidate() function:
await ShopifyCheckoutSheetKit.invalidate();
You may wish to do this if the buyer makes changes shortly before entering checkout, e.g., by changing cart quantity on a cart view.
Lifecycle Management #
Preloading renders a checkout in a background WebView, which is brought to foreground when present() is called. The content of preloaded checkout reflects the state of the cart when preload() was initially called.
If the cart is mutated after preload() is called, the application is responsible for invalidating the preloaded checkout:
- To update preloaded contents: call
preload()again - To disable preloaded content: toggle the preload configuration setting
The library will automatically invalidate/abort preload under the following conditions:
- Request results in network error or non-2XX server response code
- The checkout has successfully completed
- When
configure()is called with new settings
Note: A preloaded checkout is not automatically invalidated when checkout is closed. If a buyer loads the checkout then exits, the preloaded checkout is retained and should be updated when cart contents change.
Monitoring the Lifecycle of a Checkout Session #
Use CheckoutEventHandler to register callbacks for key lifecycle events during the checkout session:
final result = await ShopifyCheckoutSheetKit.present(
url: checkoutUrl,
eventHandler: CheckoutEventHandler(
onCheckoutCompleted: (event) {
// Called when checkout was completed successfully
},
onCheckoutCanceled: () {
// Called when checkout was canceled by the buyer
},
onCheckoutFailed: (error) {
// Called when checkout encountered an error
},
onCheckoutLinkClicked: (url) {
// Called when buyer clicks a link within checkout
},
onWebPixelEvent: (event) {
// Called when a web pixel event is emitted
},
),
);
Checkout Completed #
The onCheckoutCompleted callback receives a CheckoutCompletedEvent with detailed order information:
onCheckoutCompleted: (event) {
final order = event.orderDetails;
print('Order ID: ${order.id}');
print('Email: ${order.email}');
print('Phone: ${order.phone}');
// Cart information
if (order.cart != null) {
print('Total: ${order.cart!.price.total}');
print('Subtotal: ${order.cart!.price.subtotal}');
for (final line in order.cart!.lines) {
print('${line.title} x ${line.quantity} = ${line.price}');
}
}
// Billing address
if (order.billingAddress != null) {
print('Billing: ${order.billingAddress!.city}, ${order.billingAddress!.countryCode}');
}
// Payment methods
order.paymentMethods?.forEach((payment) {
print('Paid with: ${payment.type}');
});
}
Checkout Canceled #
Called when the buyer dismisses the checkout without completing:
onCheckoutCanceled: () {
// User canceled checkout
// Note: This will also be received after closing a completed checkout
}
Checkout Failed #
Called when an error occurs during checkout:
onCheckoutFailed: (error) {
print('Error: ${error.message}');
print('Code: ${error.code}');
print('Recoverable: ${error.isRecoverable}');
}
Link Clicked #
Called when the buyer clicks a link within checkout (email, phone, web, or deep links):
onCheckoutLinkClicked: (url) {
// Handle links:
// - mailto: email addresses
// - tel: phone numbers
// - http/https: web links
// - custom schemes: deep links
launchUrl(url);
}
Error Handling #
In the event of a checkout error occurring, the SDK may attempt to retry to recover from the error. Recovery will happen in the background by discarding the failed WebView and creating a new "recovery" instance.
Error Codes #
| Code | Description | Recommendation |
|---|---|---|
cartExpired |
The cart/checkout has expired | Create a new cart and checkout URL |
cartCompleted |
The cart was already completed | Create a new cart and checkout URL |
invalidCart |
The cart is invalid (e.g., empty) | Create a new cart and checkout URL |
checkoutUnavailable |
Checkout unavailable (network/server error) | Show checkout in a fallback WebView |
configurationError |
SDK configuration error | Resolve the configuration issue |
httpError |
Unexpected HTTP error | Show checkout in a fallback WebView |
unknown |
Unknown error | Show checkout in a fallback WebView |
Error Recovery #
Configure error recovery behavior:
await ShopifyCheckoutSheetKit.configure(
Configuration(
errorRecovery: ErrorRecovery(enabled: true), // default
),
);
When recovery is enabled, the SDK may automatically retry failed requests. Errors passed to onCheckoutFailed include an isRecoverable property indicating whether recovery was attempted.
Caveats when recovery occurs:
- The checkout experience may look different to buyers
onCheckoutCompletedwill be emitted with partial data (only order ID)onWebPixelEventwill not be emitted
Integrating with Web Pixels #
Standard and custom Web Pixel events will be relayed back to your application through the onWebPixelEvent callback.
⚠️ Important: App developers should only subscribe to pixel events if they have proper levels of consent from merchants/buyers and are responsible for adherence to local regulations like GDPR and ePrivacy directive before disseminating these events to first-party and third-party systems.
onWebPixelEvent: (event) {
if (!hasPermissionToCaptureEvents()) {
return;
}
switch (event) {
case StandardPixelEvent():
// Standard events (page_viewed, checkout_started, etc.)
print('Standard event: ${event.name}');
print('Event ID: ${event.id}');
print('Data: ${event.data}');
// Send to analytics
analyticsClient.track(event.name, event.data);
case CustomPixelEvent():
// Custom events
print('Custom event: ${event.name}');
print('Custom data: ${event.customData}');
}
}
Note: You may need to augment these events with customer/session information derived from app state.
Integrating Identity & Customer Accounts #
Buyer-aware checkout experience reduces friction and increases conversion. Depending on the context of the buyer (guest or signed-in), knowledge of buyer preferences, or account/identity system, the application can use one of the following methods to initialize a personalized buyer experience.
Cart: Buyer Identity and Preferences #
In addition to specifying line items, the Cart can include buyer identity (name, email, address, etc.) and delivery and payment preferences. See the Storefront API guide for details.
Included information will be used to present pre-filled and pre-selected choices to the buyer within checkout.
Multipass #
Shopify Plus merchants using Classic Customer Accounts can use Multipass to integrate an external identity system and initialize a buyer-aware checkout session.
{
"email": "<Customer's email address>",
"created_at": "<Current timestamp in ISO8601 encoding>",
"remote_ip": "<Client IP address>",
"return_to": "<Checkout URL obtained from Storefront API>",
...
}
- Follow the Multipass documentation to create a Multipass URL and set the
'return_to'to be the obtainedcheckoutUrl - Provide the Multipass URL to
ShopifyCheckoutSheetKit.present()
⚠️ Important: Encryption and signing should be done server-side to ensure Multipass keys are kept secret.
Note: Multipass errors are not "recoverable" due to their one-time nature. Failed requests containing Multipass URLs will require re-generating new tokens.
Shop Pay #
To initialize accelerated Shop Pay checkout, the cart can set a walletPreference to 'shop_pay'. The sign-in state of the buyer is app-local and the buyer will be prompted to sign in to their Shop account on their first checkout, and their sign-in state will be remembered for future checkout sessions.
Customer Account API #
The Customer Account API allows you to authenticate buyers and provide a personalized checkout experience. For detailed implementation instructions, see the Customer Account API Authentication Guide.
Example #
See the example folder for a complete sample app demonstrating:
- SDK configuration
- Checkout preloading
- Presenting checkout
- Handling all lifecycle events
- Error handling
- Web pixel event tracking
To run the example:
cd example
flutter run
Contributing #
We welcome code contributions, feature requests, and reporting of issues. Please see our contributing guidelines before submitting a pull request.
License #
Shopify's Checkout Kit is provided under an MIT License.