티스토리 뷰

전 게시글에 이어서 게시글 목록에서 클릭하면 상세 조회하는 페이지로 넘어가도록 한다.

마지막 코드에서 이 부분이 살짝 나왔다.

GestureDetector(
              onTap: () {
                Post post = Post.fromQuerySnapshot(postDocs[index]);
                print(post.content);
                Navigator.push(
                    context,
                    MaterialPageRoute(
                        builder: (context) => GroupBuyingDetailPage(post)));
                        })

GestureDector를 이용해 해당 영역을 클릭하면  Navigator를 이용해 그 게시글을 상세 조회하는 페이지로 이동하는 것이다.

Post.fromQuerySnapshot(postDocs[index])는 무엇일까?

이는 Post.dart에서 정의를 해두었는데, json형식을 post객체로 파싱하는 것이다.

class Post {
  String? upperCategory;
  String? lowerCategory;
  String? title;
  String? content;
  int? maxParticipants;
  int? curParticipants;
  String? date;
  String? time;
  String? meetingPlace;
  String? writerName;
  List<String>? userLocation;

  Post({
    this.upperCategory,
    this.lowerCategory,
    this.title,
    this.content,
    this.maxParticipants,
    this.curParticipants,
    this.date,
    this.time,
    this.meetingPlace,
    this.writerName,
    this.userLocation,
  });

  Post.fromJson(dynamic json) {
    upperCategory = json['UpperCategory'];
    lowerCategory = json['LowerCategory'];
    title = json['Title'];
    content = json['Content'];
    maxParticipants = json['maxParticipants'];
    curParticipants = json['curParticipants'];
    date = json['Date'];
    time = json['Time'];
    meetingPlace = json['Place'];
    writerName = json['WriterName'];
    userLocation = json['UserLocation'].cast<String>();
  }

  Post.fromQuerySnapshot(QueryDocumentSnapshot<Map<String, dynamic>> snapshot)
      : this.fromJson(snapshot.data());
  };
 }

 

 

[번역] 복잡한 JSON 파싱하기 in flutter

기억하기 위해 기록합니다.

www.oowgnoj.dev

이제 이 넘겨받은 객체를 이용해 화면에 예쁘게 출력하면 된다. (코드 깎는 도인이 되어보자...)

class GroupBuyingDetailPage extends StatefulWidget {
  final Post post;

  const GroupBuyingDetailPage(this.post, {super.key});

  @override
  State<GroupBuyingDetailPage> createState() => _GroupBuyingDetailPageState();
}

class _GroupBuyingDetailPageState extends State<GroupBuyingDetailPage> {
  String curState() {
    if (widget.post.maxParticipants == null) {
      return "null입니다";
    } else if (widget.post.maxParticipants! > widget.post.curParticipants!) {
      return "모집중";
    } else {
      return "모집완료";
    }
  }

