[Flutter/Dart] Riverpod과 Provider로 BottomNavigation 구현하기

찌니·2023년 3월 24일
1

Flutter

목록 보기
6/13
post-thumbnail

Provider와 Riverpod!?
Riverpod은 상태관리 라이브러리로, Provider의 업그레이드 버전?이라고 할 수 있다
이전에 Flutter를 처음 공부를 시작했을때 뭣도 모르고 Provider가 좋다네? 하고서 공부했던 경험이 있다.
하지만 Flutter를 조금 알게 된 후 Riverpod을 할걸...하고 후회했었다

Riverpod이란 어디서든 변경을 감지하는 상태관리 라이브러리다.
어디서든 상태값에 접근이 가능하며, 결합해 사용도 가능하다.
내가 가장 용이하다고 느낀 부분은 로드/오류 이슈를 처리할 수 있다는 점이다.

가볍게 BottomNavigation을 Provider와 Riverpod으로 구현해봤다.

Provider

class BottomNavigationProvider extends ChangeNotifier {
  int _currentPage = 2;
  int get currentPage => _currentPage;

  // page 업데이트
  setCurrentPage(int index) {
    _currentPage = index;
    notifyListeners();
  }
}
class BottomNavigation extends StatelessWidget {
  BottomNavigation({Key? key}) : super(key: key);
  late BottomNavigationProvider _bottomNavigationProvider;

  
  Widget build(BuildContext context) {
    _bottomNavigationProvider = Provider.of<BottomNavigationProvider>(context);

    return Scaffold(
      body: SafeArea(
        child: [
          SightsPage(),
          const RecommendedRoutePage(),
          const HomePage(),
          const MapSearchPage(),
          const RidingPage(),
        ].elementAt(_bottomNavigationProvider.currentPage),
      ),
      bottomNavigationBar: BottomNavigationBar(
          type: BottomNavigationBarType.fixed,
          backgroundColor: Colors.white,
          items: <BottomNavigationBarItem>[
            BottomNavigationBarItem(
              icon: Container(
                  padding: itemPadding,
                  child: Image.asset(
                    'assets/icons/bottom_nav_place.png',
                    height: 20,
                    width: 23,
                    color: unSelected,
                  )),
              activeIcon: Container(
                  padding: itemPadding,
                  child: Image.asset(
                    'assets/icons/bottom_nav_place.png',
                    height: 20,
                    width: 23,
                    color: selected,
                  )),
              label: '명소',
            ),
            BottomNavigationBarItem(
              icon: Container(
                  padding: itemPadding,
                  child: Image.asset('assets/icons/bottom_nav_route.png',
                      height: 20, width: 20, color: unSelected)),
              activeIcon: Container(
                  padding: itemPadding,
                  child: Image.asset(
                    'assets/icons/bottom_nav_route.png',
                    height: 20,
                    width: 20,
                    color: selected,
                  )),
              label: '추천경로',
            ),
            BottomNavigationBarItem(
              icon: Container(
                  padding: itemPadding,
                  child: Image.asset('assets/icons/bottom_nav_home.png',
                      height: 20, width: 23, color: unSelected)),
              activeIcon: Container(
                  padding: itemPadding,
                  child: Image.asset(
                    'assets/icons/bottom_nav_home.png',
                    color: selected,
                    height: 20,
                    width: 23,
                  )),
              label: '홈',
            ),
            BottomNavigationBarItem(
              icon: Container(
                  padding: itemPadding,
                  child: Image.asset('assets/icons/bottom_nav_search.png',
                      height: 20, width: 20, color: unSelected)),
              activeIcon: Container(
                  padding: itemPadding,
                  child: Image.asset(
                    'assets/icons/bottom_nav_search.png',
                    color: selected,
                    height: 20,
                    width: 20,
                  )),
              label: '경로검색',
            ),
            BottomNavigationBarItem(
              icon: Container(
                  padding: itemPadding,
                  child: Image.asset('assets/icons/bottom_nav_riding.png',
                      height: 20, width: 25, color: unSelected)),
              activeIcon: Container(
                  padding: itemPadding,
                  child: Image.asset(
                    'assets/icons/bottom_nav_riding.png',
                    color: selected,
                    height: 20,
                    width: 25,
                  )),
              label: '라이딩',
            ),
          ],
          currentIndex: _bottomNavigationProvider.currentPage,
          selectedItemColor: selected,
          unselectedItemColor: unSelected,
          onTap: (index) {
            if (index == 4) {
              Navigator.of(context).push(MaterialPageRoute(
                  builder: (context) => ChangeNotifierProvider(
                        create: (context) => RidingProvider(),
                        child: const RidingPage(),
                      )));
            } else {
              _bottomNavigationProvider.setCurrentPage(index);
            }
          }),
    );
  }
}

