Provider와 Riverpod!?
Riverpod은 상태관리 라이브러리로, Provider의 업그레이드 버전?이라고 할 수 있다
이전에 Flutter를 처음 공부를 시작했을때 뭣도 모르고 Provider가 좋다네? 하고서 공부했던 경험이 있다.
하지만 Flutter를 조금 알게 된 후 Riverpod을 할걸...하고 후회했었다
Riverpod이란 어디서든 변경을 감지하는 상태관리 라이브러리다.
어디서든 상태값에 접근이 가능하며, 결합해 사용도 가능하다.
내가 가장 용이하다고 느낀 부분은 로드/오류 이슈를 처리할 수 있다는 점이다.
가볍게 BottomNavigation을 Provider와 Riverpod으로 구현해봤다.
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로 감싸서 이동해야 한다. 실수로 감지 않으면 기분 나쁜 빨간 화면을 볼 수 있을 것이다.
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들을 이용한 위젯들을 보여주겟읍니다
씨야