[007] 플러터 (Flutter) 실전어플제작 - 쇼핑몰 앱 제작(UI구성7 - 제품 주문완료 페이지 만들기)

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

 

반응형