IndexedStack class
자식 목록에서 단일 자식을 표시하는 스택입니다.
표시된 자식은 주어진 인덱스를 가진 자식입니다. 스택은 항상 가장 큰 자식만큼 큽니다.
값이 null이면 아무것도 표시되지 않습니다.
공식 문서 코드
우리가 많은 앱들에서 볼 수 있는 요소가 있다. 바로 하단 바 요소인데 가장 대표적인 예로는 유튜브 하단 네비게이션바가 있다.
Home 을 클릭하면 화면이 홈화면으로 이동되어서 우리가 하던 작업을 마저 진행할 수 있고 Shorts 를 누르면 쇼츠로 이동되어 쇼츠를 볼 수 있으며 프로필을 클릭하면 계정과 관련된 여러 작업을 할 수 있다.
근데 여기서 우리가 주목해야 할 점은 물론 탭을 클릭하면 화면이 이동되는 것도 있지만 다른 탭에서 하던 작업이 유지가 되고 렌더링은 되지 않는다는 점이다. 이러한 작업을 아주 간단하게 구현할 수 있는 위젯이 있는데 바로 IndexedStack 이다.
IndexedStack 위젯은 여러 자식 위젯 중 하나만 화면에 표시하면서 나머지 위젯도 트리에는 유지되지만 화면에는 렌더링 되지 않도록 하는 위젯이다. 공식 예제에서는 위젯 안에서 작게 사용해주고 있지만 나는 바텀 네비게이션바를 제작한다는 것을 기준으로 한번 설명해 보겠다.
일단 먼저 IndexedStack 에 대해서 알아보고 가자.
하위 속성
속성명 | 타입 | 기본값 | 설명 |
alignment | AlignmentGeometry | AlignmentDirectional.topStart | 비포지셔닝 자식들의 정렬 위치를 지정 |
textDirection | TextDirection? | null | alignment 속성의 방향성을 결정하는 데 사용 |
clipBehavior | Clip | Clip.hardEdge | 자식이 Stack 범위를 넘을 경우 자르는 방식 설정 |
sizing | StackFit | StackFit.loose | 비포지셔닝 자식들의 크기를 어떻게 할당할지 결정 |
index | int? | 0 | 화면에 보여질 자식의 인덱스를 지정 |
children | List<Widget> | 빈 리스트 | Stack에 포함될 자식 위젯 목록 |
기본적으로 IndexedStack 위젯은 Stack 처럼 자식들을 겹쳐 배치하지만 하나의 자식만 화면에 보이게 만드는 위젯이라고 생각하면 된다. 모든 자식은 트리 구조상 유지되지만 index 로 지정한 자식만 화면에 보이게 된다. 즉 숨겨진 자식들도 여전히 빌드되고 메모리에 남아 있게 되는 것이다.
사실 'Stack 처럼' 이라고 했지만 배치는 그냥 만든 페이지 배치하는 게 끝이다. 그 외에는 건들지 않을 것인데 일단 먼저 하위 속성에 넣을 index 를 하나 만들어 줘야 한다.
int _currentIndex = 0;
currentIndex. 즉 현재 인덱스라는 변수를 만들어 줬는데 이게 정말 핵심이다. 이 인덱스 값이 있어야 인덱스를 통해 밑의 탭을 조정할 수도 있고 현재 위치를 추적할 수 도 있기 때문이다.
그리고 바로 IndexedStack 을 사용해줄 건데 Scaffold 안에 body 로 바로 넣어 줄 거다. 이 IndexedStack 이 화면의 최상위 위젯이 이 되어야 우리가 원하는 화면 이동(탭 이동)이 되기 때문이다.
Scaffold(
body: IndexedStack(
index: _currentIndex,
children: [
Scaffold(backgroundColor: Colors.blue,),
Scaffold(backgroundColor: Colors.red,),
Scaffold(backgroundColor: Colors.white,),
],
),
);
이렇게 만들어 주었다. index: 안에는 방금 만든 currentIndex 를 넣어서 인덱스에 접근할 수 있게 해 주고 밑에 children 에는 우리가 배치해 줄 화면, 위젯을 넣어주면 되겠다.
이 코드에서는 가볍게 그냥 Scaffold 를 넣어주고 있지만 실제로 구성할 때에는 다른 코드에서 만든 다른 화면(class)을 불러와야 할 것이다.
자 이제 이렇게만 해줘도 IndexedStack 의 역할은 끝났다. 정말 쉽지 않은가? 애당초 이 위젯의 핵심은 index 와 children 이기 때문에 이렇게만 사용해줘도 거의 다 왔다.
자 이제 바텀 네비게이션 바를 하나 만들어주겠는데 Scaffold 로 만들어 놨으니 바로 만들어줄 수 있다.
Scaffold(
bottomNavigationBar: BottomNavigationBar(),
);
이 BottomNavigationBar 가 사실은 본체(?) 라고도 할 수 있겠는데 여기서 이제 인덱스를 전환해주는 동작과 아이콘등을 넣어 줄 것이다.
onTap: (value) {
setState(() {
_currentIndex = value;
});
},
currentIndex: _currentIndex,
BottomNavigation 에 달려 있는 onTap 을 통하여 currentIndex 를 value, 즉 현재 선택한 BottomNavigationBarItem 에 대입해 주는 동작을 해주는 것인데 이제 아이템(탭)을 누를 때마다 currentIndex 의 값이 바뀔 것이다.
그리고 하위 속성인 currentIndex 에 만들어둔 currentIndex 변수를 넣으면 바뀐 값이 적용이 되어 추적할 것이다.
items: [
BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
BottomNavigationBarItem(icon: Icon(Icons.search), label: 'Search'),
BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Profile'),
],
이제 배치할 아이템(아이콘)은 BottomNavigationBarItem 으로 만들어 주면 되겠다. 참고로 label 필수이니 꼭 넣도록 하자.
이렇게 하면 밑에 같은 화면이 완성된다.
보는 것 처럼 밑의 탭을 누르면 화면이 전환되는 모습이다. 내가 이건 기능만 알려주기 위해 디자인은 전혀 하지 않았기 때문에 이렇게 되어 있는데 당연하겠지만 디자인은 필수요소이다. BottomNavigationBar 안에 디자인을 도와주는 여러 항목들과 이걸 Container 같은 걸로 감싸서 직접 디자인을 해줄 수도 있기 때문에 무궁무진하게 만들 수 있다.
이렇게 간단하게 한번 IndexedStack 을 활용하여 화면전환하는 동작을 구현해 보았다.
지금 내 코드에서는 안에서 뭐 동작하는 게 없어서 확인이 어렵긴 한데 동작이 다 유지가 되고 있다. 그리고 결국에 저걸 다 클래스로 분리를 해서 사용을 하게 될 텐데 다 같은 한 화면 안에 묶여 있지만 그렇게 되면 본질은 다 다른 화면이기 때문에 맨 처음 화면에 들어갔다고 해서 모든 화면이 렌더링 되진 않는다. 즉 iniState 가 다른 화면은 동작을 안 할 수도 있다는 뜻인데 이점 유의하길 바란다.
이번에 코드 자체는 되게 쉽게 구성해 놓았지만 어떻게 활용할지는 코드를 쓰는 자신에게 맡겨야 할 것이다. 암튼 도움이 되었길 바라며 마치겠다.