본문 바로가기

[flutter] 코딩파파 당근마켓 클론 코딩 강좌 공부 - Beamer에서 Go_Router로 전환

ironwhale 2022. 8. 14.

Beamer Stackoverflow 에러 발생 

코딩파파 당근마켓 클론코딩을 공부하다 보면 대부분의 사람들이 막히는 부분이 있습니다. 바로 beamer를 사용하는 부분일것입니다. 1.2버전까지는 코딩파파님의 강의대로 하면 되지만 이후 버전에서는 아마 오류가 나면서 이 문제로 코딩파파 슬랙에 비슷한 문제로 고민하고 계신분들이 많습니다. 

 

그러던 중 코딩파파님께서 go_router라는 패키지를 소개 해주셨고 바로 당근마켓 클론 코딩을 beamer에서 go_router로 성공적으로 전환하였습니다. 

 

이전에 간단한 코드로 go_router를 소개한 글이 있으니 참조하시기 바랍니다.

 

 


 

2023.3.23일 기준 go_router 최신 버전을 반영한 포스팅입니다. 

2023.03.23 - [Flutter] - go_router로 화면 이동 구현하기 (로그인 가드 구현 포함)

 

[Flutter] - go_router로 화면 이동 구현하기 (로그인 가드 구현 포함)

go_router는 페이지 이동을 위한 플러터 패키지 고라우터(go_router)는 플러터(flutter)의 화면 전환을 위한 패키지 입니다. 예를 들어 초기 화면에서 어떤 버튼을 누르면 글쓰기 페이지로 이동하는 기

jh-industry.tistory.com

 2022.07.28 - [flutter,beamer] beamer 대신 go_router로 guard pages 구현

 

[flutter,beamer] beamer 대신 go_router로 guard pages 구현

1. beamer 1.3 이후 버전 사용시 stackoverflow 에러발생  아마도 이 에러가 나시는 분은 1.3 이상의 최신 beamer를 사용하시면서 beamGuard의 showpage를 사용하시는 분일거 같습니다.  flutter에는 navigator..

jh-industry.tistory.com

 


필요한 패키지 

provider: ^6.0.3
go_router: ^4.2.2

Beamer를 go_router로 전환크게

  • GoRouter는 크게 routes부분과 redirect 부분으로 나누어집니다. 
  • routes는 [GoRoute(), GoRoute()]의 리스트로 이루어져있고 GoRoute()로 각 위젯으로 통하는 주소를 설정할수 있습니다.
  • redirect는 BeamGuard와 showPage부분을 합쳐놓은 부분으로 생각하시면 되어 각 조건에 맞게 각 페이지(위젯)으로 연결해줍니다. 
final UserNotifier _userNotifier = UserNotifier();

final _router = GoRouter(
    routes: [
      GoRoute(
          name: "home", path: "/", builder: (context, state) => HomeScreen()),
      GoRoute(
          name: "auth",
          path: "/auth",
          builder: (context, state) => StartScreen()),
    ],
    refreshListenable: _userNotifier,
    redirect: (state) {
      final currentPath = state.subloc == '/auth';
      final userState = _userNotifier.user;
      if (userState == null && !currentPath) {
        return '/auth';
      }
      if (userState != null && currentPath) {
        return "/";
      }
      return null;
    });
  • state.subloc로 현재 위치를 찾고 /auth 페이지에 있는지 아닌지 여부를 확인합니다. 
  • userState는 현재 로그인 하고 있는지 여부를 확인합니다. 로그아웃 상태이면 null 입니다. 
  • 로그인을 안하고 현재 /auth 페이지에 있지 않으면 /auth 페이지로 가게 합니다. 
  • 로그인하고 현재 /auth 페이지라면 '/' 으로 가게 됩니다. 

MaterialApp.router 부분 

  • 이부분은 특별히 소개 할 부분이 없어 코드만 적어 두겠습니다. 
  • 다만, 고라우터의 버전이 업그레이드 되면서routeInformationProvider: _router.routeInformationProvider 이부분이 필수로 변경되었습니다. 혹시나 다른 문서에서 없던 것이 왜 있지 하시는분이 계실까봐 적어둡니다. 
