본문 바로가기

Flutter

lesson_detail_page

lesson_detail_page는 appbar랑, body의 백그라운드 이미지를 같이 넣어주어야하기 때문에 sliverListView를 사용

sliverappbar를 사용하여 appBar에 listview의 배경색을 같이 사용하고 싶을때 사용한다. sliverappbar를 만들 때는 CustomScrollView안에다가 만들어 주고 CustomScrollView안에 Sliver가 있고 그 안에 SliverAppBar,SliverList,SliverGrid가 존재한다.

double타입으로 받은 리뷰점수를 별모양으로 표시를 해줍니다.

 

https://sub1-coding.tistory.com/296 

 

SliverAppBar

SliverAppBar 앱의 스크롤에 따라 모양이 변하는 app bar를 말한다. Body에 List뷰를 사용했을때 상단 AppBar가 확장되거나 내가 스크롤하는 움직임에 따라서 변화를 주고 싶을 때 주로 사용합니다. SliverAp

sub1-coding.tistory.com

 

실행화면

 

코드

import 'package:extended_image/extended_image.dart';
import 'package:finalproject_front/constants.dart';
import 'package:finalproject_front/pages/lesson/lesson_detail_page/model/lesson_detail_page_model.dart';
import 'package:finalproject_front/pages/lesson/lesson_detail_page/model/lesson_detail_page_view_model.dart';
import 'package:finalproject_front/dummy_models/lesson_detail_resp_dto.dart';
import 'package:finalproject_front/pages/main/home/home_page/model/home_page_model.dart';
import 'package:finalproject_front/size.dart';
import 'package:flutter_rating_bar/flutter_rating_bar.dart';

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

class LessonDetailPage extends ConsumerWidget {
  final int lessonId;

  const LessonDetailPage({required this.lessonId, Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return Scaffold(
      bottomSheet: _buildLessonBar(),
      body: CustomScrollView(
        slivers: [
          _buildSliverAppbar(context),
          SliverToBoxAdapter(
            child: Column(
              children: [
                _buildHeader(ref),
                _buildDivider(),
                _buildBody(ref),
              ],
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildBody(WidgetRef ref) {
    LessonDetailPageModel? model = ref.watch(lessonDetailPageViewModel(lessonId));
    return model == null
        ? SizedBox()
        : Padding(
            padding: const EdgeInsets.symmetric(horizontal: 20),
            child: Container(
                child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Padding(
                  padding: const EdgeInsets.only(top: 16, bottom: 16),
                  child: Text(
                    "${model.lessonRespDto.lessonDto.lessonPrice}원",
                    style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
                  ),
                ),
                _buildLessonContentBox("커리큘럼", "${model.lessonRespDto.lessonDto.curriculum}", 120, 2),
                _buildLessonContentBox("레슨시간", "${model.lessonRespDto.lessonDto.lessonTime}", 55, 1),
                _buildLessonContentBox("레슨횟수", "${model.lessonRespDto.lessonDto.lessonCount}", 55, 1),
                _buildLessonContentBox("장소", "${model.lessonRespDto.lessonDto.lessonPlace}", 55, 1),
                _buildLessonPossibleDate("${model.lessonRespDto.lessonDto.possibleDays}"),
                _buildLessonContentBox("취소 및 환불규정", "${model.lessonRespDto.lessonDto.lessonPolicy}", 200, 6),
                _buildLessonExpertInformation("${model.lessonRespDto.profileDto.expertPhoto}", "전문가정보",
                    "${model.lessonRespDto.profileDto.expertName}", "${model.lessonRespDto.profileDto.expertIntroduction}"),
                _buildLessonEvaluation(
                    "${model.lessonRespDto.lessonAvgGrade}", model.lessonRespDto.lessonAvgGrade, "${model.lessonRespDto.lessonTotalReviewsCount}"),
                Column(
                  children: model.lessonRespDto.lessonReviewList.map((e) => _buildReview(e.username, e.reviewContent, e.lessonGrade)).toList(),
                ),
                SizedBox(
                  height: gap_xxl,
                ),
              ],
            )),
          );
  }

  Widget _buildHeader(WidgetRef ref) {
    LessonDetailPageModel? model = ref.watch(lessonDetailPageViewModel(lessonId));
    return model == null
        ? SizedBox()
        : Padding(
            padding: const EdgeInsets.symmetric(horizontal: 16),
            child: Column(
              children: [
                _buildLessonTitle("${model.lessonRespDto.lessonDto.lessonName}", "${model.lessonRespDto.lessonTotalReviewsCount}",
                    model.lessonRespDto.lessonAvgGrade),
              ],
            ));
  }
}

Padding _buildLessonPrice(int lessonPrice) {
  return Padding(
    padding: const EdgeInsets.only(top: 16, bottom: 16),
    child: Text(
      "${lessonPrice}원",
      style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
    ),
  );
}

Container _buildLessonEvaluation(String evaluation, double star, String totalReview) {
  return Container(
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(
          "받은평가",
          style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
        ),
        SizedBox(height: 10),
        Container(
          width: double.infinity,
          decoration: BoxDecoration(
            color: const Color(0xffEAF2FD),
            borderRadius: BorderRadius.circular(15),
          ),
          child: Padding(
            padding: const EdgeInsets.all(14.0),
            child: Row(
              children: [
                Text(
                  "${evaluation}",
                  style: TextStyle(fontSize: 35, fontWeight: FontWeight.bold),
                ),
                SizedBox(width: 15),
                Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    RatingBarIndicator(
                      rating: star,
                      itemBuilder: (context, index) => Icon(
                        Icons.star,
                        color: Colors.amber,
                      ),
                      itemCount: 5,
                      itemSize: 18.0,
                      direction: Axis.horizontal,
                    ),
                    SizedBox(
                      height: 10,
                    ),
                    Text(
                      "${totalReview}개의 평가",
                      style: TextStyle(fontSize: 12, fontWeight: FontWeight.bold, color: gSubTextColor),
                    )
                  ],
                ),
              ],
            ),
          ),
        ),
        SizedBox(
          height: 20,
        )
      ],
    ),
  );
}

Widget _buildReview(String username, String reviewContent, double lessonGrade) {
  return Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      Row(
        children: [
          Container(
            height: 50,
            width: 50,
            decoration: BoxDecoration(
              borderRadius: BorderRadius.circular(100),
              image: DecorationImage(image: NetworkImage("https://picsum.photos/200"), fit: BoxFit.cover),
            ),
          ),
          SizedBox(width: 20),
          Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(
                "${username}",
                style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
              ),
              RatingBarIndicator(
                rating: lessonGrade,
                itemBuilder: (context, index) => Icon(
                  Icons.star,
                  color: Colors.amber,
                ),
                itemCount: 5,
                itemSize: 14.0,
                direction: Axis.horizontal,
              ),
            ],
          )
        ],
      ),
      SizedBox(height: gap_m),
      Text("${reviewContent}", style: TextStyle(fontSize: 16)),
      SizedBox(height: gap_xl),
    ],
  );
}

