2023. 11. 11. 16:45ㆍ모바일어플개발/Flutter 실전어플 개발
안녕하세요~ totally 개발자입니다.
저번 포스팅까지는 결제 시작 페이지 UI 구성을 해보았습니다. 이번 포스팅에서는 제품 주문완료 페이지를 만들어봅니다.
Step 1: item_order_result_page.dart 파일을 생성해서 item_checkout_page.dart에 있는 파일을 모두 복사해서 가져옵니다.

그리고 클래스명을 ItemOrderResultPage로 변경하신 뒤에 8번째부터 14번째까지 parameter 값으로 가져오기 위해 동일하게 추가해줍니다.
Step 2: 주문번호를 현재 시각에 맞춰서 생성하는 메소드를 만들어줍니다. 이 주문번호에 관해서는 각 쇼핑몰에서 다른 규칙을 적용하고 있습니다. 저는 연월일-시분초 형식에서 초를 millisecond로 해주었습니다.

Step 3: 무통장입금인 경우에 표시해주어야 하는 입금계좌안내 위젯을 만들어줍니다. (모든 소스 코드는 맨 아래에 있습니다. 제가 이미지로 붙여 넣는 이유는 가급적 소스 코드를 직접 타이핑 해보면서 이해하는 것이 좋다고 생각하기 때문이기 때문입니다) 여기에서 Expanded 위젯을 사용하였는데 flex: 4, flex: 6를 각각 적용하여 가로 비율이 4대6으로 나오도록 고정해두었습니다.

Step 4: 입금금액 또는 결제금액을 표시해줄 수 있는 위젯을 만들어줍니다. 여기 145번째 줄에 widget.paymentMethod가 카드결제 또는 무통장입금으로 받아오는데 서로 다르게 출력해주기 위함입니다. 금액도 이전 결제 시작 페이지에서 결정되므로 이것도 별도로 변수로 받아서 출력합니다.

Step 5: 주문번호를 보여주는 위젯을 만들어줍니다. 조금 전에 generateOrderNumber()라는 메소드를 만들었는데 그것을 사용하면 됩니다.

Step 6: 배송지를 보여주는 위젯을 만들어줍니다.

Step 7: Scaffold 부분 body에 다음처럼 모두 추가해주시면 됩니다. 무통장입금인 경우와 카드결제의 경우가 다른 부분이 있어 if문으로 처리해주면 됩니다. 주문정보관련 위젯을 더 돋보이게 해주기 위해 하양색으로 배경처리하였고 그 외 Scaffold의 배경색에는 white 색상에 투명도를 90%로 처리하였습니다.

Step 8: 그 밑에 bottomNavigationBar를 아래처럼 변경해주면 됩니다. 여기에서 중요한 부분은 popUntil 부분인데 route.isFirst 옵션을 사용하면 맨 처음에 어플이 제품 리스트 페이지를 보여주게 되는데 여태까지 쌓였던 Navigation 스택을 모두 제거하고 이 제품 리스트 페이지만 남겨두게 되어 첫 페이지로 이동하는 효과를 줄 수 있습니다. 여기에서 pop()를 사용하게 되면 그 전 페이지인 결제시작 페이지가 나오게 되어 오류를 초래할 가능성이 높습니다. 이 부분을 잘 처리해주어야 합니다.

Step 9: 마지막으로 item_checkout_page.dart 페이지로 가셔서 아래 부분을 파라미터로 전달해주시면 됩니다.

