본문 바로가기

Flutter/12 Clone 'Used Goods app'

[Flutter] Clone - 당근마켓8(authorization-login)

이번에는 auth page 에서 인증 성공후 Home Screen 으로 이동하는 기능을 추가해보겠습니다

(실제 사용자관리는 차후 예정).

개발환경 : 윈도우11, 안드로이드 스튜디오, flutter 3.0.1

 

 

 

기존에 네비게이션을 beamer 와 getx 로 이중으로 진행하였는데, beamer 보다 getx 가 가성비가 좋은거 같아서 이후로는 getx 로 네비게이션을 구현할 예정입니다.

특히, beamer 의 BeamGuard 의 기능은 GetX 에서는 middlewares 로 구현 가능하다.

그래서 상태관리도 GetX 로 구현할 예정입니다.

 

 

 

화면 흐름은 아래와 같다.

 

 

 

./src/apple_app.dart - refactorying 및 initialBinding 추가

 

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

import 'bindings/init_binding.dart';
import 'constants/common_size.dart';
import 'router/locations.dart';
import 'screens/start_screen.dart';
import 'states/user_state.dart';

// beamer 관련
final _routerDelegate = BeamerDelegate(
  guards: [
    BeamGuard(
      // '/' 루트 페이지로 이동을 시도하기 전에 check 를 확인하고
      // check 의 리턴값이 ture 면 계속 진행(루트 페이지/HomeScreen() 으로 이동)
      // check 의 리턴값이 false 면 beamToNamed 으로 이동, '/auth'(AuthLocation) 으로 이동
      pathPatterns: ['/'],
      check: (context, location) {
        return context.watch<UserProvider>().userState;
        // return context.watch<UserNotifier>().user != null;
      },
      beamToNamed: (origin, target) => '/auth',
      // showPage: BeamPage(child: StartScreen()),
    )
  ],
  locationBuilder: BeamerLocationBuilder(beamLocations: [
    HomeLocation(),
    AuthLocation(), // 이게 없으면 beamToNamed 이동시 오류 발생함
  ]),
);

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

  // themeData 공통사용하기 위해서
  final themeData = ThemeData(
    primarySwatch: Colors.red,
    // 이걸 대표로 설정하면 기본 분위기가 유사하게 적용
    fontFamily: 'Dohyeon',
    // 배달의민족 도현체
    hintColor: Colors.grey[350],
    textTheme: const TextTheme(
      headline3: TextStyle(
        fontFamily: 'Dohyeon',
        fontWeight: FontWeight.bold,
        color: Colors.blue,
      ),
      button: TextStyle(color: Colors.white),
      subtitle1: TextStyle(color: Colors.black87, fontSize: 15),
      subtitle2: TextStyle(color: Colors.grey, fontSize: 13),
      bodyText1: TextStyle(
          color: Colors.black87, fontSize: 12, fontWeight: FontWeight.normal),
      bodyText2: TextStyle(
          color: Colors.black54, fontSize: 12, fontWeight: FontWeight.w100),
    ),
    // inputDecorationTheme: const InputDecorationTheme(
    //   enabledBorder: UnderlineInputBorder(
    //     borderSide: BorderSide(color: Colors.transparent),
    //   ),
    // ),
    textButtonTheme: TextButtonThemeData(
      style: TextButton.styleFrom(
          backgroundColor: Colors.red,
          primary: Colors.white,
          minimumSize: const Size(10, 48)),
    ),
    appBarTheme: const AppBarTheme(
      backgroundColor: Colors.white,
      foregroundColor: Colors.black87,
      elevation: 2,
      titleTextStyle: TextStyle(color: Colors.black87),
      actionsIconTheme: IconThemeData(color: Colors.black87),
    ),
    bottomNavigationBarTheme: const BottomNavigationBarThemeData(
      selectedItemColor: Colors.black87,
      unselectedItemColor: Colors.black54,
    ),
  );

  @override
  Widget build(BuildContext context) {
// 여기에서 라우팅 방식을 설정함
    if (routerType == RouterType.beamer) {
      // beamer 라우팅시 설정방법
      return beamRouter(context);
    }
    // getx 라우팅시 설정방법
    return getRouter();
  }

  // getx 라우팅시 설정방법
  GetMaterialApp getRouter() {
    return GetMaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Apple Market Demo',
      theme: themeData,
      getPages: getPages(),
      // 전체에 필요한 Controller 를 초기 binding 에 추가함
      initialBinding: BindingInjection(),
    );
  }

  // beamer 라우팅시 설정방법
  ChangeNotifierProvider<UserProvider> beamRouter(BuildContext context) {
    return ChangeNotifierProvider<UserProvider>(
      create: (BuildContext context) => UserProvider(),
      child: MaterialApp.router(
        debugShowCheckedModeBanner: false,
        title: 'Apple Market Demo',
        theme: themeData,
        routeInformationParser: BeamerParser(),
        routerDelegate: _routerDelegate,
      ),
    );
  }
}

 

 

./src/middleware/check_auth.dart 

 

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

import '../states/user_state.dart';

class CheckAuth extends GetMiddleware {
  @override
  int? get priority => 2;

  bool isAuthenticated = false;

  @override
  RouteSettings? redirect(String? route) {
    // injection & binding
    Get.put(UserController());
    isAuthenticated = UserController.to.userState.value;
    if (isAuthenticated == false) {
      return const RouteSettings(name: '/auth');
    }
    return null;
  }
}

 

 

./src/screens/start/auth_page.dart - 주요 수정은 attemptVarify() 함수 관련 부분

 

void attemptVarify(BuildContext context) async {
  setState(() {
    // 인증 진행중
    _verificationStatus = VerificationStatus.verifying;
  });
  debugPrint('_verificationStatus(attemptVarify): $_verificationStatus');

  // 강제 딜레이 추가
  await Future.delayed(const Duration(seconds: 2));

  setState(() {
    // 인증 완료
    _verificationStatus = VerificationStatus.verificationDone;
  });

  if(routerType == RouterType.beamer){
    context.read<UserProvider>().setUserAuth(true);
    debugPrint('*** userState(attemptVarify): ${context.read<UserProvider>().userState}');
  } else {
    UserController.to.setUserAuth(true);
    Get.toNamed('/');
  }

  debugPrint('_verificationStatus(attemptVarify): $_verificationStatus');
}

 

 

./src/bindings/init_binding.dart - 유저 정보는 모든 곳에서 접근해야하므로 initialBinding 에 추가

 

import 'package:get/get.dart';

import '../states/user_state.dart';

class BindingInjection implements Bindings {
  @override
  void dependencies() {
    // TODO: implement dependencies
    Get.put(UserController());
  }
}

 

 

./src/states/user_state.dart 

 

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

class UserController extends GetxController {
  static UserController get to => Get.find();

  final RxBool _userLoggedIn = false.obs;

  RxBool get userState => _userLoggedIn;

  void setUserAuth(bool authState) {
    _userLoggedIn(authState);
  }
}

class UserProvider extends ChangeNotifier {
  bool _userLoggedIn = false;

  bool get userState => _userLoggedIn;

  void setUserAuth(bool authState) {
    _userLoggedIn = authState;
    notifyListeners();
  }
}