[BookBox] 추천 페이지 구현

ListView와 GridView 사용
HootJem's avatar
Dec 04, 2024
[BookBox] 추천 페이지 구현
notion image
 

1. 텍스트 위젯 추가

일단 해당 탭을 정의하는 Text 를 넣었다. 처음에는 Grid 이미지와 별개로 존재한다 생각하여 컬럼 내부에 넣었더니, 스크롤이 올라가도 "00님\nBookBox 에서 추천하는 책을 만나보세요.", 라는 텍스트가 계속 존재했음. 따라서 ListView 내부에 넣었다.
Text( "00님\nBookBox 에서 추천하는 책을 만나보세요.", style: theme.bodyLarge, ),
 

2. GridView 설정

ListView 는 스크롤이 영원히 늘어나는 속성을 갖고 있기 때문에 GridView 를 사용하기 적절하지 않다. 그러나 사용하고 싶기 때문에 그리드뷰 빌더에 두가지 속성을 추가 했다.
physics: NeverScrollableScrollPhysics(), // 그리드 스크롤 비활성화 shrinkWrap: true, // 그리드의 높이를 내용에 맞게 조절
 
기본적으로 ListView , GridView 둘 다 스크롤을 중복해서 갖게 되기 때문에, 그리드 뷰의 스크롤을 비활성화 하고, shrinkWrap 를 사용하여 높이를 임의로 할당했다.
 

2-1. 그리드 이미지 크기 구하기

그리드뷰의 넓이는 미디어 쿼리를 사용하여 계산했다.
var size = MediaQuery.of(context).size; final double itemHeight = (size.height - kToolbarHeight - 24) / 2; final double itemWidth = size.width / 2.3;
GridView.builder( physics: NeverScrollableScrollPhysics(), // 그리드 스크롤 비활성화 shrinkWrap: true, // 그리드의 높이를 내용에 맞게 조절 gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisSpacing: 10, crossAxisCount: 3, mainAxisSpacing: 10, childAspectRatio: (itemWidth / itemHeight), ),
childAspectRatio 를 사용하여 컨테이너의 크기를 지정했다.
 
그리고 리턴값에 RecommendItem 위젯을 호출한다.
 

3. 위젯 만들기

notion image
위젯은 적절하게 스타일을 주고, 글자수가 길이를 초과하는 것을 방지하기 위해 maxLines , overflow 를 사용했다. maxLines : 줄 길이 설정
overflow : 넓이 초과하는 글자 처리 법
maxLines: 2, overflow: TextOverflow.ellipsis,
 
 

전체 코드

import 'package:bookbox/core/constants/size.dart'; import 'package:bookbox/core/constants/styles.dart'; import 'package:bookbox/ui/main/home/recommend_tab/recommend_item.dart'; import 'package:flutter/material.dart'; class RecommendTab extends StatelessWidget { @override Widget build(BuildContext context) { TextTheme theme = textTheme(); var size = MediaQuery.of(context).size; final double itemHeight = (size.height - kToolbarHeight - 24) / 2; final double itemWidth = size.width / 2.3; return Padding( padding: const EdgeInsets.all(gap_s), child: ListView( children: [ Padding( padding: const EdgeInsets.only(bottom: gap_s), // 아래 간격 조정 child: Text( "00님\nBookBox 에서 추천하는 책을 만나보세요.", style: theme.bodyLarge, ), ), GridView.builder( physics: NeverScrollableScrollPhysics(), // 그리드 스크롤 비활성화 shrinkWrap: true, // 그리드의 높이를 내용에 맞게 조절 gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisSpacing: 10, crossAxisCount: 3, mainAxisSpacing: 10, childAspectRatio: (itemWidth / itemHeight), ), itemCount: 12, itemBuilder: (context, index) { return RecommendItem( imageUrl: "https://picsum.photos/id/${index + 10}/200/280", // 이미지 URL title: "책 제목\n제목길면청길면 $index", // 책 제목 author: "저자이름 $index 엄청길면", // 저자 이름 ); }, ), ], ), ); } }
import 'package:bookbox/core/constants/size.dart'; import 'package:flutter/material.dart'; class RecommendItem extends StatelessWidget { final String imageUrl; // 이미지 URL final String title; // 책 제목 final String author; // 저자 RecommendItem({ required this.imageUrl, required this.title, required this.author, }); @override Widget build(BuildContext context) { return Container( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 이미지 표시 ClipRRect( child: Container( decoration: BoxDecoration( border: Border.all( color: Colors.grey, width: 2, ), ), child: Image.network( imageUrl, // 너비를 부모에 맞추기 fit: BoxFit.cover, // 이미지가 잘리도록 설정 ), ), ), Padding( padding: const EdgeInsets.all(8.0), // 패딩 추가 child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16), maxLines: 2, overflow: TextOverflow.ellipsis, // 제목 스타일 ), Text( author, style: TextStyle(color: Colors.grey), maxLines: 1, overflow: TextOverflow.ellipsis, // 저자 스타일 ), ], ), ), ], ), ); } }
Share article

[HootJem] 개발 기록 블로그