Step 10: 실행 모습입니다.
왼쪽은 카드 결제에서 넘어가는 모습이고 오른쪽은 무통장입금으로 처리한 결과입니다.
![]() |
![]() |
[전체 소스 코드]
[item_order_result_page.dart]
import 'package:flutter/material.dart'; | |
import 'package:project/constants.dart'; | |
import 'package:project/models/product.dart'; | |
class ItemOrderResultPage extends StatefulWidget { | |
ItemOrderResultPage({ | |
super.key, | |
required this.paymentMethod, | |
required this.paymentAmount, | |
required this.receiverName, | |
required this.receiverPhone, | |
required this.zip, | |
required this.address1, | |
required this.address2, | |
}); | |
String paymentMethod = ""; | |
double paymentAmount = 0; | |
String receiverName = ""; | |
String receiverPhone = ""; | |
String zip = ""; | |
String address1 = ""; | |
String address2 = ""; | |
@override | |
State<ItemOrderResultPage> createState() => _ItemOrderResultPageState(); | |
} | |
class _ItemOrderResultPageState extends State<ItemOrderResultPage> { | |
List<Product> orderList = [ | |
Product( | |
productNo: 1, | |
productName: "노트북(Laptop)", | |
productImageUrl: "https://picsum.photos/id/1/300/300", | |
price: 600000), | |
Product( | |
productNo: 4, | |
productName: "키보드(Keyboard)", | |
productImageUrl: "https://picsum.photos/id/60/300/300", | |
price: 50000), | |
]; | |
List<Map<int, int>> quantityList = [ | |
{1: 2}, | |
{4: 3}, | |
]; | |
double totalPrice = 0; | |
@override | |
void initState() { | |
super.initState(); | |
} | |
String generateOrderNumber() { | |
final dateTime = DateTime.now(); | |
String orderNo = | |
"${dateTime.year}${dateTime.month}${dateTime.day}-${dateTime.hour}${dateTime.minute}${dateTime.millisecond}"; | |
return orderNo; | |
} | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
backgroundColor: Colors.white.withOpacity(0.9), | |
appBar: AppBar( | |
title: const Text("주문완료"), | |
centerTitle: true, | |
), | |
body: Container( | |
alignment: Alignment.topCenter, | |
child: SingleChildScrollView( | |
child: Column( | |
children: [ | |
Container( | |
margin: const EdgeInsets.only(top: 20), | |
padding: const EdgeInsets.all(8), | |
child: const Text("주문이 완료되었습니다.", textScaleFactor: 1.2), | |
), | |
if (widget.paymentMethod == "무통장입금") | |
Container( | |
padding: const EdgeInsets.all(8), | |
child: const Text("아래 계좌 정보로 입금해 주시면 결제 완료처리가 됩니다."), | |
), | |
//! 주문정보관련 | |
Container( | |
margin: const EdgeInsets.all(30), | |
padding: const EdgeInsets.only(top: 10), | |
color: Colors.white, | |
child: Column( | |
children: [ | |
if (widget.paymentMethod == "무통장입금") depositInfoRow(), | |
if (widget.paymentMethod == "무통장입금") const Divider(), | |
orderNumberRow(), | |
const Divider(), | |
paymentAmountRow(), | |
const Divider(), | |
shippingAddressRow(), | |
], | |
), | |
), | |
], | |
)), | |
), | |
bottomNavigationBar: Padding( | |
padding: const EdgeInsets.all(20), | |
child: FilledButton( | |
onPressed: () { | |
Navigator.of(context).popUntil((route) => route.isFirst); | |
}, | |
child: const Text("홈으로"), | |
)), | |
); | |
} | |
Widget depositInfoRow() { | |
return Container( | |
padding: const EdgeInsets.all(15), | |
child: const Row( | |
mainAxisSize: MainAxisSize.max, | |
mainAxisAlignment: MainAxisAlignment.center, | |
children: [ | |
Expanded( | |
flex: 4, | |
child: Text("입금계좌안내"), | |
), | |
Expanded( | |
flex: 6, | |
child: Text("12345678901234 (은행명)"), | |
), | |
], | |
), | |
); | |
} | |
Widget paymentAmountRow() { | |
return Container( | |
padding: const EdgeInsets.all(15), | |
child: Row( | |
mainAxisSize: MainAxisSize.max, | |
mainAxisAlignment: MainAxisAlignment.center, | |
children: [ | |
Expanded( | |
flex: 4, | |
child: Text(widget.paymentMethod == "무통장입금" ? "입금금액" : "결제금액"), | |
), | |
Expanded( | |
flex: 6, | |
child: Text("${numberFormat.format(widget.paymentAmount)}원"), | |
), | |
], | |
), | |
); | |
} | |
Widget orderNumberRow() { | |
return Container( | |
padding: const EdgeInsets.all(15), | |
child: Row( | |
mainAxisSize: MainAxisSize.max, | |
mainAxisAlignment: MainAxisAlignment.center, | |
children: [ | |
const Expanded( | |
flex: 4, | |
child: Text("주문번호"), | |
), | |
Expanded( | |
flex: 6, | |
child: Text(generateOrderNumber()), | |
) | |
], | |
), | |
); | |
} | |
Widget shippingAddressRow() { | |
return Container( | |
padding: const EdgeInsets.all(15), | |
child: Row( | |
mainAxisSize: MainAxisSize.max, | |
mainAxisAlignment: MainAxisAlignment.center, | |
children: [ | |
const Expanded( | |
flex: 4, | |
child: Column( | |
crossAxisAlignment: CrossAxisAlignment.start, | |
children: [ | |
Text("배송지"), | |
], | |
), | |
), | |
Expanded( | |
flex: 6, | |
child: Column( | |
crossAxisAlignment: CrossAxisAlignment.start, | |
children: [ | |
Text( | |
"${widget.receiverName} (${widget.receiverPhone})", | |
style: const TextStyle( | |
fontWeight: FontWeight.bold, | |
), | |
), | |
Text("${widget.address1} ${widget.address2}"), | |
Text("(${widget.zip})"), | |
], | |
), | |
), | |
], | |
), | |
); | |
} | |
} |
[유튜브 강좌 영상]
https://youtu.be/Vst5O_lhfMU?si=GfSRDHL7fPPsM3Mo
'모바일어플개발 > Flutter 실전어플 개발' 카테고리의 다른 글
[009] 플러터 (Flutter) 실전어플제작 - 쇼핑몰 앱 제작(로직구성1 - 장바구니에 제품 담기) (2) | 2023.11.26 |
---|---|
[008] 플러터 (Flutter) 실전어플제작 - 쇼핑몰 앱 제작(UI구성8 - 제품 주문조회 페이지 만들기) (2) | 2023.11.18 |
[006] 플러터 (Flutter) 실전어플제작 - 쇼핑몰 앱 제작(UI구성6 - 제품 결제시작 페이지 만들기 3) (1) | 2023.11.04 |
[005] 플러터 (Flutter) 실전어플제작 - 쇼핑몰 앱 제작(UI구성5 - 제품 결제시작 페이지 만들기 2) (2) | 2023.10.14 |
[004] 플러터 (Flutter) 실전어플제작 - 쇼핑몰 앱 제작(UI구성4 - 제품 결제시작 페이지 만들기 1) (1) | 2023.10.09 |