[flutter] 코딩파파 당근마켓 클론 코딩 강좌 공부 - Beamer에서 Go_Router로 전환
Beamer Stackoverflow 에러 발생
코딩파파 당근마켓 클론코딩을 공부하다 보면 대부분의 사람들이 막히는 부분이 있습니다. 바로 beamer를 사용하는 부분일것입니다. 1.2버전까지는 코딩파파님의 강의대로 하면 되지만 이후 버전에서는 아마 오류가 나면서 이 문제로 코딩파파 슬랙에 비슷한 문제로 고민하고 계신분들이 많습니다.
그러던 중 코딩파파님께서 go_router라는 패키지를 소개 해주셨고 바로 당근마켓 클론 코딩을 beamer에서 go_router로 성공적으로 전환하였습니다.
이전에 간단한 코드로 go_router를 소개한 글이 있으니 참조하시기 바랍니다.
2023.3.23일 기준 go_router 최신 버전을 반영한 포스팅입니다.
2023.03.23 - [Flutter] - go_router로 화면 이동 구현하기 (로그인 가드 구현 포함)
2022.07.28 - [flutter,beamer] beamer 대신 go_router로 guard pages 구현
필요한 패키지
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))),
),
);
}
}
댓글