6177214e-ce7c-49e3-99de-ff9721b26f63 — Commit 08da2f11

AuthorMikkel Thygesen<mikkelet@gmail.com>
Date2026-02-27 13:36:16 +0100
2922: added confirm order screen

Changed files

concierge/lib/domain/models/data_state.dart        |  2 +
 .../confirm_order/confirm_order_screen.dart        | 62 +++++++++++++++++-----
 .../widgets/confirm_order_action_bar.dart          | 34 ++++++++++++
 .../widgets/confirm_order_product_list_tile.dart   | 28 ++++++++++
 .../screens/review_order/review_order_route.dart   |  4 +-
 .../screens/review_order/review_order_screen.dart  | 27 ++--------
 .../lib/presentation/widgets/bevelled_app_bar.dart |  8 ++-
 .../lib/presentation/widgets/comment_field.dart    | 36 +++++++++++++
 8 files changed, 163 insertions(+), 38 deletions(-)

Diff

diff --git a/concierge/lib/domain/models/data_state.dart b/concierge/lib/domain/models/data_state.dart
index b568fb99..5f9f4207 100644
--- a/concierge/lib/domain/models/data_state.dart
+++ b/concierge/lib/domain/models/data_state.dart
@@ -10,6 +10,8 @@ sealed class DataState {
static failure(AppError e) => Failure(e);
static success<T>(T data) => Success(data);
+
+ bool get isReady => this is Success;
}
class Initial extends DataState {
diff --git a/concierge/lib/presentation/screens/confirm_order/confirm_order_screen.dart b/concierge/lib/presentation/screens/confirm_order/confirm_order_screen.dart
index 71b12136..4623ffd0 100644
--- a/concierge/lib/presentation/screens/confirm_order/confirm_order_screen.dart
+++ b/concierge/lib/presentation/screens/confirm_order/confirm_order_screen.dart
@@ -1,8 +1,15 @@
+import 'package:concierge/presentation/app/cart_cubit.dart';
+import 'package:concierge/presentation/screens/confirm_order/widgets/confirm_order_action_bar.dart';
+import 'package:concierge/presentation/screens/confirm_order/widgets/confirm_order_product_list_tile.dart';
import 'package:concierge/presentation/screens/payment/payment_route.dart';
+import 'package:concierge/presentation/widgets/bevelled_app_bar.dart';
+import 'package:concierge/presentation/widgets/comment_field.dart';
+import 'package:concierge/presentation/widgets/padded_column.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:concierge/presentation/screens/confirm_order/bloc/confirm_order_cubit.dart';
import 'package:concierge/presentation/screens/confirm_order/bloc/confirm_order_state.dart';
+import 'package:gap/gap.dart';
class ConfirmOrderScreen extends StatelessWidget {
const ConfirmOrderScreen({super.key});
@@ -12,6 +19,7 @@ class ConfirmOrderScreen extends StatelessWidget {
return BlocBuilder<ConfirmOrderCubit, ConfirmOrderState>(
builder: (context, state) {
final cubit = context.read<ConfirmOrderCubit>();
+ final cartCubit = context.read<CartCubit>();
return MultiBlocListener(
listeners: [
BlocListener<ConfirmOrderCubit, ConfirmOrderState>(
@@ -20,23 +28,53 @@ class ConfirmOrderScreen extends StatelessWidget {
),
],
child: Scaffold(
- appBar: AppBar(),
- body: Center(
- child: Column(
- children: [
- Text("ConfirmOrder"),
- TextButton(
- onPressed: () {
- PaymentRoute().push(context);
- },
- child: Text("Gå til betaling"),
+ bottomNavigationBar: ConfirmOrderActionBar(),
+ body: Stack(
+ children: [
+ SingleChildScrollView(
+ child: PaddedColumn(
+ padding: EdgeInsets.symmetric(horizontal: 16),
+ children: [
+ const Gap(100),
+ const Gap(36),
+ Text("Ordreoversigt", style: TextStyle(fontSize: 20)),
+ const Gap(36),
+ ...buildOrderOverview(context),
+ const Gap(16),
+ ...buildCommentField(context),
+ const Gap(36),
+ Text("Betaling", style: TextStyle(fontSize: 20)),
+ ],
),
- ],
- ),
+ ),
+ BevelledAppBar(
+ showBack: true,
+ showClose: false,
+ ),
+ ],
),
),
);
},
);
}
+
+ Iterable<Widget> buildOrderOverview(BuildContext context) sync* {
+ final cartCubit = context.read<CartCubit>();
+ yield Divider(color: Colors.grey.shade300);
+ for (final product in cartCubit.products) {
+ yield const Gap(12);
+ yield ConfirmOrderProductListTile(product: product);
+ yield const Gap(12);
+ yield Divider(color: Colors.grey.shade300);
+ }
+ yield Row(
+ children: [
+ Text("I alt", style: TextStyle(fontSize: 20)),
+ Expanded(child: Spacer()),
+ Text("${cartCubit.totalPrice} kr.", style: TextStyle(fontSize: 20)),
+ ],
+ );
+ yield Divider(color: Colors.grey.shade300);
+ }
}
diff --git a/concierge/lib/presentation/screens/confirm_order/widgets/confirm_order_action_bar.dart b/concierge/lib/presentation/screens/confirm_order/widgets/confirm_order_action_bar.dart
new file mode 100644
index 00000000..ae7b80f0
--- /dev/null
+++ b/concierge/lib/presentation/screens/confirm_order/widgets/confirm_order_action_bar.dart
@@ -0,0 +1,34 @@
+import 'package:concierge/presentation/screens/payment/payment_route.dart';
+import 'package:concierge/presentation/theme/app_colors.dart';
+import 'package:flutter/material.dart';
+
+class ConfirmOrderActionBar extends StatelessWidget {
+ const ConfirmOrderActionBar({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ decoration: BoxDecoration(
+ border: Border(top: BorderSide(color: Colors.grey.shade300, width: 1)),
+ ),
+ child: Padding(
+ padding: const EdgeInsets.all(16.0),
+ child: SafeArea(
+ child: TextButton(
+ style: ButtonStyle(
+ backgroundColor: WidgetStatePropertyAll(AppColors.sandColor),
+ minimumSize: WidgetStatePropertyAll(Size(0, 50)),
+ ),
+ onPressed: () {
+ PaymentRoute().push(context);
+ },
+ child: Text(
+ "Bekræft ordre",
+ style: TextStyle(color: Colors.white),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/concierge/lib/presentation/screens/confirm_order/widgets/confirm_order_product_list_tile.dart b/concierge/lib/presentation/screens/confirm_order/widgets/confirm_order_product_list_tile.dart
new file mode 100644
index 00000000..b9d797a0
--- /dev/null
+++ b/concierge/lib/presentation/screens/confirm_order/widgets/confirm_order_product_list_tile.dart
@@ -0,0 +1,28 @@
+import 'package:concierge/data/remote/models/product.dart';
+import 'package:concierge/presentation/app/cart_cubit.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:gap/gap.dart';
+
+class ConfirmOrderProductListTile extends StatelessWidget {
+ const ConfirmOrderProductListTile({super.key, required this.product});
+
+ final Product product;
+
+ @override
+ Widget build(BuildContext context) {
+ final cartCubit = context.read<CartCubit>();
+ final quantity = cartCubit.getQuantity(product.id);
+ final price = product.price * quantity;
+ return Row(
+ children: [
+ Text("${quantity}x"),
+ const Gap(16),
+ Text(product.title),
+ Expanded(child: Spacer()),
+ const Gap(16),
+ Text("$price kr."),
+ ],
+ );
+ }
+}
diff --git a/concierge/lib/presentation/screens/review_order/review_order_route.dart b/concierge/lib/presentation/screens/review_order/review_order_route.dart
index 596e7c0c..39476129 100644
--- a/concierge/lib/presentation/screens/review_order/review_order_route.dart
+++ b/concierge/lib/presentation/screens/review_order/review_order_route.dart
@@ -1,8 +1,8 @@
+import 'package:concierge/presentation/navigation/transitions/slide_in_transition.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:concierge/presentation/navigation/app_routes.dart';
import 'package:go_router/go_router.dart';
-import 'package:concierge/presentation/navigation/transitions/slide_up_transition.dart';
import 'package:concierge/presentation/screens/review_order/bloc/review_order_cubit.dart';
import 'package:concierge/presentation/screens/review_order/review_order_screen.dart';
@@ -12,7 +12,7 @@ part '../../../_generated/presentation/screens/review_order/review_order_route.g
class ReviewOrderRoute extends GoRouteData with $ReviewOrderRoute {
@override
Page<void> buildPage(BuildContext context, GoRouterState state) {
- return slideUpTransition(
+ return slideInTransition(
state: state,
child: BlocProvider(
create: (context) => ReviewOrderCubit(),
diff --git a/concierge/lib/presentation/screens/review_order/review_order_screen.dart b/concierge/lib/presentation/screens/review_order/review_order_screen.dart
index 80071efd..f66f453d 100644
--- a/concierge/lib/presentation/screens/review_order/review_order_screen.dart
+++ b/concierge/lib/presentation/screens/review_order/review_order_screen.dart
@@ -1,5 +1,6 @@
import 'package:concierge/data/remote/models/product.dart';
import 'package:concierge/presentation/app/cart_cubit.dart';
+import 'package:concierge/presentation/widgets/comment_field.dart';
import 'package:concierge/presentation/screens/review_order/widget/delivery_method_picker.dart';
import 'package:concierge/presentation/screens/review_order/widget/delivery_time_picker.dart';
import 'package:concierge/presentation/screens/review_order/widget/product_in_cart_list_tile.dart';
@@ -43,7 +44,10 @@ class ReviewOrderScreen extends StatelessWidget {
},
),
),
- BevelledAppBar(),
+ BevelledAppBar(
+ showBack: true,
+ showClose: false,
+ ),
],
);
},
@@ -76,27 +80,6 @@ class ReviewOrderScreen extends StatelessWidget {
yield DeliveryTimePicker();
}
- Iterable<Widget> buildCommentField(BuildContext context) sync* {
- final cartCubit = context.watch<CartCubit>();
- yield Text("Kommentar til ordre", style: TextStyle(fontSize: 20));
- yield Gap(16);
- yield TextField(
- controller: cartCubit.orderCommentController,
- minLines: 3,
- maxLines: 5,
- decoration: InputDecoration(
- focusedBorder: OutlineInputBorder(
- borderSide: BorderSide(color: Colors.grey, width: 1),
- borderRadius: BorderRadius.circular(12),
- ),
- border: OutlineInputBorder(
- borderSide: BorderSide(color: Colors.grey, width: 1),
- borderRadius: BorderRadius.circular(12),
- ),
- ),
- );
- }
-
Iterable<Widget> buildProducts(BuildContext context, Iterable<Product> products) sync* {
if (products.isEmpty) {
yield Gap(36);
diff --git a/concierge/lib/presentation/widgets/bevelled_app_bar.dart b/concierge/lib/presentation/widgets/bevelled_app_bar.dart
index 8d6df388..af4d7bf9 100644
--- a/concierge/lib/presentation/widgets/bevelled_app_bar.dart
+++ b/concierge/lib/presentation/widgets/bevelled_app_bar.dart
@@ -2,7 +2,10 @@ import 'package:concierge/presentation/theme/app_colors.dart';
import 'package:flutter/material.dart';
class BevelledAppBar extends StatelessWidget {
- const BevelledAppBar({super.key});
+ const BevelledAppBar({super.key, this.showClose = true, this.showBack = false});
+
+ final bool showClose;
+ final bool showBack;
@override
Widget build(BuildContext context) {
@@ -17,8 +20,9 @@ class BevelledAppBar extends StatelessWidget {
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
+ if (showBack) BackButton(),
Spacer(),
- CloseButton(color: Colors.black),
+ if (showClose) CloseButton(color: Colors.black),
],
),
),
diff --git a/concierge/lib/presentation/widgets/comment_field.dart b/concierge/lib/presentation/widgets/comment_field.dart
new file mode 100644
index 00000000..dbade936
--- /dev/null
+++ b/concierge/lib/presentation/widgets/comment_field.dart
@@ -0,0 +1,36 @@
+import 'package:concierge/presentation/app/cart_cubit.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:gap/gap.dart';
+
+class CommentField extends StatelessWidget {
+ const CommentField({super.key, required this.controller});
+
+ final TextEditingController controller;
+
+ @override
+ Widget build(BuildContext context) {
+ return TextField(
+ controller: controller,
+ minLines: 3,
+ maxLines: 5,
+ decoration: InputDecoration(
+ focusedBorder: OutlineInputBorder(
+ borderSide: BorderSide(color: Colors.grey, width: 1),
+ borderRadius: BorderRadius.circular(12),
+ ),
+ border: OutlineInputBorder(
+ borderSide: BorderSide(color: Colors.grey, width: 1),
+ borderRadius: BorderRadius.circular(12),
+ ),
+ ),
+ );
+ }
+}
+
+Iterable<Widget> buildCommentField(BuildContext context) sync* {
+ final cartCubit = context.watch<CartCubit>();
+ yield Text("Kommentar til ordre", style: TextStyle(fontSize: 20));
+ yield Gap(16);
+ yield CommentField(controller: cartCubit.orderCommentController);
+}