Screens / commerce
Cart and checkout
Shows the cart and pays for it: quantities, undoable removal, promo code, saved address and payment method in a sheet, the totals from the service and a single payment at a time.
Import
Cart and checkout is part of Fliin Pro. With a license key, sign in once with fliin login, then add it in a Flutter app where fliin init has run:
fliin login
fliin add checkoutAlso installs: alert, app-bar, button, cart-line, core, empty-state, field, icons, link-button, list-row, order-summary, page, radio, result-card, section-header, separator, sheet, skeleton, surface, text-field
Examples
Cart
The cart, its address and payment method, and the total. Changing a quantity or applying a code goes through your service; onPaid receives the paid order.
import 'package:your_app/ui/fliin/screens/checkout/checkout_controller.dart';
import 'package:your_app/ui/fliin/screens/checkout/checkout_data.dart';
import 'package:your_app/ui/fliin/screens/checkout/checkout_screen.dart';
import 'package:flutter/widgets.dart';
class CheckoutCartExample extends StatelessWidget {
const CheckoutCartExample({super.key});
@override
Widget build(BuildContext context) {
return CheckoutScreen.standalone(
service: SampleCheckoutService(latency: const Duration(milliseconds: 600)),
initial: CheckoutReady(sampleCheckoutCart),
onPaid: (_) {},
onBack: () {},
onContinueShopping: () {},
// Your payment provider's sheet: it returns the method the user saved.
onAddPaymentMethod: () => Future.delayed(
const Duration(milliseconds: 600),
() => const CheckoutPaymentMethod(id: 'pm_new', brand: 'Amex', last4: '0005', expiry: '12/29'),
),
);
}
}Loading
While the cart loads, placeholders keep its layout.
import 'package:your_app/ui/fliin/screens/checkout/checkout_controller.dart';
import 'package:your_app/ui/fliin/screens/checkout/checkout_data.dart';
import 'package:your_app/ui/fliin/screens/checkout/checkout_screen.dart';
import 'package:flutter/widgets.dart';
class CheckoutLoadingExample extends StatelessWidget {
const CheckoutLoadingExample({super.key});
@override
Widget build(BuildContext context) {
return CheckoutScreen.standalone(
service: SampleCheckoutService(latency: const Duration(milliseconds: 600)),
initial: const CheckoutLoading(),
onPaid: (_) {},
onBack: () {},
onContinueShopping: () {},
// Your payment provider's sheet: it returns the method the user saved.
onAddPaymentMethod: () => Future.delayed(
const Duration(milliseconds: 600),
() => const CheckoutPaymentMethod(id: 'pm_new', brand: 'Amex', last4: '0005', expiry: '12/29'),
),
);
}
}Empty
An empty cart sends the user back to shopping with onContinueShopping.
import 'package:your_app/ui/fliin/screens/checkout/checkout_controller.dart';
import 'package:your_app/ui/fliin/screens/checkout/checkout_data.dart';
import 'package:your_app/ui/fliin/screens/checkout/checkout_screen.dart';
import 'package:flutter/widgets.dart';
class CheckoutEmptyExample extends StatelessWidget {
const CheckoutEmptyExample({super.key});
@override
Widget build(BuildContext context) {
return CheckoutScreen.standalone(
service: SampleCheckoutService(latency: const Duration(milliseconds: 600)),
initial: CheckoutReady(sampleCheckoutCart.copyWith(items: const [])),
onPaid: (_) {},
onBack: () {},
onContinueShopping: () {},
// Your payment provider's sheet: it returns the method the user saved.
onAddPaymentMethod: () => Future.delayed(
const Duration(milliseconds: 600),
() => const CheckoutPaymentMethod(id: 'pm_new', brand: 'Amex', last4: '0005', expiry: '12/29'),
),
);
}
}Updating
While your backend recomputes the total, the amounts wait and payment is held.
import 'package:your_app/ui/fliin/screens/checkout/checkout_controller.dart';
import 'package:your_app/ui/fliin/screens/checkout/checkout_data.dart';
import 'package:your_app/ui/fliin/screens/checkout/checkout_screen.dart';
import 'package:flutter/widgets.dart';
class CheckoutUpdatingExample extends StatelessWidget {
const CheckoutUpdatingExample({super.key});
@override
Widget build(BuildContext context) {
return CheckoutScreen.standalone(
service: SampleCheckoutService(latency: const Duration(milliseconds: 600)),
initial: CheckoutReady(sampleCheckoutCart, updating: true),
onPaid: (_) {},
onBack: () {},
onContinueShopping: () {},
// Your payment provider's sheet: it returns the method the user saved.
onAddPaymentMethod: () => Future.delayed(
const Duration(milliseconds: 600),
() => const CheckoutPaymentMethod(id: 'pm_new', brand: 'Amex', last4: '0005', expiry: '12/29'),
),
);
}
}Paying
While the payment goes through, the pay button waits and the cart is locked.
import 'package:your_app/ui/fliin/screens/checkout/checkout_controller.dart';
import 'package:your_app/ui/fliin/screens/checkout/checkout_data.dart';
import 'package:your_app/ui/fliin/screens/checkout/checkout_screen.dart';
import 'package:flutter/widgets.dart';
class CheckoutPayingExample extends StatelessWidget {
const CheckoutPayingExample({super.key});
@override
Widget build(BuildContext context) {
return CheckoutScreen.standalone(
service: SampleCheckoutService(latency: const Duration(milliseconds: 600)),
initial: CheckoutReady(sampleCheckoutCart, paying: true),
onPaid: (_) {},
onBack: () {},
onContinueShopping: () {},
// Your payment provider's sheet: it returns the method the user saved.
onAddPaymentMethod: () => Future.delayed(
const Duration(milliseconds: 600),
() => const CheckoutPaymentMethod(id: 'pm_new', brand: 'Amex', last4: '0005', expiry: '12/29'),
),
);
}
}Failed
A declined payment keeps the cart and says that nothing was charged, so the user can pick another method.
import 'package:your_app/ui/fliin/screens/checkout/checkout_controller.dart';
import 'package:your_app/ui/fliin/screens/checkout/checkout_data.dart';
import 'package:your_app/ui/fliin/screens/checkout/checkout_screen.dart';
import 'package:flutter/widgets.dart';
class CheckoutFailedExample extends StatelessWidget {
const CheckoutFailedExample({super.key});
@override
Widget build(BuildContext context) {
return CheckoutScreen.standalone(
service: SampleCheckoutService(latency: const Duration(milliseconds: 600)),
initial: CheckoutReady(sampleCheckoutCart, notice: CheckoutNotice.paymentDeclined),
onPaid: (_) {},
onBack: () {},
onContinueShopping: () {},
// Your payment provider's sheet: it returns the method the user saved.
onAddPaymentMethod: () => Future.delayed(
const Duration(milliseconds: 600),
() => const CheckoutPaymentMethod(id: 'pm_new', brand: 'Amex', last4: '0005', expiry: '12/29'),
),
);
}
}Offline
Without a connection, the screen asks to reconnect before anything is charged.
import 'package:your_app/ui/fliin/screens/checkout/checkout_controller.dart';
import 'package:your_app/ui/fliin/screens/checkout/checkout_data.dart';
import 'package:your_app/ui/fliin/screens/checkout/checkout_screen.dart';
import 'package:flutter/widgets.dart';
class CheckoutOfflineExample extends StatelessWidget {
const CheckoutOfflineExample({super.key});
@override
Widget build(BuildContext context) {
return CheckoutScreen.standalone(
service: SampleCheckoutService(latency: const Duration(milliseconds: 600)),
initial: const CheckoutOffline(),
onPaid: (_) {},
onBack: () {},
onContinueShopping: () {},
// Your payment provider's sheet: it returns the method the user saved.
onAddPaymentMethod: () => Future.delayed(
const Duration(milliseconds: 600),
() => const CheckoutPaymentMethod(id: 'pm_new', brand: 'Amex', last4: '0005', expiry: '12/29'),
),
);
}
}Error
When the cart cannot load, the screen says so and offers to try again.
import 'package:your_app/ui/fliin/screens/checkout/checkout_controller.dart';
import 'package:your_app/ui/fliin/screens/checkout/checkout_data.dart';
import 'package:your_app/ui/fliin/screens/checkout/checkout_screen.dart';
import 'package:flutter/widgets.dart';
class CheckoutErrorExample extends StatelessWidget {
const CheckoutErrorExample({super.key});
@override
Widget build(BuildContext context) {
return CheckoutScreen.standalone(
service: SampleCheckoutService(latency: const Duration(milliseconds: 600)),
initial: const CheckoutFailed(),
onPaid: (_) {},
onBack: () {},
onContinueShopping: () {},
// Your payment provider's sheet: it returns the method the user saved.
onAddPaymentMethod: () => Future.delayed(
const Duration(milliseconds: 600),
() => const CheckoutPaymentMethod(id: 'pm_new', brand: 'Amex', last4: '0005', expiry: '12/29'),
),
);
}
}No promo
The cart of a shop without promo codes: showPromoCode: false drops the field, the lines go straight to the address and payment method.
import 'package:your_app/ui/fliin/screens/checkout/checkout_controller.dart';
import 'package:your_app/ui/fliin/screens/checkout/checkout_data.dart';
import 'package:your_app/ui/fliin/screens/checkout/checkout_screen.dart';
import 'package:flutter/widgets.dart';
class CheckoutNoPromoExample extends StatelessWidget {
const CheckoutNoPromoExample({super.key});
@override
Widget build(BuildContext context) {
return CheckoutScreen.standalone(
service: SampleCheckoutService(latency: const Duration(milliseconds: 600)),
initial: CheckoutReady(sampleCheckoutCart),
showPromoCode: false,
onPaid: (_) {},
onBack: () {},
onContinueShopping: () {},
);
}
}Routing
fliin add checkout prints the route for the router it finds in your pubspec, a file next to the page that wires its navigation. Each of them:
go_router
// lib/checkout_route.dart, next to checkout_page.dart.
import 'package:go_router/go_router.dart';
import 'checkout_page.dart';
/// Add checkoutRoute to the routes of your GoRouter.
final checkoutRoute = GoRoute(
path: '/checkout',
builder: (context, state) => CheckoutPage(
onPaid: (receipt) => context.pushReplacement('/order/${Uri.encodeComponent(receipt.orderId)}'),
// Alone in the stack (deep link, go): your app's home.
onBack: () => context.canPop() ? context.pop() : context.go('/'),
// '/': your app's home.
onContinueShopping: () => context.go('/'),
),
);navigator
// lib/checkout_route.dart, next to checkout_page.dart.
import 'package:flutter/material.dart';
import 'checkout_page.dart';
import 'order_confirmation_route.dart';
/// Opens “Cart and checkout”: `Navigator.of(context).push(checkoutRoute(parcelTrackingPage: …, returnPage: …, supportPage: …, cartPage: …))`.
/// [parcelTrackingPage] builds the parcel tracking in your app.
/// [returnPage] builds the return of an order in your app.
/// [supportPage] builds the help on an order in your app.
/// [cartPage] builds the cart: the page of Fliin's checkout screen with its callbacks, or yours (checkoutRoute cannot come from here, checkout leads to order).
Route<void> checkoutRoute({
required Widget Function(String orderId) parcelTrackingPage,
required Widget Function(String orderId) returnPage,
required Widget Function(String orderId) supportPage,
required WidgetBuilder cartPage,
}) => MaterialPageRoute<void>(
builder: (context) => CheckoutPage(
onPaid: (receipt) => Navigator.of(context).pushReplacement(orderConfirmationRoute(receipt.orderId, parcelTrackingPage: parcelTrackingPage, returnPage: returnPage, supportPage: supportPage, cartPage: cartPage)),
onBack: () => Navigator.of(context).maybePop(),
onContinueShopping: () => Navigator.of(context).popUntil((route) => route.isFirst),
),
);State
The controller is a ChangeNotifier from widgets.dart, with no package. fliin add checkout prints the page that holds it with the state management it finds, its imports written for your package; below they read your_app. Your backend implements the Service interface of the screen's _data.dart file:
none
// lib/checkout_page.dart
// Your backend: implement CheckoutService (checkout_data.dart) and pass it instead of the sample service.
import 'package:your_app/ui/fliin/screens/checkout/checkout_data.dart';
import 'package:your_app/ui/fliin/screens/checkout/checkout_screen.dart';
import 'package:flutter/widgets.dart';
class CheckoutPage extends StatelessWidget {
const CheckoutPage({
super.key,
required this.onPaid,
required this.onBack,
required this.onContinueShopping,
});
final ValueChanged<CheckoutReceipt> onPaid;
final VoidCallback onBack;
final VoidCallback onContinueShopping;
@override
Widget build(BuildContext context) => CheckoutScreen.standalone(
service: SampleCheckoutService(),
onPaid: onPaid,
onBack: onBack,
onContinueShopping: onContinueShopping,
);
}provider
// lib/checkout_page.dart
// Your backend: implement CheckoutService (checkout_data.dart) and pass it instead of the sample service.
import 'package:your_app/ui/fliin/screens/checkout/checkout_controller.dart';
import 'package:your_app/ui/fliin/screens/checkout/checkout_data.dart';
import 'package:your_app/ui/fliin/screens/checkout/checkout_screen.dart';
import 'package:flutter/widgets.dart';
import 'package:provider/provider.dart';
class CheckoutPage extends StatelessWidget {
const CheckoutPage({
super.key,
required this.onPaid,
required this.onBack,
required this.onContinueShopping,
});
final ValueChanged<CheckoutReceipt> onPaid;
final VoidCallback onBack;
final VoidCallback onContinueShopping;
@override
Widget build(BuildContext context) => ChangeNotifierProvider(
create: (_) => CheckoutController(service: SampleCheckoutService())..load(),
child: Builder(
builder: (context) => CheckoutScreen(
controller: context.read<CheckoutController>(),
onPaid: onPaid,
onBack: onBack,
onContinueShopping: onContinueShopping,
),
),
);
}riverpod
// lib/checkout_page.dart
// Your backend: implement CheckoutService (checkout_data.dart) and pass it instead of the sample service.
import 'package:your_app/ui/fliin/screens/checkout/checkout_controller.dart';
import 'package:your_app/ui/fliin/screens/checkout/checkout_data.dart';
import 'package:your_app/ui/fliin/screens/checkout/checkout_screen.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
final checkoutControllerProvider = Provider.autoDispose<CheckoutController>((ref) {
final controller = CheckoutController(service: SampleCheckoutService())..load();
ref.onDispose(controller.dispose);
return controller;
});
class CheckoutPage extends ConsumerWidget {
const CheckoutPage({
super.key,
required this.onPaid,
required this.onBack,
required this.onContinueShopping,
});
final ValueChanged<CheckoutReceipt> onPaid;
final VoidCallback onBack;
final VoidCallback onContinueShopping;
@override
Widget build(BuildContext context, WidgetRef ref) => CheckoutScreen(
controller: ref.watch(checkoutControllerProvider),
onPaid: onPaid,
onBack: onBack,
onContinueShopping: onContinueShopping,
);
}bloc
// lib/checkout_page.dart
// Your backend: implement CheckoutService (checkout_data.dart) and pass it instead of the sample service.
import 'package:your_app/ui/fliin/screens/checkout/checkout_controller.dart';
import 'package:your_app/ui/fliin/screens/checkout/checkout_data.dart';
import 'package:your_app/ui/fliin/screens/checkout/checkout_screen.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
/// The screen's controller as a bloc, for your BlocListener and BlocBuilder:
/// one emission per controller notification. `version` tells them apart
/// when `state` does not change (mode, filters, text…, read them on
/// `controller`). The screen listens to the controller itself.
class CheckoutCubit extends Cubit<({CheckoutState state, int version})> {
CheckoutCubit(this.controller) : super((state: controller.state, version: 0)) {
controller.addListener(_changed);
}
final CheckoutController controller;
void _changed() => emit((state: controller.state, version: state.version + 1));
@override
Future<void> close() {
controller
..removeListener(_changed)
..dispose();
return super.close();
}
}
class CheckoutPage extends StatelessWidget {
const CheckoutPage({
super.key,
required this.onPaid,
required this.onBack,
required this.onContinueShopping,
});
final ValueChanged<CheckoutReceipt> onPaid;
final VoidCallback onBack;
final VoidCallback onContinueShopping;
@override
Widget build(BuildContext context) => BlocProvider(
create: (_) => CheckoutCubit(CheckoutController(service: SampleCheckoutService())..load()),
child: Builder(
builder: (context) => CheckoutScreen(
controller: context.read<CheckoutCubit>().controller,
onPaid: onPaid,
onBack: onBack,
onContinueShopping: onContinueShopping,
),
),
);
}The snippets lead to order-confirmation: install these screens too and paste their snippets.
API
CheckoutScreen: class extends StatefulWidget
CheckoutScreen.new: const new({super.key, required CheckoutController controller, required ValueChanged<CheckoutReceipt> onPaid, Future<CheckoutPaymentMethod?> Function()? onAddPaymentMethod, Future<CheckoutAddress?> Function()? onAddAddress, VoidCallback? onBack, VoidCallback? onContinueShopping, bool showPromoCode})
CheckoutScreen.standalone: const new({super.key, required CheckoutService service, required ValueChanged<CheckoutReceipt> onPaid, CheckoutState? initial, Future<CheckoutPaymentMethod?> Function()? onAddPaymentMethod, Future<CheckoutAddress?> Function()? onAddAddress, VoidCallback? onBack, VoidCallback? onContinueShopping, bool showPromoCode})
CheckoutScreen.controller: get CheckoutController?
CheckoutScreen.service: get CheckoutService?
CheckoutScreen.initial: get CheckoutState?
CheckoutScreen.onPaid: get ValueChanged<CheckoutReceipt>
CheckoutScreen.onAddPaymentMethod: get Future<CheckoutPaymentMethod?> Function()?
CheckoutScreen.onAddAddress: get Future<CheckoutAddress?> Function()?
CheckoutScreen.onBack: get VoidCallback?
CheckoutScreen.onContinueShopping: get VoidCallback?
CheckoutScreen.showPromoCode: get bool
CheckoutScreen.createState: State<CheckoutScreen>()
CheckoutState: sealed class permits {CheckoutFailed, CheckoutLoading, CheckoutOffline, CheckoutPaid, CheckoutReady}
CheckoutState.new: const new()
CheckoutLoading: final class extends CheckoutState
CheckoutLoading.new: const new()
CheckoutFailed: final class extends CheckoutState
CheckoutFailed.new: const new()
CheckoutOffline: final class extends CheckoutState
CheckoutOffline.new: const new()
CheckoutNotice: enum {removed, updateFailed, updateOffline, paymentDeclined, paymentUnconfirmed, paymentNotTaken}
CheckoutNotice.removed: static final CheckoutNotice
CheckoutNotice.updateFailed: static final CheckoutNotice
CheckoutNotice.updateOffline: static final CheckoutNotice
CheckoutNotice.paymentDeclined: static final CheckoutNotice
CheckoutNotice.paymentUnconfirmed: static final CheckoutNotice
CheckoutNotice.paymentNotTaken: static final CheckoutNotice
CheckoutRemoval: final class
CheckoutRemoval.new: const new(CheckoutItem item, int index)
CheckoutRemoval.item: get CheckoutItem
CheckoutRemoval.index: get int
CheckoutReady: final class extends CheckoutState
CheckoutReady.new: const new(CheckoutCart cart, {bool updating, bool applyingPromo, bool promoRejected, bool paying, CheckoutNotice? notice, CheckoutRemoval? removal})
CheckoutReady.cart: get CheckoutCart
CheckoutReady.updating: get bool
CheckoutReady.applyingPromo: get bool
CheckoutReady.promoRejected: get bool
CheckoutReady.paying: get bool
CheckoutReady.notice: get CheckoutNotice?
CheckoutReady.removal: get CheckoutRemoval?
CheckoutReady.canPay: get bool
CheckoutPaid: final class extends CheckoutState
CheckoutPaid.new: const new(CheckoutReceipt receipt)
CheckoutPaid.receipt: get CheckoutReceipt
CheckoutController: class extends ChangeNotifier
CheckoutController.new: new({required CheckoutService service, CheckoutState initial})
CheckoutController.service: get CheckoutService
CheckoutController.state: get CheckoutState
CheckoutController.load: Future<bool>()
CheckoutController.setQuantity: bool(String itemId, int quantity)
CheckoutController.remove: bool(String itemId)
CheckoutController.undoRemove: bool()
CheckoutController.applyPromo: bool(String code)
CheckoutController.removePromo: bool()
CheckoutController.selectAddress: bool(CheckoutAddress address)
CheckoutController.selectPayment: bool(CheckoutPaymentMethod method)
CheckoutController.dismissNotice: void()
CheckoutController.pay: Future<bool>()
CheckoutController.checkPayment: Future<bool>()
CheckoutController.dispose: void()
CheckoutItem: final class
CheckoutItem.new: const new({required String id, required String title, required String price, required int quantity, String? variant, int maxQuantity, ImageProvider? image})
CheckoutItem.id: get String
CheckoutItem.title: get String
CheckoutItem.price: get String
CheckoutItem.quantity: get int
CheckoutItem.variant: get String?
CheckoutItem.maxQuantity: get int
CheckoutItem.image: get ImageProvider?
CheckoutItem.withQuantity: CheckoutItem(int quantity)
CheckoutTotals: final class
CheckoutTotals.new: const new({required String subtotal, required String shipping, required String total, String? discount, String? tax})
CheckoutTotals.subtotal: get String
CheckoutTotals.shipping: get String
CheckoutTotals.discount: get String?
CheckoutTotals.tax: get String?
CheckoutTotals.total: get String
CheckoutAddress: final class
CheckoutAddress.new: const new({required String id, required String name, required String lines})
CheckoutAddress.id: get String
CheckoutAddress.name: get String
CheckoutAddress.lines: get String
CheckoutPaymentMethod: final class
CheckoutPaymentMethod.new: const new({required String id, required String brand, required String last4, String? expiry})
CheckoutPaymentMethod.id: get String
CheckoutPaymentMethod.brand: get String
CheckoutPaymentMethod.last4: get String
CheckoutPaymentMethod.expiry: get String?
CheckoutPaymentMethod.toString: String()
CheckoutCart: final class
CheckoutCart.new: const new({required List<CheckoutItem> items, required CheckoutTotals totals, required String paymentKey, bool paymentPending, String? promoCode, CheckoutAddress? address, CheckoutPaymentMethod? payment, List<CheckoutAddress> addresses, List<CheckoutPaymentMethod> paymentMethods})
CheckoutCart.items: get List<CheckoutItem>
CheckoutCart.totals: get CheckoutTotals
CheckoutCart.paymentKey: get String
CheckoutCart.paymentPending: get bool
CheckoutCart.promoCode: get String?
CheckoutCart.address: get CheckoutAddress?
CheckoutCart.payment: get CheckoutPaymentMethod?
CheckoutCart.addresses: get List<CheckoutAddress>
CheckoutCart.paymentMethods: get List<CheckoutPaymentMethod>
CheckoutCart.copyWith: CheckoutCart({List<CheckoutItem>? items, CheckoutAddress? address, List<CheckoutAddress>? addresses, CheckoutPaymentMethod? payment, List<CheckoutPaymentMethod>? paymentMethods, bool? paymentPending})
CheckoutPayment: final class
CheckoutPayment.new: const new({required String idempotencyKey, required String total, required String paymentMethodId})
CheckoutPayment.idempotencyKey: get String
CheckoutPayment.total: get String
CheckoutPayment.paymentMethodId: get String
CheckoutReceipt: final class
CheckoutReceipt.new: const new({required String orderId})
CheckoutReceipt.orderId: get String
CheckoutNoNetwork: final class implements Exception
CheckoutNoNetwork.new: const new()
CheckoutPromoRejected: final class implements Exception
CheckoutPromoRejected.new: const new()
CheckoutPaymentDeclined: final class implements Exception
CheckoutPaymentDeclined.new: const new(CheckoutCart cart)
CheckoutPaymentDeclined.cart: get CheckoutCart
CheckoutPaymentOutcome: sealed class permits {CheckoutPaymentAccepted, CheckoutPaymentNotTaken, CheckoutPaymentStillPending}
CheckoutPaymentOutcome.new: const new()
CheckoutPaymentAccepted: final class extends CheckoutPaymentOutcome
CheckoutPaymentAccepted.new: const new(CheckoutReceipt receipt)
CheckoutPaymentAccepted.receipt: get CheckoutReceipt
CheckoutPaymentNotTaken: final class extends CheckoutPaymentOutcome
CheckoutPaymentNotTaken.new: const new(CheckoutCart cart)
CheckoutPaymentNotTaken.cart: get CheckoutCart
CheckoutPaymentStillPending: final class extends CheckoutPaymentOutcome
CheckoutPaymentStillPending.new: const new()
CheckoutService: abstract interface class
CheckoutService.new: new()
CheckoutService.load: abstract Future<CheckoutCart>()
CheckoutService.setQuantity: abstract Future<CheckoutCart>(String itemId, int quantity)
CheckoutService.remove: abstract Future<CheckoutCart>(String itemId)
CheckoutService.restore: abstract Future<CheckoutCart>(CheckoutItem item, int index)
CheckoutService.applyPromo: abstract Future<CheckoutCart>(String code)
CheckoutService.removePromo: abstract Future<CheckoutCart>()
CheckoutService.selectAddress: abstract Future<CheckoutCart>(CheckoutAddress address)
CheckoutService.selectPayment: abstract Future<CheckoutCart>(CheckoutPaymentMethod method)
CheckoutService.pay: abstract Future<CheckoutReceipt>(CheckoutPayment payment)
CheckoutService.paymentOutcome: abstract Future<CheckoutPaymentOutcome>(String paymentKey)
sampleCheckoutCart: get dynamic
SampleCheckoutService: final class implements CheckoutService
SampleCheckoutService.new: new({Duration latency, bool empty})
SampleCheckoutService.latency: get Duration
SampleCheckoutService.load: Future<CheckoutCart>()
SampleCheckoutService.setQuantity: Future<CheckoutCart>(String itemId, int quantity)
SampleCheckoutService.remove: Future<CheckoutCart>(String itemId)
SampleCheckoutService.restore: Future<CheckoutCart>(CheckoutItem item, int index)
SampleCheckoutService.applyPromo: Future<CheckoutCart>(String code)
SampleCheckoutService.removePromo: Future<CheckoutCart>()
SampleCheckoutService.selectAddress: Future<CheckoutCart>(CheckoutAddress address)
SampleCheckoutService.selectPayment: Future<CheckoutCart>(CheckoutPaymentMethod method)
SampleCheckoutService.pay: Future<CheckoutReceipt>(CheckoutPayment payment)
SampleCheckoutService.paymentOutcome: Future<CheckoutPaymentOutcome>(String paymentKey)
checkoutL10n: const Map<String, Map<String, String>>
CheckoutStrings: final class
CheckoutStrings.new: const new(String language)
CheckoutStrings.of: factory new(BuildContext context)
CheckoutStrings.language: get String
CheckoutStrings.title: get String
CheckoutStrings.back: get String
CheckoutStrings.loading: get String
CheckoutStrings.items: get String
CheckoutStrings.quantity: get String
CheckoutStrings.quantityOf: String(String item)
CheckoutStrings.decrease: String(String item)
CheckoutStrings.increase: String(String item)
CheckoutStrings.remove: get String
CheckoutStrings.removeItem: String(String item)
CheckoutStrings.removed: String(String item)
CheckoutStrings.undo: get String
CheckoutStrings.promo: get String
CheckoutStrings.promoApply: get String
CheckoutStrings.promoRejected: get String
CheckoutStrings.promoApplied: String(String code)
CheckoutStrings.promoRemove: get String
CheckoutStrings.delivery: get String
CheckoutStrings.chooseAddress: get String
CheckoutStrings.noAddress: get String
CheckoutStrings.payment: get String
CheckoutStrings.choosePayment: get String
CheckoutStrings.card: String(String brand, String last4)
CheckoutStrings.expires: String(String expiry)
CheckoutStrings.addPayment: get String
CheckoutStrings.addAddress: get String
CheckoutStrings.noSavedAddress: get String
CheckoutStrings.noSavedPayment: get String
CheckoutStrings.needAddress: get String
CheckoutStrings.needPayment: get String
CheckoutStrings.noPayment: get String
CheckoutStrings.close: get String
CheckoutStrings.summary: get String
CheckoutStrings.subtotal: get String
CheckoutStrings.shipping: get String
CheckoutStrings.discount: get String
CheckoutStrings.tax: get String
CheckoutStrings.total: get String
CheckoutStrings.updating: get String
CheckoutStrings.pay: String(String total)
CheckoutStrings.payWaiting: get String
CheckoutStrings.updateFailed: get String
CheckoutStrings.updateOffline: get String
CheckoutStrings.paymentDeclined: get String
CheckoutStrings.paymentUnconfirmed: get String
CheckoutStrings.paymentNotTaken: get String
CheckoutStrings.checkPayment: get String
CheckoutStrings.payingWait: get String
CheckoutStrings.newTotal: String(String total)
CheckoutStrings.paid: get String
CheckoutStrings.emptyTitle: get String
CheckoutStrings.emptyMessage: get String
CheckoutStrings.continueShopping: get String
CheckoutStrings.errorTitle: get String
CheckoutStrings.errorMessage: get String
CheckoutStrings.offlineTitle: get String
CheckoutStrings.offlineMessage: get String
CheckoutStrings.retry: get StringStrings
Every label in English, French and Arabic. The screen follows the closest Localizations; the same table is in the manifest, ready for your ARB files.
| Key | en | fr | ar |
|---|---|---|---|
checkoutTitle | Cart | Panier | السلة |
checkoutBack | Back | Retour | رجوع |
checkoutLoading | Loading your cart | Chargement du panier | جارٍ تحميل السلة |
checkoutItems | Items | Articles | المنتجات |
checkoutQuantity | Quantity | Quantité | الكمية |
checkoutQuantityOf | Quantity, {item} | Quantité, {item} | الكمية، {item} |
checkoutDecrease | Decrease quantity of {item} | Diminuer la quantité de {item} | إنقاص كمية {item} |
checkoutIncrease | Increase quantity of {item} | Augmenter la quantité de {item} | زيادة كمية {item} |
checkoutRemove | Remove | Retirer | إزالة |
checkoutRemoveItem | Remove {item} | Retirer {item} | إزالة {item} |
checkoutRemoved | {item} was removed. | {item} est retiré du panier. | تمت إزالة {item}. |
checkoutUndo | Undo | Annuler | تراجع |
checkoutPromo | Promo code | Code promo | رمز الخصم |
checkoutPromoApply | Apply | Appliquer | تطبيق |
checkoutPromoRejected | This code is not valid. | Ce code n’est pas valable. | هذا الرمز غير صالح. |
checkoutPromoApplied | Code {code} applied | Code {code} appliqué | تم تطبيق الرمز {code} |
checkoutPromoRemove | Remove code | Retirer le code | إزالة الرمز |
checkoutDelivery | Delivery | Livraison | التوصيل |
checkoutChooseAddress | Delivery address | Adresse de livraison | عنوان التوصيل |
checkoutNoAddress | Add a delivery address | Ajouter une adresse de livraison | أضف عنوان توصيل |
checkoutPayment | Payment | Paiement | الدفع |
checkoutChoosePayment | Payment method | Moyen de paiement | طريقة الدفع |
checkoutCard | {brand} ending in {last4} | {brand} se terminant par {last4} | {brand} تنتهي بـ {last4} |
checkoutExpires | Expires {expiry} | Expire fin {expiry} | تنتهي في {expiry} |
checkoutAddPayment | Add a payment method | Ajouter un moyen de paiement | إضافة طريقة دفع |
checkoutAddAddress | Add an address | Ajouter une adresse | إضافة عنوان |
checkoutNoSavedAddress | No saved address | Aucune adresse enregistrée | لا يوجد عنوان محفوظ |
checkoutNoSavedPayment | No saved payment method | Aucun moyen de paiement enregistré | لا توجد طريقة دفع محفوظة |
checkoutNeedAddress | Add a delivery address to pay. | Ajoutez une adresse de livraison pour payer. | أضف عنوان توصيل لتتمكن من الدفع. |
checkoutNeedPayment | Add a payment method to pay. | Ajoutez un moyen de paiement pour payer. | أضف طريقة دفع لتتمكن من الدفع. |
checkoutNoPayment | Choose a payment method | Choisir un moyen de paiement | اختر طريقة الدفع |
checkoutClose | Close | Fermer | إغلاق |
checkoutSummary | Summary | Récapitulatif | الملخص |
checkoutSubtotal | Subtotal | Sous-total | المجموع الفرعي |
checkoutShipping | Shipping | Livraison | الشحن |
checkoutDiscount | Discount | Remise | الخصم |
checkoutTax | Taxes | Taxes | الضرائب |
checkoutTotal | Total | Total | الإجمالي |
checkoutUpdating | Updating the total | Mise à jour du total | جارٍ تحديث الإجمالي |
checkoutPay | Pay {total} | Payer {total} | ادفع {total} |
checkoutPayWaiting | Pay | Payer | ادفع |
checkoutUpdateFailed | This change couldn’t be saved. | Ce changement n’a pas pu être enregistré. | تعذّر حفظ هذا التغيير. |
checkoutUpdateOffline | You are offline. This change wasn’t saved. | Vous êtes hors ligne. Ce changement n’a pas été enregistré. | أنت غير متصل. لم يُحفظ هذا التغيير. |
checkoutPaymentDeclined | Payment declined. You weren’t charged. Try another payment method. | Paiement refusé. Rien n’a été débité. Essayez un autre moyen de paiement. | تم رفض الدفع. لم يُخصم أي مبلغ. جرّب طريقة دفع أخرى. |
checkoutPaymentUnconfirmed | We couldn’t confirm the payment. It may have gone through: check it before paying again. | Le paiement n’a pas pu être confirmé. Il a peut-être été débité : vérifiez-le avant de payer à nouveau. | تعذّر تأكيد الدفع. ربما تم خصم المبلغ: تحقّق منه قبل الدفع مرة أخرى. |
checkoutPaymentNotTaken | The payment didn’t go through. You weren’t charged. | Le paiement n’est pas passé. Rien n’a été débité. | لم يتم الدفع. لم يُخصم أي مبلغ. |
checkoutCheckPayment | Check the payment | Vérifier le paiement | التحقق من الدفع |
checkoutPayingWait | Payment in progress. Stay on this screen until it’s done. | Paiement en cours. Restez sur cet écran jusqu’à la réponse. | الدفع قيد التنفيذ. ابقَ على هذه الشاشة حتى يكتمل. |
checkoutNewTotal | New total: {total} | Nouveau total : {total} | الإجمالي الجديد: {total} |
checkoutPaid | Payment accepted | Paiement accepté | تم قبول الدفع |
checkoutEmptyTitle | Your cart is empty | Votre panier est vide | سلتك فارغة |
checkoutEmptyMessage | Items you add will show up here. | Les articles que vous ajoutez s’afficheront ici. | ستظهر هنا المنتجات التي تضيفها. |
checkoutContinueShopping | Continue shopping | Continuer mes achats | متابعة التسوق |
checkoutErrorTitle | Your cart could not load | Impossible de charger le panier | تعذّر تحميل السلة |
checkoutErrorMessage | Something went wrong on our side. | Un problème est survenu de notre côté. | حدثت مشكلة من جهتنا. |
checkoutOfflineTitle | You are offline | Vous êtes hors ligne | أنت غير متصل |
checkoutOfflineMessage | Check your connection, then try again. | Vérifiez votre connexion, puis réessayez. | تحقق من اتصالك ثم أعد المحاولة. |
checkoutRetry | Try again | Réessayer | أعد المحاولة |
Reference renders
The 16 images published with version 0.2.5, rendered by the shared harness on macOS with Flutter 3.47.7. Each shows every state side by side, at the size of an iPhone (390 × 844) or of an Android phone (412 × 915). fliin verify checkout compares your copy to them.
















Use it to
- submit payment for the cart with a saved card or wallet
- change quantities right before paying
- remove an item from the bag and undo it
- use a coupon or discount code at checkout
- choose where the order is delivered
- show the subtotal, shipping, taxes and total
- Not for
- a digital subscription: use the plans screen (plans)
- a single item to add: use the product page (product)
- States