feat: API 인증 시스템 구현 및 로그인 화면 연동

- AuthService, AuthRemoteDataSource 구현
- JWT 토큰 관리 (SecureStorage 사용)
- 로그인 화면 API 연동 및 에러 처리
- freezed 패키지로 Auth 관련 DTO 모델 생성
- 의존성 주입 설정 업데이트
This commit is contained in:
JiWoong Sul
2025-07-24 15:14:53 +09:00
parent 2b31d3af5f
commit c573096d84
26 changed files with 2063 additions and 59 deletions

View File

@@ -175,22 +175,22 @@ class EquipmentController extends ChangeNotifier {
- POST /api/v1/auth/refresh - POST /api/v1/auth/refresh
**작업 Task**: **작업 Task**:
- [ ] AuthService 클래스 생성 - [x] AuthService 클래스 생성
- [ ] JWT 토큰 저장/관리 로직 구현 - [x] JWT 토큰 저장/관리 로직 구현
- [x] SecureStorage 설정 - [x] SecureStorage 설정
- [ ] Access Token 저장 - [x] Access Token 저장
- [ ] Refresh Token 저장 - [x] Refresh Token 저장
- [ ] 로그인 폼 검증 추가 - [x] 로그인 폼 검증 추가
- [ ] 이메일 형식 검증 - [x] 이메일 형식 검증
- [ ] 비밀번호 최소 길이 검증 - [x] 비밀번호 최소 길이 검증
- [ ] 로그인 실패 에러 처리 - [x] 로그인 실패 에러 처리
- [ ] 401: 잘못된 인증 정보 - [x] 401: 잘못된 인증 정보
- [ ] 429: 너무 많은 시도 - [ ] 429: 너무 많은 시도
- [ ] 500: 서버 오류 - [x] 500: 서버 오류
- [ ] 자동 로그인 구현 - [ ] 자동 로그인 구현
- [ ] 토큰 유효성 검사 - [ ] 토큰 유효성 검사
- [ ] 토큰 자동 갱신 - [ ] 토큰 자동 갱신
- [ ] 로그아웃 기능 구현 - [x] 로그아웃 기능 구현
### 4.2 대시보드 ### 4.2 대시보드
@@ -692,13 +692,13 @@ class ErrorHandler {
**1주차: 네트워크 레이어** **1주차: 네트워크 레이어**
- [x] Dio 설정 및 인터셉터 구현 - [x] Dio 설정 및 인터셉터 구현
- [x] API 클라이언트 기본 구조 - [x] API 클라이언트 기본 구조
- [ ] 에러 처리 프레임워크 - [x] 에러 처리 프레임워크
- [x] 환경 설정 관리 - [x] 환경 설정 관리
**2주차: 인증 시스템** **2주차: 인증 시스템** *(2025-07-24 진행)*
- [ ] AuthService 구현 - [x] AuthService 구현
- [ ] 토큰 관리 로직 - [x] 토큰 관리 로직
- [ ] 로그인/로그아웃 화면 연동 - [x] 로그인/로그아웃 화면 연동
- [ ] 자동 토큰 갱신 - [ ] 자동 토큰 갱신
**3주차: 기본 데이터 레이어** **3주차: 기본 데이터 레이어**
@@ -890,4 +890,39 @@ class ErrorHandler {
--- ---
_마지막 업데이트: 2025-07-24_ (네트워크 레이어 및 기본 인프라 구현 완료) ## 🔄 구현 진행 상황 (2025-07-24)
### 완료된 작업
1. **Auth 관련 DTO 모델 생성**
- LoginRequest, LoginResponse, TokenResponse, RefreshTokenRequest
- AuthUser, LogoutRequest
- Freezed 패키지 적용 및 코드 생성 완료
2. **AuthRemoteDataSource 구현**
- login, logout, refreshToken 메서드 구현
- 에러 처리 및 응답 변환 로직 완료
3. **AuthService 구현**
- 토큰 저장/관리 (SecureStorage 사용)
- 로그인 상태 관리 및 스트림
- 자동 토큰 갱신 준비
4. **로그인 화면 API 연동**
- LoginController 수정 (API 호출 로직 추가)
- 이메일 형식 검증 및 에러 메시지 표시
- 로딩 상태 관리
5. **의존성 주입 설정**
- AuthRemoteDataSource, AuthService DI 등록
- GetIt을 통한 의존성 관리
### 다음 작업
1. API 서버 실행 및 연동 테스트
2. 자동 로그인 구현
3. AuthInterceptor 개선 (AuthService 사용)
4. 로그아웃 기능 UI 추가
5. 대시보드 및 기타 화면 API 연동
---
_마지막 업데이트: 2025-07-24_ (인증 시스템 구현 완료)

View File

@@ -0,0 +1,104 @@
import 'package:dartz/dartz.dart';
import 'package:injectable/injectable.dart';
import 'package:superport/core/errors/exceptions.dart';
import 'package:superport/core/errors/failures.dart';
import 'package:superport/data/datasources/remote/api_client.dart';
import 'package:superport/data/models/auth/login_request.dart';
import 'package:superport/data/models/auth/login_response.dart';
import 'package:superport/data/models/auth/logout_request.dart';
import 'package:superport/data/models/auth/refresh_token_request.dart';
import 'package:superport/data/models/auth/token_response.dart';
abstract class AuthRemoteDataSource {
Future<Either<Failure, LoginResponse>> login(LoginRequest request);
Future<Either<Failure, void>> logout(LogoutRequest request);
Future<Either<Failure, TokenResponse>> refreshToken(RefreshTokenRequest request);
}
@LazySingleton(as: AuthRemoteDataSource)
class AuthRemoteDataSourceImpl implements AuthRemoteDataSource {
final ApiClient _apiClient;
AuthRemoteDataSourceImpl(this._apiClient);
@override
Future<Either<Failure, LoginResponse>> login(LoginRequest request) async {
try {
final response = await _apiClient.post(
'/auth/login',
data: request.toJson(),
);
if (response.success && response.data != null) {
final loginResponse = LoginResponse.fromJson(response.data);
return Right(loginResponse);
} else {
return Left(ServerFailure(
message: response.error?.message ?? '로그인 실패',
));
}
} catch (e) {
if (e is ApiException) {
if (e.statusCode == 401) {
return Left(AuthenticationFailure(
message: '이메일 또는 비밀번호가 올바르지 않습니다.',
));
}
return Left(ServerFailure(message: e.message));
}
return Left(ServerFailure(message: '로그인 중 오류가 발생했습니다.'));
}
}
@override
Future<Either<Failure, void>> logout(LogoutRequest request) async {
try {
final response = await _apiClient.post(
'/auth/logout',
data: request.toJson(),
);
if (response.success) {
return const Right(null);
} else {
return Left(ServerFailure(
message: response.error?.message ?? '로그아웃 실패',
));
}
} catch (e) {
if (e is ApiException) {
return Left(ServerFailure(message: e.message));
}
return Left(ServerFailure(message: '로그아웃 중 오류가 발생했습니다.'));
}
}
@override
Future<Either<Failure, TokenResponse>> refreshToken(RefreshTokenRequest request) async {
try {
final response = await _apiClient.post(
'/auth/refresh',
data: request.toJson(),
);
if (response.success && response.data != null) {
final tokenResponse = TokenResponse.fromJson(response.data);
return Right(tokenResponse);
} else {
return Left(ServerFailure(
message: response.error?.message ?? '토큰 갱신 실패',
));
}
} catch (e) {
if (e is ApiException) {
if (e.statusCode == 401) {
return Left(AuthenticationFailure(
message: '인증이 만료되었습니다. 다시 로그인해주세요.',
));
}
return Left(ServerFailure(message: e.message));
}
return Left(ServerFailure(message: '토큰 갱신 중 오류가 발생했습니다.'));
}
}
}

View File

@@ -0,0 +1,18 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'auth_user.freezed.dart';
part 'auth_user.g.dart';
@freezed
class AuthUser with _$AuthUser {
const factory AuthUser({
required int id,
required String email,
@JsonKey(name: 'first_name') required String firstName,
@JsonKey(name: 'last_name') required String lastName,
required String role,
}) = _AuthUser;
factory AuthUser.fromJson(Map<String, dynamic> json) =>
_$AuthUserFromJson(json);
}

View File

@@ -0,0 +1,256 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'auth_user.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
AuthUser _$AuthUserFromJson(Map<String, dynamic> json) {
return _AuthUser.fromJson(json);
}
/// @nodoc
mixin _$AuthUser {
int get id => throw _privateConstructorUsedError;
String get email => throw _privateConstructorUsedError;
@JsonKey(name: 'first_name')
String get firstName => throw _privateConstructorUsedError;
@JsonKey(name: 'last_name')
String get lastName => throw _privateConstructorUsedError;
String get role => throw _privateConstructorUsedError;
/// Serializes this AuthUser to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of AuthUser
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$AuthUserCopyWith<AuthUser> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $AuthUserCopyWith<$Res> {
factory $AuthUserCopyWith(AuthUser value, $Res Function(AuthUser) then) =
_$AuthUserCopyWithImpl<$Res, AuthUser>;
@useResult
$Res call(
{int id,
String email,
@JsonKey(name: 'first_name') String firstName,
@JsonKey(name: 'last_name') String lastName,
String role});
}
/// @nodoc
class _$AuthUserCopyWithImpl<$Res, $Val extends AuthUser>
implements $AuthUserCopyWith<$Res> {
_$AuthUserCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of AuthUser
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? id = null,
Object? email = null,
Object? firstName = null,
Object? lastName = null,
Object? role = null,
}) {
return _then(_value.copyWith(
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int,
email: null == email
? _value.email
: email // ignore: cast_nullable_to_non_nullable
as String,
firstName: null == firstName
? _value.firstName
: firstName // ignore: cast_nullable_to_non_nullable
as String,
lastName: null == lastName
? _value.lastName
: lastName // ignore: cast_nullable_to_non_nullable
as String,
role: null == role
? _value.role
: role // ignore: cast_nullable_to_non_nullable
as String,
) as $Val);
}
}
/// @nodoc
abstract class _$$AuthUserImplCopyWith<$Res>
implements $AuthUserCopyWith<$Res> {
factory _$$AuthUserImplCopyWith(
_$AuthUserImpl value, $Res Function(_$AuthUserImpl) then) =
__$$AuthUserImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{int id,
String email,
@JsonKey(name: 'first_name') String firstName,
@JsonKey(name: 'last_name') String lastName,
String role});
}
/// @nodoc
class __$$AuthUserImplCopyWithImpl<$Res>
extends _$AuthUserCopyWithImpl<$Res, _$AuthUserImpl>
implements _$$AuthUserImplCopyWith<$Res> {
__$$AuthUserImplCopyWithImpl(
_$AuthUserImpl _value, $Res Function(_$AuthUserImpl) _then)
: super(_value, _then);
/// Create a copy of AuthUser
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? id = null,
Object? email = null,
Object? firstName = null,
Object? lastName = null,
Object? role = null,
}) {
return _then(_$AuthUserImpl(
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int,
email: null == email
? _value.email
: email // ignore: cast_nullable_to_non_nullable
as String,
firstName: null == firstName
? _value.firstName
: firstName // ignore: cast_nullable_to_non_nullable
as String,
lastName: null == lastName
? _value.lastName
: lastName // ignore: cast_nullable_to_non_nullable
as String,
role: null == role
? _value.role
: role // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
@JsonSerializable()
class _$AuthUserImpl implements _AuthUser {
const _$AuthUserImpl(
{required this.id,
required this.email,
@JsonKey(name: 'first_name') required this.firstName,
@JsonKey(name: 'last_name') required this.lastName,
required this.role});
factory _$AuthUserImpl.fromJson(Map<String, dynamic> json) =>
_$$AuthUserImplFromJson(json);
@override
final int id;
@override
final String email;
@override
@JsonKey(name: 'first_name')
final String firstName;
@override
@JsonKey(name: 'last_name')
final String lastName;
@override
final String role;
@override
String toString() {
return 'AuthUser(id: $id, email: $email, firstName: $firstName, lastName: $lastName, role: $role)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$AuthUserImpl &&
(identical(other.id, id) || other.id == id) &&
(identical(other.email, email) || other.email == email) &&
(identical(other.firstName, firstName) ||
other.firstName == firstName) &&
(identical(other.lastName, lastName) ||
other.lastName == lastName) &&
(identical(other.role, role) || other.role == role));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode =>
Object.hash(runtimeType, id, email, firstName, lastName, role);
/// Create a copy of AuthUser
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$AuthUserImplCopyWith<_$AuthUserImpl> get copyWith =>
__$$AuthUserImplCopyWithImpl<_$AuthUserImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$AuthUserImplToJson(
this,
);
}
}
abstract class _AuthUser implements AuthUser {
const factory _AuthUser(
{required final int id,
required final String email,
@JsonKey(name: 'first_name') required final String firstName,
@JsonKey(name: 'last_name') required final String lastName,
required final String role}) = _$AuthUserImpl;
factory _AuthUser.fromJson(Map<String, dynamic> json) =
_$AuthUserImpl.fromJson;
@override
int get id;
@override
String get email;
@override
@JsonKey(name: 'first_name')
String get firstName;
@override
@JsonKey(name: 'last_name')
String get lastName;
@override
String get role;
/// Create a copy of AuthUser
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$AuthUserImplCopyWith<_$AuthUserImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@@ -0,0 +1,25 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'auth_user.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$AuthUserImpl _$$AuthUserImplFromJson(Map<String, dynamic> json) =>
_$AuthUserImpl(
id: (json['id'] as num).toInt(),
email: json['email'] as String,
firstName: json['first_name'] as String,
lastName: json['last_name'] as String,
role: json['role'] as String,
);
Map<String, dynamic> _$$AuthUserImplToJson(_$AuthUserImpl instance) =>
<String, dynamic>{
'id': instance.id,
'email': instance.email,
'first_name': instance.firstName,
'last_name': instance.lastName,
'role': instance.role,
};

View File

@@ -0,0 +1,15 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'login_request.freezed.dart';
part 'login_request.g.dart';
@freezed
class LoginRequest with _$LoginRequest {
const factory LoginRequest({
required String email,
required String password,
}) = _LoginRequest;
factory LoginRequest.fromJson(Map<String, dynamic> json) =>
_$LoginRequestFromJson(json);
}

View File

@@ -0,0 +1,183 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'login_request.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
LoginRequest _$LoginRequestFromJson(Map<String, dynamic> json) {
return _LoginRequest.fromJson(json);
}
/// @nodoc
mixin _$LoginRequest {
String get email => throw _privateConstructorUsedError;
String get password => throw _privateConstructorUsedError;
/// Serializes this LoginRequest to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of LoginRequest
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$LoginRequestCopyWith<LoginRequest> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $LoginRequestCopyWith<$Res> {
factory $LoginRequestCopyWith(
LoginRequest value, $Res Function(LoginRequest) then) =
_$LoginRequestCopyWithImpl<$Res, LoginRequest>;
@useResult
$Res call({String email, String password});
}
/// @nodoc
class _$LoginRequestCopyWithImpl<$Res, $Val extends LoginRequest>
implements $LoginRequestCopyWith<$Res> {
_$LoginRequestCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of LoginRequest
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? email = null,
Object? password = null,
}) {
return _then(_value.copyWith(
email: null == email
? _value.email
: email // ignore: cast_nullable_to_non_nullable
as String,
password: null == password
? _value.password
: password // ignore: cast_nullable_to_non_nullable
as String,
) as $Val);
}
}
/// @nodoc
abstract class _$$LoginRequestImplCopyWith<$Res>
implements $LoginRequestCopyWith<$Res> {
factory _$$LoginRequestImplCopyWith(
_$LoginRequestImpl value, $Res Function(_$LoginRequestImpl) then) =
__$$LoginRequestImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({String email, String password});
}
/// @nodoc
class __$$LoginRequestImplCopyWithImpl<$Res>
extends _$LoginRequestCopyWithImpl<$Res, _$LoginRequestImpl>
implements _$$LoginRequestImplCopyWith<$Res> {
__$$LoginRequestImplCopyWithImpl(
_$LoginRequestImpl _value, $Res Function(_$LoginRequestImpl) _then)
: super(_value, _then);
/// Create a copy of LoginRequest
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? email = null,
Object? password = null,
}) {
return _then(_$LoginRequestImpl(
email: null == email
? _value.email
: email // ignore: cast_nullable_to_non_nullable
as String,
password: null == password
? _value.password
: password // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
@JsonSerializable()
class _$LoginRequestImpl implements _LoginRequest {
const _$LoginRequestImpl({required this.email, required this.password});
factory _$LoginRequestImpl.fromJson(Map<String, dynamic> json) =>
_$$LoginRequestImplFromJson(json);
@override
final String email;
@override
final String password;
@override
String toString() {
return 'LoginRequest(email: $email, password: $password)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$LoginRequestImpl &&
(identical(other.email, email) || other.email == email) &&
(identical(other.password, password) ||
other.password == password));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType, email, password);
/// Create a copy of LoginRequest
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$LoginRequestImplCopyWith<_$LoginRequestImpl> get copyWith =>
__$$LoginRequestImplCopyWithImpl<_$LoginRequestImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$LoginRequestImplToJson(
this,
);
}
}
abstract class _LoginRequest implements LoginRequest {
const factory _LoginRequest(
{required final String email,
required final String password}) = _$LoginRequestImpl;
factory _LoginRequest.fromJson(Map<String, dynamic> json) =
_$LoginRequestImpl.fromJson;
@override
String get email;
@override
String get password;
/// Create a copy of LoginRequest
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$LoginRequestImplCopyWith<_$LoginRequestImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@@ -0,0 +1,19 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'login_request.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$LoginRequestImpl _$$LoginRequestImplFromJson(Map<String, dynamic> json) =>
_$LoginRequestImpl(
email: json['email'] as String,
password: json['password'] as String,
);
Map<String, dynamic> _$$LoginRequestImplToJson(_$LoginRequestImpl instance) =>
<String, dynamic>{
'email': instance.email,
'password': instance.password,
};

View File

@@ -0,0 +1,19 @@
import 'package:freezed_annotation/freezed_annotation.dart';
import 'auth_user.dart';
part 'login_response.freezed.dart';
part 'login_response.g.dart';
@freezed
class LoginResponse with _$LoginResponse {
const factory LoginResponse({
@JsonKey(name: 'access_token') required String accessToken,
@JsonKey(name: 'refresh_token') required String refreshToken,
@JsonKey(name: 'token_type') required String tokenType,
@JsonKey(name: 'expires_in') required int expiresIn,
required AuthUser user,
}) = _LoginResponse;
factory LoginResponse.fromJson(Map<String, dynamic> json) =>
_$LoginResponseFromJson(json);
}

View File

@@ -0,0 +1,280 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'login_response.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
LoginResponse _$LoginResponseFromJson(Map<String, dynamic> json) {
return _LoginResponse.fromJson(json);
}
/// @nodoc
mixin _$LoginResponse {
@JsonKey(name: 'access_token')
String get accessToken => throw _privateConstructorUsedError;
@JsonKey(name: 'refresh_token')
String get refreshToken => throw _privateConstructorUsedError;
@JsonKey(name: 'token_type')
String get tokenType => throw _privateConstructorUsedError;
@JsonKey(name: 'expires_in')
int get expiresIn => throw _privateConstructorUsedError;
AuthUser get user => throw _privateConstructorUsedError;
/// Serializes this LoginResponse to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of LoginResponse
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$LoginResponseCopyWith<LoginResponse> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $LoginResponseCopyWith<$Res> {
factory $LoginResponseCopyWith(
LoginResponse value, $Res Function(LoginResponse) then) =
_$LoginResponseCopyWithImpl<$Res, LoginResponse>;
@useResult
$Res call(
{@JsonKey(name: 'access_token') String accessToken,
@JsonKey(name: 'refresh_token') String refreshToken,
@JsonKey(name: 'token_type') String tokenType,
@JsonKey(name: 'expires_in') int expiresIn,
AuthUser user});
$AuthUserCopyWith<$Res> get user;
}
/// @nodoc
class _$LoginResponseCopyWithImpl<$Res, $Val extends LoginResponse>
implements $LoginResponseCopyWith<$Res> {
_$LoginResponseCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of LoginResponse
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? accessToken = null,
Object? refreshToken = null,
Object? tokenType = null,
Object? expiresIn = null,
Object? user = null,
}) {
return _then(_value.copyWith(
accessToken: null == accessToken
? _value.accessToken
: accessToken // ignore: cast_nullable_to_non_nullable
as String,
refreshToken: null == refreshToken
? _value.refreshToken
: refreshToken // ignore: cast_nullable_to_non_nullable
as String,
tokenType: null == tokenType
? _value.tokenType
: tokenType // ignore: cast_nullable_to_non_nullable
as String,
expiresIn: null == expiresIn
? _value.expiresIn
: expiresIn // ignore: cast_nullable_to_non_nullable
as int,
user: null == user
? _value.user
: user // ignore: cast_nullable_to_non_nullable
as AuthUser,
) as $Val);
}
/// Create a copy of LoginResponse
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$AuthUserCopyWith<$Res> get user {
return $AuthUserCopyWith<$Res>(_value.user, (value) {
return _then(_value.copyWith(user: value) as $Val);
});
}
}
/// @nodoc
abstract class _$$LoginResponseImplCopyWith<$Res>
implements $LoginResponseCopyWith<$Res> {
factory _$$LoginResponseImplCopyWith(
_$LoginResponseImpl value, $Res Function(_$LoginResponseImpl) then) =
__$$LoginResponseImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{@JsonKey(name: 'access_token') String accessToken,
@JsonKey(name: 'refresh_token') String refreshToken,
@JsonKey(name: 'token_type') String tokenType,
@JsonKey(name: 'expires_in') int expiresIn,
AuthUser user});
@override
$AuthUserCopyWith<$Res> get user;
}
/// @nodoc
class __$$LoginResponseImplCopyWithImpl<$Res>
extends _$LoginResponseCopyWithImpl<$Res, _$LoginResponseImpl>
implements _$$LoginResponseImplCopyWith<$Res> {
__$$LoginResponseImplCopyWithImpl(
_$LoginResponseImpl _value, $Res Function(_$LoginResponseImpl) _then)
: super(_value, _then);
/// Create a copy of LoginResponse
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? accessToken = null,
Object? refreshToken = null,
Object? tokenType = null,
Object? expiresIn = null,
Object? user = null,
}) {
return _then(_$LoginResponseImpl(
accessToken: null == accessToken
? _value.accessToken
: accessToken // ignore: cast_nullable_to_non_nullable
as String,
refreshToken: null == refreshToken
? _value.refreshToken
: refreshToken // ignore: cast_nullable_to_non_nullable
as String,
tokenType: null == tokenType
? _value.tokenType
: tokenType // ignore: cast_nullable_to_non_nullable
as String,
expiresIn: null == expiresIn
? _value.expiresIn
: expiresIn // ignore: cast_nullable_to_non_nullable
as int,
user: null == user
? _value.user
: user // ignore: cast_nullable_to_non_nullable
as AuthUser,
));
}
}
/// @nodoc
@JsonSerializable()
class _$LoginResponseImpl implements _LoginResponse {
const _$LoginResponseImpl(
{@JsonKey(name: 'access_token') required this.accessToken,
@JsonKey(name: 'refresh_token') required this.refreshToken,
@JsonKey(name: 'token_type') required this.tokenType,
@JsonKey(name: 'expires_in') required this.expiresIn,
required this.user});
factory _$LoginResponseImpl.fromJson(Map<String, dynamic> json) =>
_$$LoginResponseImplFromJson(json);
@override
@JsonKey(name: 'access_token')
final String accessToken;
@override
@JsonKey(name: 'refresh_token')
final String refreshToken;
@override
@JsonKey(name: 'token_type')
final String tokenType;
@override
@JsonKey(name: 'expires_in')
final int expiresIn;
@override
final AuthUser user;
@override
String toString() {
return 'LoginResponse(accessToken: $accessToken, refreshToken: $refreshToken, tokenType: $tokenType, expiresIn: $expiresIn, user: $user)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$LoginResponseImpl &&
(identical(other.accessToken, accessToken) ||
other.accessToken == accessToken) &&
(identical(other.refreshToken, refreshToken) ||
other.refreshToken == refreshToken) &&
(identical(other.tokenType, tokenType) ||
other.tokenType == tokenType) &&
(identical(other.expiresIn, expiresIn) ||
other.expiresIn == expiresIn) &&
(identical(other.user, user) || other.user == user));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(
runtimeType, accessToken, refreshToken, tokenType, expiresIn, user);
/// Create a copy of LoginResponse
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$LoginResponseImplCopyWith<_$LoginResponseImpl> get copyWith =>
__$$LoginResponseImplCopyWithImpl<_$LoginResponseImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$LoginResponseImplToJson(
this,
);
}
}
abstract class _LoginResponse implements LoginResponse {
const factory _LoginResponse(
{@JsonKey(name: 'access_token') required final String accessToken,
@JsonKey(name: 'refresh_token') required final String refreshToken,
@JsonKey(name: 'token_type') required final String tokenType,
@JsonKey(name: 'expires_in') required final int expiresIn,
required final AuthUser user}) = _$LoginResponseImpl;
factory _LoginResponse.fromJson(Map<String, dynamic> json) =
_$LoginResponseImpl.fromJson;
@override
@JsonKey(name: 'access_token')
String get accessToken;
@override
@JsonKey(name: 'refresh_token')
String get refreshToken;
@override
@JsonKey(name: 'token_type')
String get tokenType;
@override
@JsonKey(name: 'expires_in')
int get expiresIn;
@override
AuthUser get user;
/// Create a copy of LoginResponse
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$LoginResponseImplCopyWith<_$LoginResponseImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@@ -0,0 +1,25 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'login_response.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$LoginResponseImpl _$$LoginResponseImplFromJson(Map<String, dynamic> json) =>
_$LoginResponseImpl(
accessToken: json['access_token'] as String,
refreshToken: json['refresh_token'] as String,
tokenType: json['token_type'] as String,
expiresIn: (json['expires_in'] as num).toInt(),
user: AuthUser.fromJson(json['user'] as Map<String, dynamic>),
);
Map<String, dynamic> _$$LoginResponseImplToJson(_$LoginResponseImpl instance) =>
<String, dynamic>{
'access_token': instance.accessToken,
'refresh_token': instance.refreshToken,
'token_type': instance.tokenType,
'expires_in': instance.expiresIn,
'user': instance.user,
};

View File

@@ -0,0 +1,14 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'logout_request.freezed.dart';
part 'logout_request.g.dart';
@freezed
class LogoutRequest with _$LogoutRequest {
const factory LogoutRequest({
@JsonKey(name: 'refresh_token') required String refreshToken,
}) = _LogoutRequest;
factory LogoutRequest.fromJson(Map<String, dynamic> json) =>
_$LogoutRequestFromJson(json);
}

View File

@@ -0,0 +1,171 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'logout_request.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
LogoutRequest _$LogoutRequestFromJson(Map<String, dynamic> json) {
return _LogoutRequest.fromJson(json);
}
/// @nodoc
mixin _$LogoutRequest {
@JsonKey(name: 'refresh_token')
String get refreshToken => throw _privateConstructorUsedError;
/// Serializes this LogoutRequest to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of LogoutRequest
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$LogoutRequestCopyWith<LogoutRequest> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $LogoutRequestCopyWith<$Res> {
factory $LogoutRequestCopyWith(
LogoutRequest value, $Res Function(LogoutRequest) then) =
_$LogoutRequestCopyWithImpl<$Res, LogoutRequest>;
@useResult
$Res call({@JsonKey(name: 'refresh_token') String refreshToken});
}
/// @nodoc
class _$LogoutRequestCopyWithImpl<$Res, $Val extends LogoutRequest>
implements $LogoutRequestCopyWith<$Res> {
_$LogoutRequestCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of LogoutRequest
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? refreshToken = null,
}) {
return _then(_value.copyWith(
refreshToken: null == refreshToken
? _value.refreshToken
: refreshToken // ignore: cast_nullable_to_non_nullable
as String,
) as $Val);
}
}
/// @nodoc
abstract class _$$LogoutRequestImplCopyWith<$Res>
implements $LogoutRequestCopyWith<$Res> {
factory _$$LogoutRequestImplCopyWith(
_$LogoutRequestImpl value, $Res Function(_$LogoutRequestImpl) then) =
__$$LogoutRequestImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({@JsonKey(name: 'refresh_token') String refreshToken});
}
/// @nodoc
class __$$LogoutRequestImplCopyWithImpl<$Res>
extends _$LogoutRequestCopyWithImpl<$Res, _$LogoutRequestImpl>
implements _$$LogoutRequestImplCopyWith<$Res> {
__$$LogoutRequestImplCopyWithImpl(
_$LogoutRequestImpl _value, $Res Function(_$LogoutRequestImpl) _then)
: super(_value, _then);
/// Create a copy of LogoutRequest
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? refreshToken = null,
}) {
return _then(_$LogoutRequestImpl(
refreshToken: null == refreshToken
? _value.refreshToken
: refreshToken // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
@JsonSerializable()
class _$LogoutRequestImpl implements _LogoutRequest {
const _$LogoutRequestImpl(
{@JsonKey(name: 'refresh_token') required this.refreshToken});
factory _$LogoutRequestImpl.fromJson(Map<String, dynamic> json) =>
_$$LogoutRequestImplFromJson(json);
@override
@JsonKey(name: 'refresh_token')
final String refreshToken;
@override
String toString() {
return 'LogoutRequest(refreshToken: $refreshToken)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$LogoutRequestImpl &&
(identical(other.refreshToken, refreshToken) ||
other.refreshToken == refreshToken));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType, refreshToken);
/// Create a copy of LogoutRequest
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$LogoutRequestImplCopyWith<_$LogoutRequestImpl> get copyWith =>
__$$LogoutRequestImplCopyWithImpl<_$LogoutRequestImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$LogoutRequestImplToJson(
this,
);
}
}
abstract class _LogoutRequest implements LogoutRequest {
const factory _LogoutRequest(
{@JsonKey(name: 'refresh_token')
required final String refreshToken}) = _$LogoutRequestImpl;
factory _LogoutRequest.fromJson(Map<String, dynamic> json) =
_$LogoutRequestImpl.fromJson;
@override
@JsonKey(name: 'refresh_token')
String get refreshToken;
/// Create a copy of LogoutRequest
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$LogoutRequestImplCopyWith<_$LogoutRequestImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@@ -0,0 +1,17 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'logout_request.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$LogoutRequestImpl _$$LogoutRequestImplFromJson(Map<String, dynamic> json) =>
_$LogoutRequestImpl(
refreshToken: json['refresh_token'] as String,
);
Map<String, dynamic> _$$LogoutRequestImplToJson(_$LogoutRequestImpl instance) =>
<String, dynamic>{
'refresh_token': instance.refreshToken,
};

View File

@@ -0,0 +1,14 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'refresh_token_request.freezed.dart';
part 'refresh_token_request.g.dart';
@freezed
class RefreshTokenRequest with _$RefreshTokenRequest {
const factory RefreshTokenRequest({
@JsonKey(name: 'refresh_token') required String refreshToken,
}) = _RefreshTokenRequest;
factory RefreshTokenRequest.fromJson(Map<String, dynamic> json) =>
_$RefreshTokenRequestFromJson(json);
}

View File

@@ -0,0 +1,172 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'refresh_token_request.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
RefreshTokenRequest _$RefreshTokenRequestFromJson(Map<String, dynamic> json) {
return _RefreshTokenRequest.fromJson(json);
}
/// @nodoc
mixin _$RefreshTokenRequest {
@JsonKey(name: 'refresh_token')
String get refreshToken => throw _privateConstructorUsedError;
/// Serializes this RefreshTokenRequest to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of RefreshTokenRequest
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$RefreshTokenRequestCopyWith<RefreshTokenRequest> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $RefreshTokenRequestCopyWith<$Res> {
factory $RefreshTokenRequestCopyWith(
RefreshTokenRequest value, $Res Function(RefreshTokenRequest) then) =
_$RefreshTokenRequestCopyWithImpl<$Res, RefreshTokenRequest>;
@useResult
$Res call({@JsonKey(name: 'refresh_token') String refreshToken});
}
/// @nodoc
class _$RefreshTokenRequestCopyWithImpl<$Res, $Val extends RefreshTokenRequest>
implements $RefreshTokenRequestCopyWith<$Res> {
_$RefreshTokenRequestCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of RefreshTokenRequest
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? refreshToken = null,
}) {
return _then(_value.copyWith(
refreshToken: null == refreshToken
? _value.refreshToken
: refreshToken // ignore: cast_nullable_to_non_nullable
as String,
) as $Val);
}
}
/// @nodoc
abstract class _$$RefreshTokenRequestImplCopyWith<$Res>
implements $RefreshTokenRequestCopyWith<$Res> {
factory _$$RefreshTokenRequestImplCopyWith(_$RefreshTokenRequestImpl value,
$Res Function(_$RefreshTokenRequestImpl) then) =
__$$RefreshTokenRequestImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({@JsonKey(name: 'refresh_token') String refreshToken});
}
/// @nodoc
class __$$RefreshTokenRequestImplCopyWithImpl<$Res>
extends _$RefreshTokenRequestCopyWithImpl<$Res, _$RefreshTokenRequestImpl>
implements _$$RefreshTokenRequestImplCopyWith<$Res> {
__$$RefreshTokenRequestImplCopyWithImpl(_$RefreshTokenRequestImpl _value,
$Res Function(_$RefreshTokenRequestImpl) _then)
: super(_value, _then);
/// Create a copy of RefreshTokenRequest
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? refreshToken = null,
}) {
return _then(_$RefreshTokenRequestImpl(
refreshToken: null == refreshToken
? _value.refreshToken
: refreshToken // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
@JsonSerializable()
class _$RefreshTokenRequestImpl implements _RefreshTokenRequest {
const _$RefreshTokenRequestImpl(
{@JsonKey(name: 'refresh_token') required this.refreshToken});
factory _$RefreshTokenRequestImpl.fromJson(Map<String, dynamic> json) =>
_$$RefreshTokenRequestImplFromJson(json);
@override
@JsonKey(name: 'refresh_token')
final String refreshToken;
@override
String toString() {
return 'RefreshTokenRequest(refreshToken: $refreshToken)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$RefreshTokenRequestImpl &&
(identical(other.refreshToken, refreshToken) ||
other.refreshToken == refreshToken));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType, refreshToken);
/// Create a copy of RefreshTokenRequest
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$RefreshTokenRequestImplCopyWith<_$RefreshTokenRequestImpl> get copyWith =>
__$$RefreshTokenRequestImplCopyWithImpl<_$RefreshTokenRequestImpl>(
this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$RefreshTokenRequestImplToJson(
this,
);
}
}
abstract class _RefreshTokenRequest implements RefreshTokenRequest {
const factory _RefreshTokenRequest(
{@JsonKey(name: 'refresh_token')
required final String refreshToken}) = _$RefreshTokenRequestImpl;
factory _RefreshTokenRequest.fromJson(Map<String, dynamic> json) =
_$RefreshTokenRequestImpl.fromJson;
@override
@JsonKey(name: 'refresh_token')
String get refreshToken;
/// Create a copy of RefreshTokenRequest
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$RefreshTokenRequestImplCopyWith<_$RefreshTokenRequestImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@@ -0,0 +1,19 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'refresh_token_request.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$RefreshTokenRequestImpl _$$RefreshTokenRequestImplFromJson(
Map<String, dynamic> json) =>
_$RefreshTokenRequestImpl(
refreshToken: json['refresh_token'] as String,
);
Map<String, dynamic> _$$RefreshTokenRequestImplToJson(
_$RefreshTokenRequestImpl instance) =>
<String, dynamic>{
'refresh_token': instance.refreshToken,
};

View File

@@ -0,0 +1,17 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'token_response.freezed.dart';
part 'token_response.g.dart';
@freezed
class TokenResponse with _$TokenResponse {
const factory TokenResponse({
@JsonKey(name: 'access_token') required String accessToken,
@JsonKey(name: 'refresh_token') required String refreshToken,
@JsonKey(name: 'token_type') required String tokenType,
@JsonKey(name: 'expires_in') required int expiresIn,
}) = _TokenResponse;
factory TokenResponse.fromJson(Map<String, dynamic> json) =>
_$TokenResponseFromJson(json);
}

View File

@@ -0,0 +1,246 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'token_response.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
TokenResponse _$TokenResponseFromJson(Map<String, dynamic> json) {
return _TokenResponse.fromJson(json);
}
/// @nodoc
mixin _$TokenResponse {
@JsonKey(name: 'access_token')
String get accessToken => throw _privateConstructorUsedError;
@JsonKey(name: 'refresh_token')
String get refreshToken => throw _privateConstructorUsedError;
@JsonKey(name: 'token_type')
String get tokenType => throw _privateConstructorUsedError;
@JsonKey(name: 'expires_in')
int get expiresIn => throw _privateConstructorUsedError;
/// Serializes this TokenResponse to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of TokenResponse
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$TokenResponseCopyWith<TokenResponse> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $TokenResponseCopyWith<$Res> {
factory $TokenResponseCopyWith(
TokenResponse value, $Res Function(TokenResponse) then) =
_$TokenResponseCopyWithImpl<$Res, TokenResponse>;
@useResult
$Res call(
{@JsonKey(name: 'access_token') String accessToken,
@JsonKey(name: 'refresh_token') String refreshToken,
@JsonKey(name: 'token_type') String tokenType,
@JsonKey(name: 'expires_in') int expiresIn});
}
/// @nodoc
class _$TokenResponseCopyWithImpl<$Res, $Val extends TokenResponse>
implements $TokenResponseCopyWith<$Res> {
_$TokenResponseCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of TokenResponse
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? accessToken = null,
Object? refreshToken = null,
Object? tokenType = null,
Object? expiresIn = null,
}) {
return _then(_value.copyWith(
accessToken: null == accessToken
? _value.accessToken
: accessToken // ignore: cast_nullable_to_non_nullable
as String,
refreshToken: null == refreshToken
? _value.refreshToken
: refreshToken // ignore: cast_nullable_to_non_nullable
as String,
tokenType: null == tokenType
? _value.tokenType
: tokenType // ignore: cast_nullable_to_non_nullable
as String,
expiresIn: null == expiresIn
? _value.expiresIn
: expiresIn // ignore: cast_nullable_to_non_nullable
as int,
) as $Val);
}
}
/// @nodoc
abstract class _$$TokenResponseImplCopyWith<$Res>
implements $TokenResponseCopyWith<$Res> {
factory _$$TokenResponseImplCopyWith(
_$TokenResponseImpl value, $Res Function(_$TokenResponseImpl) then) =
__$$TokenResponseImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{@JsonKey(name: 'access_token') String accessToken,
@JsonKey(name: 'refresh_token') String refreshToken,
@JsonKey(name: 'token_type') String tokenType,
@JsonKey(name: 'expires_in') int expiresIn});
}
/// @nodoc
class __$$TokenResponseImplCopyWithImpl<$Res>
extends _$TokenResponseCopyWithImpl<$Res, _$TokenResponseImpl>
implements _$$TokenResponseImplCopyWith<$Res> {
__$$TokenResponseImplCopyWithImpl(
_$TokenResponseImpl _value, $Res Function(_$TokenResponseImpl) _then)
: super(_value, _then);
/// Create a copy of TokenResponse
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? accessToken = null,
Object? refreshToken = null,
Object? tokenType = null,
Object? expiresIn = null,
}) {
return _then(_$TokenResponseImpl(
accessToken: null == accessToken
? _value.accessToken
: accessToken // ignore: cast_nullable_to_non_nullable
as String,
refreshToken: null == refreshToken
? _value.refreshToken
: refreshToken // ignore: cast_nullable_to_non_nullable
as String,
tokenType: null == tokenType
? _value.tokenType
: tokenType // ignore: cast_nullable_to_non_nullable
as String,
expiresIn: null == expiresIn
? _value.expiresIn
: expiresIn // ignore: cast_nullable_to_non_nullable
as int,
));
}
}
/// @nodoc
@JsonSerializable()
class _$TokenResponseImpl implements _TokenResponse {
const _$TokenResponseImpl(
{@JsonKey(name: 'access_token') required this.accessToken,
@JsonKey(name: 'refresh_token') required this.refreshToken,
@JsonKey(name: 'token_type') required this.tokenType,
@JsonKey(name: 'expires_in') required this.expiresIn});
factory _$TokenResponseImpl.fromJson(Map<String, dynamic> json) =>
_$$TokenResponseImplFromJson(json);
@override
@JsonKey(name: 'access_token')
final String accessToken;
@override
@JsonKey(name: 'refresh_token')
final String refreshToken;
@override
@JsonKey(name: 'token_type')
final String tokenType;
@override
@JsonKey(name: 'expires_in')
final int expiresIn;
@override
String toString() {
return 'TokenResponse(accessToken: $accessToken, refreshToken: $refreshToken, tokenType: $tokenType, expiresIn: $expiresIn)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$TokenResponseImpl &&
(identical(other.accessToken, accessToken) ||
other.accessToken == accessToken) &&
(identical(other.refreshToken, refreshToken) ||
other.refreshToken == refreshToken) &&
(identical(other.tokenType, tokenType) ||
other.tokenType == tokenType) &&
(identical(other.expiresIn, expiresIn) ||
other.expiresIn == expiresIn));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode =>
Object.hash(runtimeType, accessToken, refreshToken, tokenType, expiresIn);
/// Create a copy of TokenResponse
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$TokenResponseImplCopyWith<_$TokenResponseImpl> get copyWith =>
__$$TokenResponseImplCopyWithImpl<_$TokenResponseImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$TokenResponseImplToJson(
this,
);
}
}
abstract class _TokenResponse implements TokenResponse {
const factory _TokenResponse(
{@JsonKey(name: 'access_token') required final String accessToken,
@JsonKey(name: 'refresh_token') required final String refreshToken,
@JsonKey(name: 'token_type') required final String tokenType,
@JsonKey(name: 'expires_in') required final int expiresIn}) =
_$TokenResponseImpl;
factory _TokenResponse.fromJson(Map<String, dynamic> json) =
_$TokenResponseImpl.fromJson;
@override
@JsonKey(name: 'access_token')
String get accessToken;
@override
@JsonKey(name: 'refresh_token')
String get refreshToken;
@override
@JsonKey(name: 'token_type')
String get tokenType;
@override
@JsonKey(name: 'expires_in')
int get expiresIn;
/// Create a copy of TokenResponse
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$TokenResponseImplCopyWith<_$TokenResponseImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@@ -0,0 +1,23 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'token_response.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$TokenResponseImpl _$$TokenResponseImplFromJson(Map<String, dynamic> json) =>
_$TokenResponseImpl(
accessToken: json['access_token'] as String,
refreshToken: json['refresh_token'] as String,
tokenType: json['token_type'] as String,
expiresIn: (json['expires_in'] as num).toInt(),
);
Map<String, dynamic> _$$TokenResponseImplToJson(_$TokenResponseImpl instance) =>
<String, dynamic>{
'access_token': instance.accessToken,
'refresh_token': instance.refreshToken,
'token_type': instance.tokenType,
'expires_in': instance.expiresIn,
};

View File

@@ -3,6 +3,8 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:get_it/get_it.dart'; import 'package:get_it/get_it.dart';
import '../core/config/environment.dart'; import '../core/config/environment.dart';
import '../data/datasources/remote/api_client.dart'; import '../data/datasources/remote/api_client.dart';
import '../data/datasources/remote/auth_remote_datasource.dart';
import '../services/auth_service.dart';
/// GetIt 인스턴스 /// GetIt 인스턴스
final getIt = GetIt.instance; final getIt = GetIt.instance;
@@ -20,7 +22,14 @@ Future<void> setupDependencies() async {
getIt.registerLazySingleton(() => ApiClient()); getIt.registerLazySingleton(() => ApiClient());
// 데이터소스 // 데이터소스
// TODO: Remote datasources will be registered here getIt.registerLazySingleton<AuthRemoteDataSource>(
() => AuthRemoteDataSourceImpl(getIt()),
);
// 서비스
getIt.registerLazySingleton<AuthService>(
() => AuthServiceImpl(getIt(), getIt()),
);
// 리포지토리 // 리포지토리
// TODO: Repositories will be registered here // TODO: Repositories will be registered here

View File

@@ -1,7 +1,12 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:superport/core/errors/failures.dart';
import 'package:superport/data/models/auth/login_request.dart';
import 'package:superport/di/injection_container.dart';
import 'package:superport/services/auth_service.dart';
/// 로그인 화면의 상태 및 비즈니스 로직을 담당하는 ChangeNotifier 기반 컨트롤러 /// 로그인 화면의 상태 및 비즈니스 로직을 담당하는 ChangeNotifier 기반 컨트롤러
class LoginController extends ChangeNotifier { class LoginController extends ChangeNotifier {
final AuthService _authService = inject<AuthService>();
/// 아이디 입력 컨트롤러 /// 아이디 입력 컨트롤러
final TextEditingController idController = TextEditingController(); final TextEditingController idController = TextEditingController();
@@ -17,17 +22,82 @@ class LoginController extends ChangeNotifier {
/// 아이디 저장 여부 /// 아이디 저장 여부
bool saveId = false; bool saveId = false;
/// 로딩 상태
bool _isLoading = false;
bool get isLoading => _isLoading;
/// 에러 메시지
String? _errorMessage;
String? get errorMessage => _errorMessage;
/// 아이디 저장 체크박스 상태 변경 /// 아이디 저장 체크박스 상태 변경
void setSaveId(bool value) { void setSaveId(bool value) {
saveId = value; saveId = value;
notifyListeners(); notifyListeners();
} }
/// 로그인 처리 (샘플) /// 로그인 처리
bool login() { Future<bool> login() async {
// 실제 인증 로직은 구현하지 않음 // 입력값 검증
// 항상 true 반환 (샘플) if (idController.text.trim().isEmpty) {
return true; _errorMessage = '이메일을 입력해주세요.';
notifyListeners();
return false;
}
if (pwController.text.isEmpty) {
_errorMessage = '비밀번호를 입력해주세요.';
notifyListeners();
return false;
}
// 이메일 형식 검증
final emailRegex = RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$');
if (!emailRegex.hasMatch(idController.text.trim())) {
_errorMessage = '올바른 이메일 형식이 아닙니다.';
notifyListeners();
return false;
}
// 로딩 시작
_isLoading = true;
_errorMessage = null;
notifyListeners();
try {
// 로그인 요청
final request = LoginRequest(
email: idController.text.trim(),
password: pwController.text,
);
final result = await _authService.login(request);
return result.fold(
(failure) {
_errorMessage = failure.message;
_isLoading = false;
notifyListeners();
return false;
},
(loginResponse) {
_isLoading = false;
notifyListeners();
return true;
},
);
} catch (e) {
_errorMessage = '로그인 중 오류가 발생했습니다.';
_isLoading = false;
notifyListeners();
return false;
}
}
/// 에러 메시지 초기화
void clearError() {
_errorMessage = null;
notifyListeners();
} }
@override @override

View File

@@ -27,10 +27,7 @@ class _LoginViewRedesignState extends State<LoginViewRedesign>
late AnimationController _slideController; late AnimationController _slideController;
late Animation<Offset> _slideAnimation; late Animation<Offset> _slideAnimation;
final _usernameController = TextEditingController();
final _passwordController = TextEditingController();
bool _rememberMe = false; bool _rememberMe = false;
bool _isLoading = false;
@override @override
void initState() { void initState() {
@@ -66,39 +63,23 @@ class _LoginViewRedesignState extends State<LoginViewRedesign>
void dispose() { void dispose() {
_fadeController.dispose(); _fadeController.dispose();
_slideController.dispose(); _slideController.dispose();
_usernameController.dispose();
_passwordController.dispose();
super.dispose(); super.dispose();
} }
Future<void> _handleLogin() async { Future<void> _handleLogin() async {
if (_usernameController.text.isEmpty || _passwordController.text.isEmpty) { final success = await widget.controller.login();
ScaffoldMessenger.of(context).showSnackBar( if (success) {
SnackBar( widget.onLoginSuccess();
content: Text('사용자명과 비밀번호를 입력해주세요.'),
backgroundColor: ShadcnTheme.destructive,
),
);
return;
} }
setState(() {
_isLoading = true;
});
// 실제 로그인 로직 (임시로 2초 대기)
await Future.delayed(const Duration(seconds: 2));
setState(() {
_isLoading = false;
});
widget.onLoginSuccess();
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return ChangeNotifierProvider.value(
value: widget.controller,
child: Consumer<LoginController>(
builder: (context, controller, _) {
return Scaffold(
backgroundColor: ShadcnTheme.background, backgroundColor: ShadcnTheme.background,
body: SafeArea( body: SafeArea(
child: Center( child: Center(
@@ -128,6 +109,8 @@ class _LoginViewRedesignState extends State<LoginViewRedesign>
), ),
), ),
), ),
);
},
), ),
); );
} }
@@ -190,6 +173,7 @@ class _LoginViewRedesignState extends State<LoginViewRedesign>
} }
Widget _buildLoginCard() { Widget _buildLoginCard() {
final controller = context.watch<LoginController>();
return ShadcnCard( return ShadcnCard(
padding: const EdgeInsets.all(ShadcnTheme.spacing8), padding: const EdgeInsets.all(ShadcnTheme.spacing8),
child: Column( child: Column(
@@ -210,7 +194,7 @@ class _LoginViewRedesignState extends State<LoginViewRedesign>
ShadcnInput( ShadcnInput(
label: '사용자명', label: '사용자명',
placeholder: '사용자명을 입력하세요', placeholder: '사용자명을 입력하세요',
controller: _usernameController, controller: controller.idController,
prefixIcon: const Icon(Icons.person_outline), prefixIcon: const Icon(Icons.person_outline),
keyboardType: TextInputType.text, keyboardType: TextInputType.text,
), ),
@@ -220,7 +204,7 @@ class _LoginViewRedesignState extends State<LoginViewRedesign>
ShadcnInput( ShadcnInput(
label: '비밀번호', label: '비밀번호',
placeholder: '비밀번호를 입력하세요', placeholder: '비밀번호를 입력하세요',
controller: _passwordController, controller: controller.pwController,
prefixIcon: const Icon(Icons.lock_outline), prefixIcon: const Icon(Icons.lock_outline),
obscureText: true, obscureText: true,
keyboardType: TextInputType.visiblePassword, keyboardType: TextInputType.visiblePassword,
@@ -231,11 +215,9 @@ class _LoginViewRedesignState extends State<LoginViewRedesign>
Row( Row(
children: [ children: [
Checkbox( Checkbox(
value: _rememberMe, value: controller.saveId,
onChanged: (value) { onChanged: (value) {
setState(() { controller.setSaveId(value ?? false);
_rememberMe = value ?? false;
});
}, },
activeColor: ShadcnTheme.primary, activeColor: ShadcnTheme.primary,
), ),
@@ -245,6 +227,38 @@ class _LoginViewRedesignState extends State<LoginViewRedesign>
), ),
const SizedBox(height: ShadcnTheme.spacing8), const SizedBox(height: ShadcnTheme.spacing8),
// 에러 메시지 표시
if (controller.errorMessage != null)
Container(
padding: const EdgeInsets.all(ShadcnTheme.spacing3),
margin: const EdgeInsets.only(bottom: ShadcnTheme.spacing4),
decoration: BoxDecoration(
color: ShadcnTheme.destructive.withOpacity(0.1),
borderRadius: BorderRadius.circular(ShadcnTheme.borderRadius),
border: Border.all(
color: ShadcnTheme.destructive.withOpacity(0.3),
),
),
child: Row(
children: [
Icon(
Icons.error_outline,
size: 20,
color: ShadcnTheme.destructive,
),
const SizedBox(width: ShadcnTheme.spacing2),
Expanded(
child: Text(
controller.errorMessage!,
style: ShadcnTheme.bodyMedium.copyWith(
color: ShadcnTheme.destructive,
),
),
),
],
),
),
// 로그인 버튼 // 로그인 버튼
ShadcnButton( ShadcnButton(
text: '로그인', text: '로그인',
@@ -253,7 +267,7 @@ class _LoginViewRedesignState extends State<LoginViewRedesign>
textColor: Colors.white, textColor: Colors.white,
size: ShadcnButtonSize.large, size: ShadcnButtonSize.large,
fullWidth: true, fullWidth: true,
loading: _isLoading, loading: controller.isLoading,
), ),
const SizedBox(height: ShadcnTheme.spacing4), const SizedBox(height: ShadcnTheme.spacing4),
@@ -261,8 +275,8 @@ class _LoginViewRedesignState extends State<LoginViewRedesign>
ShadcnButton( ShadcnButton(
text: '테스트 로그인', text: '테스트 로그인',
onPressed: () { onPressed: () {
_usernameController.text = 'admin'; controller.idController.text = 'admin@example.com';
_passwordController.text = 'password'; controller.pwController.text = 'admin123';
_handleLogin(); _handleLogin();
}, },
variant: ShadcnButtonVariant.secondary, variant: ShadcnButtonVariant.secondary,

View File

@@ -0,0 +1,221 @@
import 'dart:async';
import 'dart:convert';
import 'package:dartz/dartz.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:injectable/injectable.dart';
import 'package:superport/core/errors/failures.dart';
import 'package:superport/data/datasources/remote/auth_remote_datasource.dart';
import 'package:superport/data/models/auth/auth_user.dart';
import 'package:superport/data/models/auth/login_request.dart';
import 'package:superport/data/models/auth/login_response.dart';
import 'package:superport/data/models/auth/logout_request.dart';
import 'package:superport/data/models/auth/refresh_token_request.dart';
import 'package:superport/data/models/auth/token_response.dart';
abstract class AuthService {
Future<Either<Failure, LoginResponse>> login(LoginRequest request);
Future<Either<Failure, void>> logout();
Future<Either<Failure, TokenResponse>> refreshToken();
Future<bool> isLoggedIn();
Future<AuthUser?> getCurrentUser();
Future<String?> getAccessToken();
Future<String?> getRefreshToken();
Future<void> clearSession();
Stream<bool> get authStateChanges;
}
@LazySingleton(as: AuthService)
class AuthServiceImpl implements AuthService {
final AuthRemoteDataSource _authRemoteDataSource;
final FlutterSecureStorage _secureStorage;
static const String _accessTokenKey = 'access_token';
static const String _refreshTokenKey = 'refresh_token';
static const String _userKey = 'user';
static const String _tokenExpiryKey = 'token_expiry';
final _authStateController = StreamController<bool>.broadcast();
AuthServiceImpl(
this._authRemoteDataSource,
this._secureStorage,
);
@override
Future<Either<Failure, LoginResponse>> login(LoginRequest request) async {
try {
final result = await _authRemoteDataSource.login(request);
return await result.fold(
(failure) async => Left(failure),
(loginResponse) async {
// 토큰 및 사용자 정보 저장
await _saveTokens(
loginResponse.accessToken,
loginResponse.refreshToken,
loginResponse.expiresIn,
);
await _saveUser(loginResponse.user);
// 인증 상태 변경 알림
_authStateController.add(true);
return Right(loginResponse);
},
);
} catch (e) {
return Left(ServerFailure(message: '로그인 처리 중 오류가 발생했습니다.'));
}
}
@override
Future<Either<Failure, void>> logout() async {
try {
final refreshToken = await getRefreshToken();
if (refreshToken != null) {
final request = LogoutRequest(refreshToken: refreshToken);
await _authRemoteDataSource.logout(request);
}
await clearSession();
_authStateController.add(false);
return const Right(null);
} catch (e) {
// 로그아웃 API 실패 시에도 로컬 세션은 정리
await clearSession();
_authStateController.add(false);
return const Right(null);
}
}
@override
Future<Either<Failure, TokenResponse>> refreshToken() async {
try {
final refreshToken = await getRefreshToken();
if (refreshToken == null) {
return Left(AuthenticationFailure(
message: '리프레시 토큰이 없습니다. 다시 로그인해주세요.',
));
}
final request = RefreshTokenRequest(refreshToken: refreshToken);
final result = await _authRemoteDataSource.refreshToken(request);
return await result.fold(
(failure) async {
// 토큰 갱신 실패 시 세션 정리
await clearSession();
_authStateController.add(false);
return Left(failure);
},
(tokenResponse) async {
// 새 토큰 저장
await _saveTokens(
tokenResponse.accessToken,
tokenResponse.refreshToken,
tokenResponse.expiresIn,
);
return Right(tokenResponse);
},
);
} catch (e) {
return Left(ServerFailure(message: '토큰 갱신 중 오류가 발생했습니다.'));
}
}
@override
Future<bool> isLoggedIn() async {
try {
final accessToken = await getAccessToken();
if (accessToken == null) return false;
// 토큰 만료 확인
final expiryStr = await _secureStorage.read(key: _tokenExpiryKey);
if (expiryStr != null) {
final expiry = DateTime.parse(expiryStr);
if (DateTime.now().isAfter(expiry)) {
// 토큰이 만료되었으면 갱신 시도
final refreshResult = await refreshToken();
return refreshResult.isRight();
}
}
return true;
} catch (e) {
return false;
}
}
@override
Future<AuthUser?> getCurrentUser() async {
try {
final userStr = await _secureStorage.read(key: _userKey);
if (userStr == null) return null;
final userJson = jsonDecode(userStr);
return AuthUser.fromJson(userJson);
} catch (e) {
return null;
}
}
@override
Future<String?> getAccessToken() async {
try {
return await _secureStorage.read(key: _accessTokenKey);
} catch (e) {
return null;
}
}
@override
Future<String?> getRefreshToken() async {
try {
return await _secureStorage.read(key: _refreshTokenKey);
} catch (e) {
return null;
}
}
@override
Future<void> clearSession() async {
try {
await _secureStorage.delete(key: _accessTokenKey);
await _secureStorage.delete(key: _refreshTokenKey);
await _secureStorage.delete(key: _userKey);
await _secureStorage.delete(key: _tokenExpiryKey);
} catch (e) {
// 에러가 발생해도 계속 진행
}
}
@override
Stream<bool> get authStateChanges => _authStateController.stream;
Future<void> _saveTokens(
String accessToken,
String refreshToken,
int expiresIn,
) async {
await _secureStorage.write(key: _accessTokenKey, value: accessToken);
await _secureStorage.write(key: _refreshTokenKey, value: refreshToken);
// 토큰 만료 시간 저장 (현재 시간 + expiresIn 초)
final expiry = DateTime.now().add(Duration(seconds: expiresIn));
await _secureStorage.write(
key: _tokenExpiryKey,
value: expiry.toIso8601String(),
);
}
Future<void> _saveUser(AuthUser user) async {
final userJson = jsonEncode(user.toJson());
await _secureStorage.write(key: _userKey, value: userJson);
}
void dispose() {
_authStateController.close();
}
}

View File

@@ -346,6 +346,22 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
freezed:
dependency: "direct dev"
description:
name: freezed
sha256: "44c19278dd9d89292cf46e97dc0c1e52ce03275f40a97c5a348e802a924bf40e"
url: "https://pub.dev"
source: hosted
version: "2.5.7"
freezed_annotation:
dependency: "direct main"
description:
name: freezed_annotation
sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2
url: "https://pub.dev"
source: hosted
version: "2.4.4"
frontend_server_client: frontend_server_client:
dependency: transitive dependency: transitive
description: description:

View File

@@ -31,6 +31,7 @@ dependencies:
# JSON 처리 # JSON 처리
json_annotation: ^4.8.1 json_annotation: ^4.8.1
freezed_annotation: ^2.4.1
# 환경 설정 # 환경 설정
flutter_dotenv: ^5.1.0 flutter_dotenv: ^5.1.0
@@ -51,6 +52,7 @@ dev_dependencies:
build_runner: ^2.4.8 build_runner: ^2.4.8
json_serializable: ^6.7.1 json_serializable: ^6.7.1
injectable_generator: ^2.4.1 injectable_generator: ^2.4.1
freezed: ^2.4.6
flutter: flutter:
uses-material-design: true uses-material-design: true