[PLAY STEEM x WebApp] Bottom Navigation Bar 구현

PLAY STEEM 개발자 이타인클럽입니다.

웹앱 개발에 속도를 내고 있습니다.

첫화면 UI를 간단히 잡아봤고, 가장 일반적인 Bottom Navigation Bar를 구현하고 있습니다.

Bottom Navigation Bar는 소셜 앱에서 가장 흔하게 보이는 아래쪽 메뉴 바입니다.

image.png

BottomNavigationBar

플러터에서 Bottom Navigation Bar를 구현하는 방법은 크게 세가지 입니다.

아래 사이트에 잘 정리되어 있습니다.
참고. https://muhly.tistory.com/22

  1. DefaultTabController
  2. TabController
  3. BottomNavigationBar

처음 앱을 만들었을 때, 스와이핑으로 화면 전환을 해보려고 이를 지원하는 DefaultTabController를 사용해봤습니다. 정말 간단하게 스와이핑이 가능한 화면 네이게이션 구현이 되었습니다.

이번에는 BottomNavigationBar를 사용해서 구현해 봅니다. 이 방법이 가장 많이 사용되는 방법이라고 합니다. 특징이라면 선택된 탭의 번호를 알 수 있다는 것입니다. 이를 위해서 어떤 탭이 눌렸는지 상태를 관리해야 합니다. 이를 위해 BloC을 사용할 수도 있지만 좀 더 간단한 Cubit을 사용합니다.

  • Bottom Navigation Bar Widget
// navigation menu
enum BottomNavItem {
  feed,
  profile,
  wallet,
  notifications,
  messages,
}

// bottom navigation bar widget
class BottomNavBar extends StatelessWidget {
  final Map<BottomNavItem, IconData> items;
  final BottomNavItem selectedItem;
  final Function(int) onTap;

  const BottomNavBar({
    Key? key,
    required this.items,
    required this.selectedItem,
    required this.onTap,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return BottomNavigationBar(
        type: BottomNavigationBarType.fixed,
        backgroundColor: Colors.white,
        showSelectedLabels: false,
        showUnselectedLabels: false,
        selectedItemColor: Theme.of(context).primaryColor,
        unselectedItemColor: Colors.grey,
        currentIndex: BottomNavItem.values.indexOf(selectedItem),
        onTap: onTap,
        items: items
            .map((item, icon) => MapEntry(
                item.toString(),
                BottomNavigationBarItem(
                  label: '',
                  icon: Icon(icon, size: 30.0),
                )))
            .values
            .toList());
  }
}
  • 사용
class NavScreen extends StatelessWidget {
...

@override
  Widget build(BuildContext context) {
    return BlocProvider<BottomNavBarCubit>(
      create: (context) => BottomNavBarCubit(),
      child: WillPopScope(
        onWillPop: () async => false,
        child: BlocBuilder<BottomNavBarCubit, BottomNavBarState>(
          builder: (context, state) {
            print('state selected item: ${state.selectedItem}');
            return Scaffold(
              body: Stack(
                children: items
                    .map((item, _) => MapEntry(
                          item,
                          _buildOffstageNavigator(
                              item, item == state.selectedItem),
                        ))
                    .values
                    .toList(),
              ),
              bottomNavigationBar: BottomNavBar(
                  items: items,
                  selectedItem: state.selectedItem,
                  onTap: (index) {
                    final selectedItem = BottomNavItem.values[index];
                    print(index);
                    _selectBottomNavItem(context, selectedItem,
                        selectedItem == state.selectedItem);
                  }),
            );
          },
        ),
      ),
    );
  }

위 코드에서 BottomNavBarCubit이 사용되는 것을 볼 수 있습니다.

이 Cubit은 단순히 선택된 메뉴의 상태를 관리하는 것입니다. 메뉴 상태가 바뀌었다면 상태를 만들어서 BlocBuilder가 실행되게 합니다.

// cubit
class BottomNavBarCubit extends Cubit<BottomNavBarState> {
  BottomNavBarCubit()
      : super(BottomNavBarState(selectedItem: BottomNavItem.feed));

  void updateSelectedItem(BottomNavItem item) {
    print('updateSelectedItem: $item');
    print('updateSelectedItem. state item: ${state.selectedItem}');
    emit(BottomNavBarState(selectedItem: item));

    if (item != state.selectedItem) {
      print('update. item: $item');
      emit(BottomNavBarState(selectedItem: item));
    }
  }
}

코드에서 생략된 부분이 많습니다만, 큰 그림 차원에서 중요한 부분만 표시했습니다.

Moment of Truth

  • Feed Screen
    image.png

  • Profile Screen

image.png

메뉴를 클릭시 화면 전환이 잘 됩니다. 그리고 Bottom Navigation Bar 메뉴 아이콘도 잘 변경됩니다.

위 코드 중 뒤로 가기를 눌러도 첫화면(메뉴 선택 화면)으로 돌아가지 못하게 하는 부분이 있습니다. 아래처럼 Scaffold Widget을 WillPopScope로 감싸고, onWillPop에 () async => false하면 됩니다!

child: WillPopScope(
        onWillPop: () async => false,

많은 리스팀과 응원부탁드립니다.

Thank you for support

@steemcurator01
@steemcurator06

https://playsteem.app



Sort:  

start success go! go! go!

점점 발전하는 앱의 모습이 느껴지네요^^

한 번 만들어봐서 좀 더 빨리 만들 수 있네요. 감사합니다.

정확히 문제점을 알았습니다. 확실히 푸쉬 노티피케이션에 있는 방해 금지 시간 때문입니다.
제가 방해 금지 시간을 밤 10시부터 다음날 6시까지로 설정했었는데 그것을 한시간 전에 해제했는데 그러니까 알람이 왔습니다.
그것을 보고 지금 다시 들어갔는데 음... 모든 푸쉬 항목들이 다 꺼져있네요... 알람은 정상적으로 왔는데 항목들은 자동으로 꺼져있고...
하여간 이 부분에 좀 오류가 있는 듯 합니다. ^^

그런가요? 푸시 설정 표시는 최근 업데이트 후 이상하게 다 표시가 안되는 현상이 나타났습니다. 그리고 방해금지시간도 같이 살펴보겠습니다.

의견 감사합니다!

오늘도 어플로 글구경하고 댓글남겨요! 개발자님 응원합니다아🙌🙌

응원 감사해요~

Coin Marketplace

STEEM 0.28
TRX 0.13
JST 0.032
BTC 65035.33
ETH 2950.72
USDT 1.00
SBD 3.66