Navigator.of(context).push의 경우 이전에 있던 모든 화면 위에 새 창을 띄우는 식. 그래서 뒤로가기를 통해 이전 화면으로 갈 수가 있음. 그런데 굳이 돌아가지 않아도 되는 화면들이 있음. 예를 들면 회원가입 완료 후 메인화면에 들어가게 되면, 회원가입 하던 화면은 돌아갈 수 없게 처리가 돼야 함!
-> 이런 케이스엔 이전 화면을 삭제해버리면 됨
-> Navigator.of(context).push 대신, Navigator.of(context).pushAndRemoveUntil 함수를 사용!
라우트 개수를 설정하고 return 값으로 false를 주면 이전 라우트를 삭제함
하단부 내비게이션 바로 스크린 이동 세팅하는 방법
- Scaffold에서 bottomNavigationBar 속성 이용
- items는 필수 속성! 아이콘 원하는 개수만큼 세팅
items: const [
BottomNavigationBarItem(
icon: FaIcon(FontAwesomeIcons.house),
label: "Home",
tooltip: "What are you?",
backgroundColor: Colors.yellow),
BottomNavigationBarItem(
icon: FaIcon(FontAwesomeIcons.magnifyingGlass),
label: "Search",
tooltip: "What are you?",
backgroundColor: Colors.cyan),
]
- 아이콘을 눌렀을 때 이동할 스크린을 개수 맞춰서 세팅
final screens = [
const Center(
child: Text('Home'),
),
const Center(
child: Text('Screen'),
),
]
- currentIndex 속성은 가장 처음 들어갔을 때 먼저 나오게 할 인덱스를 세팅하는 부분. 초기 변수를 0으로 세팅하고 속성값도 세팅
int _selectedIndex = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
bottomNavigationBar: BottomNavigationBar(
currentIndex: _selectedIndex,
- bottomNavigationBar의 onTap 속성은 특정 아이템을 눌렀을 때 아이템의 인덱스를 파라미터로 가짐. onTap 시 selectedIndex를 업데이트해줌.
void _onTap(int index) {
setState(() {
_selectedIndex = index;
});
}
Widget build(BuildContext context) {
return Scaffold(
bottomNavigationBar: BottomNavigationBar(
onTap: _onTap,
- 탭했을 때 보여질 화면을 세팅. 아까 생성한 screens에서 탭한 아이템의 인덱스에 해당하는 스크린을 Scaffold의 body에 넣어줌
Widget build(BuildContext context) {
return Scaffold(
bottomNavigationBar: BottomNavigationBar(
body: screens[_selectedIndex],
- 아이템이 4개 이상 있을 때, type 속성을 shifting으로 세팅하면, onTap 상태의 현재 인덱스의 속성을 반영해줌. (4개 이상 아니면 반영 안 됨) + 아이템 개수와 스크린 개수가 같아야 함!
@override
Widget build(BuildContext context) {
return Scaffold(
body: screens[_selectedIndex],
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.shifting,
(4개 이상 아이템이 있을 때만 배경 바뀌는 거 적용)
아이템 개수와 스크린 개수가 같아야 함
BottomNavigationBar말고 다른 라이브러리도 있음!
= NavigationBar -> Material Design
용어만 다르지 기능은 똑같아서 참고해서 사용!
@override
Widget build(BuildContext context) {
return Scaffold(
body: screens[_selectedIndex],
bottomNavigationBar: NavigationBar(
labelBehavior: NavigationDestinationLabelBehavior.onlyShowSelected,
selectedIndex: _selectedIndex,
onDestinationSelected: _onTap,
destinations: const [
NavigationDestination(
icon: FaIcon(FontAwesomeIcons.house),
label: 'Home',
),
NavigationDestination(
icon: FaIcon(
FontAwesomeIcons.magnifyingGlass,
color: Colors.amber,
),
label: 'Search',
),
],
),
);
}
아이폰처럼 하고 싶으면 CupertinoTabBar를 사용! Scaffold도 CupertinoTabScaffold로 만들어야 함
@override
Widget build(BuildContext context) {
return CupertinoTabScaffold(
tabBar: CupertinoTabBar(
items: const [
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.house),
label: "Home",
),
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.search),
label: "Search",
),
],
),
tabBuilder: (context, index) => screens[index],
);
}
# 커스텀 내비게이션바 만들기
- 좁은 영역의 내비게이션 바 내부에 Column을 생성할 경우 mainAxisSize를 minimum으로 세팅하지 않으면 화면 전체로 넘치는 오류가 남. Column은 기본적으로 세로로 길게 뻗어가려는 속성이 있기 때문에 꼭 이 속성을 세팅해주자!
- 네비게이션바의 아이콘에 컨테이너를 감싸지 않으면 아이콘을 정확히 눌러야만 onTap 이벤트가 작동하는 아쉬움이 있음. 아이콘을 포함한 컬럼이나 로우를 컨테이너로 감싸고 맨 위에 Expanded 위젯으로 감싸면 아이콘 주변부를 눌러도 이벤트가 발생한다!
(Expanded -> GestureDetector -> Container -> AnimatedOpacity -> Column 순으로 감싸야 함)
- 여러개의 screen을 세팅할 때, 일정한 양식이 있어서 별도의 위젯으로 빼서 세팅한다면 아래와 같이 될 것
final screens = [
StfScreen(),
StfScreen(),
Container(),
StfScreen(),
StfScreen(),
]
그런데 이렇게 되면 Flutter가 각 스크린이 다른 화면인지 구분을 할 수가 없어서 GlobalKey를 세팅해줘야 구분할 수 있게됨.
final screens = [
StfScreen(key: GlobalKey()),
StfScreen(key: GlobalKey()),
Container(),
StfScreen(key: GlobalKey()),
StfScreen(key: GlobalKey()),
];
이렇게 세팅하면 새로운 탭을 들어갔을 때 기존 탭에서 작업한 내역이 유지되지 않고 새로운 스크린이 뜸.
문제는 다시 기존 탭으로 돌아가면 기존 탭에서 작업한 내역 역시 다시 리셋되는 것. 우리가 앱을 이용할 때는 일반적이지 않은 상황
기획자 관점에서 보면 이런 상황은 탭을 이동할 때마다 데이터를 계속해서 fetch해야 하고, 클라우드나 외부서버망을 이용하면 이게 다 돈이 됨.
-> 탭을 이동할 때마다 매번 새로 탭이 뜨게 하는 게 아니라, 모든 스크린을 한번에 로드하고, 탭을 이동할 때만 보이게 하면 어떨까?
create이 아니라 visible의 개념으로!
-> 이런 일을 하는 게 Offstage 위젯!!!
Offstage 위젯에서 offstage 속성을 true로 두면 화면이 생성돼도 보이지가 않음!
body: Stack(
children: [
Offstage(
offstage: _selectedIndex != 0,
child: const StfScreen(),
),
Offstage(
offstage: _selectedIndex != 1,
child: const StfScreen(),
),
Container(),
Offstage(
offstage: _selectedIndex != 3,
child: const StfScreen(),
),
Offstage(
offstage: _selectedIndex != 4,
child: const StfScreen(),
),
필요에 따라 사용하면 좋지만, 남발하면 너무 많은 데이터를 한번에 렌더하느라 리소스 소모가 커질 수 있음
겹치는 모양의 아이콘을 직접 커스텀할 때 Stack과 Positioned를 사용하면 좋음!
Stack 위젯 내에 빽으로 깔아 둘 컨테이너를 먼저 깔아서 Positioned 위젯으로 감싸주고, 맨 마지막에 기준이 될 Container를 깔아준 뒤 Positioned의 left와 right 속성을 활용해 원하는 아이콘을 구현할 수 있다.
예시)
Stack(
clipBehavior: Clip.none,
children: [
Positioned(
right: 20,
child: Container(
height: 30,
width: 25,
padding: const EdgeInsets.symmetric(horizontal: Sizes.size8),
decoration: BoxDecoration(
color: const Color(0xff61D4F0),
borderRadius: BorderRadius.circular(Sizes.size8),
),
),
),
Positioned(
left: 20,
child: Container(
height: 30,
width: 25,
padding: const EdgeInsets.symmetric(horizontal: Sizes.size8),
decoration: BoxDecoration(
color: Theme.of(context).primaryColor,
borderRadius: BorderRadius.circular(Sizes.size8),
),
),
),
Container(
padding: const EdgeInsets.symmetric(horizontal: Sizes.size12),
height: 30,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(Sizes.size6),
),
child: const Center(
child: FaIcon(
FontAwesomeIcons.plus,
color: Colors.black,
size: Sizes.size20,
),
),
),
],
);
출처) 노마드 코더 강의
'Flutter & Dart' 카테고리의 다른 글
공부하다 배운 Flutter 꿀팁 무작정 적어두기 (6) (1) | 2023.12.26 |
---|---|
공부하다 배운 Flutter 꿀팁 무작정 적어두기 (5) - 동영상 플레이어 만들기 (1) | 2023.12.05 |
공부하다 배운 Flutter 꿀팁 무작정 적어두기 (3) (1) | 2023.11.24 |
공부하다 배운 Flutter 꿀팁 무작정 적어두기 (2) (2) | 2023.11.23 |
공부하다 배운 Flutter 꿀팁 무작정 적어두기 (1) (0) | 2023.11.22 |