Icon _buildStar(IconData mIcon) {
  return Icon(
    mIcon,
    color: Colors.yellow,
    size: 14,
  );
}

Container _buildLessonExpertInformation(String image, String title, String name, String content) {
  return Container(
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(
          "${title}",
          style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
        ),
        SizedBox(height: 10),
        Container(
          width: double.infinity,
          decoration: BoxDecoration(
            color: const Color(0xffEAF2FD),
            borderRadius: BorderRadius.circular(15),
          ),
          child: Column(
            children: [
              Padding(
                padding: const EdgeInsets.all(16.0),
                child: Row(
                  children: [
                    Container(
                      height: 60,
                      width: 60,
                      decoration: BoxDecoration(
                        borderRadius: BorderRadius.circular(100),
                        image: DecorationImage(
                            image: NetworkImage(
                              "https://picsum.photos/251",
                            ),
                            fit: BoxFit.cover),
                      ),
                    ),
                    SizedBox(width: 10),
                    Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Text(
                          "${name}",
                          style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
                        ),
                      ],
                    ),
                  ],
                ),
              ),
              Padding(
                padding: const EdgeInsets.only(left: 16, bottom: 16, right: 16),
                child: Align(
                  alignment: Alignment.centerLeft,
                  child: Text(
                    "${content}",
                    style: TextStyle(fontSize: 16, fontWeight: FontWeight.normal),
                    maxLines: 3,
                    overflow: TextOverflow.ellipsis,
                  ),
                ),
              )
            ],
          ),
        ),
        SizedBox(
          height: 20,
        )
      ],
    ),
  );
}

Container _buildLessonPossibleDate(String possibleDays) {
  String possibleDays1 = "${possibleDays}";
  String result = possibleDays1.substring(1, possibleDays1.indexOf(']'));
  return Container(
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(
          "가능일",
          style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
        ),
        SizedBox(height: 10),
        Container(
          width: double.infinity,
          decoration: BoxDecoration(
            color: const Color(0xffEAF2FD),
            borderRadius: BorderRadius.circular(15),
          ),
          child: Padding(
              padding: const EdgeInsets.all(14.0),
              child: Text(
                "${result}",
                style: TextStyle(
                  fontSize: 16,
                ),
              )),
        ),
        SizedBox(
          height: 20,
        )
      ],
    ),
  );
}

