Develop/Flutter

[Flutter] 플러터 Bottom Navigation Bar 구현

JunJangE 2021. 11. 5. 14:14

이번에는 앱 개발에 많이 쓰이는 Bottom Navigation Bar를 구현해보자.

Bottom Navigation Bar는 동적으로 화면이 변화하므로 Stateful Widget을 통해 구현해야한다.

main.dart

import 'package:flutter/material.dart';
import 'package:flutter_application_1/home.dart';

void main()  => runApp(MyApp()); // 프로그램을 실행할 때 MyApp 부터 실행하겠어!


// StatelessWidget은 변화지 않는 화면을 작업할 때 사용.
// 변화는 화면을 작업 하고싶을 경우에는 StatefulWidget을 사용.
class MyApp extends StatelessWidget {

  // MaterialApp = 앱으로서 기능을 할 수 있도록 도와주는 뼈대
  @override
  Widget build(BuildContext context) {

    // return MaterialApp() -> Material 디자인 테마를 사용 
    return MaterialApp(
      title: "MyApp", // 앱 이름
      debugShowCheckedModeBanner: false, // 타이틀 바 우측 띠 제거

      // 앱의 기본적인 테마를 지정
      theme: ThemeData(
        primarySwatch: Colors.blue, // priamrySwatch 기본적인 앱의 색상을 지정
        ),

      home: MyWidget(), // 앱이 실행될 때 표시할 화면의 함수를 호출
      );
  }
}

// 앱이 실행 될때 표시할 화면의 함수
class MyWidget extends StatelessWidget {

  // scaffold = 구성된 앱에서 디자인적인 부분을 도와주는 뼈대

  // 화면 구성
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      // 앱의 body 부분 
      body: Center(
      child: TabPage(),
      ),  
    );
  }
}

// Bottom Navigation Bar
// 동적으로 화면을 변화하므로 StatefulWdiget 사용
class TabPage extends StatefulWidget {
  @override
  _TabPageState createState() => _TabPageState();
}

class _TabPageState extends State<TabPage> {

  int _selectedIndex = 0; // 처음에 나올 화면 지정

  // 이동할 페이지
  List _pages = [Home(), Text('page2'), Text('page3')];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      
        body: Center(
          child: _pages[_selectedIndex], // 페이지와 연결
        ),

        // BottomNavigationBar 위젯
        bottomNavigationBar: BottomNavigationBar(
          //type: BottomNavigationBarType.fixed, // bottomNavigationBar item이 4개 이상일 경우
         
          // 클릭 이벤트 
          onTap: _onItemTapped,

          currentIndex: _selectedIndex, // 현재 선택된 index

          // BottomNavigationBarItem 위젯
          items: <BottomNavigationBarItem>[
            BottomNavigationBarItem(
                icon: Icon(Icons.home), title: Text('Home')),

            BottomNavigationBarItem(
                icon: Icon(Icons.search), title: Text('Search')),

            BottomNavigationBarItem(
                icon: Icon(Icons.settings), title: Text('Settings')),

         ],
        )
      );
    }

  void _onItemTapped(int index) {
    // state 갱신
    setState(() {
      _selectedIndex = index; // index는 item 순서로 0, 1, 2로 구성
    });
  }
}

 

코드를 보게되면 Stateless Widget으로 앱으로서의 기능을 구성하고 화면에 뿌려질 위젯들을 Stateful Widget으로 구성한 것을 확인할 수 있다.

Stateful Widget으로 구성한 이유는 위에서 설명했듯이 동적인 BottomNavigation Bar를 구현하기 위해서이다. 자바와 코틀린으로 Bottom Navigation Bar를 구현하는 것보다 더 쉽게 구현할 수 있는 것 같다.

이렇게 main.dart를 작업했다면 간단하게 홈 화면에는 하나의 위젯과 연결하고 다른 화면에는 text만 입혀포도록 해보자.

Home.dart

import'package:flutter/material.dart';

// StatelessWidget은 변화지 않는 화면을 작업할 때 사용.
// 변화는 화면을 작업 하고싶을 경우에는 StatefulWidget을 사용.
class Home extends StatelessWidget {

  // MaterialApp = 앱으로서 기능을 할 수 있도록 도와주는 뼈대
  @override
  Widget build(BuildContext context) {

    // return MaterialApp() -> Material 디자인 테마를 사용 
    return MaterialApp(
      title: "MyApp", // 앱 이름
      debugShowCheckedModeBanner: false, // 타이틀 바 우측 띠 제거

      // 앱의 기본적인 테마를 지정
      theme: ThemeData(
        primarySwatch: Colors.blue, // priamrySwatch 기본적인 앱의 색상을 지정
        ),

      home: MyWidget(), // 앱이 실행될 때 표시할 화면의 함수를 호출
      );
  }
}

// 앱이 실행 될때 표시할 화면의 함수
class MyWidget extends StatelessWidget {

  // scaffold = 구성된 앱에서 디자인적인 부분을 도와주는 뼈대

  // 화면 구성
  @override
  Widget build(BuildContext context) {
    return Scaffold(

      // appBar에 AppBar 위젯을 가져온다.
      appBar: AppBar(title: Text("Flutter study"), // 타이틀 이름 지정
      centerTitle: true, // 타이틀 이름을 가운데 정렬
      elevation: 0.0, //elevation 속성을 통해 그림자 효과 제어

      // appBar 높이
      toolbarHeight: 70,

      // 좌측 아이콘 버튼
      leading: IconButton(onPressed: () {}, icon: const Icon(Icons.menu),),
      ),

      // 앱의 body 부분 
      body: Row( // Row 위젯을 가져온다.

      // Row 위젯이므로 가로축 기준으로 가운데 정렬
      mainAxisAlignment: MainAxisAlignment.center, 
      
      // Row의 하위 위젯
      children: <Widget>[
          cntState(), // 함수를 불러온다.
          ],
      ),
      
    );
  }
}

// 버튼을 눌렀을 때 숫자를 카운트 하기위해서는 화면을 변하게끔 작업해야한다.
// 즉, StatefulWidget 선언해야 한다.
class cntState extends StatefulWidget {
  @override
  _cntState createState() => _cntState(); // StatefulWidget은 상태를 생성하는 createState() 메서드로 구현한다.
}

int _cnt = 0;

class _cntState extends State<cntState> {
  @override
  Widget build(BuildContext context) {
    return Center(
      // Elevated Button 위젯 
      child: ElevatedButton(
        // 버튼에 Text를 입힌다.
        child: Text('현재 숫자 : $_cnt'),

        // 클릭 이벤트
        onPressed: () {

          // setState() 메서드를 수행시 다시 build() 메서드가 실행되며 동적 화면이 구현된다.
          setState(() {
            _cnt++; 
            print("$_cnt"); 
          });     
        },
      ),
    );
  }
}

 

위 코드를 모두 작성했다면 다음과 같은 화면이 나오는 것을 확인할 수 있다.

 

참고

 

[플러터] bottom Navigation을 구현하는 3가지 방법

하단에 위치하는 네비게이션바를 구현하는 방법은 크게 3가지가 있다. 약간의 차이점이 있으니 필요에 따라 사용하면 되겠다. 1. DefaultTabController 사용 return MaterialApp( title: 'chat used firebase', th..

muhly.tistory.com