Don't think! Just do it!

종합 IT 기술 정체성 카오스 블로그! 이... 이곳은 어디지?

Flutter/Flutter Project

[소셜차트] 앱 제작기 #3. Navigation 시스템 구성

방피터 2022. 10. 21. 22:42

2022.10.14 - [Flutter/Flutter Project] - [소셜차트] 앱 제작기 #2. 기본 설정

 

[소셜차트] 앱 제작기 #2. 기본 설정

우선 프로젝트 생성. 👇 %flutter create --org com.petercircuitsoft socialchart --org 옵션으로 com.petercircuitsoft 줬는데 이건 우리 회사 도메인이야. 각자 회사 도메인 이름으로 하자. 제발~~ 이렇게 설정..

engschool.tistory.com

카카오가 터지는 바람에 몇일 블로그를 못썼네;;; 망할 카카오.. 다른데로 블로그를 옮길까봐! ㅎㅎ 일단 앱 만들던건 만들고 ㅋ

기본 설정까지하고 지나갔는데 약간 진도가 나갔어. 소셜차트는 4개의 탭으로 구성되는데 그리고 각 탭은 여러 개의 스크린을 가진 독립적인 네비게이터가 필요해. 이런 걸 Nested navigation이라도 함.

Nested Navigator 테스트

GetX를 적극적으로 써보려는 생각에 Navigator들도 전부 GetX를 활용해보려고 했는데 나처럼 Bottom Tab navigator와 또 그 안에 Tab별로 navigator가 있는 중첩된(nested) navigator는 GetX로는 힘들다고 그러더라고. 그래서 보통의 방식을 사용했어.

MainNavigator는 Scaffold Body에 Stack으로 중첩된 Navigator들을 가지고 있고 bottomNavigationBar에 BottomTabNavigator를 가지고 있는 구조야.👇

//MainNavigator.dart

import 'package:flutter/material.dart';
import 'BottomTabNavigator.dart';

import 'NavigationTree.dart';
import 'package:get/get.dart';

class MainNavigator extends StatelessWidget {
  const MainNavigator({super.key});

  @override
  Widget build(BuildContext context) {
    IndexController indexController = Get.put(IndexController());
    return Scaffold(
      body: Stack(
        children: [
          _buildOffstageNavigator(TabItem.home),
          _buildOffstageNavigator(TabItem.explore),
          _buildOffstageNavigator(TabItem.notice),
          _buildOffstageNavigator(TabItem.profile),
        ],
      ),
      bottomNavigationBar: BottomTabNavigator(),
    );
  }
}

Widget _buildOffstageNavigator(TabItem tabItem) {
  IndexController indexController = Get.find();

  return Obx(() => Offstage(
        offstage: indexController.currentIndex.value != tabItem,//선택한 탭만 보이도록
        child: bottomTabs[tabItem],//각 tab의 네비게이터가 들어 있는 List
      ));
}

BottomTabNabigator에서 Tab이 선택되면 indexController(GetxController)에 currentIndex로 저장이 되는데 이걸 Offstage 위젯에 사용해 선택된 tab의 Navigator만 보이게 하는거지.

BottomTabNavigator 은 특별한 건 없어. GetX IndexController가 있다는 것 정도? 그런데 컨트롤로도 별도로 파일을 만들어서 옮길거니까 뭐 ㅎㅎㅎ Tab 설정한 거 빼고는 없다고 봐야지.

import 'package:flutter/material.dart';
import 'package:get/get.dart';

import 'NavigationTree.dart';

class IndexController extends GetxController {
  static IndexController get to => Get.find();
  var currentIndex = TabItem.home.obs;

  void changeTabIndex(TabItem index) => currentIndex.value = index;
}

class BottomTabNavigator extends StatelessWidget {
  const BottomTabNavigator({super.key});

  @override
  Widget build(BuildContext context) {
    IndexController indexController = Get.find();//GetxController
    return Obx(() => BottomNavigationBar(
          type: BottomNavigationBarType.fixed,
          items: [
            _buildItem(TabItem.home),
            _buildItem(TabItem.explore),
            _buildItem(TabItem.notice),
            _buildItem(TabItem.profile),
          ],
          currentIndex: bottomTabs[indexController.currentIndex.value]!.index,
          onTap: (index) =>
              indexController.changeTabIndex(TabItem.values[index]),//Get update
        ));
  }

  BottomNavigationBarItem _buildItem(TabItem tabItem) {
    return BottomNavigationBarItem(
      icon: Icon(bottomTabs[tabItem]!.icon),
      label: bottomTabs[tabItem]!.name,
    );
  }
}

그런데 가만보면 전부 bottomTabs라는 List에서 정보나 위젯을 가져오는데 이건 NavigationTree.dart라는 파일에 const로 선언된 List야. 음... 나는 파일 하나에서 전체 네비게이션 트리가 관리되도록 하고 하고 싶었거든 ㅎㅎ 👇

