[TIL] Flutter 9기 사전 캠프 Day 7 : BottomNavigationBar & IndexedStack & 페이지 라우팅

현서·2025년 11월 14일

[TIL] Flutter 9기

목록 보기
7/65

📝 BottomNavigationBar

BottomNavigationBar란?

  • 화면 하단에 고정된 탭 메뉴를 만드는 데 사용
  • 앱에서 여러 페이지(예: 홈, 설정, 프로필 등)를 전환할 수 있게 함
  • 일반적으로 StatefulWidget과 함께 사용하여 선택된 탭 인덱스를 상태로 관리함
  • ScaffoldbottomNavigationBar 속성에 사용

주요 속성

  • items: 탭으로 표시할 아이콘과 라벨 리스트
  • currentIndex: 현재 선택된 탭 인덱스
  • onTap: 탭 터치 시 호출되는 함수(터치된 탭의 인덱스가 인자로 넘어옴)
  • selectedItemColor: 선택된 탭의 아이콘과 텍스트 색상
  • iconSize: 아이콘 크기 설정 (기본값은 24.0)
  • selectedLabelStyle: 선택된 탭의 텍스트 스타일 (ex. 볼드체)
  • unselectedItemColor: 선택되지 않은 탭의 색상 (생략 가능, 기본은 회색)
  • unselectedLabelStyle: 선택되지 않은 텍스트 스타일도 따로 설정 가능

사용 예제

class HomePage extends StatefulWidget {
  
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  int selectedIndex = 0;

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Test")),
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: selectedIndex,
        selectedItemColor: Colors.blueAccent,
        // unselectedItemColor: ,
        iconSize: 24,
        selectedLabelStyle: TextStyle(fontWeight: FontWeight.bold),
        // unselectedLabelStyle: ,
        onTap: (index) {
          setState(() {
            selectedIndex = index;
          });
        },
        items: [
          BottomNavigationBarItem(icon: Icon(Icons.home), label: '홈'),
          BottomNavigationBarItem(icon: Icon(Icons.settings), label: '설정'),
          BottomNavigationBarItem(icon: Icon(Icons.person), label: '마이페이지'),
        ],
      ),
      body: Container(),
    );
  }
}

📝 IndexedStack

IndexedStack이란?

  • 여러 위젯을 겹쳐서(stack) 배치한 뒤, 그 중 지정한 index에 해당하는 위젯만 보여주는 위젯
  • 나머지 위젯들은 없어진게 아닌 숨겨져 있기 때문에, 탭 전환해도 상태가 유지됨
  • BottomNavigationBar와 함께 자주 사용됨

주요 속성

  • index
    - 현재 표시할 자식 위젯의 인덱스
    - 해당 인덱스에 해당하는 위젯만 화면에 보임
  • children
    - 겹쳐놓을 위젯 리스트
    - 모든 위젯이 메모리에는 유지됨 (build됨)

사용 예제

class HomePage extends StatefulWidget {
  
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  int selectedIndex = 0;

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Test")),
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: selectedIndex,
        selectedItemColor: Colors.blueAccent,
        // unselectedItemColor: ,
        iconSize: 24,
        selectedLabelStyle: TextStyle(fontWeight: FontWeight.bold),
        // unselectedLabelStyle: ,
        onTap: (index) {
          setState(() {
            selectedIndex = index;
          });
        },
        items: [
          BottomNavigationBarItem(icon: Icon(Icons.home), label: '홈'),
          BottomNavigationBarItem(icon: Icon(Icons.settings), label: '설정'),
          BottomNavigationBarItem(icon: Icon(Icons.person), label: '마이페이지'),
        ],
      ),
      body: IndexedStack(
        index: selectedIndex,
        children: [
          Container(color: Colors.amber), // index 0
          Container(color: Colors.blue), // index 1
          Container(color: Colors.cyan), // index 2
        ],
      ),
    );
  }
}

📝 페이지 라우팅

페이지 이동

  • 새로운 페이지로 이동
    Navigator.push() 함수 사용
  • 뒤로가기
    Navigator.pop() 함수 사용

예제 코드

