Fliin
On this page

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.

16 of 16 variants pass
Cart and checkout, cart state
Platform
Brightness
Text scale
Direction

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 checkout

Also 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.

Cart and checkout, cart state
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.

Cart and checkout, loading state
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.

Cart and checkout, empty state
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.

Cart and checkout, updating state
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.

Cart and checkout, paying state
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.

Cart and checkout, failed state
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.

Cart and checkout, offline state
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.

Cart and checkout, error state
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.

Cart and checkout, no-promo state
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 String

Strings

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.

Keyenfrar
checkoutTitleCartPanierالسلة
checkoutBackBackRetourرجوع
checkoutLoadingLoading your cartChargement du panierجارٍ تحميل السلة
checkoutItemsItemsArticlesالمنتجات
checkoutQuantityQuantityQuantitéالكمية
checkoutQuantityOfQuantity, {item}Quantité, {item}الكمية، {item}
checkoutDecreaseDecrease quantity of {item}Diminuer la quantité de {item}إنقاص كمية {item}
checkoutIncreaseIncrease quantity of {item}Augmenter la quantité de {item}زيادة كمية {item}
checkoutRemoveRemoveRetirerإزالة
checkoutRemoveItemRemove {item}Retirer {item}إزالة {item}
checkoutRemoved{item} was removed.{item} est retiré du panier.تمت إزالة {item}.
checkoutUndoUndoAnnulerتراجع
checkoutPromoPromo codeCode promoرمز الخصم
checkoutPromoApplyApplyAppliquerتطبيق
checkoutPromoRejectedThis code is not valid.Ce code n’est pas valable.هذا الرمز غير صالح.
checkoutPromoAppliedCode {code} appliedCode {code} appliquéتم تطبيق الرمز {code}
checkoutPromoRemoveRemove codeRetirer le codeإزالة الرمز
checkoutDeliveryDeliveryLivraisonالتوصيل
checkoutChooseAddressDelivery addressAdresse de livraisonعنوان التوصيل
checkoutNoAddressAdd a delivery addressAjouter une adresse de livraisonأضف عنوان توصيل
checkoutPaymentPaymentPaiementالدفع
checkoutChoosePaymentPayment methodMoyen de paiementطريقة الدفع
checkoutCard{brand} ending in {last4}{brand} se terminant par {last4}{brand} تنتهي بـ {last4}
checkoutExpiresExpires {expiry}Expire fin {expiry}تنتهي في {expiry}
checkoutAddPaymentAdd a payment methodAjouter un moyen de paiementإضافة طريقة دفع
checkoutAddAddressAdd an addressAjouter une adresseإضافة عنوان
checkoutNoSavedAddressNo saved addressAucune adresse enregistréeلا يوجد عنوان محفوظ
checkoutNoSavedPaymentNo saved payment methodAucun moyen de paiement enregistréلا توجد طريقة دفع محفوظة
checkoutNeedAddressAdd a delivery address to pay.Ajoutez une adresse de livraison pour payer.أضف عنوان توصيل لتتمكن من الدفع.
checkoutNeedPaymentAdd a payment method to pay.Ajoutez un moyen de paiement pour payer.أضف طريقة دفع لتتمكن من الدفع.
checkoutNoPaymentChoose a payment methodChoisir un moyen de paiementاختر طريقة الدفع
checkoutCloseCloseFermerإغلاق
checkoutSummarySummaryRécapitulatifالملخص
checkoutSubtotalSubtotalSous-totalالمجموع الفرعي
checkoutShippingShippingLivraisonالشحن
checkoutDiscountDiscountRemiseالخصم
checkoutTaxTaxesTaxesالضرائب
checkoutTotalTotalTotalالإجمالي
checkoutUpdatingUpdating the totalMise à jour du totalجارٍ تحديث الإجمالي
checkoutPayPay {total}Payer {total}ادفع {total}
checkoutPayWaitingPayPayerادفع
checkoutUpdateFailedThis change couldn’t be saved.Ce changement n’a pas pu être enregistré.تعذّر حفظ هذا التغيير.
checkoutUpdateOfflineYou are offline. This change wasn’t saved.Vous êtes hors ligne. Ce changement n’a pas été enregistré.أنت غير متصل. لم يُحفظ هذا التغيير.
checkoutPaymentDeclinedPayment declined. You weren’t charged. Try another payment method.Paiement refusé. Rien n’a été débité. Essayez un autre moyen de paiement.تم رفض الدفع. لم يُخصم أي مبلغ. جرّب طريقة دفع أخرى.
checkoutPaymentUnconfirmedWe 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.تعذّر تأكيد الدفع. ربما تم خصم المبلغ: تحقّق منه قبل الدفع مرة أخرى.
checkoutPaymentNotTakenThe payment didn’t go through. You weren’t charged.Le paiement n’est pas passé. Rien n’a été débité.لم يتم الدفع. لم يُخصم أي مبلغ.
checkoutCheckPaymentCheck the paymentVérifier le paiementالتحقق من الدفع
checkoutPayingWaitPayment in progress. Stay on this screen until it’s done.Paiement en cours. Restez sur cet écran jusqu’à la réponse.الدفع قيد التنفيذ. ابقَ على هذه الشاشة حتى يكتمل.
checkoutNewTotalNew total: {total}Nouveau total : {total}الإجمالي الجديد: {total}
checkoutPaidPayment acceptedPaiement acceptéتم قبول الدفع
checkoutEmptyTitleYour cart is emptyVotre panier est videسلتك فارغة
checkoutEmptyMessageItems you add will show up here.Les articles que vous ajoutez s’afficheront ici.ستظهر هنا المنتجات التي تضيفها.
checkoutContinueShoppingContinue shoppingContinuer mes achatsمتابعة التسوق
checkoutErrorTitleYour cart could not loadImpossible de charger le panierتعذّر تحميل السلة
checkoutErrorMessageSomething went wrong on our side.Un problème est survenu de notre côté.حدثت مشكلة من جهتنا.
checkoutOfflineTitleYou are offlineVous êtes hors ligneأنت غير متصل
checkoutOfflineMessageCheck your connection, then try again.Vérifiez votre connexion, puis réessayez.تحقق من اتصالك ثم أعد المحاولة.
checkoutRetryTry againRé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.

