Screens / commerce
Order confirmation
Confirms a paid order with its number, estimated delivery, address, items and amounts paid, then tracking or home, with no way back to the payment.
Import
Order confirmation 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 order-confirmationAlso installs: app-bar, button, core, empty-state, icons, list-row, order-summary, page, result-card, section-header, separator, skeleton, surface, text
Examples
Confirmed
The order just paid: its number, items and delivery, with a way to track it (onTrack) or go home.
import 'package:your_app/ui/fliin/screens/order_confirmation/order_confirmation_controller.dart';
import 'package:your_app/ui/fliin/screens/order_confirmation/order_confirmation_data.dart';
import 'package:your_app/ui/fliin/screens/order_confirmation/order_confirmation_screen.dart';
import 'package:flutter/widgets.dart';
class OrderConfirmationConfirmedExample extends StatelessWidget {
const OrderConfirmationConfirmedExample({super.key});
@override
Widget build(BuildContext context) {
return OrderConfirmationScreen.standalone(
service: const SampleOrderConfirmationService(latency: Duration(milliseconds: 600)),
orderId: sampleOrder.id,
initial: const OrderConfirmationShown(sampleOrder),
onHome: () {},
onTrack: (_) {},
);
}
}Loading
While the order loads, placeholders keep its layout.
import 'package:your_app/ui/fliin/screens/order_confirmation/order_confirmation_controller.dart';
import 'package:your_app/ui/fliin/screens/order_confirmation/order_confirmation_data.dart';
import 'package:your_app/ui/fliin/screens/order_confirmation/order_confirmation_screen.dart';
import 'package:flutter/widgets.dart';
class OrderConfirmationLoadingExample extends StatelessWidget {
const OrderConfirmationLoadingExample({super.key});
@override
Widget build(BuildContext context) {
return OrderConfirmationScreen.standalone(
service: const SampleOrderConfirmationService(latency: Duration(milliseconds: 600)),
orderId: sampleOrder.id,
initial: const OrderConfirmationLoading(),
onHome: () {},
onTrack: (_) {},
);
}
}Error
When the order cannot load, the screen says so and offers to try again; the payment itself went through.
import 'package:your_app/ui/fliin/screens/order_confirmation/order_confirmation_controller.dart';
import 'package:your_app/ui/fliin/screens/order_confirmation/order_confirmation_data.dart';
import 'package:your_app/ui/fliin/screens/order_confirmation/order_confirmation_screen.dart';
import 'package:flutter/widgets.dart';
class OrderConfirmationErrorExample extends StatelessWidget {
const OrderConfirmationErrorExample({super.key});
@override
Widget build(BuildContext context) {
return OrderConfirmationScreen.standalone(
service: const SampleOrderConfirmationService(latency: Duration(milliseconds: 600)),
orderId: sampleOrder.id,
initial: const OrderConfirmationFailed(),
onHome: () {},
onTrack: (_) {},
);
}
}Offline
Without a connection, the screen asks to reconnect to show the order.
import 'package:your_app/ui/fliin/screens/order_confirmation/order_confirmation_controller.dart';
import 'package:your_app/ui/fliin/screens/order_confirmation/order_confirmation_data.dart';
import 'package:your_app/ui/fliin/screens/order_confirmation/order_confirmation_screen.dart';
import 'package:flutter/widgets.dart';
class OrderConfirmationOfflineExample extends StatelessWidget {
const OrderConfirmationOfflineExample({super.key});
@override
Widget build(BuildContext context) {
return OrderConfirmationScreen.standalone(
service: const SampleOrderConfirmationService(latency: Duration(milliseconds: 600)),
orderId: sampleOrder.id,
initial: const OrderConfirmationOffline(),
onHome: () {},
onTrack: (_) {},
);
}
}Referral
A short confirmation with your own ending: showItems: false leaves out the items, footer adds an invitation under the amounts.
import 'package:your_app/ui/fliin/components/button/fl_button.dart';
import 'package:your_app/ui/fliin/components/surface/fl_surface.dart';
import 'package:your_app/ui/fliin/components/text/fl_text.dart';
import 'package:your_app/ui/fliin/core/fliin.dart';
import 'package:your_app/ui/fliin/screens/order_confirmation/order_confirmation_controller.dart';
import 'package:your_app/ui/fliin/screens/order_confirmation/order_confirmation_data.dart';
import 'package:your_app/ui/fliin/screens/order_confirmation/order_confirmation_screen.dart';
import 'package:flutter/widgets.dart';
class OrderConfirmationReferralExample extends StatelessWidget {
const OrderConfirmationReferralExample({super.key});
@override
Widget build(BuildContext context) {
final space = FlScope.of(context).space;
return OrderConfirmationScreen.standalone(
service: const SampleOrderConfirmationService(latency: Duration(milliseconds: 600)),
orderId: sampleOrder.id,
initial: const OrderConfirmationShown(sampleOrder),
showItems: false,
footer: FlSurface(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: space.sm,
children: [
const FlText('Give €10, get €10', type: FlTextType.h6),
const FlText(
'Your friends get €10 off their first order, and you get €10 when it ships.',
type: FlTextType.bodySm,
tone: FlTextTone.muted,
),
Padding(
padding: EdgeInsetsDirectional.only(top: space.xs),
child: FlButton(
label: 'Invite friends',
variant: FlButtonVariant.outline,
size: FlButtonSize.sm,
onPressed: () {},
),
),
],
),
),
onHome: () {},
onTrack: (_) {},
);
}
}Routing
fliin add order-confirmation 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/order_confirmation_route.dart, next to order_confirmation_page.dart.
import 'package:go_router/go_router.dart';
import 'order_confirmation_page.dart';
/// Add orderConfirmationRoute to the routes of your GoRouter.
final orderConfirmationRoute = GoRoute(
path: '/order/:id',
builder: (context, state) => OrderConfirmationPage(
orderId: state.pathParameters['id']!,
// '/': your app's home.
onHome: () => context.go('/'),
onTrack: (id) => context.push('/orders/${Uri.encodeComponent(id)}'),
),
);navigator
// lib/order_confirmation_route.dart, next to order_confirmation_page.dart.
import 'package:flutter/material.dart';
import 'order_confirmation_page.dart';
import 'order_route.dart';
/// Opens “Order confirmation”: `Navigator.of(context).pushReplacement(orderConfirmationRoute(orderId, 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> orderConfirmationRoute(
String orderId, {
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) => OrderConfirmationPage(
orderId: orderId,
onHome: () => Navigator.of(context).popUntil((route) => route.isFirst),
onTrack: (id) => Navigator.of(context).push(orderRoute(id, parcelTrackingPage: parcelTrackingPage, returnPage: returnPage, supportPage: supportPage, cartPage: cartPage)),
),
);State
The controller is a ChangeNotifier from widgets.dart, with no package. fliin add order-confirmation 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/order_confirmation_page.dart
// Your backend: implement OrderConfirmationService (order_confirmation_data.dart) and pass it instead of the sample service.
import 'package:your_app/ui/fliin/screens/order_confirmation/order_confirmation_data.dart';
import 'package:your_app/ui/fliin/screens/order_confirmation/order_confirmation_screen.dart';
import 'package:flutter/widgets.dart';
class OrderConfirmationPage extends StatelessWidget {
const OrderConfirmationPage({
super.key,
required this.orderId,
required this.onHome,
required this.onTrack,
});
final String orderId;
final VoidCallback onHome;
final ValueChanged<String> onTrack;
@override
Widget build(BuildContext context) => OrderConfirmationScreen.standalone(
key: ValueKey(orderId),
service: const SampleOrderConfirmationService(),
orderId: orderId,
onHome: onHome,
onTrack: onTrack,
);
}provider
// lib/order_confirmation_page.dart
// Your backend: implement OrderConfirmationService (order_confirmation_data.dart) and pass it instead of the sample service.
import 'package:your_app/ui/fliin/screens/order_confirmation/order_confirmation_controller.dart';
import 'package:your_app/ui/fliin/screens/order_confirmation/order_confirmation_data.dart';
import 'package:your_app/ui/fliin/screens/order_confirmation/order_confirmation_screen.dart';
import 'package:flutter/widgets.dart';
import 'package:provider/provider.dart';
class OrderConfirmationPage extends StatelessWidget {
const OrderConfirmationPage({
super.key,
required this.orderId,
required this.onHome,
required this.onTrack,
});
final String orderId;
final VoidCallback onHome;
final ValueChanged<String> onTrack;
@override
Widget build(BuildContext context) => ChangeNotifierProvider(
create: (_) => OrderConfirmationController(service: const SampleOrderConfirmationService(), orderId: orderId)..load(),
child: Builder(
builder: (context) => OrderConfirmationScreen(
controller: context.read<OrderConfirmationController>(),
onHome: onHome,
onTrack: onTrack,
),
),
);
}riverpod
// lib/order_confirmation_page.dart
// Your backend: implement OrderConfirmationService (order_confirmation_data.dart) and pass it instead of the sample service.
import 'package:your_app/ui/fliin/screens/order_confirmation/order_confirmation_controller.dart';
import 'package:your_app/ui/fliin/screens/order_confirmation/order_confirmation_data.dart';
import 'package:your_app/ui/fliin/screens/order_confirmation/order_confirmation_screen.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
final orderConfirmationControllerProvider = Provider.autoDispose.family<OrderConfirmationController, String>((ref, orderId) {
final controller = OrderConfirmationController(service: const SampleOrderConfirmationService(), orderId: orderId)..load();
ref.onDispose(controller.dispose);
return controller;
});
class OrderConfirmationPage extends ConsumerWidget {
const OrderConfirmationPage({
super.key,
required this.orderId,
required this.onHome,
required this.onTrack,
});
final String orderId;
final VoidCallback onHome;
final ValueChanged<String> onTrack;
@override
Widget build(BuildContext context, WidgetRef ref) => OrderConfirmationScreen(
controller: ref.watch(orderConfirmationControllerProvider(orderId)),
onHome: onHome,
onTrack: onTrack,
);
}bloc
// lib/order_confirmation_page.dart
// Your backend: implement OrderConfirmationService (order_confirmation_data.dart) and pass it instead of the sample service.
import 'package:your_app/ui/fliin/screens/order_confirmation/order_confirmation_controller.dart';
import 'package:your_app/ui/fliin/screens/order_confirmation/order_confirmation_data.dart';
import 'package:your_app/ui/fliin/screens/order_confirmation/order_confirmation_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 OrderConfirmationCubit extends Cubit<({OrderConfirmationState state, int version})> {
OrderConfirmationCubit(this.controller) : super((state: controller.state, version: 0)) {
controller.addListener(_changed);
}
final OrderConfirmationController controller;
void _changed() => emit((state: controller.state, version: state.version + 1));
@override
Future<void> close() {
controller
..removeListener(_changed)
..dispose();
return super.close();
}
}
class OrderConfirmationPage extends StatelessWidget {
const OrderConfirmationPage({
super.key,
required this.orderId,
required this.onHome,
required this.onTrack,
});
final String orderId;
final VoidCallback onHome;
final ValueChanged<String> onTrack;
@override
Widget build(BuildContext context) => BlocProvider(
create: (_) => OrderConfirmationCubit(OrderConfirmationController(service: const SampleOrderConfirmationService(), orderId: orderId)..load()),
child: Builder(
builder: (context) => OrderConfirmationScreen(
controller: context.read<OrderConfirmationCubit>().controller,
onHome: onHome,
onTrack: onTrack,
),
),
);
}The snippets lead to order: install these screens too and paste their snippets.
API
OrderConfirmationScreen: class extends StatefulWidget
OrderConfirmationScreen.new: const new({super.key, required OrderConfirmationController controller, required VoidCallback onHome, ValueChanged<String>? onTrack, bool showItems, Widget? footer})
OrderConfirmationScreen.standalone: const new({super.key, required OrderConfirmationService service, required String orderId, required VoidCallback onHome, OrderConfirmationState? initial, ValueChanged<String>? onTrack, bool showItems, Widget? footer})
OrderConfirmationScreen.controller: get OrderConfirmationController?
OrderConfirmationScreen.service: get OrderConfirmationService?
OrderConfirmationScreen.orderId: get String?
OrderConfirmationScreen.initial: get OrderConfirmationState?
OrderConfirmationScreen.onHome: get VoidCallback
OrderConfirmationScreen.onTrack: get ValueChanged<String>?
OrderConfirmationScreen.showItems: get bool
OrderConfirmationScreen.footer: get Widget?
OrderConfirmationScreen.createState: State<OrderConfirmationScreen>()
OrderConfirmationState: sealed class permits {OrderConfirmationFailed, OrderConfirmationLoading, OrderConfirmationOffline, OrderConfirmationShown}
OrderConfirmationState.new: const new()
OrderConfirmationLoading: final class extends OrderConfirmationState
OrderConfirmationLoading.new: const new()
OrderConfirmationShown: final class extends OrderConfirmationState
OrderConfirmationShown.new: const new(Order order)
OrderConfirmationShown.order: get Order
OrderConfirmationFailed: final class extends OrderConfirmationState
OrderConfirmationFailed.new: const new()
OrderConfirmationOffline: final class extends OrderConfirmationState
OrderConfirmationOffline.new: const new()
OrderConfirmationController: class extends ChangeNotifier
OrderConfirmationController.new: new({required OrderConfirmationService service, required String orderId, OrderConfirmationState initial})
OrderConfirmationController.service: get OrderConfirmationService
OrderConfirmationController.orderId: get String
OrderConfirmationController.state: get OrderConfirmationState
OrderConfirmationController.load: Future<void>()
OrderConfirmationController.dispose: void()
OrderLine: final class
OrderLine.new: const new({required String title, required String price, required int quantity, String? variant, ImageProvider? image})
OrderLine.title: get String
OrderLine.price: get String
OrderLine.quantity: get int
OrderLine.variant: get String?
OrderLine.image: get ImageProvider?
OrderTotals: final class
OrderTotals.new: const new({required String subtotal, required String shipping, required String total, String? discount, String? tax})
OrderTotals.subtotal: get String
OrderTotals.shipping: get String
OrderTotals.discount: get String?
OrderTotals.tax: get String?
OrderTotals.total: get String
Order: final class
Order.new: const new({required String id, required String number, required List<OrderLine> lines, required OrderTotals totals, required String estimatedDelivery, required String address, String? customerName, String? email})
Order.id: get String
Order.number: get String
Order.lines: get List<OrderLine>
Order.totals: get OrderTotals
Order.estimatedDelivery: get String
Order.address: get String
Order.customerName: get String?
Order.email: get String?
OrderConfirmationNoNetwork: final class implements Exception
OrderConfirmationNoNetwork.new: const new()
OrderConfirmationService: abstract interface class
OrderConfirmationService.new: new()
OrderConfirmationService.load: abstract Future<Order>(String id)
sampleOrder: const dynamic
SampleOrderConfirmationService: final class implements OrderConfirmationService
SampleOrderConfirmationService.new: const new({Order order, Duration latency})
SampleOrderConfirmationService.order: get Order
SampleOrderConfirmationService.latency: get Duration
SampleOrderConfirmationService.load: Future<Order>(String id)
orderConfirmationL10n: const Map<String, Map<String, String>>
OrderConfirmationStrings: final class
OrderConfirmationStrings.new: const new(String language)
OrderConfirmationStrings.of: factory new(BuildContext context)
OrderConfirmationStrings.language: get String
OrderConfirmationStrings.title: get String
OrderConfirmationStrings.loading: get String
OrderConfirmationStrings.thanks: String(String name)
OrderConfirmationStrings.number: String(String number)
OrderConfirmationStrings.email: String(String email)
OrderConfirmationStrings.estimate: get String
OrderConfirmationStrings.address: get String
OrderConfirmationStrings.items: get String
OrderConfirmationStrings.quantity: String(String quantity)
OrderConfirmationStrings.summary: get String
OrderConfirmationStrings.subtotal: get String
OrderConfirmationStrings.shipping: get String
OrderConfirmationStrings.discount: get String
OrderConfirmationStrings.tax: get String
OrderConfirmationStrings.total: get String
OrderConfirmationStrings.track: get String
OrderConfirmationStrings.home: get String
OrderConfirmationStrings.errorTitle: get String
OrderConfirmationStrings.errorMessage: get String
OrderConfirmationStrings.offlineTitle: get String
OrderConfirmationStrings.offlineMessage: get String
OrderConfirmationStrings.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 |
|---|---|---|---|
orderConfirmationTitle | Order confirmed | Commande confirmée | تم تأكيد الطلب |
orderConfirmationLoading | Loading your order | Chargement de la commande | جارٍ تحميل الطلب |
orderConfirmationThanks | Thank you, {name}! | Merci, {name} ! | شكرًا لك، {name}! |
orderConfirmationNumber | Order {number} | Commande {number} | الطلب {number} |
orderConfirmationEmail | A receipt is on its way to {email}. | Un reçu part à {email}. | سيصلك إيصال على {email}. |
orderConfirmationEstimate | Estimated delivery | Livraison estimée | موعد التوصيل المتوقع |
orderConfirmationAddress | Delivery address | Adresse de livraison | عنوان التوصيل |
orderConfirmationItems | Items | Articles | المنتجات |
orderConfirmationQuantity | Quantity: {quantity} | Quantité : {quantity} | الكمية: {quantity} |
orderConfirmationSummary | Summary | Récapitulatif | الملخص |
orderConfirmationSubtotal | Subtotal | Sous-total | المجموع الفرعي |
orderConfirmationShipping | Shipping | Livraison | الشحن |
orderConfirmationDiscount | Discount | Remise | الخصم |
orderConfirmationTax | Taxes | Taxes | الضرائب |
orderConfirmationTotal | Total paid | Total payé | الإجمالي المدفوع |
orderConfirmationTrack | Track my order | Suivre ma commande | تتبّع طلبي |
orderConfirmationHome | Back to home | Retour à l’accueil | العودة إلى الرئيسية |
orderConfirmationErrorTitle | Your order could not load | Impossible de charger la commande | تعذّر تحميل الطلب |
orderConfirmationErrorMessage | Your order is placed. Only this page could not load. | Votre commande est bien passée. Seule cette page n’a pas pu se charger. | تم تقديم طلبك. تعذّر تحميل هذه الصفحة فقط. |
orderConfirmationOfflineTitle | You are offline | Vous êtes hors ligne | أنت غير متصل |
orderConfirmationOfflineMessage | Your order is placed. Connect to see its details. | Votre commande est bien passée. Connectez-vous pour en voir le détail. | تم تقديم طلبك. اتصل لعرض تفاصيله. |
orderConfirmationRetry | 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 order-confirmation compares your copy to them.
















Use it to
- confirm an order as soon as it is placed
- show the order number and the expected delivery date
- offer to track the order once bought
- show a receipt of the items and the amount paid
- show a thanks note for a completed purchase
- Not for
- the cart before payment: use the checkout screen (checkout)
- a list of past orders: use list rows (list-row)
- States