이번에는 게시글 작성자의 다른 게시글을 하단에 추가해보겠습니다.
개발환경 : 윈도우11, 안드로이드 스튜디오, flutter 3.0.1
구현 전/후 화면은 아래와 같습니다.
./src/repo/item_service.dart - 유저 정보 하위에 저장된 글정보 가져오는 함수 추가
Future<List<ItemModel2>> getUserItems({required String userKey, String? itemKey}) async {
CollectionReference<Map<String, dynamic>> collectionReference =
FirebaseFirestore.instance.collection(COL_USERS).doc(userKey).collection(COL_USER_ITEMS);
QuerySnapshot<Map<String, dynamic>> snapshots = await collectionReference.get();
List<ItemModel2> items = [];
for (var snapshot in snapshots.docs) {
// ItemModel2 itemModel = ItemModel2.fromQuerySnapshot(snapshot);
ItemModel2 itemModel = ItemModel2.fromJson(snapshot.data());
itemModel.reference = snapshot.reference;
itemModel.itemKey = snapshot.id;
if (itemKey == null || itemModel.itemKey != itemKey) {
items.add(itemModel);
}
}
// logger.d(items[0].toJson());
return items;
}
./src/screens/home/item_detail_page.dart - SliverToBoxAdapter 을 이용하여 해당 영역의 제목과 GridView 구현
1. Future 는 Sliver 타입 함수가 없어서 SliverToBoxAdapter 로 wrapping 하여 사용하였다.
2. 여기서 GridView 사용시 주의 사항은 자체 영역 스크롤과 화면 영역 제한에 대해서 주의해야 한다.
physics: GridView 자체의 스크롤을 off 시키는 옵션,
shrinkWrap: 기본은 false 인데, false 면 전체화면을 차지하려하여 다른 위젯과 사용시 에러발생, ture 면, 실제 데이터를 개수만큼만 화면을 차지한다,
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: padding_16),
// 판매자의 다른 상품 보기
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'판매자의 다른 상품',
style: Theme.of(context).textTheme.bodyText1,
),
SizedBox(
width: _size!.width / 4,
child: MaterialButton(
// padding: EdgeInsets.zero,
onPressed: () {},
child: Align(
alignment: Alignment.centerRight,
child: Text(
'더보기',
style: Theme.of(context)
.textTheme
.button!
.copyWith(color: Colors.grey),
),
),
),
),
],
),
),
),
// 일반위젯을 sliver 안에 넣으러면 SliverToBoxAdapter 로 wrapping 해야 함,
SliverToBoxAdapter(
child: FutureBuilder<List<ItemModel2>>(
future: ItemService().getUserItems(
userKey: itemModel.userKey,
itemKey: itemModel.itemKey,
),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Padding(
padding: const EdgeInsets.all(padding_08),
child: GridView.count(
padding: const EdgeInsets.symmetric(horizontal: padding_08),
//EdgeInsets.zero,
// GridView 자체의 스크롤을 off 시키는 옵션,
physics: const NeverScrollableScrollPhysics(),
// 기본은 false 인데, false 면 전체화면을 차지하려하여 에러발생,
// ture 면, 가져온 정보를 기초로 화면을 차지함,
shrinkWrap: true,
crossAxisCount: 2,
mainAxisSpacing: padding_08,
crossAxisSpacing: padding_16,
childAspectRatio: 6 / 7,
children: List.generate(snapshot.data!.length,
(index) => SimilarItem(snapshot.data![index])),
),
);
}
return Container();
},
),
),
./src/screens/home/similar_item.dart - GridView 상세 부분을 별도의 위젯으로 구현, GridView 에서 보이는 게시글을 클릭하면 해당 게시물로 다시 이동한다.
import 'package:extended_image/extended_image.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../constants/common_size.dart';
import '../../constants/data_keys.dart';
import '../../models/item_model.dart';
class SimilarItem extends StatelessWidget {
final ItemModel2 _itemModel;
const SimilarItem(this._itemModel, {Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return InkWell(
onTap: () {
Get.toNamed(
ROUTE_ITEM_DETAIL,
arguments: {'itemKey': _itemModel.itemKey},
// 같은 페이지는 호출시, 중복방지가 기본설정인, false 하면 중복 호출 가능,
preventDuplicates: false
);
debugPrint('itemKey: ${_itemModel.itemKey}');
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
AspectRatio(
aspectRatio: 5 / 4,
child: ExtendedImage.network(
_itemModel.imageDownloadUrls[0],
fit: BoxFit.cover,
borderRadius: BorderRadius.circular(8),
shape: BoxShape.rectangle,
),
),
Text(
_itemModel.title,
overflow: TextOverflow.ellipsis,
maxLines: 1,
style: Theme.of(context).textTheme.subtitle1,
),
Padding(
padding: const EdgeInsets.only(bottom: padding_08),
child: Text(
'${_itemModel.price.toString()}원',
style: Theme.of(context).textTheme.subtitle2,
),
),
],
),
);
}
}
./src/utils/time_calculation.dart - 게시글 조회 시점기준으로 해당 게시물 작성시간 계산하는 함수
class TimeCalculation {
static String getTimeDiff(DateTime createdDate) {
DateTime _now = DateTime.now();
Duration timeDiff = _now.difference(createdDate);
if (timeDiff.inHours <= 1) {
return '방금 전';
} else if (timeDiff.inHours <= 24) {
return '${timeDiff.inHours}시간 전';
} else {
return '${timeDiff.inDays}일 전';
}
}
}
./src/models/item_model.dart - null 인경우, 처리 로직 추가
factory ItemModel2.fromJson(Map<String, dynamic> json) => ItemModel2(
itemKey: json["itemKey"] ?? '',
userKey: json["userKey"] ?? '',
userPhone: json["userPhone"] ?? '',
imageDownloadUrls: List<String>.from(json["imageDownloadUrls"].map((x) => x)),
title: json["title"] ?? '',
category: json["category"] ?? 'none',
price: json["price"] ?? 0,
negotiable: json["negotiable"] ?? false,
detail: json["detail"] ?? '',
address: json["address"] ?? '',
geoFirePoint: json[DOC_GEOFIREPOINT] == null
? GeoFirePoint(0, 0)
: GeoFirePoint((json[DOC_GEOFIREPOINT][DOC_GEOPOINT]).latitude,
(json[DOC_GEOFIREPOINT][DOC_GEOPOINT]).longitude),
createdDate: json[DOC_CREATEDDATE] == null
? DateTime.now().toUtc()
: (json[DOC_CREATEDDATE] as Timestamp).toDate(),
// reference: json["reference"],
);
./src/screens/home/item_screen.dart - 글 게시간 표시 부분 수정
Text(
TimeCalculation.getTimeDiff(_item.createdDate),
// '50일전',
style: Theme.of(context).textTheme.subtitle2,
),
'Flutter > 12 Clone 'Used Goods app'' 카테고리의 다른 글
[Flutter] Clone - 당근마켓45(Map - 2) (0) | 2022.08.26 |
---|---|
[Flutter] Clone - 당근마켓44(Map - 1) (0) | 2022.08.25 |
[Flutter] Clone - 당근마켓42(Item detail & PageView) - 4 (0) | 2022.08.23 |
[Flutter] Clone - 당근마켓41(Item detail & PageView) - 3 (2) | 2022.08.22 |
[Flutter] Clone - 당근마켓40(Item detail & PageView) - 2 (0) | 2022.08.21 |