2022.10.10 - [Flutter] - React Native에서 Flutter로 갈아타기 #8 - Navigation
👆👆 (이전 글에서) React navigation으로 했던 bottom tab navigator와 stack navigator를 합친 구조를 Flutter에서 구현해봤어. 네비게이터는 하나만 만들었었는데. 오늘은 3개 전부 다 만들어 볼거구. 스크린도 데이터를 받도록 변경해서 탭 이름을 나타내도록 할거야.
우선 ScreenA.dart를 변경해볼게. 심플해. int tabIndex를 받을 거고 그걸 화면에 뿌려줄거야.👇👇
import 'package:flutter/material.dart';
import 'ScreenB.dart';
class ScreenA extends StatelessWidget {
const ScreenA({super.key, required this.tabIndex});
final int tabIndex;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("ScreenA")),
body: Center(
child: Column(children: [
Text("from tab: ${tabIndex.toString()}"),
TextButton(
child: Text("Go to ScreenB"),
onPressed: () {
Navigator.pushNamed(context, '/ScreenB');
},
)
]),
));
}
}
ScreenB.dart도 동일하게 했어. 그래서 코드는 생략. 그리고 tab 별로 3개의 navigator를 준비해주고 자신의 스크린에 tabIndex를 넘겨줄거야. 그 중 하나만 PersonNavigator.dart 만 보면👇👇 screen에 전달할 데이터 넣어준거 말고는 없어.
import 'package:flutter/material.dart';
import 'package:study_flutter/screens/ScreenA.dart';
import 'package:study_flutter/screens/ScreenB.dart';
class PersonNavigator extends StatelessWidget {
const PersonNavigator({super.key, required this.tabIndex});
final int tabIndex;
Map<String, WidgetBuilder> _routeBuilder(BuildContext context) {
return {
"/": (context) => ScreenA(
tabIndex: tabIndex, //이거 말고는 변한게 없음!
),
"/ScreenB": (context) => ScreenB(
tabIndex: tabIndex, //이거 말고는 변한게 없음!
),
};
}
@override
Widget build(BuildContext context) {
final routeBuilder = _routeBuilder(context);
return Navigator(
initialRoute: '/',
onGenerateRoute: ((settings) {
return MaterialPageRoute(
builder: (context) => routeBuilder[settings.name!]!(context),
);
}),
);
}
}
이제 main.dart만 수정해주면 끝! scaffold body에 PersonNavigator 위젯만 넣어줬었는데 이제는 tab별로 navigator를 넣어줘야 하잖아? 그 작업을 임시로 해봤어. 왜 "임시"인줄은 잠시 후 설명해줌.
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
int _currentTabIndex = 0;
void _tabSelect(int tabIndex) {
setState(() {
_currentTabIndex = tabIndex;
});
}
//여기부터
List<Widget> _tabNavigatorBuilder() {
return [
PersonNavigator(tabIndex: _currentTabIndex),
AlarmNavigator(tabIndex: _currentTabIndex),
UmbrellaNavigator(tabIndex: _currentTabIndex),
];
}
//여기까지 추가
@override
Widget build(BuildContext context) {
//여기부터
final TabNavigationBuilder = _tabNavigatorBuilder();
//여기까지
return Scaffold(
//여기부터
body: Center(child: TabNavigationBuilder.elementAt(_currentTabIndex)),
//여기까지
bottomNavigationBar: BottomNavigationBar(
items: [
BottomNavigationBarItem(icon: Icon(Icons.person), label: "Person"),
BottomNavigationBarItem(icon: Icon(Icons.alarm), label: "alarm"),
BottomNavigationBarItem(icon: Icon(Icons.umbrella), label: "umbrella")
],
currentIndex: _currentTabIndex,
onTap: (value) => _tabSelect(value),
),
);
}
}
tabIndex는 사실 그냥 숫자야. 첫번째 탭은 0 그 다음은 1 그 다음은 2. 그래서 navigator list를 하나 만들고 elementAt(index) 함수를 이용해서 원하는 tabNavigator를 body에 보여주도록 했어.👆👆 이제 실행시켜보면 👇👇 각 tab마다 index 번호가 잘 보이는 걸 확인할 수 있어.🎉🎉
어.. 근데... 각 tab마다 navigator 상태가 유지가 안되네;;; flutter는 상태가 변경되면 자신에 포함된 자식 전체를 다시 그려버리기 때문에 다른 탭으로 넘가갔다가 돌아오면 네비게이션의 상태가 초기화되버려. 이건 우리가 원한 결과가 아니잖아~ 상태가 그대로 유지가 되어야지! 이게 바로 아까 elementAt(index)를 "임시"로 사용한 이유야.
나는 각 tab의 navigator의 상태를 유지하기 위한 state를 유지해야 하나? 라고 생각하고 있었는데;;; 이래 저래 찾아보니까;; 해법은 매우 심플! 요약하면 모든 네비게이션을 다 그려버리고 필요없는 건 안보이게 한다야 😅😅 👇
이렇게 하기 위해서는 Stack 위젯과 OffStage라는 위젯을 사용하는데 Stack 위젯으로 모든 tab의 navigator를 겹치게 그리고 OffStage 위젯으로 각 navigator를 감싸서 현재 탭이 자기 것이 아니면 안보이도록 하는 방식이야. 음 왠지 무식하지만 심플한게 마음에 쏙드는 방식이군!! 설명한데로 main.dart의 scaffold body 부분을 Stack과 OffStage를 사용하도록 변경했어👇👇
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
int _currentTabIndex = 0;
void _tabSelect(int tabIndex) {
setState(() {
_currentTabIndex = tabIndex;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
//여기부터
body: Stack(children: [
Offstage(
offstage: _currentTabIndex != 0,
child: PersonNavigator(tabIndex: 0),
),
Offstage(
offstage: _currentTabIndex != 1,
child: AlarmNavigator(tabIndex: 1),
),
Offstage(
offstage: _currentTabIndex != 2,
child: UmbrellaNavigator(tabIndex: 2),
),
]),
//여기까지 수정
bottomNavigationBar: BottomNavigationBar(
items: [
BottomNavigationBarItem(icon: Icon(Icons.person), label: "Person"),
BottomNavigationBarItem(icon: Icon(Icons.alarm), label: "alarm"),
BottomNavigationBarItem(icon: Icon(Icons.umbrella), label: "umbrella")
],
currentIndex: _currentTabIndex,
onTap: (value) => _tabSelect(value),
),
);
}
}
그리고 나서 테스트를 해보면 👇👇아래와 같이 Screen 상태가 유지되는 걸 확인할 수 있어!
음 이제 이제 React native에서 Flutter로 갈아타기는 마무리하는게 좋겠어. 물론 React Query 같은 Data fetch는 매우 중요해서 비교를 하려고 했는데 비슷한게 Flutter에 있는지 아직 확인을 못했기도 하고 Stream builder같은 React에는 없는 것들도 막 튀어나오고 그래서 말야... 이제부터는 React native와 비교 없이 Flutter만 집중해서 하도록 할게.
이제 Firebase auth, firestore data fetch, firebase storage file upload 를 해볼건데 이거 끝나면 실제로 프로젝트 기획해서 upload까지 달려볼거야. 안녕!
'Flutter > Flutter Study' 카테고리의 다른 글
[Flutter] - Firebase auth! 소셜 로그인 (0) | 2022.10.12 |
---|---|
[Flutter] - Firebase auth! 이메일 로그인. (0) | 2022.10.11 |
React Native에서 Flutter로 갈아타기 #8 - Navigation (0) | 2022.10.10 |
React Native에서 Flutter로 갈아타기 #7 - State management (2) | 2022.10.06 |
React Native에서 Flutter로 갈아타기 #6 - Props 전달 (0) | 2022.10.06 |