이번에는 글목록 페이지에서 더미 데이터를 실제 데이터로 변경하는 부분 및 상세 페이지 추가해보겠습니다.
글목록에서 상세페이지로 이동시, arguments 를 통해서 "아이템 키값" 을 전달하는 방법도 구현해보겠습니다.
개발환경 : 윈도우11, 안드로이드 스튜디오, flutter 3.0.1
구현된 화면은 아래와 같습니다.
./src/repo/item_service.dart - 저장된 글목록 로딩 함수 추가
Future<ItemModel2> getItem(String itemKey) async {
if (itemKey[0] == ':') {
String orgItemKey = itemKey;
itemKey = itemKey.substring(1);
logger.d('[${orgItemKey.substring(0, 10)}...], ==>> [${itemKey.substring(0, 9)}...]');
}
DocumentReference<Map<String, dynamic>> docRef =
FirebaseFirestore.instance.collection(COL_ITEMS).doc(itemKey);
final DocumentSnapshot<Map<String, dynamic>> documentSnapshot = await docRef.get();
// fromSnapshot 을 대체하려면, 2줄 코딩으로 변경필요함,
ItemModel2 itemModel = ItemModel2.fromJson(documentSnapshot.data()!);
itemModel.reference = documentSnapshot.reference;
return itemModel;
}
Future<List<ItemModel2>> getItems(String userKey) async {
logger.d('userKey[' + userKey.toString() + ']');
CollectionReference<Map<String, dynamic>> collectionReference =
FirebaseFirestore.instance.collection(COL_ITEMS);
// collection.get() is Future, collection.snapshots() is Stream
// 모든 게시글 가져오기
QuerySnapshot<Map<String, dynamic>> snapshots = await collectionReference.get();
// 자신의 게시글 제외하고 가져오기
// QuerySnapshot<Map<String, dynamic>> snapshots =
// await collectionReference.where(DOC_USERKEY, isNotEqualTo: userKey).get();
List<ItemModel2> items = [];
for (var snapshot in snapshots.docs) {
//fromQuerySnapshot 대체품 만들것,
// ItemModel2 itemModel = ItemModel2.fromQuerySnapshot(snapshot);
ItemModel2 itemModel = ItemModel2.fromJson(snapshot.data());
itemModel.reference = snapshot.reference;
items.add(itemModel);
}
return items;
}
./src/screens/home/items_screen.dart - item_page 에서 이름변경
1. future 대상 객체 변경.
2. _listView(imgSize, snapshot.data!) - 추가된 인자를 통해서 데이터 전달.
return FutureBuilder<List<ItemModel2>>(
// future: Future.delayed(const Duration(seconds: 2), () => 100),
future: ItemService().getItems(UserController.to.user.value!.uid),
//(화면전화 이전에 매핑되므로 사용가능)FirebaseAuth.instance.currentUser!.uid
//(화면전화 이전에 매핑되므로 사용가능)UserController.to.user.value!.uid
//(화면전화 이후에 매핑되므로 사용불가)UserController.to.userModel.value!.userKey
builder: (context, snapshot) {
return AnimatedSwitcher(
duration: const Duration(seconds: 1),
// child: (snapshot.connectionState == ConnectionState.done)
child: (snapshot.hasData && snapshot.data!.isNotEmpty)
? _listView(imgSize, snapshot.data!)
: _shimmerListView(imgSize),
);
});
3. 전달받은 인자를 이용하여 화면에 게시글 정보 표시.
4. Get 를 통한 라우팅시 인자를 통해서 상세 정보를 로딩할 "아이템 키값" 전달
Get.toNamed(ROUTE_ITEM_DETAIL, arguments: {'itemKey': _item.itemKey});
Widget _listView(double imgSize, List<ItemModel2> items) {
return ListView.separated(
padding: const EdgeInsets.all(padding_16),
separatorBuilder: (context, index) {
return Divider(
thickness: 1,
// 실제 라인 두께
color: Colors.grey[400],
height: padding_16 * 2 + 1,
// 라인 위/아래의 공간
indent: padding_16,
// 시작 부분 공간
endIndent: padding_16, // 끝나는 부분 공간
);
},
itemCount: items.length,
itemBuilder: (context, index) {
ItemModel2 _item = items[index];
return InkWell(
//InkWell
onTap: () {
debugPrint('call Item detail page[$index/${_item.itemKey}]');
Get.toNamed(ROUTE_ITEM_DETAIL, arguments: {'itemKey': _item.itemKey});
},
child: SizedBox(
height: imgSize,
child: Row(
children: <Widget>[
SizedBox(
height: imgSize,
width: imgSize,
child: ExtendedImage.network(
_item.imageDownloadUrls[0],
fit: BoxFit.cover,
// borderRadius 를 사용하기 위해서는, BoxShape.rectangle 설정도 같이 해야함,
shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(15.0),
)),
const SizedBox(
width: padding_16,
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(_item.title, style: Theme.of(context).textTheme.subtitle1),
Text(
'50일전',
style: Theme.of(context).textTheme.subtitle2,
),
Text(_item.price.toString() + '원'),
// 금액과 하트 사이에 공백을 최대한 주기위해서 Expanded 사용함,
Expanded(child: Container()),
// Row 가 2번 사용된 이유는 아래와 같다.
Row(
// 오론쪽 끝으로 정렬하기
mainAxisAlignment: MainAxisAlignment.end,
children: [
// 폰트 사이즈를 줄이기 위해서 사이즈박스로 처리
SizedBox(
height: 16,
// 이걸로 다시 FittedBox 로 감싸면, 위젯 밖으로 나가지 못한다.
// 하지만 하위에 위치한 Row 정렬이 제 기능을 하지 못한다.
// 그래서 SizedBox 를 다시 한번더 Row 로 감싸고 정렬을 추가한다
child: FittedBox(
fit: BoxFit.fitHeight,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: const [
Icon(CupertinoIcons.chat_bubble_2, color: Colors.grey),
Text('23', style: TextStyle(color: Colors.grey)),
Icon(CupertinoIcons.heart, color: Colors.grey),
Text('123', style: TextStyle(color: Colors.grey)),
],
),
),
),
],
),
],
),
),
],
),
),
);
},
);
}
./src/router/locations.dart - 새로운 화면 라우팅 정보 추가
GetPage(
name: ROUTE_ITEM_DETAIL,
page: () => const ItemDetailPage(),
transition: Transition.fadeIn,
middlewares: [CheckAuth()], // 미들웨어를 먼저 확인(로그인 여부 확인)하고 "page:" 로 이동함
),
./src/screens/home/item_detail_page.dart - 전달된 인자를 추출하는 방법
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class ItemDetailPage extends StatefulWidget {
const ItemDetailPage({Key? key}) : super(key: key);
@override
State<ItemDetailPage> createState() => _ItemDetailPageState();
}
class _ItemDetailPageState extends State<ItemDetailPage> {
late String newItemKey;
@override
void initState() {
newItemKey = Get.arguments['itemKey'];
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(child: Text(newItemKey)),
);
}
}
'Flutter > 12 Clone 'Used Goods app'' 카테고리의 다른 글
[Flutter] Clone - 당근마켓39(Item detail & PageView) (0) | 2022.08.21 |
---|---|
[Flutter] Clone - 당근마켓38(Item detail & CustomScrollView) (0) | 2022.08.21 |
[Flutter] Clone - 당근마켓36(ItemModel upload - validators) (0) | 2022.08.19 |
[Flutter] Clone - 당근마켓35(ItemModel upload) (0) | 2022.08.18 |
[Flutter] Clone - 당근마켓34(InputScreen - image uploading) (0) | 2022.08.18 |