[BookBox] 카테고리 토글 기능

ExpansionPanel 클래스 활용하기
HootJem's avatar
Dec 04, 2024
[BookBox] 카테고리 토글 기능
notion image
notion image
 
 
 
 
만들어야 하는 모습 html 로는 구현 한 적이 있는데 플러터로는 처음이라 당황스럽다.
제일 큰 차이점은
제목 (접기/열기)
내용
이 아니라 제목에 해당하는 칸 들도 내용에 해당한다는 부분이었다.
 
열나는 서치 후 ExpansionPanel class 이라는 클래스를 발견하였다.
이 역시 제목-내용 형식을 띄고 있으나 진행하기로 했다.
 

 
class Cate { int categoryId; String categoryName; Cate({ required this.categoryId, required this.categoryName, }); } List<Cate> cateList = [ Cate(categoryId: 112011, categoryName: '소설'), Cate(categoryId: 170, categoryName: '경제경영'), //생략 Cate(categoryId: 8257, categoryName: '대학교재/전문서적'), ];
cateList 를 확인하여 for 문을 돌릴 수 있게 되었습니다.
 
notion image
오늘 만들 것. 제일 처음 넣은 더미 데이터들이 버튼이 되어 나오고 있습니다.
 
이런 기능을 지원하는 ExpansionPanel 클래스를 사용하여 구현해 봅니다.
bool _isOpen = false; // 상태 변수 초기화
해당 칸이 열려 있는지 여부를 저장하는 isOpen 이 필요하고, 따라서 이 클래스는 StatefulWidget 을 상속하도록 변경 되어야 합니다.
ExpansionPanelList 내부에 간단한 스타일 지정을 하고, children 내부에 판넬 코드를 작성합니다.
Widget _buildExpansionPanelList(TextTheme theme) { return ExpansionPanelList( dividerColor: Colors.transparent, elevation: 0, // 그림자 효과 expandedHeaderPadding: EdgeInsets.all(0), children: [ _buildExpansionPanel(theme), // 메서드로 분리된 개별 패널 ], expansionCallback: (panelIndex, isOpen) { setState(() { _isOpen = !_isOpen; }); // 패널 상태 변경 }, ); }
보통은 노션의 토글 기능처럼 제목-컨텐츠로 이루어 지지만, 이번 경우에는 카테고리[0-3], 카테고리[4-끝] 이 안에 나와야 하는 문제가 있었습니다.
notion image
child: Wrap( spacing: 8.0, runSpacing: 8.0, children: cateList.take(4).map((cate) { return _buildCategoryButton(cate.categoryName, cate.categoryId); }).toList(), ),
Wrap 을 사용하여 해당 넓이를 초과하지 않도록 하고, cateList.take(4).map 을 통해 초반 표시될 카테고리의 갯수를 지정했습니다.
 
child: Wrap( spacing: 8.0, runSpacing: 8.0, children: cateList.sublist(4).map((cate) { return _buildCategoryButton(cate.categoryName, cate.categoryId); }).toList(), ),
cateList.sublist(4).map 을 통해 인문학 이후의 카테고리 부터 표시 되도록 설정했습니다.

전체코드(_buildExpansionPanel)

// 개별 ExpansionPanel 빌드 메서드 ExpansionPanel _buildExpansionPanel(TextTheme theme) { final double panelHeight = 20.0; return ExpansionPanel( backgroundColor: Colors.white, headerBuilder: (context, isOpen) { return Container( height: panelHeight, // Set the height alignment: Alignment.centerLeft, // Optional: Align content child: Wrap( spacing: 8.0, runSpacing: 8.0, children: cateList.take(4).map((cate) { return _buildCategoryButton(cate.categoryName, cate.categoryId); }).toList(), ), ); }, body: Container( alignment: Alignment.centerLeft, child: Wrap( spacing: 8.0, runSpacing: 8.0, children: cateList.sublist(4).map((cate) { return _buildCategoryButton(cate.categoryName, cate.categoryId); }).toList(), ), ), isExpanded: _isOpen, ); } }
 

_buildCategoryButton

버튼 스타일 입니다. 버튼이 눌릴 때 마다 ajax 요청을 보내야 하기 때문에 onTap 사용이 필요합니다. 따라서 container 였던 위젯을 InkWell 로 변경 했습니다.
Widget _buildCategoryButton(String categoryName, int categoryId) { return InkWell( onTap: () { print('카테고리 ID: $categoryId'); }, borderRadius: BorderRadius.circular(20), // 클릭 영역 둥글게 설정 child: Container( padding: EdgeInsets.symmetric(horizontal: 12, vertical: 4), decoration: BoxDecoration( color: Colors.grey[200], // 배경 색상 설정 borderRadius: BorderRadius.circular(20), // 모서리 둥글게 border: Border.all(color: Colors.grey[400]!), ), child: Text( categoryName, // 카테고리 이름 표시 style: TextStyle( color: Colors.black, // 텍스트 색상 ), ), ), ); }
 
 
Share article

[HootJem] 개발 기록 블로그