위 코드를 보면 다른 화면으로 이동할때 ChangeNotifierProvider로 감싸서 이동해야 한다. 실수로 감지 않으면 기분 나쁜 빨간 화면을 볼 수 있을 것이다.

Riverpod

class BottomNavState extends StateNotifier<int> {
  BottomNavState() : super(0);

  
  set state(int value) {
    super.state = value;
  }
}
final bottomNavProvider =
    StateNotifierProvider<BottomNavState, int>((ref) => BottomNavState());

class BottomNavigation extends ConsumerStatefulWidget {
  const BottomNavigation({Key? key}) : super(key: key);

  
  BottomNavigationState createState() => BottomNavigationState();
}

class BottomNavigationState extends ConsumerState<BottomNavigation> {
  
  Widget build(BuildContext context) {
    const Color selected = Color.fromRGBO(63, 66, 72, 1);
    const Color unSelected = Color.fromRGBO(204, 210, 223, 1);
    final currentPage = ref.watch(bottomNavProvider);

    final defaultScreen = [
      const HomeSceen(),
      CounselorScreen(),
      const CommunityScreen(),
      const UserProfileScreen()
    ];

    return Scaffold(
      body: SafeArea(
          child: defaultScreen.elementAt(currentPage)),
      bottomNavigationBar: BottomNavigationBar(
          type: BottomNavigationBarType.fixed,
          items: const <BottomNavigationBarItem>[
            BottomNavigationBarItem(
                icon: Icon(
                  Icons.home,
                  color: unSelected,
                ),
                activeIcon: Icon(
                  Icons.home,
                  color: selected,
                ),
                label: 'Home'),
            BottomNavigationBarItem(
                icon: Icon(
                  Icons.chat,
                  color: unSelected,
                ),
                activeIcon: Icon(
                  Icons.chat,
                  color: selected,
                ),
                label: 'Counselor'),
            BottomNavigationBarItem(
                icon: Icon(
                  Icons.article_rounded,
                  color: unSelected,
                ),
                activeIcon: Icon(
                  Icons.article_rounded,
                  color: selected,
                ),
                label: 'Community'),
            BottomNavigationBarItem(
                icon: Icon(
                  Icons.account_circle_rounded,
                  color: unSelected,
                ),
                activeIcon: Icon(
                  Icons.account_circle_rounded,
                  color: selected,
                ),
                label: 'MyPage'),
          ],
          currentIndex: currentPage,
          selectedItemColor: selected,
          unselectedItemColor: unSelected,
          onTap: (index) {
           if (index == 4) {
              Navigator.pushAndRemoveUntil(
                  context,
                  MaterialPageRoute(builder: (context) => ChatScreen()),
                  (route) => false);
            } else {
              _bottomNavigationProvider.setCurrentPage(index);
            }
          }),
    );
  }

보이는가 이 차이가..
사실 저렇게 구현할 필요도 없다. StateProvider로 상태값만 만들어줘도 충분하다

final bottomNavProvider =
    StateProvider<int>((ref) => 0);

다음 글에서는 Riverpod의 Provider들을 이용한 위젯들을 보여주겟읍니다
씨야

profile
찌니's develog

0개의 댓글