//NavigationTree.dart
import 'package:flutter/material.dart';
import 'package:socialchart/screens/ScreenLogin.dart';
import 'package:socialchart/screens/ScreenSignin.dart';

//Tab navigator
import 'TabNavigator.dart';

//Screens
import 'package:socialchart/screens/ScreenHome.dart';
import 'package:socialchart/screens/ScreenExplore.dart';
import 'package:socialchart/screens/ScreenNotice.dart';
import 'package:socialchart/screens/ScreenProfile.dart';
import 'package:socialchart/screens/ScreenChart.dart';
import 'package:socialchart/screens/ScreenInsightCard.dart';

enum TabItem { home, explore, notice, profile }

class ScreenRoute {
  const ScreenRoute({required this.path, required this.screen});
  final String path;
  final Widget screen;
}

class BottomTab extends StatelessWidget {
  const BottomTab({
    required this.name,
    required this.icon,
    required this.index,
    required this.routes,
  });

  final String name;
  final IconData icon;
  final int index;
  final List<ScreenRoute> routes;
  // final Widget? widget;

  @override
  Widget build(BuildContext context) {
    return TabNavigator(
      routes: routes,
    );
  }
}

const Map<TabItem, BottomTab?> bottomTabs = {
  TabItem.home: BottomTab(
    name: "홈",
    icon: Icons.home,
    index: 0,
    routes: [
      ScreenRoute(path: '/', screen: ScreenHome()),
      ScreenRoute(path: '/ScreenHome', screen: ScreenHome()),
      ScreenRoute(path: '/ScreenInsightCard', screen: ScreenInsightCard()),
      ScreenRoute(path: '/ScreenChart', screen: ScreenChart()),
    ],
  ),
  TabItem.explore: BottomTab(
    name: "탐색",
    icon: Icons.search,
    index: 1,
    routes: [
      ScreenRoute(path: '/', screen: ScreenExplore()),
      ScreenRoute(path: '/ScreenExplore', screen: ScreenExplore()),
    ],
  ),
  TabItem.notice: BottomTab(
    name: "알림",
    icon: Icons.notifications_none,
    index: 2,
    routes: [
      ScreenRoute(path: '/', screen: ScreenNotice()),
      ScreenRoute(path: '/ScreenNotice', screen: ScreenNotice()),
    ],
  ),
  TabItem.profile: BottomTab(
    name: "내정보",
    icon: Icons.person,
    index: 3,
    routes: [
      ScreenRoute(path: '/', screen: ScreenProfile()),
      ScreenRoute(path: '/ScreenProfile', screen: ScreenProfile()),
    ],
  ),
};

const List<ScreenRoute> loginRoutes = [
  ScreenRoute(path: '/', screen: ScreenLogin()),
  ScreenRoute(path: '/ScreenLogin', screen: ScreenLogin()),
  ScreenRoute(path: '/ScreenSignin', screen: ScreenSignin()),
];

여기에 모든 네비게이터 정보가 다 있어. 그래서 뭔가 수정할 때 이 파일 저 파일 왔다갔다 하는 걸 조금 줄여주겠지? 라고 하는 헛된 희망을 가져봤어. 그리고 각 탭의 네비게이터에서 사용될 Screen들을 임시로 만들어줬음. Flutter에서는 Page라는 용어를 사용하는 거 같은데... 난 React Native에서 넘어와서 그런지;;; Screen이 편하더라고 그래서 Screen으로 부름 ㅋㅋ 내 앱임 ㅋ 내 맘임 ㅋ

같은 스크린 테스트

시험 삼아서 홈 탭과 탐색 탭의 첫 화면으로 둘 다 똑같은 ListView를 Home Screen으로 지정해봤어. 같은 스크린을 사용하더라도 각 네비게이션 안에서는 독립적으로 동작하는 걸 확인할 수가 있어! 로그인 Navigator랑 Screen 까지 어느 정도 됐지만 블로깅은 내일!

10월 14일날 시작해서 지금 딱 1주일 지났어. 사실 한달안에 앱스토어 심사 넣는게 목표였는데 ㅎㅎ 가능할까? ㅎ 안되면 말고! 안녕!

2022.10.22 - [Flutter/Flutter Project] - [소셜차트] 앱 제작기 #4. 로그인 시스템 구성

 

[소셜차트] 앱 제작기 #4. 로그인 시스템 구성

2022.10.21 - [Flutter/Flutter Project] - [소셜차트] 앱 제작기 #3. Navigation 시스템 구성 [소셜차트] 앱 제작기 #3. Navigation 시스템 구성 2022.10.14 - [Flutter/Flutter Project] - [소셜차트] 앱 제작기..

engschool.tistory.com

 

반응형