[Project] 2차 프로젝트 - 주제별(ExpansionPanel) 토글 같은 기능

김호정's avatar
Oct 25, 2024
[Project] 2차 프로젝트 - 주제별(ExpansionPanel) 토글 같은 기능
일단 더미 데이터를 넣어줍니다.
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

keepgoing