[Flutter] GoRouter 공부

Hee Tae Shin·2023년 2월 13일
4

Flutter

목록 보기
7/16
post-thumbnail

네비게이터의 진화버전!

나중에 참고하면 좋은 사이트
https://www.kodeco.com/flutter/paths/flutter-fundamentals

GoRouter

  • GoRote : 각각의 페이지로 가는 길
  • redirect : 옵션, 조건문을 걸어서 다른 길로 이동
class MyRouter {
	// 로그인 상태 
  final LoginState loginState;

  MyRouter(this.loginState);

// 기본 Router()
  late final router = GoRouter(
    // 각페이지의 루트 포함
    routes: [],
    // loginState 상태를 지켜본다.
    refreshListenable: loginState,
    // 개발시 디버그하기, 앱출시에는 false 로 변경해야한다.
    debugLogDiagnostics: true,
  );
}

errorhandler

late final router = GoRouter(
    // 길 들
    routes: [
      // 길 등록
      GoRoute(
        // 길
        path: '/login',
        // 길 이름 (겹치면 안된다.)
        name: loginRouteName,
        // 도착치 
        builder: (context, state) {
          return const Login();
        },
      ),
      // 길 등록,
      // 길 등록,
    ],
    refreshListenable: loginState,
    debugLogDiagnostics: true,
  );

에러 핸들러 작성

late final router = GoRouter(
	errorBuilder: (context, state) {
    	// 에러 페이지출력
    	return ErrorPage(error: state.error)
    }
    errorPageBuilder: (context, state) {
    	return MaterialPage<void>(child: ErrorPage());
    }
	...
);

errorBuilder 와 errorPageBuilder 차이

결과적으로 같다.
기본적인 트랜지션을 사용할때는 errorBuilder 사용
커스텀 트랜지션은 errorPageBuilder 사용한다.
errorPagebuilder 같은 경우는 MaterialPage처럼컨트롤 가능해진다.

go() 와 goNamed() 와 pushNamed() 차이

context.go('/login');
context.goNamed(routeName);
context.pushNamed(routeName);

go 는 라우트 스택에 안쌓인다.
push 는 라우트 스택에 쌓인다. 그래서 뒤로가기 버튼 생김

redirect

어떤 경로로 다시 보내버리겠다. 라는 의미이다.
refreshListenable: loginState
loginState 의 상태가 변경되어 리프래쉬 되는 것을 읽겠다.

GoRouter(
	  ...
      routes: [라우트들],
      redirect: (context, state) {
      // 현재 우리가 보고있는 페이지 확인
      // 현재 로그인 상태 어떤지 확인
      final loggedIn = loginState.loggedIn;
      // state.subloc : 현재 위치해 있는 쿼리파라미터를 리턴한다.
      final inAuthPages = state.subloc.contains(loginRouteName) ||
          state.subloc.contains(createAccountRouteName);

      // inAuth && true => go to home
      if(inAuthPages && loggedIn) return '/';
      // noInAuth && false => go to loginPage
      if(!inAuthPages && !loggedIn) return '/login';
    },
	// loginState 상태를 지켜본다.
    refreshListenable: loginState,
)

경로에서 매개변수 전달 받기

HomeScreen() 에는 tab 을 받아서 바텀시트를 변경하는데, 하드코딩으로 하지말고, state의 파람을 매개변수를 받아 전달하면 동적으로 잘 전달된다.

GoRoute(
        path: '/',
        name: rootRouteName,
        builder: (context, state) {
          return HomeScreen(tab: 'shopping');
        },
      ),

경로의 매개변수 받기!

GoRoute(
        path: '/:tab',
        name: rootRouteName,
        builder: (context, state) {
          final tab = state.params['tab'];
          // 빈값을 
          return HomeScreen(tab: tab ?? '');
        },
      ),

서브 경로 넣어주기

기존 경로에 서브 경로를 넣어줄 수있다.

