플러터관련 정보/플러터-안드로이드 개발

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

ironwhale 2022. 8. 14. 00:33

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))),
      ),
    );
  }
}