guricode

[flutter]자취의 정석 -3 : TabBar를 사용한 카테고리 선택 구현 본문

앱/Flutter&Dart

[flutter]자취의 정석 -3 : TabBar를 사용한 카테고리 선택 구현

agentrakugaki 2025. 9. 16. 23:55

 

 

 

자취의 정석 커뮤니티 페이지를 구현중이다.

 

디자인 나오는대로 만들고있는데

 

상단에 DefaultTabController

를 이용하면 좌우로도 스와이프가 가능한 TabBar를 구현할수 있었다.

 

DefaultTabController는 TabBar와 TabBarView를 연결해주는 컨트롤러를 자동으로 만들어 주는 위젯이다.

지금까지는 TabController를 써본적이 없지만 이걸 직접쓰지 않아도 DefaultTabController를 사용하면 된다.

 

사용방법

final tabs = ['홈', '검색', '마이페이지'];

DefaultTabController(
  length: tabs.length, // 탭 개수와 꼭 맞춰야 함
  child: Scaffold(
    appBar: AppBar(
      title: const Text('앱 타이틀'),
      bottom: TabBar(
        tabs: tabs.map((t) => Tab(text: t)).toList(),
      ),
    ),
    body: TabBarView(
      children: tabs.map((t) => Center(child: Text('$t 화면'))).toList(),
    ),
  ),
);

 

 

초기탭 지정 방법

DefaultTabController(
  length: 3,
  initialIndex: 1, // 두 번째 탭부터 시작
  child: ...
);

 

 

스크롤 가능한 탭

TabBar(
  isScrollable: true, // 글자가 길거나 탭이 많을 때
  tabs: [
    Tab(text: '자유게시판'),
    Tab(text: '공지사항'),
    Tab(text: 'Q&A'),
    Tab(text: '후기'),
  ],
)

 

 

탭 글자 스타일 지정 방법

TabBar(
  labelStyle: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
  unselectedLabelStyle: const TextStyle(fontSize: 14),
  labelColor: Colors.black,
  unselectedLabelColor: Colors.grey,
  tabs: ...
)

 

 

밑줄 커스텀 하는방법

 

TabBar(
  indicator: const UnderlineTabIndicator(
    borderSide: BorderSide(color: Colors.black, width: 3),
    insets: EdgeInsets.symmetric(horizontal: 16),
  ),
  indicatorSize: TabBarIndicatorSize.tab, // 탭 전체 밑줄
  tabs: ...
)

 

 

 

실제로 사용한 코드

Widget build(BuildContext context) {
    final List<String> tabs = ['자유', '요리', '청소', '운동', '미션', '산책'];
    return DefaultTabController(
      length: tabs.length,
      child: Scaffold(
        appBar: AppBar(
          titleSpacing: 10,
          title: Row(
            children: [
              const SizedBox(width: 8),
              const Icon(Icons.arrow_drop_down),
              const SizedBox(width: 4),
              const Text('동작구'),
              const Spacer(),
              IconButton(icon: const Icon(Icons.search), onPressed: () {}),
            ],
          ),
          bottom: TabBar(
            padding: EdgeInsets.symmetric(horizontal: 16),
            indicator: UnderlineTabIndicator(
              //  insets: EdgeInsets.zero,
              borderSide: BorderSide(color: Colors.black, width: 2),
            ),
            indicatorSize: TabBarIndicatorSize.tab,
            indicatorColor: Colors.black,
            labelColor: Colors.black,
            isScrollable: false,
            labelPadding: EdgeInsets.symmetric(horizontal: 16),
            indicatorWeight: 3,
            labelStyle: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
            tabs: tabs.map((e) => Tab(text: e)).toList(),
          ),
        ),
        body: Container(
          padding: EdgeInsets.only(left: 24, right: 24, top: 18),
          child: TabBarView(
            children:
                //TODO:카테고리템 이름, 게시글 리스트
                //TODO: 카테고리별 게시글정보들 어떻게 넣을것인지
                tabs.map((e) {
                  return _CategoryTab(e);
                }).toList(),
          ),
        ),
        floatingActionButton: SizedBox(
          width: 55,
          height: 55,
          child: FloatingActionButton.small(
            onPressed: () {},
            backgroundColor: Colors.black,
            foregroundColor: Colors.white,
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(50),
            ),
            child: const Icon(Icons.edit),
          ),
        ),
      ),
    );
  }

 

 

 

 

+추가 설명

    bottom: TabBar(
            padding: EdgeInsets.symmetric(horizontal: 16),
            indicator: UnderlineTabIndicator(
              //  insets: EdgeInsets.zero,
              borderSide: BorderSide(color: Colors.black, width: 2),
            ),
            indicatorSize: TabBarIndicatorSize.tab,
            indicatorColor: Colors.black,
            labelColor: Colors.black,
            isScrollable: false,
            labelPadding: EdgeInsets.symmetric(horizontal: 16),
            indicatorWeight: 3,
            labelStyle: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
            tabs: tabs.map((e) => Tab(text: e)).toList(),
          ),

 

   indicatorSize: TabBarIndicatorSize.tab, <-밑줄 라인의 사이즈를 결정해준다. tab은 tab의 영역만큼 그려준다
            indicatorColor: Colors.black, <- 밑줄라인 색상
            labelColor: Colors.black, <- 텍스트 색상
            isScrollable: false, <-스크롤 비활성화
            labelPadding: EdgeInsets.symmetric(horizontal: 16), <-글자 영역 패딩 (TabBar에 따로 패딩을 넣어서 지금은 지웠다)
            indicatorWeight: 3,<-밑줄라인 굵기

 

 

 

 

다음엔 2depth로 만들어야한다. 탭바를 중첩으로 사용할거다