월 26,400원
5개월 할부 시다른 수강생들이 자주 물어보는 질문이 궁금하신가요?
- 미해결[코드팩토리] [중급] Flutter 진짜 실전! 상태관리, 캐시관리, Code Generation, GoRouter, 인증로직 등 중수가 되기 위한 필수 스킬들!
restaurant_repository.g.dart 에러가 발생했어요
이번 강의를 다 듣고 코드를 수정하였습니다만아래 restaurant_repository.g.dart 파일에 pagiante함수에서 queryParameters.addAll(paginationParams?.toJson() ?? <String, dynamic>{}); 이부분에 toJson이 에러가 납니다. 에러 내용은 paginationParams에 toJson 함수가 없어서 입니다.해당 강의에서 강사님 코드에서 toJson을 구현하지 않았는데 어떤 부분을 제가 놓친 것인지 모르겠습니다,@override Future<CursorPagination<RestaurantModel>> paginate( {paginationParams = const PaginationParams()}) async { const _extra = <String, dynamic>{}; final queryParameters = <String, dynamic>{}; queryParameters.addAll(paginationParams?.toJson() ?? <String, dynamic>{}); queryParameters.removeWhere((k, v) => v == null); final _headers = <String, dynamic>{r'accessToken': 'true'}; _headers.removeWhere((k, v) => v == null); final Map<String, dynamic>? _data = null; final _result = await _dio.fetch<Map<String, dynamic>>( _setStreamType<CursorPagination<RestaurantModel>>(Options( method: 'GET', headers: _headers, extra: _extra, ) .compose( _dio.options, '/', queryParameters: queryParameters, data: _data, ) .copyWith(baseUrl: baseUrl ?? _dio.options.baseUrl))); final value = CursorPagination<RestaurantModel>.fromJson( _result.data!, (json) => RestaurantModel.fromJson(json as Map<String, dynamic>), ); return value; }
- 미해결[코드팩토리] [중급] Flutter 진짜 실전! 상태관리, 캐시관리, Code Generation, GoRouter, 인증로직 등 중수가 되기 위한 필수 스킬들!
왜 강의에 많은 부분이 생략된 것 같죠??
영상이 원래 이 분량인가요?로그인하는 부분을 구현한 적 없는데 갑자기 로그인이 구현되어 있어서 조금 당황스럽네요..ㄷㄷ혹시 제가 놓친 강의가 있나요?
- 미해결[코드팩토리] [중급] Flutter 진짜 실전! 상태관리, 캐시관리, Code Generation, GoRouter, 인증로직 등 중수가 되기 위한 필수 스킬들!
차후 강의관련 질문사항입니다.
제발 파이어베이스를 플러터를 연동해서 앱을 만드는 강의 찍어 올려주시면 안될까요? 제발제발요 따로 강의내셔도 결제하고 들을게요 ㅠㅠ
- 미해결[코드팩토리] [중급] Flutter 진짜 실전! 상태관리, 캐시관리, Code Generation, GoRouter, 인증로직 등 중수가 되기 위한 필수 스킬들!
로그인 방식에도 차이가 있나요?
안녕하세요 코팩님 로그인 처리시 로그인 방식에 차이가 있는지 여쭈어 보고 싶습니다. 강의에서는 refresh token과 access token을 이용한 방식을 사용하는걸로 알고 있는데요..현재 저희 회사에서 진행하고 있는 프로젝트에서 선임분께서는 다음과 같은 방식으로 로그인 코드를 작성하였는데요.. 무슨 차이인지 여쭈어보고 싶습니다.[login.Screen.dart]
- 해결됨[코드팩토리] [중급] Flutter 진짜 실전! 상태관리, 캐시관리, Code Generation, GoRouter, 인증로직 등 중수가 되기 위한 필수 스킬들!
디스코드관련
안녕하세요유료강의구매시 디스코드 프리미엄채널에 추가관련해서메시지를 보냈는데 답이없으셔서 글남깁니다.
- 미해결[코드팩토리] [중급] Flutter 진짜 실전! 상태관리, 캐시관리, Code Generation, GoRouter, 인증로직 등 중수가 되기 위한 필수 스킬들!
Sliver 를 사용한 이유.
ListView 대신에 Sliver 를 사용하신 이유가 먼가요?어떤 걸 써도 동일한 효과인가요?
- 해결됨[코드팩토리] [중급] Flutter 진짜 실전! 상태관리, 캐시관리, Code Generation, GoRouter, 인증로직 등 중수가 되기 위한 필수 스킬들!
토큰 재발급 관련 질문입니다.
안녕하세요. 기초를 수강하고, 중급 강의 절반 정도 수강한 학생입니다. 우선, 기초와 중급 모두 좋은 퀄리티의 영상과 강의를 올려주셔서 정말 감사드린다는 말씀 드립니다!현재 강의와는 별개로 따로 진행하고 있는 프로젝트에서 한 페이지에 여러 api 호출을 진행하는 과정에서 access 토큰이 만료되었을 때의 상황에서 문제를 겪고 있습니다.예를 들어, 3개의 api를 호출하여 FutureBuilder 3개 혹은 Future.wait으로 3개의 데이터를 가져오는 상황입니다.우선, 모든 요청에 access 토큰 만료를 백엔드에서 계산하고 있습니다. 만료가 되었을 때, 프론트로 401이 던져지고 onError에서 이를 캐치하여 재발급 api를 호출하고 다시 토큰을 갱신하여 secure storage에 저장하고 있습니다.(백엔드에서는 access 토큰이 만료되어 재발급 요청을 받으면, 유효한 refresh 토큰인지 확인하여 유효시 두 토큰 모두 갱신하여 재발급해줍니다.)이 과정에서 3개의 모든 api 요청에 대한 재발급을 시도하여 3번의 재발급 요청이 이루어지게 됩니다. 그리고나서 3개의 모든 api 요청에 다시 resolve를 하게 되는데, 이러한 순서의 로직이 맞는 부분인지 궁금합니다.어려움을 겪고 있는 부분에는 3개의 요청 중 2개의 요청은 요청된 시간이 밀리세컨드 단위로 다르지만, 재발급 된 토큰이 동일해 요청이 성공적으로 진행됩니다. (5개라면 2개가 성공할 때가 있고, 3개가 성공할 때가 있고 시시각각 변합니다..) 하지만, 남은 api 요청은 앞선 요청에서 이미 새롭게 토큰이 발급되었기 때문에 갱신되지 않은 토큰으로 요청을 보내게 되고, 서버에서는 토큰이 새로 갱신되었기 때문에 토큰이 유효하지 않다는 오류가 발생하게 됩니다. 여러 레퍼런스를 참고하여 QueuedInterceptorsWrapper 및 일부 코드를 추가하여 임시로 해결해놓은 상태이지만, 근본적인 원인 해결이 되지 않았다고 생각하여 질문드립니다.(여전히 5개의 요청이라면, 5개의 재발급 요청을 보내는 상황입니다...)아직 남은 강의를 모두 들어보지 않아 해결하지 못하는 문제일 수 있지만, 미리 질문부터 드리는 점 양해 드립니다,,ㅠ서둘러 완강해보도록 하겠습니다!감사합니다 :)
- 미해결[코드팩토리] [중급] Flutter 진짜 실전! 상태관리, 캐시관리, Code Generation, GoRouter, 인증로직 등 중수가 되기 위한 필수 스킬들!
goNamed질문
GoRoute( path: 'chef/register/two', name: ChefRegisteScreen2.routeName, builder: (_,state) => ChefRegisteScreen2( //rid: 123 ) ),goNamed로 String값 말고 Map이나 리스트 자료형을 넘기는 방법이 있을까요?
- 미해결[코드팩토리] [중급] Flutter 진짜 실전! 상태관리, 캐시관리, Code Generation, GoRouter, 인증로직 등 중수가 되기 위한 필수 스킬들!
주문페이지 리프레쉬 인디케이터 문제
안녕하세요 선생님해당 강의내용에서 당겨서 새로고침하는 리프레쉬 인디케이터를 사용하면 상단에 로딩바가 하나 도는데다시 화면이 리프레쉬되면서 화면 중앙에 로딩바가 도는 것이 2중 로딩으로 보여지는 것 같아서pagination_list_view.dart 파일내RefreshIndicator( onRefresh: () async { ref.read(widget.provider.notifier).paginate(forceRefetch: true); },위 코드의 forceRefetch: true 를 삭제하는게 더 낫겠다는 판단에RefreshIndicator( onRefresh: () async { ref.read(widget.provider.notifier).paginate(); },해당코드를 사용 하려 하는데요근데 이렇게 포스리펫치를 삭제 하게 되면[홈] 메뉴와 [음식] 메뉴에서는 정상적으로 REQ RES 포스트 겟 요청이 가는데[주문] 메뉴에서는 아무런 응답이 없어서 새로고침이 되지 않습니다. 현재 강의에서 장바구니에서 주문완료를 눌러도 장바구니 내용이 비워지지 않는 문제와 로딩이 이중으로 되는 문제를 확인했는데 장바구니 부분은 주문완료버튼을 눌렀을때 주문내용을 비워버리면 해결 될 것 같다는 개념이라도 잡히는데포스리펫치 부분은 어디서 손을 대야 될지 아직 개념이 잡히지 않는데요 홈메뉴와 음식메뉴에서는 forceRefetch:true가 아니더라도 서버에서 자료를 받는 로직이 있는데주문메뉴에서는 앱에서 발생된 주문건에 대해서만 받아오는 로직이라 그게 아닌가? 싶은 생각만 듭니다.1.앱을 처음 로딩해서 주문메뉴에 들어갔을때 -> 로딩이 강제로 되니 새로운 주문건이 화면에 표시됨2.포스리팻치 true일경우 -> 강제 새로고침기능이 작동하니 새로운 주문건이 화면에 표시됨3.포스리팻치를 삭제하고 리프레쉬인디게이터만 사용할경우 -> 새로운 주문건이 표시가 안됨. 어떤 부분을 확인하거나 살펴보는게 좋을지 힌트를 주셨으면 좋겠습니다.만일 forceRefetch:true를 꼭 사용해야 되는 설계라면로딩바 대신에 스켈레톤을 사용한다면 2중 로딩느낌이 안날 것 같다는 생각이 듭니다.
- 미해결[코드팩토리] [중급] Flutter 진짜 실전! 상태관리, 캐시관리, Code Generation, GoRouter, 인증로직 등 중수가 되기 위한 필수 스킬들!
The instance member 'simulatorIP' can't be accessed in an initializer
simulatorIP와 emulatorIP가 빨간색 줄로 나타나며The instance member 'simulatorIP' can't be accessed in an initializer 다음과 같은 오류가 발생합니다. Stackoverflow를 통해 찾아보았으나 구체적으로 무슨 오류인지 파악하기가 어려워 질문드리고자 합니다.
- 미해결[코드팩토리] [중급] Flutter 진짜 실전! 상태관리, 캐시관리, Code Generation, GoRouter, 인증로직 등 중수가 되기 위한 필수 스킬들!
ios promotion 지원
강사님 안녕하세요. 강의 잘 배우고, 실전 어플 만들어서 배포까지 완료하였는데요.14Pro 에서 스크롤 및 애니메이션 구현 시 약간의 버벅거림이 있더라구요. 아이폰 미니에서는 해당 증상 없는걸로 보아. 제 생각엔 ProMotion 지원이 안되는 것 같아보입니다. 혹시 강사님은 이 부분 어떻게 처리하시나요?민감한 사람에게는 좀 적응이 어려울 것 같아 보이는데, 혹시 플러터 개발팀에서 차후에 이 부분 개선해 줄 가능성이 있을까요? 추가로, 플러터로 만들었다고 하는 '네이버지식인' 앱도 스크롤시 상당히 버벅거립니다.
- 미해결[코드팩토리] [중급] Flutter 진짜 실전! 상태관리, 캐시관리, Code Generation, GoRouter, 인증로직 등 중수가 되기 위한 필수 스킬들!
리뷰 작성 페이지 강의는 언제쯤 나올까요?
안녕하세요?항상 좋은 강의 감사하게 생각하고 있습니다.마지막 강의에 보니 추가 강의 "리뷰 작성 페이지"가 나온다고 했는데, 언제쯤 볼 수 있을까요?
- 미해결[코드팩토리] [중급] Flutter 진짜 실전! 상태관리, 캐시관리, Code Generation, GoRouter, 인증로직 등 중수가 되기 위한 필수 스킬들!
스태틱으로 만든 메서드 빨간줄 안사라짐
클래스 1번 GestureDetector( onTap: () { Schedule.onTap(context); }, ---------------------------------------------- 클래스 2번 class Schedule { static void onTap(BuildContext context) async { (데이터불러오기) return AlertDialog 입니다. 약간에 변형을 줘서 공부중인데 1번클래스에서 온탭 누르면 알림창은 문제 없이 뜨고있습니다 그러나 onTab 밑에 빨간줄이 영~신경쓰이는데 마우스 대보면 The method 'onTap' isn't defined for the type 'Schedule 이렇게 나오는데 관련 자료 부터 구글 다뒤져봐도 onTab에 빨간줄이 안사라집니다 방법좀 알려주세요 ㅠ
- 해결됨[코드팩토리] [중급] Flutter 진짜 실전! 상태관리, 캐시관리, Code Generation, GoRouter, 인증로직 등 중수가 되기 위한 필수 스킬들!
주문하기 클릭 시 Order 생성 안 되고 500에러가 뜹니다.
질문 내용주문 시 계속해서 주문에 실패했다고 떠서, 무엇이 문제이지 하고 봤는데, Status 500 에러가 던져집니다.그래서 서버 쪽 로그를 봤는데, 서버에서 이런 Exception이 던져지고 있습니다.[Nest] 63134 - 2023. 08. 13. 오전 2:35:36 ERROR [ExceptionsHandler] Cannot read properties of undefined (reading 'restaurant') TypeError: Cannot read properties of undefined (reading 'restaurant') at OrderService.postOrder (/Users/nx006/Documents/vscode/flutter-lv2-server/src/order/order.service.ts:36:8) at OrderController.postOrder (/Users/nx006/Documents/vscode/flutter-lv2-server/src/order/order.controller.ts:63:30) at /Users/nx006/Documents/vscode/flutter-lv2-server/node_modules/@nestjs/core/router/router-execution-context.js:38:29 at processTicksAndRejections (node:internal/process/task_queues:95:5) 'restaurant' 속성을 읽지 못한다는 게 무슨 말일까요? 애초에 Request Body에 Restaurant 속성 자체가 없는데, 400 에러도 아니고 왜 이런 에러가 서버 쪽에서 나는 지 모르겠습니다.코드 전문일단은, 현재 PostBody와 Response Body에 대한 모델입니다:// ignore_for_file: invalid_annotation_target /// order_model.dart import 'package:delivery_app/common/model/model_with_id.dart'; import 'package:delivery_app/common/utils/data_utils.dart'; import 'package:delivery_app/restaurant/model/restaurant_model.dart'; import 'package:flutter/foundation.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; part 'order_model.freezed.dart'; part 'order_model.g.dart'; @freezed class OrderProductModel with _$OrderProductModel { factory OrderProductModel({ required String id, required String name, required String detail, @JsonKey(fromJson: DataUtils.pathToUrl) required String imgUrl, required int price, }) = _OrderProductModel; factory OrderProductModel.fromJson(Map<String, dynamic> json) => _$OrderProductModelFromJson(json); } @freezed class OrderProductAndCountModel with _$OrderProductAndCountModel { factory OrderProductAndCountModel({ required OrderProductModel product, required int count, }) = _OrderProductAndCount; factory OrderProductAndCountModel.fromJson(Map<String, dynamic> json) => _$OrderProductAndCountModelFromJson(json); } @freezed class OrderModel with _$OrderModel implements IModelWithId { factory OrderModel({ required String id, required RestaurantModel restaurant, required List<OrderProductAndCountModel> products, required int totalPrice, @JsonKey(fromJson: DataUtils.stringToDateTime) required DateTime createdAt, }) = _OrderModel; factory OrderModel.fromJson(Map<String, dynamic> json) => _$OrderModelFromJson(json); } /// post_order_body.dart import 'package:freezed_annotation/freezed_annotation.dart'; part 'post_order_body.freezed.dart'; part 'post_order_body.g.dart'; @freezed class PostOrderBody with _$PostOrderBody { const factory PostOrderBody({ required String id, required List<PostOrderBodyProduct> products, required int totalPrice, required String createdAt, }) = _PostOrderBody; factory PostOrderBody.fromJson(Map<String, dynamic> json) => _$PostOrderBodyFromJson(json); } @freezed class PostOrderBodyProduct with _$PostOrderBodyProduct { const factory PostOrderBodyProduct({ required String id, required int count, }) = _PostOrderBodyProduct; factory PostOrderBodyProduct.fromJson(Map<String, dynamic> json) => _$PostOrderBodyProductFromJson(json); } 그리고 다음은 Repository 코드입니다./// order_provider.dart import 'package:delivery_app/common/const/data.dart'; import 'package:delivery_app/common/dio/dio.dart'; import 'package:delivery_app/order/model/order_model.dart'; import 'package:delivery_app/order/model/post_order_body.dart'; import 'package:dio/dio.dart' hide Headers; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:retrofit/retrofit.dart'; part 'order_repository.g.dart'; final orderRepositoryProvider = Provider((ref) { final dio = ref.watch(dioProvider); return OrderRepository(dio, baseUrl: 'http://$ip/order'); }); // baseUrl : http://$ip/order @RestApi() abstract class OrderRepository { factory OrderRepository(Dio dio, {String baseUrl}) = _OrderRepository; @POST('/') @Headers({'accessToken': 'true'}) Future<OrderModel> postOrder({ @Body() required PostOrderBody body, }); } 다음은 Provider 입니다./// order_provider.dart // ignore_for_file: public_member_api_docs, sort_constructors_first import 'package:delivery_app/order/model/post_order_body.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:uuid/uuid.dart'; import 'package:delivery_app/order/model/order_model.dart'; import 'package:delivery_app/order/repository/order_repository.dart'; import 'package:delivery_app/user/provider/basket_provider.dart'; final orderProvider = StateNotifierProvider<OrderStateNotifier, List<OrderModel>>( (ref) => OrderStateNotifier( ref: ref, repository: ref.watch(orderRepositoryProvider), )); class OrderStateNotifier extends StateNotifier<List<OrderModel>> { final Ref ref; final OrderRepository repository; OrderStateNotifier({ required this.ref, required this.repository, }) : super([]); Future<bool> postOrder() async { const uuid = Uuid(); final id = uuid.v4(); final state = ref.read(basketProvider); try { await repository.postOrder( body: PostOrderBody( id: id, products: state .map((e) => PostOrderBodyProduct( id: e.product.id, count: e.count, )) .toList(), totalPrice: state.fold<int>( 0, (previousValue, element) => previousValue + element.product.price * element.count, ), createdAt: DateTime.now().toString(), ), ); return true; } catch (e) { print('error: $e'); return false; } } } 그리고 View 단, 특 주문하기 버튼입니다:ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: primaryColor, ), onPressed: basket.isEmpty ? null : () async { final response = await ref .read(orderProvider.notifier) .postOrder(); if (context.mounted) { if (response) { context.goNamed(OrderDonePage.routeName); } else { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('주문에 실패했습니다.'), ), ); } } }, child: const Text('결제하기'), ),서버 쪽 코드혹시 몰라서, 에러가 발생하는 서버 쪽 코드 역시 첨부합니다. 일단 제가 건든 코드는 없습니다.import { Injectable } from '@nestjs/common'; import { CreateOrderDto } from './dto/create-order.dto'; import { CacheService } from '../cache/cache.service'; import { User } from '../user/entities/user.entity'; import { Order } from './entities/order.entity'; import { CoreService } from '../core/core.service'; import { PaginationDto } from '../core/dto/pagination.dto'; import { Pagination } from '../core/entity/pagination.entity'; import { OrderProduct } from './entities/order-product-entity'; @Injectable() export class OrderService { constructor( private cacheService: CacheService, private coreService: CoreService, ) {} paginateOrders(user: User, paginationDto: PaginationDto): Pagination<Order> { const result = this.coreService.paginate( this.cacheService.orders, paginationDto, ); return { ...result, data: result.data.map((item) => new Order(item)), }; } postOrder(user: User, createOrderDto: CreateOrderDto): Order { const newOrder = new Order({ id: createOrderDto.id, user, restaurant: this.cacheService.products.find( (x) => createOrderDto.products[0].productId === x.id, ).restaurant, products: createOrderDto.products.map((basketItem) => ({ product: new OrderProduct( this.cacheService.products.find( (product) => basketItem.productId === product.id, ), ), count: basketItem.count, })), totalPrice: createOrderDto.totalPrice, createdAt: createOrderDto.createdAt, }); this.cacheService.orders = [newOrder, ...this.cacheService.orders]; return newOrder; } } @ApiTags('order') @ApiExtraModels(PaginationDto, Order) @Controller('order') export class OrderController { constructor(private readonly orderService: OrderService) {} @UseGuards(AccessTokenGuard) @ApiOperation({ summary: '주문 Pagination', }) @ApiPaginatedOkResponseDecorator(Order, { description: 'Pagination 결과', }) @Get() paginateOrder( @Request() req, @Query() paginationDto: PaginationDto, ): Pagination<Order> { return this.orderService.paginateOrders(req.user, paginationDto); } @UseGuards(AccessTokenGuard) @Post() @ApiOperation({ summary: '주문 생성하기', }) @ApiBody({ type: CreateOrderDto, }) @ApiOkResponse({ status: 201, type: Order, }) postOrder(@Request() req, @Body() body: CreateOrderDto): Order { return this.orderService.postOrder(req.user, body); } }
- 미해결[코드팩토리] [중급] Flutter 진짜 실전! 상태관리, 캐시관리, Code Generation, GoRouter, 인증로직 등 중수가 되기 위한 필수 스킬들!
페이지 fetchMore 중복요청 문제
섹션 9 상태관리 프로젝트에 적용하기 > 완성된 Pagination 로직 실행해보기8분경에 요청이 중복으로 들어가는 부분을 고친다고 말씀하셨는데저부분을 고치는 강의가 뒤에 있나요?다른 프로젝트에 적용할때 같은 문제가 발생해서언급을 하셨다면 어느부분인지 찾지를 못하겠네요.
- 해결됨[코드팩토리] [중급] Flutter 진짜 실전! 상태관리, 캐시관리, Code Generation, GoRouter, 인증로직 등 중수가 되기 위한 필수 스킬들!
UserMeProvider 회원가입
안녕하세요 강사님 또 저입니다ㅠㅠ 중급강의의 로그인과 인증로직을 조금씩 변형해 현재 진행중인 프로젝트에 적용하려하는데 회원가입 POST가 필요한데 이를 예를들면 강의기준으로 UserMeStateNotifier내부에 Future<UserModelBase> login가 있듯이, Future<UserModel> postUser(UserModel userModel)로 Provider파트에 선언하고 @POST() Future<UserModel> postUser(@Body() UserModel user); 이런형태로 한 UserModel내에 선언해도될지 감이 안잡혀서 여쭤봅니다.이런부분에서 막힌거보니 아직 완전 이해를 못한거같습니다ㅠㅠ
- 해결됨[코드팩토리] [중급] Flutter 진짜 실전! 상태관리, 캐시관리, Code Generation, GoRouter, 인증로직 등 중수가 되기 위한 필수 스킬들!
115강 5분 10초, model의 타입을 ProductModel로 지정했을 때 발생하는 타입 및 null 에러가 있습니다
class ProductPage extends StatelessWidget { const ProductPage({super.key}); @override Widget build(BuildContext context) { return PaginationListView<ProductModel>( provider: productProvider, itemBuilder: <ProductModel>(_, index, ProductModel model) => GestureDetector( onTap: () => context.goNamed( RestaurantDetailPage.routeName, pathParameters: { 'rid': model.restaurant.id, }, ), child: ProductCard.fromProductModel( model: model, ), ), ); } }여기서 이상한 에러가 뜹니다. 먼저 'rid': model.restaurant.id 이 부분에서는 아래와 같은 에러가 뜹니다.The property 'restaurant' can't be unconditionally accessed because the receiver can be 'null'.Try making the access conditional (using '?.') or adding a null check to the target ('!').dartunchecked_use_of_nullable_value 그리고 두 번째로, ProductCard.fromProductModel(model: model)에서는 이런 에러가 뜹니다.The argument type 'ProductModel' can't be assigned to the parameter type 'ProductModel'.dartargument_type_not_assignable 두 에러 모두 이해되지 않습니다. ProductModel 타입을 ProductModel 타입으로 Assign할 수 없다니요? 같은 타입인데 이런 에러가 뜹니다. 또한 첫 번째 에러의 경우에도, 분명히 nullable 타입이 존재하지 않는데 nullable 체크를 하라고 하고 있습니다. 당황스럽습니다. 한편, 강의는 이런 식으로 되어 있습니다.itemBuilder: <ProductModel>(_, index, model)즉 model의 타입을 따로 지정해주지 않았습니다. 이렇게 했을경우 model의 타입은 dynamic이 되며, 자동 완성 기능은 수행할 수 없지만, 결론적으로 잘 작동은 합니다. 그런데 왜 저런 이상한 에러가 발생하는 지 모르겠습니다. 여기 이와 관련된 코드를 덧붙입니다. 그러나 대부분 강의와 동일합니다.@JsonSerializable() class ProductModel implements IModelWithId { @override final String id; /// 상품 이름 final String name; /// 상품 상세 정보 final String detail; /// 상품 이미지 URL @JsonKey(fromJson: DataUtils.pathToUrl) final String imgUrl; /// 상품 가격 final int price; /// 레스토랑 정보 final RestaurantModel restaurant; ProductModel({ required this.id, required this.name, required this.detail, required this.imgUrl, required this.price, required this.restaurant, }); factory ProductModel.fromJson(Map<String, dynamic> json) => _$ProductModelFromJson(json); }typedef PaginationWidgetBuilder<T extends IModelWithId> = Widget Function( BuildContext context, int index, T model, );문제가 발생하고 있는 스크린샷:
- 미해결[코드팩토리] [중급] Flutter 진짜 실전! 상태관리, 캐시관리, Code Generation, GoRouter, 인증로직 등 중수가 되기 위한 필수 스킬들!
보안 및 content type 질문
Token 발급 과정에서 username:password 값을 base64 로 인코딩 후 authorization 헤더에 Basic $token 형태로 전송하는 것이 정석이라고 말씀해주셨습니다.질문1.Basic $token 형태는 인증정보가 그대로 base64 형태로 인코딩해줍니다. 그러면 누군가가 이 패킷을 까보기만 하면 데이터를 알 수 있는 것이 아닌가요..? 이 부분이 이해가 잘 안되네요. 설명부탁드립니다..질문2.추가로 챗gpt 에게 질문했을 때, 로그인할 때 content type 을 application/x-www-form-urlencoded 형태로 보내라고 하는데, 대부분은 json 형태로 전송하더라구요.. 설명부탁드립니다...
- 미해결[코드팩토리] [중급] Flutter 진짜 실전! 상태관리, 캐시관리, Code Generation, GoRouter, 인증로직 등 중수가 되기 위한 필수 스킬들!
안녕하세요. 강사님 듣다가 헷갈리는 부분이 있습니다.
ref.watch를 작성하게 되면 ref 값이 변하게 되면 빌드를 다시하는거고ref.listen은 값이 변할때 빌드는 다시하지 않지만 값의 상태를 확인하고 이벤트를 실행해주는 것으로 이해해도 될까요?
- 해결됨[코드팩토리] [중급] Flutter 진짜 실전! 상태관리, 캐시관리, Code Generation, GoRouter, 인증로직 등 중수가 되기 위한 필수 스킬들!
riverpod 2.0 Notifer 관련하여 질문드립니다
riverpod 2.0에서는 StateNotifier대신 Notifier이나 AsyncNotifier를 사용하라고 되어있던데그러면 현재 강의에서 StateNotifer를 Notifier로 바꿀려고하니 의존성주입을 할때,StateNotifierProvider에서 생성자로 ref.watch로 authRepository,userMeRepository,storage를 넣어주는데궁금한것이 1.이를 Notifier에서는 어떻게 넣어주는게 가장 좋은 선택인가요??(생성시에 ref.watch로 한번만 만들기 vs 필요한곳마다 ref.watch사용하기 )초기 생성할때 UserModelLoading()을 한뒤에 state업데이트를 getMe함수에서 하는데 이를 try-finally로 하는것이 맞는 방법인가요?late final AuthRepository, late final UserMeRepository, ... 과 같이 final을 붙일시에 다시 build되는 상황이 생기던데 에러가 뜨던데 final을 안해도 괜찮은건가요?아래는 저가 바꿔본 코드입니다.final userMeProvider = StateNotifierProvider<UserMeStateNotifier,UserModelBase?>((ref) { final authRepository = ref.watch(authRepositoryProvider); final userMeRepository = ref.watch(userMeRepositoryProvider); final storage = ref.watch(secureStorageProvider); return UserMeStateNotifier( authRepository: authRepository, repository: userMeRepository, storage: storage, ); }); class UserMeStateNotifier extends StateNotifier<UserModelBase?> { final AuthRepository authRepository; final UserMeRepository repository; final FlutterSecureStorage storage; UserMeStateNotifier({ required this.authRepository, required this.repository, required this.storage, }) : super(UserModelLoading()) { //내 정보 가져오기기 getMe(); } Future<void> getMe() async { final refreshToken = await storage.read(key: REFRESH_TOKEN_KEY); final accessToken = await storage.read(key: ACCESS_TOKEN_KEY); if (refreshToken == null || accessToken == null) { state = null; return; } final resp = await repository.getMe(); state = resp; } /* *login, logout생략 */ } final userMeProvider = NotifierProvider<UserMeNotifier,UserModelBase?>(UserMeNotifier.new); class UserMeNotifier extends Notifier<UserModelBase?>{ late AuthRepository authRepository; late UserMeRepository repository; late FlutterSecureStorage storage; @override UserModelBase? build() { try{ authRepository = ref.watch(authRepositoryProvider); repository = ref.watch(userMeRepositoryProvider); storage = ref.watch(secureStorageProvider); return UserModelLoading(); }finally{ getMe(); } } Future<void> getMe() async { final refreshToken = await storage.read(key: REFRESH_TOKEN_KEY); final accessToken = await storage.read(key: ACCESS_TOKEN_KEY); if (refreshToken == null || accessToken == null) { state = null; return; } final resp = await repository.getMe(); state = resp; } }