[015] 플러터 (Flutter) 실전어플제작 - 쇼핑몰 앱 제작(로직구성7 - 주문 데이터 파이어스토어에서 가져오기 - 완결)

2024. 3. 10. 18:35모바일어플개발/Flutter 실전어플 개발

반응형

안녕하세요 totally 개발자입니다.

 

저번 시간에는 주문 데이터를 파이어베이스 파이어스토어에 삽입하였고 이번 포스팅에서는 주문 데이터 리스트를 조회하는 방법에 대해 살펴보도록 하겠습니다. 로그인을 구현한 강좌가 아니기 때문에 여기에서는 모든 주문한 데이터를 가져올 것입니다. 

 

Step 1: lib > models > order.dart 파일을 열어주시고 unitPrice와 totalPrice에 지정된 double.parse 부분을 제거해줍니다. (파이어스토어에 저장된 값이 이미 number 형태)

 

 

Step 2: lib > my_order_list_page.dart로 가셔서 기존에 만들어두었던 변수들을 제거하시고 orderListRef 변수를 선언해줍니다.

 

 

Step 3: 기존에 사용했던 로직을 그대로 활용하기 위해 StreamBuilder를 중첩해서 사용하였습니다. 아래 방법은 참고만 하시고 실무에서는 아래처럼 구현하는 것이 아니라 효율적인 로직으로 구현해야 합니다. 40번째 Stream에서는 orderListRef 변수의 데이터들을 가져옵니다. 그 후 46-52번째 줄을 통해 제품의 상세 정보를 가져옵니다. 54번째 줄의 Stream에 전달해주시고 58번째 줄에서 product 변수에 스트림에서 받아온 데이터를 할당한 후 orderContainer에 필요한 데이터들을 매개변수로 전달하시면 됩니다. 

 

 

Step 4: 테스트를 진행해봅니다. 아래 프로필 모양 아이콘을 눌러주시면 나의 주문 목록으로 넘어갈 수 있습니다.

 

 

 

이번 포스팅에서는 기존의 Stream 로직을 그대로 사용해서 구현했기 때문에 새로운 내용은 없었습니다. 쇼핑몰 앱 강좌는 여기에서 완결하고 나머지 구현되지 못한 부분들은 직접 실습을 진행해보시면 개발에 도움이 되실 것으로 판단합니다. 여기까지 진행하시느라 수고 많으셨습니다.

 

"해당 쇼핑몰 앱 강좌 내용 및 유튜브 강좌 영상들 모두 무단으로 배포 및 가공을 금합니다"

 

[my_order_list_page.dart]

 

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:project/constants.dart';
import 'package:project/enums/delivery_status.dart';
import 'package:project/enums/payment_status.dart';
import 'package:project/models/order.dart';
import 'package:project/models/product.dart';
class MyOrderListPage extends StatefulWidget {
const MyOrderListPage({super.key});
@override
State<MyOrderListPage> createState() => _MyOrderListPageState();
}
class _MyOrderListPageState extends State<MyOrderListPage> {
//! 먼저 주문 데이터를 가져옵니다.
final orderListRef = FirebaseFirestore.instance
.collection("orders")
.withConverter(
fromFirestore: (snapshot, _) =>
ProductOrder.fromJson(snapshot.data()!),
toFirestore: (product, _) => product.toJson());
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("나의 주문목록"),
centerTitle: true,
),
body: StreamBuilder(
//! 주문 번호를 기준으로 내림차순 정렬
stream: orderListRef.orderBy("orderNo", descending: true).snapshots(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView(
children: snapshot.data!.docs.map((document) {
//! 제품 상세 정보를 가져오기 위해 제품 번호를 기준으로 쿼리해서 가져옵니다.
final productDetailsRef = FirebaseFirestore.instance
.collection("products")
.withConverter(
fromFirestore: (snapshot, _) =>
Product.fromJson(snapshot.data()!),
toFirestore: (product, _) => product.toJson())
.where("productNo", isEqualTo: document.data().productNo);
return StreamBuilder(
stream: productDetailsRef.snapshots(),
builder: (context, productSnapshot) {
if (productSnapshot.hasData) {
//! 제품 상세 정보를 가져온 후, orderContainer에 데이터를 전달합니다.
Product product = productSnapshot.data!.docs.first.data();
return orderContainer(
productNo: document.data().productNo ?? 0,
productName: product.productName ?? "",
productImageUrl: product.productImageUrl ?? "",
price: document.data().unitPrice ?? 0,
quantity: document.data().quantity ?? 0,
orderDate: document.data().orderDate ?? "",
orderNo: document.data().orderNo ?? "",
paymentStatus: document.data().paymentStatus ?? "",
deliveryStatus: document.data().deliveryStatus ?? "",
);
} else if (productSnapshot.hasError) {
return const Center(
child: Text("오류가 발생 했습니다."),
);
} else {
return const Center(
child: CircularProgressIndicator(
strokeWidth: 2,
),
);
}
},
);
}).toList(),
);
} else if (snapshot.hasError) {
return const Center(
child: Text("오류가 발생 했습니다."),
);
} else {
return const Center(
child: CircularProgressIndicator(
strokeWidth: 2,
),
);
}
},
),
bottomNavigationBar: Padding(
padding: const EdgeInsets.all(20),
child: FilledButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text("홈으로"),
),
),
);
}
Widget orderContainer(
{required int productNo,
required String productName,
required String productImageUrl,
required double price,
required int quantity,
required String orderDate,
required String orderNo,
required String paymentStatus,
required String deliveryStatus}) {
return Container(
padding: const EdgeInsets.all(8),
child: Column(
children: [
Container(
alignment: Alignment.centerLeft,
padding: const EdgeInsets.symmetric(vertical: 5),
child: Text(
"주문날짜: $orderDate",
style: const TextStyle(
fontWeight: FontWeight.bold,
),
),
),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CachedNetworkImage(
width: MediaQuery.of(context).size.width * 0.3,
fit: BoxFit.cover,
imageUrl: productImageUrl,
placeholder: (context, url) {
return const Center(
child: CircularProgressIndicator(
strokeWidth: 2,
),
);
},
errorWidget: (context, url, error) {
return const Center(
child: Text("오류 발생"),
);
},
),
Container(
padding:
const EdgeInsets.symmetric(horizontal: 15, vertical: 8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
productName,
textScaleFactor: 1.2,
style: const TextStyle(
fontWeight: FontWeight.bold,
),
),
Text("${numberFormat.format(price)}원"),
Text("수량: $quantity"),
Text("합계: ${numberFormat.format(price * quantity)}원"),
Text(
"${PaymentStatus.getStatusName(paymentStatus).statusName} / ${DeliveryStatus.getStatusName(deliveryStatus).statusName}",
style: const TextStyle(
fontWeight: FontWeight.bold,
),
),
],
),
),
],
),
const SizedBox(
height: 8,
),
Row(
children: [
FilledButton.tonal(
onPressed: () {},
child: const Text("주문취소"),
),
const SizedBox(width: 10),
FilledButton(
onPressed: () {},
child: const Text("배송조회"),
),
],
)
],
),
);
}
}

 

[유튜브 강좌 영상] 

https://youtu.be/H3hIooTTY2Q

 

 

 

반응형