GoRoute(
          path: '/:tab',
          name: rootRouteName,
          builder: (context, state) {
            final tab = state.params['tab'];
            return HomeScreen(tab: tab ?? '');
          },
          routes: [
            GoRoute(
              name: profilePersonalRouteName,
              path: 'personal',
              builder: (context, state) {
                return const PersonalInfo();
              },
            ),
          ]),
/:tab/profile-personal
/profile/profile-personal
   onTap: () {
                // TODO: Add Personal Page Route
                context.pushNamed(
                  profilePersonalRouteName,
                  // /:tab 을 넣어줘야 에러가 안난다.
                  params: {'tab': 'profile'},
                );
              },

debugLogDiagnostics: true, 했을시 나오는 bash

[GoRouter] Full paths for routes:
  => /login
  => /create-account
  => /:tab
  =>   /:tab/personal
known full paths for route names:
  login => /login
  create-account => /create-account
  root => /:tab
  profile-personal => /:tab/personal

[GoRouter] setting initial location /login
[GoRouter] redirecting to RouteMatchList(/:tab)
[GoRouter] Using MaterialApp configuration

여기서 문제점 해결

바텀 모델을 클릭해도 위의 경로가 바뀌지 않는다.
크롬으로 확인해보자!

바텀 시트 코드 변경

onTap: (index) {
          switch (index) {
            case 0:
              context.go('/shop');
              break;
            case 1:
              context.go('/cart');
              break;
            case 2:
              context.go('/profile');
              break;
          }

해결

서브라우트 연결

GoRoute(
	name: shopDetailsRouteName,
	path: 'details/:item',
	builder: (context, state) {
		return Details(description: state.params['item']!);
	},
),
onTap: () {
	final value = items[index];
	context.goNamed(
	shopDetailsRouteName,
	params: {'tab': 'shop', 'item': value},
);

결과

이런식으로 profile 의 서브 경로도 연결해주자!

context.pushNamed(
	profilePersonalRouteName,
    params: {'tab': 'profile'},
);

로그아웃 기능도 적용

ListTile(
	title: Text(
		'log out',
	style: TextStyle(fontWeight: FontWeight.bold),
),
	onTap: () {
		logOut(context);
	},
),

// 로그아웃 함수
void logOut(BuildContext context) {
    Provider.of<LoginState>(context, listen: false).loggedIn = false;
  }

그리고 goRouter 등록할때, redirect를 하기

redirect: (context, state) {
      // 현재 우리가 보고있는 페이지 확인
      // 현재 로그인 상태 어떤지 확인
      final loggedIn = loginState.loggedIn;
      // state.subloc : 현재 위치해 있는 쿼리파라미터를 리턴한다.
      final inAuthPages = state.subloc.contains(loginRouteName) ||
          state.subloc.contains(createAccountRouteName);

      // inAuth && true => go to home
      if (inAuthPages && loggedIn) return '/shop';
      // noInAuth && false => go to loginPage
      if (!inAuthPages && !loggedIn) return '/login';
    },

라우터를 사용할때 데이터 전달하는 법

전달 하는 곳

context.goNamed(
	shopDetailsRouteName,
	params: {'tab': 'shop', 'item': value},
    // 어떤 Object? 든 보낼수 있다.
	extra: 'TestText - $value'
);

전달 받는 라우터 설정

GoRoute(
	name: shopDetailsRouteName,
	path: 'details/:item',
	builder: (context, state) {
		return Details(
        	description: state.params['item']!, 
            // extra 로 던져주기
        	extra: state.extra,
        );
	},
),

전달 받는 곳

class Details extends StatelessWidget {
  final Object? extra;

	Details({..., this.extra});
    
   return(
   ...
   	// String 인거는 이미 아니깐, 캐스팅해준다.
   	Text('extra- ${extra as String}'),
   )
profile
안녕하세요

1개의 댓글

comment-user-thumbnail
2023년 7월 10일

정리가 참 잘되어있네요 감사합니다!!

답글 달기