class TomatoApp extends StatelessWidget {
  const TomatoApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider<UserNotifier>.value(value: _userNotifier)
      ],
      child: MaterialApp.router(
        routeInformationProvider: _router.routeInformationProvider,
        routeInformationParser: _router.routeInformationParser,
        routerDelegate: _router.routerDelegate,
        theme: ThemeData(
        
        
        ///  생략 ///

 

UserNotifier 부분

이부분은 기존 강의의 코드 내용이랑 같습니다. 

import 'package:carrot_reboot/util/logger.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';

class UserNotifier extends ChangeNotifier {
  User? _user;

  User? get user => _user;

  UserNotifier() {
    initUser();
  }

  void initUser() {
    FirebaseAuth.instance.authStateChanges().listen((user) {
      _user = user;
      logger.d("user - $user");
      notifyListeners();
    });
  }
}

main.dart 전체 코드

부분부분 코드만 보면 헷갈릴수 있으니 전체 코드도 함께 게시하겠습니다.

import 'package:carrot_reboot/firebase_options.dart';
import 'package:carrot_reboot/screens/start_screen.dart';
import 'package:carrot_reboot/screens/home_screen.dart';
import 'package:carrot_reboot/screens/splash_sceen.dart';
import 'package:carrot_reboot/state/user_notifier.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:provider/provider.dart';

final UserNotifier _userNotifier = UserNotifier();

final _router = GoRouter(
    routes: [
      GoRoute(
          name: "home", path: "/", builder: (context, state) => HomeScreen()),
      GoRoute(
          name: "auth",
          path: "/auth",
          builder: (context, state) => StartScreen()),
    ],
    refreshListenable: _userNotifier,
    redirect: (state) {
      final currentPath = state.subloc == '/auth';
      final userState = _userNotifier.user;
      if (userState == null && !currentPath) {
        return '/auth';
      }
      if (userState != null && currentPath) {
        return "/";
      }
      return null;
    });

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  Provider.debugCheckInvalidValueType = null;
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  MyApp({Key? key}) : super(key: key);

  final Future<FirebaseApp> initializeFireBase = Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
        future: initializeFireBase,
        builder: (_, snapshot) {
          return AnimatedSwitcher(
              duration: const Duration(milliseconds: 300),
              child: splashLoadingWidget(snapshot));
        });
  }

  StatelessWidget splashLoadingWidget(AsyncSnapshot<Object?> snapshot) {
    if (snapshot.connectionState == ConnectionState.done) {
      return const TomatoApp();
    } else {
      return const SplashScreen();
    }
  }
}

class TomatoApp extends StatelessWidget {
  const TomatoApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider<UserNotifier>.value(value: _userNotifier)
      ],
      child: MaterialApp.router(
        routeInformationProvider: _router.routeInformationProvider,
        routeInformationParser: _router.routeInformationParser,
        routerDelegate: _router.routerDelegate,
        theme: ThemeData(
            bottomNavigationBarTheme: BottomNavigationBarThemeData(
                selectedItemColor: Colors.black87,
                unselectedItemColor: Colors.black45),
            appBarTheme: AppBarTheme(
              iconTheme: const IconThemeData(color: Colors.black87),
              backgroundColor: Colors.white,
              titleTextStyle: Theme.of(context)
                  .textTheme
                  .headline6!
                  .copyWith(color: Colors.black87, fontWeight: FontWeight.bold),
              elevation: 2,
            ),
            textButtonTheme: TextButtonThemeData(
                style: TextButton.styleFrom(
                    minimumSize: const Size(48, 48),
                    backgroundColor: Colors.red,
                    primary: Colors.white)),
            hintColor: Colors.grey[350],
            fontFamily: "HappyFont",
            primarySwatch: Colors.red,
            textTheme: const TextTheme(
                subtitle1: TextStyle(color: Colors.black87, fontSize: 15),
                subtitle2: TextStyle(color: Colors.grey, fontSize: 13),
                button: TextStyle(color: Colors.white))),
      ),
    );
  }
}

댓글