  void showPopup(context) {
    showDialog(
      context: context,
      builder: (context) {
        return Dialog(
            child: Container(
                width: MediaQuery.of(context).size.width * 0.7,
                height: 150,
                decoration: BoxDecoration(
                    borderRadius: BorderRadius.circular(10),
                    color: Colors.white),
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    Text("이 공동구매에 참여하시겠습니까?",
                        style: TextStyle(
                            fontSize: 15,
                            fontWeight: FontWeight.bold,
                            color: Colors.black),
                        textAlign: TextAlign.center),
                    Padding(
                      padding: const EdgeInsets.only(top: 25),
                      child: Row(
                        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                        children: [
                          SizedBox(
                            width: 110,
                            child: ElevatedButton.icon(
                              onPressed: () async {
                                Navigator.push(
                                  context,
                                  MaterialPageRoute(builder: (context) {
                                    return MessageListScreen();
                                  }),
                                );
                              },
                              icon: const Icon(Icons.circle_outlined),
                              label: const Text('네'),
                              style: ElevatedButton.styleFrom(
                                primary: Color(0xff686EFF),
                                onPrimary: Colors.white, // Background color
                                shape: RoundedRectangleBorder(
                                  borderRadius: BorderRadius.circular(18.0),
                                ),
                              ),
                            ),
                          ),
                          SizedBox(
                            width: 110,
                            child: ElevatedButton.icon(
                              onPressed: () {
                                Navigator.pop(context);
                              },
                              icon: const Icon(Icons.close),
                              label: const Text('아니요'),
                              style: ElevatedButton.styleFrom(
                                primary: Color(0xff686EFF),
                                onPrimary: Colors.white,
                                shape: RoundedRectangleBorder(
                                  borderRadius: BorderRadius.circular(18.0),
                                ), // Background color
                              ),
                            ),
                          ),
                        ],
                      ),
                    )
                  ],
                )));
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    var stringlist = widget.post.userLocation!.join(" ");
    return Scaffold(
      appBar: AppBar(
        title: Text(
          '공동 구매',
          style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18),
        ),
        elevation: 3.0,
        backgroundColor: Colors.white,
      ),
      body: ListView(children: [
        Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
          Padding(
            padding: const EdgeInsets.fromLTRB(15, 20, 10, 5),
            child: Container(
              height: MediaQuery.of(context).size.width * 0.08,
              width: MediaQuery.of(context).size.width * 0.15,
              child: DecoratedBox(
                decoration: BoxDecoration(
                    color: Palette.lightgrey,
                    borderRadius: BorderRadius.circular(10.0)),
                child: Center(
                  child: Text(
                    widget.post.lowerCategory.toString(),
                    style: TextStyle(
                      fontSize: 15,
                    ),
                  ),
                ),
              ),
            ),
          ),
          Padding(
            padding: const EdgeInsets.fromLTRB(15, 8, 20, 5),
            child: Row(
              children: [
                Icon(Icons.person_pin, color: Colors.blueAccent, size: 60),
                Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text(" ${widget.post.writerName.toString()}",
                          style: TextStyle(
                              fontWeight: FontWeight.bold,
                              fontSize: 20,
                              color: Colors.black)),
                      Text(" " + stringlist,
                          style: TextStyle(fontSize: 13, color: Colors.grey)),
                    ],
                  ),
                ),
              ],
            ),
          ),
          Padding(
            padding: const EdgeInsets.only(bottom: 15, top: 10),
            child: Container(
              decoration: BoxDecoration(
                color: Colors.grey,
                boxShadow: [
                  BoxShadow(
                    color: Colors.grey.withOpacity(1),
                    spreadRadius: 0,
                    blurRadius: 2,
                    offset: Offset(0, 2), // changes position of shadow
                  ),
                ],
              ),
              height: 1.0,
              width: 500.0,
            ),
          ),
          Padding(
            padding: const EdgeInsets.fromLTRB(20, 5, 20, 5),
            child: Row(
              children: [
                Text("${curState()}  ",
                    style: TextStyle(
                        fontWeight: FontWeight.bold,
                        fontSize: 18,
                        color: curState() == "모집중"
                            ? Palette.blue
                            : Colors.redAccent)),
                Text("${widget.post.title}",
                    style: TextStyle(
                        fontWeight: FontWeight.bold,
                        fontSize: 17,
                        color: Colors.black)),
              ],
            ),
          ),
          Padding(
              padding: const EdgeInsets.fromLTRB(20, 5, 20, 5),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  RichText(
                    text: TextSpan(
                      children: [
                        WidgetSpan(
                          child: Icon(
                            Icons.people,
                          ),
                        ),
                        TextSpan(
                            text:
                                "  ${widget.post.curParticipants}/${widget.post.maxParticipants}명 참여\n",
                            style: TextStyle(
                              fontSize: 13,
                              color: Colors.black,
                            )),
                        WidgetSpan(
                          child: Padding(
                            padding: const EdgeInsets.only(top: 3),
                            child: Icon(
                              Icons.calendar_month,
                            ),
                          ),
                        ),
                        TextSpan(
                          text: "  ${widget.post.date} ${widget.post.time}\n",
                          style: TextStyle(
                            fontSize: 13,
                            color: Colors.black,
                          ),
                        ),
                        WidgetSpan(
                          child: Padding(
                            padding: const EdgeInsets.only(top: 5),
                            child: Icon(
                              Icons.location_on,
                            ),
                          ),
                        ),
                        TextSpan(
                          text: "  ${widget.post.meetingPlace}",
                          style: TextStyle(
                            fontSize: 13,
                            color: Colors.black,
                          ),
                        ),
                      ],
                    ),
                  ),
                  Container(
                    padding: EdgeInsets.fromLTRB(0, 18, 0, 7),
                    width: MediaQuery.of(context).size.width * 0.98,
                    child: Column(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        DecoratedBox(
                          decoration: BoxDecoration(
                            border: Border.all(
                              width: 1,
                              color: Palette
                                  .grey, //                   <--- border width here
                            ),
                            borderRadius: BorderRadius.circular(10.0),
                            //color: Palette.lightgrey,
                          ),
                          child: Padding(
                            padding: EdgeInsets.fromLTRB(13, 15, 13, 15),
                            child: Text(
                              widget.post.content.toString(),
                              style: TextStyle(
                                fontSize: 14,
                              ),
                            ),
                          ),
                        ),
                      ],
                    ),
                  ),
                ],
              )),
          Padding(
            padding: const EdgeInsets.only(bottom: 15, top: 10),
            child: Container(
              decoration: BoxDecoration(
                color: Colors.grey,
                boxShadow: [
                  BoxShadow(
                    color: Colors.grey.withOpacity(1),
                    spreadRadius: 0,
                    blurRadius: 2,
                    offset: Offset(0, 2), // changes position of shadow
                  ),
                ],
              ),
              height: 1.0,
              width: 500.0,
            ),
          ),
          Center(
            child: Padding(
              padding: const EdgeInsets.only(top: 10),
              child: Container(
                decoration: BoxDecoration(
                  gradient: LinearGradient(
                    colors: [
                      Color.fromRGBO(104, 150, 235, 1),
                      Color.fromRGBO(104, 100, 255, 1),
                    ],
                    begin: Alignment.centerLeft,
                    end: Alignment.centerRight,
                  ),
                  borderRadius: const BorderRadius.all(
                    Radius.circular(25.0),
                  ),
                ),
                height: MediaQuery.of(context).size.width * 0.15,
                width: MediaQuery.of(context).size.width * 0.90,
                child: OutlinedButton(
                  onPressed: () {
                    showPopup(context);
                  },
                  style: OutlinedButton.styleFrom(
                    shape: RoundedRectangleBorder(
                      borderRadius: BorderRadius.circular(23.0),
                    ),
                  ),
                  child: const Text(
                    '참여하기',
                    style: TextStyle(
                        fontWeight: FontWeight.bold,
                        color: Colors.white,
                        fontSize: 20),
                  ),
                ),
              ),
            ),
          ),
        ]),
      ]),
    );
  }
}

최종 화면