class PageA extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("A 페이지")),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context) {
                  return PageB();
                },
              ),
            );
          },
          child: Text("B 페이지로 이동"),
        ),
      ),
    );
  }
}

class PageB extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("B 페이지")),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.pop(context);
          },
          child: Text("뒤로가기"),
        ),
      ),
    );
  }
}

Navigator는 Flutter 앱에서 화면(Page, Route) 간 전환을 관리하는 핵심 위젯

내부적으로 스택(Stack) 자료구조를 사용하여
스택 자료구조 : 데이터를 담을 때 마지막에 넣은 데이터가 가장 먼저 나오는 구조
청록 - 노랑 - 주황 - 빨강 순으로 담으면 꺼낼 땐 빨간색 제일 먼저

  • 새로운 페이지는 push()로 쌓고 이전 페이지는 pop()으로 꺼내며 이동을 처리
  • PageA → PageB 이동 시 라우트 스택 상황

페이지 이동하는데 context는 왜 필요할까?

  • context는 Flutter 위젯 트리 내에서 현재 위젯의 위치와 관련된 정보를 담고 있는 객체
  • 정확한 타입은 BuildContext이며, 이를 통해 Flutter는 위젯 트리를 상위로 타고 올라가며 필요한 위젯을 찾을 수 있음
  • Navigator 위젯을 찾아서 push 의 두번째 인자에서 전달해준 위젯을 스택에 push
┌──────────────────────────────┐
│       MaterialApp            │
│  (Navigator 🧭 내장되어 있음)   │
└──────────────────────────────┘
          ⬆️
┌────────────────────┐
│   PageA(context)   │
└────────────────────┘
          ⬆️
┌────────────────────┐
│      Scaffold      │
└────────────────────┘
          ⬆️
┌────────────────────┐
│   ElevatedButton   │
└────────────────────┘

데이터 전달

페이지 이동 시 데이터 전달 (push 시 인자 전달)

  • 전달받는 페이지
// 전달받는 페이지(DetailPage)
class DetailPage extends StatelessWidget {
  final String message;
  const DetailPage({required this.message});

  
  Widget build(BuildContext context) {
    return Text(message);
  }
}
  • 이동할 때
Navigator.push(
  context,
  MaterialPageRoute(
    builder: (context) => DetailPage(message: 'Hello from PageA'),
  ),
);

돌아올 때 값 전달 (pop 시 결과 반환)

  • 돌아갈 때 전달
Navigator.pop(context, '결과값');
  • 데이터 받기
// PageA → PageB에서 돌아올 때 결과 받기
final result = await Navigator.push(
  context,
  MaterialPageRoute(builder: (_) => PageB()),
);

print('PageB에서 받은 값: $result');

예제 코드

class PageA extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("A 페이지")),
      body: Center(
        child: ElevatedButton(
          onPressed: () async {
            final result = await Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context) {
                  return PageB(message: "Hello Flutter");
                },
              ),
            );
            print("반환결과 : $result");
          },
          child: Text("B 페이지로 이동"),
        ),
      ),
    );
  }
}

class PageB extends StatelessWidget {
  PageB({required this.message});

  String message;

  
  Widget build(BuildContext context) {
    print("받은 데이터 :$message");
    return Scaffold(
      appBar: AppBar(title: Text("B 페이지")),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.pop(context, "안녕 플러터");
          },
          child: Text("뒤로가기"),
        ),
      ),
    );
  }
}

to do 어플 만들기 찍먹

기본 화면 세팅하는 것까지 들었다

공부 소감

오늘은 알고리즘 문제 풀고 3강 마저 들었다
기본 위젯 강의랑 심화 강의까지 들었을 때
어떻게 적용하고 코드 짤지 실습해보며 배우는 게 좋을 것 같다

route 구성하고 navigator로 이동하는 건
react랑 뭔가 비슷한 것 같기도 하면서
데이터 전달하는 부분은 아직 헷갈린다

근데 이동하는 함수가 좀 귀여운듯 ㅋㅁㅋ 푸쉬팝~
Navigator.push()
Navigator.pop()

0개의 댓글