Container _buildLessonContentBox(String title, String content, double heig, int max) {
  return Container(
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(
          "${title}",
          style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
        ),
        SizedBox(height: 10),
        Container(
          height: heig,
          width: double.infinity,
          decoration: BoxDecoration(
            color: const Color(0xffEAF2FD),
            borderRadius: BorderRadius.circular(15),
          ),
          child: Padding(
            padding: const EdgeInsets.all(14.0),
            child: Text(
              "${content}",
              style: TextStyle(
                fontSize: 16,
              ),
              maxLines: max,
              overflow: TextOverflow.ellipsis,
            ),
          ),
        ),
        SizedBox(
          height: 20,
        )
      ],
    ),
  );
}

Container _buildLessonBox(String title, int content, double heig, int max) {
  return Container(
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(
          "${title}",
          style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
        ),
        SizedBox(height: 10),
        Container(
          height: heig,
          width: double.infinity,
          decoration: BoxDecoration(
            color: const Color(0xffEAF2FD),
            borderRadius: BorderRadius.circular(15),
          ),
          child: Padding(
            padding: const EdgeInsets.all(14.0),
            child: Text(
              "${content}",
              style: TextStyle(
                fontSize: 16,
              ),
              maxLines: max,
              overflow: TextOverflow.ellipsis,
            ),
          ),
        ),
        SizedBox(
          height: 20,
        )
      ],
    ),
  );
}

Container _buildLessonTitle(String lessonTitle, String totalReview, double lessonGrade) {
  return Container(
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        SizedBox(height: 30),
        Text(
          "${lessonTitle}",
          style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
        ),
        Padding(
          padding: const EdgeInsets.only(top: 15, bottom: 15),
          child: Row(
            children: [
              RatingBarIndicator(
                rating: lessonGrade,
                itemBuilder: (context, index) => Icon(
                  Icons.star,
                  color: Colors.amber,
                ),
                itemCount: 5,
                itemSize: 20.0,
                direction: Axis.horizontal,
              ),
              SizedBox(width: gap_s),
              Text("평가 ${totalReview}개", style: TextStyle(color: gSubTextColor, fontWeight: FontWeight.bold, fontSize: 14))
            ],
          ),
        ),
      ],
    ),
  );
}

Divider _buildDivider() {
  return Divider(
    thickness: 1,
    height: 0,
    color: gDivider,
  );
}

SliverAppBar _buildSliverAppbar(BuildContext context) {
  Size _size = MediaQuery.of(context).size;
  return SliverAppBar(
      leading: IconButton(
          icon: Icon(
            CupertinoIcons.back,
            color: Colors.black,
            size: 30,
          ),
          onPressed: () {
            Navigator.pop(context);
          }),
      pinned: true,
      expandedHeight: 200.0,
      primary: true,
      flexibleSpace: FlexibleSpaceBar(
        background: SizedBox(
          height: 30,
          width: _size.width,
          child: ExtendedImage.network(
            "https://picsum.photos/250",
            fit: BoxFit.cover,
          ),
        ),
      ));
}

class _buildLessonBar extends StatefulWidget {
  const _buildLessonBar({Key? key}) : super(key: key);

  @override
  State<_buildLessonBar> createState() => _buildLessonBarState();
}

class _buildLessonBarState extends State<_buildLessonBar> {
  bool click = false;
  @override
  Widget build(BuildContext context) {
    return Container(
      child: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            ConstrainedBox(
              constraints: BoxConstraints.tightFor(height: 50, width: 270),
              child: TextButton(
                style: TextButton.styleFrom(
                  backgroundColor: Color(0xff4880ED),
                ),
                onPressed: () {
                  Navigator.pushNamed(context, "/orderDetail");
                },
                child: Text(
                  "구매",
                  style: TextStyle(color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold),
                ),
              ),
            ),
            Container(
              height: 50,
              decoration: BoxDecoration(
                  borderRadius: BorderRadius.circular(5),
                  border: Border.all(
                    color: Colors.grey,
                    width: 2,
                  )),
              child: Center(
                child: ElevatedButton(
                  style: ElevatedButton.styleFrom(primary: Colors.white),
                  onPressed: () {
                    setState(() {
                      click = !click;
                    });
                  },
                  child: Padding(
                    padding: const EdgeInsets.only(top: 6, bottom: 12),
                    child: Icon(
                      (click == false) ? CupertinoIcons.heart : CupertinoIcons.heart_fill,
                      color: Colors.red,
                      size: 40,
                    ),
                  ),
                ),
              ),
            )
          ],
        ),
      ),
    );
  }
}

'Flutter' 카테고리의 다른 글

datePicker 달력  (0) 2022.12.04
Category_detail_page  (0) 2022.12.04
home_page  (0) 2022.12.02
DropDownButton  (0) 2022.11.29
SliverAppBar  (0) 2022.11.28