iOS · Light
Cart and checkout, iOS, Light, text 1.0×, Left to right
Text 1.0× · Left to right
Cart and checkout, iOS, Light, text 1.0×, Right to left
Text 1.0× · Right to left
Cart and checkout, iOS, Light, text 2.0×, Left to right
Text 2.0× · Left to right
Cart and checkout, iOS, Light, text 2.0×, Right to left
Text 2.0× · Right to left
iOS · Dark
Cart and checkout, iOS, Dark, text 1.0×, Left to right
Text 1.0× · Left to right
Cart and checkout, iOS, Dark, text 1.0×, Right to left
Text 1.0× · Right to left
Cart and checkout, iOS, Dark, text 2.0×, Left to right
Text 2.0× · Left to right
Cart and checkout, iOS, Dark, text 2.0×, Right to left
Text 2.0× · Right to left
Android · Light
Cart and checkout, Android, Light, text 1.0×, Left to right
Text 1.0× · Left to right
Cart and checkout, Android, Light, text 1.0×, Right to left
Text 1.0× · Right to left
Cart and checkout, Android, Light, text 2.0×, Left to right
Text 2.0× · Left to right
Cart and checkout, Android, Light, text 2.0×, Right to left
Text 2.0× · Right to left
Android · Dark
Cart and checkout, Android, Dark, text 1.0×, Left to right
Text 1.0× · Left to right
Cart and checkout, Android, Dark, text 1.0×, Right to left
Text 1.0× · Right to left
Cart and checkout, Android, Dark, text 2.0×, Left to right
Text 2.0× · Left to right
Cart and checkout, Android, Dark, text 2.0×, Right to left
Text 2.0× · Right to left

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
  • Cart
  • Loading
  • Empty
  • Updating
  • Paying
  • Failed
  • Offline
  • Error

Arrows to move, Enter to open, Escape to close.