묻고 답해요
160만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결[코드팩토리] [중급] Flutter 진짜 실전! 상태관리, 캐시관리, Code Generation, GoRouter, 인증로직 등 중수가 되기 위한 필수 스킬들!
ScrollController 동작이 안되서 질문드립니다.
"완성된 Pagination 로직 실행해보기" 강의에서 Scroll 위치를 찾기 위해서 반영한 ScrollController에서 Listener를 등록해도 스크롤 할때 동작을 하지 않아서 문의드립니다. - 리스너를 등록해도 스크롤 시 print('run') 이 동작을 안합니다..
-
미해결[코드팩토리] [중급] Flutter 진짜 실전! 상태관리, 캐시관리, Code Generation, GoRouter, 인증로직 등 중수가 되기 위한 필수 스킬들!
결제하기 버튼 누른후 주문탭 바로 갱신
안녕하세요. 정말 좋은강의 감사드립니다. 제가 지금 첫번째로 듣고있는중이라 정확히 모든걸 이해하면서 듣진 못하고있는데 장바구니에서 결제하기 눌러서 결제 완료하고 주문탭가면 방금 주문한 내역이 반영되지 않고 리프레쉬한번 해야 반영이 되는데 리프레쉬 안하고 바로 주문탭에 반영시키는 방법 생각해보다가 그냥basket_screen.dart에서 결제하기 버튼을 눌렀을때onPressed: () async {final resp = await ref.read(orderProvider.notifier).postOrder();if(resp){ await ref.read(orderProvider.notifier).paginate(forceRefetch:true);context.goNamed(OrderDoneScreen.routeName); }else{ScaffoldMessenget.of~~}}위와 같은식으로 그냥 버튼 누르고 바로 paginate(forceRefetch:true);을 통해 강제로 데이터 한번 갱신해버리는식으로 했는데 이렇게하면 안좋은게 있을까요..? 만약 안좋다면 어떤식으로 해야 주문탭에 바로 반영이 되게 할 수 있을까요 ㅜㅜ 주문탭에 옵티미스틱 리스폰스 코드를 어떻게 만들어서 어디에 적용시켜야할지 잘 모르겠습니다 ㅜㅜ
-
미해결[코드팩토리] [중급] Flutter 진짜 실전! 상태관리, 캐시관리, Code Generation, GoRouter, 인증로직 등 중수가 되기 위한 필수 스킬들!
Provider가 빠른 이유와 streambuilder
안녕하세요. futurebuilder대신 provider를 이용하는게 더 이득이라고 하셨는데 futurebuilder는 build가 될때마다 값을 가져오게 되는 것이고, provider는 build마다 값을 가져오지 않아도 되는 이유인가요??provider가 이렇게 이득이 생기는 이유는 flutter에서 provider는 항상 caching을 해두기 때문일까요?? 그리고 futurebuilder가 아닌 streambuilder도 provider와 비슷하게 값이 변경되면 실행되는 것으로 알고있는데 streambuilder를 대체할경우에도 이득이 있을까요? 감사합니다.
-
미해결[코드팩토리] [중급] Flutter 진짜 실전! 상태관리, 캐시관리, Code Generation, GoRouter, 인증로직 등 중수가 되기 위한 필수 스킬들!
redirectionLogic 에러관련 질문입니다
안녕하세요 코드팩토리님! 좋은 강의 잘 듣고 있습니다. 현재 115강까지 수강한 상태이고 go_router.dart 파일 안에 redirectionLogic부분에 에러가 발생하였습니다. 강사님과 달리 현재 go router 7 버전을 사용하고 있습니다. 버전 차이로 인해 추가로 수정해야하는 부분이 있거나 혹은 제가 실수한 부분이 있다면 알려주시면 감사하겠습니다 :) go_router.dartauth_provider.dartString? redirectLogic(GoRouterState state) { final UserModelBase? user = ref.read(userMeProvider); final logginIn = state.location == '/login'; if (user == null) { return logginIn ? null : '/login'; } // user is not null // UserModel // there is user information and // user is logging in or at the SplashScreen, // move on to home screen if (user is UserModel) { return logginIn || state.location == '/splash' ? '/' : null; } // UserModelError if (user is UserModelError) { return !logginIn ? '/login' : null; } return null; } 코드 실행 시 발생한 에러 메시지 입니다
-
미해결[코드팩토리] [중급] Flutter 진짜 실전! 상태관리, 캐시관리, Code Generation, GoRouter, 인증로직 등 중수가 되기 위한 필수 스킬들!
Cursor based pagination 내 신규 데이터 갱신 관련 문의
Cursor based pagination 사용 시 변경 데이터 (신규데이터 또는 삭제데이터) 는 패스하게 되는데, 변경데이터가 있는 경우에만 데이터를 갱신하는 refetch 로직은 어떤식으로 구성하면 될까요?
-
해결됨[입문] Qt QML과 C++로 시작하는 크로스플랫폼 앱 개발
onParentChanged -> onParentsignal 이유 문의
강의를 따라하면서 도중에 갑자기onParentChanged가 onParentSignal로 수정이 되었던데 onParentChanged는 언제 사용하는 걸까요?
-
미해결[코드팩토리] [중급] Flutter 진짜 실전! 상태관리, 캐시관리, Code Generation, GoRouter, 인증로직 등 중수가 되기 위한 필수 스킬들!
ios 이미지 전송 질문
Future<void> uploadFile() async { // file picker를 통해 파일 선택 final filePath = _image!.path; // 파일 경로를 통해 formData 생성 var dio = Dio(); var formData = FormData.fromMap({ 'file' : await MultipartFile.fromFile(filePath!) }); dio.options.contentType = 'multipart/form-data'; dio.options.maxRedirects.isFinite; final token = await ref.read(secureStorageProvider).read(key:ACCESS_TOKEN_KEY); dio.options.headers.addAll({ 'authorization': 'bearer $token', }); print("ok"); // 업로드 요청 final response = await dio.post("http://$ip/s3/upload", data: formData); _downloadUrl = response.data; print(response.statusCode.toString() + "hihi"); }이미지 업로드 코드로 위 코드를 사용하고있는데 안드로이드 애뮬레이터에서는 전송이 잘 되지만 ios에서 해당 코드를 실행하면[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: DioError [DioErrorType.response]: Http status error [401] 계속 위와 같은 에러가 뜹니다 구글에 검색해도 잘 안나와서 질문드립니다.
-
미해결[코드팩토리] [중급] Flutter 진짜 실전! 상태관리, 캐시관리, Code Generation, GoRouter, 인증로직 등 중수가 되기 위한 필수 스킬들!
riverpod 2 (async) notifier 사용?
https://riverpod.dev/ko/docs/migration/from_state_notifierriverpod 2 공식문서에 보면 Notifier/ AsyncNotifer 가 새롭게 도입되면서 StateNotifier는 더이상 사용되지 않는다고 나오는데 새로운 방식 강의 업데이트 안 해주시나요...?
-
해결됨[입문] Qt QML과 C++로 시작하는 크로스플랫폼 앱 개발
실행이 안되네요...
강의에서 알려주신 것처럼Visual studio 2019 community와 QT6를 설치했는데 아래 에러가 뜨면서 실행이 안되네요.. "ninja: build stopped: subcommand failed." 영상에서 알려준 설정을 모두 동일하게 적용했는데 왜 그럴까요..ㅜㅜ
-
미해결[코드팩토리] [중급] Flutter 진짜 실전! 상태관리, 캐시관리, Code Generation, GoRouter, 인증로직 등 중수가 되기 위한 필수 스킬들!
GoRouter 재빌드
리버팟 codegen을 쓰면서 강의를 듣다가 리버팟 codegen으로는 changeNotifier를 작성하지 못한다는 기사를 읽었습니다.다음 코드에서 막혔습니다.@riverpod GoRouter goRouter(GoRouterRef ref) { final provider = ref.read(authProvider.notifier); final user = ref.watch(userMeProvider); return GoRouter( routes: provider.routes, initialLocation: '/splash', // refreshListenable: ???, redirect: (_, state) { return provider.redirectLogic(state); }, ); }여기서 refreshListenable에 값을 넣어줘야 하는데 강의에서는 ChangeNotiferProvider로 했기 때문에 refreshListenable에 값을 넣어주는게 가능했는데 저는 codegen으로 하다보니 타입 에러가 뜨고 값을 넣어 줄 수가 없습니다.final user = ref.watch(userMeProvider);이 코드를 넣어서 user의 상태가 바뀌면 고라우터를 재빌드 시켜서 리다이렉트 함수를 다시 실행시키려고 했는데, user의 상태가 UserModelLoading에서 UserModel로 분명히 바뀜에도 불구하고 고라우터가 재빌드가 안됩니다. print() 넣어서 확인했습니다.그냥 리버팟 codegen 쓰지말고 v1처럼 하는게 나을까요? 아니면 refreshListenable에 넣어 줄 수 있는 다른 값이나 고라우터의 리다이렉트 함수를 또 실행시켜줄 장치가 있을까요?
-
미해결[코드팩토리] [중급] Flutter 진짜 실전! 상태관리, 캐시관리, Code Generation, GoRouter, 인증로직 등 중수가 되기 위한 필수 스킬들!
riverpod v2 관련 질문
안녕하세요저는 강의처럼 리버팟 v1이 아닌 어노테이션을 사용하는 v2버전으로 코딩을 하며 배우는 중입니다.그리고 강의처럼 클래스 안에 변수를 선언해서 생성자를 통해 레포지토리를 받는걸 하려고 합니다.@riverpod class UserMe extends _$UserMe { final UserMeRepository repo; UserMe({ required this.repo, }); @override UserModelBase? build() { return UserModelLoading(); } }이후 user_me_privder.g.dart를 보면 빨간줄이 보입니다.저 빨간줄에 커서 가져다 대면The argument type 'UserMe Function({required UserMeRepository repo})' can't be assigned to the parameter type 'UserMe Function()'. 요런 메세지가 뜹니다.해결방법이 있을까요?
-
미해결[코드팩토리] [중급] Flutter 진짜 실전! 상태관리, 캐시관리, Code Generation, GoRouter, 인증로직 등 중수가 되기 위한 필수 스킬들!
Dio Interceptor 내부 onError 콜백
@override void onError(DioException err, ErrorInterceptorHandler handler) async { debugPrint('ERR [${err.response}] => PATH: ${err.requestOptions.path}'); return super.onError(err, handler);이렇게 인터셉터를 찍어보고 있는데 err.response가 null이 날라오네요,, 이유를 모르겠는데 혹시 추측가시는 이유가 있으신가요?서버에서는 정상적으로 리턴이 옵니다.URL도 일치합니다.request도 날라갑니다.
-
미해결[코드팩토리] [중급] Flutter 진짜 실전! 상태관리, 캐시관리, Code Generation, GoRouter, 인증로직 등 중수가 되기 위한 필수 스킬들!
GoRouter v13
안녕하세요 코팩님!이제 드디어 GoRouter에 대한 강의를 들으려고 합니다.그런데 지금 GoRouter가 v13까지 업데이트 되어있네요 ㄷㄷ혹시 GoRouter v13 추가로 강의하실 계획 있으신가요??그리고 제가 v13으로 강의를 듣고 코딩을 해도 크게 문제 없을까요?
-
미해결[코드팩토리] [중급] Flutter 진짜 실전! 상태관리, 캐시관리, Code Generation, GoRouter, 인증로직 등 중수가 되기 위한 필수 스킬들!
List 타입 에러
[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: type 'List<IModelWithId>' is not a subtype of type 'List<RatingModel>?' of 'data' #0 CursorPagination.copyWith (package:codefactory_lecture/common/model/cursor_pagination_model.dart:28:14) #1 Pagination.paginate (package:codefactory_lecture/common/provider/pagination_provider.dart:91:22) <asynchronous suspension>이건 디테일 스크린에서 rating을 fetchmore로 더 불러올때 나는 에러입니다.[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: type 'List<IModelWithId>' is not a subtype of type 'List<RestaurantModel>?' of 'data' #0 CursorPagination.copyWith (package:codefactory_lecture/common/model/cursor_pagination_model.dart:28:14) #1 Pagination.paginate (package:codefactory_lecture/common/provider/pagination_provider.dart:91:22) <asynchronous suspension>그리고 이건 restaurant_screen.dart에서 쭉 내려서 데이터를 더 불러오려고 할 때 에러입니다. 둘 다 비슷한 에러입니다. state = resp.copyWith(data: [ ...pState.data, ...resp.data, ]);페이지네이션 코드 중 이 부분에 문제가 있었고, CursorPagination copyWith({ CursorPaginationMeta? meta, List<T>? data, }) { return CursorPagination<T>( meta: meta ?? this.meta, data: data ?? this.data); }여기서 List<T>? data의 타입이 맞지 않는 것 같습니다. 데이터 타입이 알맞게 각각 들어가야 할텐데, 자꾸 List<IModelWithId> 타입이 들어가는 거 같아요... 어느 부분을 고쳐야 할까요?
-
해결됨[코드팩토리] [중급] Flutter 진짜 실전! 상태관리, 캐시관리, Code Generation, GoRouter, 인증로직 등 중수가 되기 위한 필수 스킬들!
토큰을 하드코딩해서 임시로 사용하려고 하는데 안되네요 ㅠㅠ
결론적으로 JWT토큰도 서버측 페이지(예, index.php)에 하드코딩해서 클라인언트 앱으로 전송할 수 있네요... 문제의 핵심은 제가 임시로 구축한 서버(아파치 웹서버)로 요청하는 주소 끝에 정확한 페이지 주소를 붙이든(예,http://제 도메인 주소/auth/token/index.php) 아니면 http://제 도메인 주소/auth/token/와 같은식으로 끝에 "/"는 꼭 붙여야지 응답을 받을 수 있는데, 제가 강의를 따라서 그대로 요청하는 주소를 아래와 같이 끝에 "/"를 붙이지 않고 Dio 요청을 했기 때문에 생기는 문제였습니다.http://제 도메인 주소/auth/tokenhttp://제 도메인 주소/auth/login 여전히 POSTMAN으로 요청을 테스트해보면, POSTMAN에서는 요청하는 주소 끝에 "/"붙이지 않더라도정상적으로 응답을 받을 수 있는데, 왜 Dio로 요청했을 때는 응답을 받을 수가 없는지는 알수가 없지만혹시라도 저하고 똑같은 문제를 겪으시는 분이 계실까봐 글남깁니다...^^;; ................................ 이하 기존 작성했 던 글.......................안녕하세요? 너무 초보적인 질문같아서 좀 창피하기도 한데, 다름이 아니고, 중급강의 최종 소스를 바탕으로 제가 예전에 만들어 보고 싶었던 앱을 만들려고 하고 있습니다.백엔드는 예전에 PHP를 좀 다뤄본적이 있어서 일단은, 그래도 친숙한 PHP로 백엔드를 만들어볼려고 하는데,로그인 단계부터 잘 진행이 안되네요..ㅠㅠ 강사님 강의 덕분에 JWT 및 토큰 관리 개념을 배워서 이걸 나중에 PHP로 구현해볼려고는 하고 있기는 한데,우선 flutter로 앱 만드는 것에 집중하고 싶어서, 일단은 앱에서 로그인 요청을 하면 서버측 페이지에 "하드코딩"한 JWT 토큰 정보를 사용자측 앱으로 전달되게해서 로그인을 할 수 있게 하고 싶은데, 아무리 해도 로그인이 안되네요. 제가 임시로 구축한 사이트에 강의에 맞춰서 아래 경로를 맞췄고http://제 도메인 주소/auth/login이 페이지를 호출하면 POSTMAN에서 로그인 후 응답받았던 아래 토큰을 복사해서페이지에 담은 후클라이언트 앱으로 전달되게 했는데 아무리 해도 로그인이 안되고 에러가 뜹니다. { "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InRlc3RAY29kZWZhY3RvcnkuYWkiLCJzdWIiOiJmNTViMzJkMi00ZDY4LTRjMWUtYTNjYS1kYTlkN2QwZDkyZTUiLCJ0eXBlIjoicmVmcmVzaCIsImlhdCI6MTcwNjI1ODYwMCwiZXhwIjoxNzA2MzQ1MDAwfQ.dvMz8WgEk-28q3F2J4E5CLLUURO1w8S7MlgqGje5ils", "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InRlc3RAY29kZWZhY3RvcnkuYWkiLCJzdWIiOiJmNTViMzJkMi00ZDY4LTRjMWUtYTNjYS1kYTlkN2QwZDkyZTUiLCJ0eXBlIjoiYWNjZXNzIiwiaWF0IjoxNzA2MjU4NjAwLCJleHAiOjE3MDYyNTg5MDB9.lnCJ7Okwvk1xh2WNz8n4oXbHWczy1H72sVb4znOXWUs" } 제 짧은 소견으로는 어찌되었건 클라이언트앱으로 토큰 정보가 전송되면 로그인이 진행되어야 할 텐데안되는 이유가 무엇인지 모르겠습니다... ............................................... 위의 질문을 쓰고, 몇가지 더 테스트를 해본 결과를 종합하면,Dio에서 JWT형식으로 된 토큰(하드코딩한 토큰)이 담긴 응답은 내부적으로 어떤 검증 절차를 거쳐서오류를 일으키는 것 같습니다. 오류는 다음과 같은 오류가 뜨는데,다른 페이지는 아래와 같은 오류가 뜨지 않고 제대로 정보를 수신합니다. I/flutter (22276): Dio 에러 상세정보:I/flutter (22276): 타입: DioErrorType.responseI/flutter (22276): 메시지: Http status error [301]I/flutter (22276): 에러: Http status error [301]I/flutter (22276): 응답: <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">I/flutter (22276): <html><head>I/flutter (22276): <title>301 Moved Permanently</title>I/flutter (22276): </head><body>I/flutter (22276): <h1>Moved Permanently</h1>I/flutter (22276): <p>The document has moved <a href="http://제 도메인 주소/auth/token/">here</a>.</p>I/flutter (22276): </body></html> 결론적으로 하드코딩된 JWT 토큰은 Dio 패키지를 통해서 정상적으로 응답을 수신할 수 없으며,JWT 토큰을 제대로 서버측에서 수신하려면 서버측에 JWT 토큰을 제대로 인증하고 발급할 수 있게구축을 해야한다로 귀결되는 것 같습니다. 이러한 결론이 맞을까요? 이 /auth/login/auth/login
-
미해결[코드팩토리] [중급] Flutter 진짜 실전! 상태관리, 캐시관리, Code Generation, GoRouter, 인증로직 등 중수가 되기 위한 필수 스킬들!
CursorPagination<dynamic> 타입관련 오류나시는 분들..
Restaurant_screen.dart에서 스크롤시type 'CursorPagination<dynamic>' is not a subtype of type 'CursorPagination<RestaurantModel>' in type cast오류나시는 분들 다다음 강의에서 오류 해결 방법 나옵니당
-
미해결[코드팩토리] [중급] Flutter 진짜 실전! 상태관리, 캐시관리, Code Generation, GoRouter, 인증로직 등 중수가 되기 위한 필수 스킬들!
안드로이드 에뮬레이터 403 에러
E/flutter ( 9967): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: DioException [bad response]: This exception was thrown because the response has a status code of 403 and RequestOptions.validateStatus was configured to throw for this status code. E/flutter ( 9967): The status code of 403 has the following meaning: "Client error - the request contains bad syntax or cannot be fulfilled" E/flutter ( 9967): Read more about status codes at https://developer.mozilla.org/en-US/docs/Web/HTTP/Status E/flutter ( 9967): In order to resolve this exception you typically have either to verify and fix your request code or you have to fix the server code. E/flutter ( 9967): E/flutter ( 9967): #0 DioMixin.fetch.<anonymous closure> (package:dio/src/dio_mixin.dart:507:7) E/flutter ( 9967): #1 _FutureListener.handleError (dart:async/future_impl.dart:180:22) E/flutter ( 9967): #2 Future._propagateToListeners.handleError (dart:async/future_impl.dart:858:47) E/flutter ( 9967): #3 Future._propagateToListeners (dart:async/future_impl.dart:879:13) E/flutter ( 9967): #4 Future._completeError (dart:async/future_impl.dart:655:5) E/flutter ( 9967): #5 _SyncCompleter._completeError (dart:async/future_impl.dart:63:12) E/flutter ( 9967): #6 _Completer.completeError (dart:async/future_impl.dart:27:5) E/flutter ( 9967): #7 Future.any.onError (dart:async/future.dart:618:45) E/flutter ( 9967): #8 _RootZone.runBinary (dart:async/zone.dart:1666:54) E/flutter ( 9967): #9 _FutureListener.handleError (dart:async/future_impl.dart:177:22) E/flutter ( 9967): #10 Future._propagateToListeners.handleError (dart:async/future_impl.dart:858:47) E/flutter ( 9967): #11 Future._propagateToListeners (dart:async/future_impl.dart:879:13) E/flutter ( 9967): #12 Future._completeError (dart:async/future_impl.dart:655:5) E/flutter ( 9967): #13 Future._asyncCompleteError.<anonymous closure> (dart:async/future_impl.dart:745:7) E/flutter ( 9967): #14 _microtaskLoop (dart:async/schedule_microtask.dart:40:21) E/flutter ( 9967): #15 _startMicrotaskLoop (dart:async/schedule_microtask.dart:49:5)IOS 시뮬레이터로 잘만 해왔다가, 안드로이드 에뮬로도 잘 되는지 확인해보려고 했는데 로그인 할 때 403에러가 뜨네요... 어떻게 해결해야 할까요? // localhost const emulatorIP = '10.0.2.2:3000'; const simulatorIP = '127.0.0.1:3000'; final ip = Platform.isAndroid ? emulatorIP : simulatorIP;
-
해결됨[코드팩토리] [중급] Flutter 진짜 실전! 상태관리, 캐시관리, Code Generation, GoRouter, 인증로직 등 중수가 되기 위한 필수 스킬들!
현재는 Stream 프로바이더 생성 되는것 같습니다! 강의 들으시는분들 참고해보셔요~
@riverpod Stream<int> gStateStream(GStateStreamRef ref) async* { await Future.delayed(Duration(seconds: 2)); for (int i = 0; i < 10; i++) { await Future.delayed(Duration(seconds: 1)); yield i; } }현재시각 기준으로 Stream 프로바이더도 생성 되는것같네요~
-
미해결[코드팩토리] [중급] Flutter 진짜 실전! 상태관리, 캐시관리, Code Generation, GoRouter, 인증로직 등 중수가 되기 위한 필수 스킬들!
모든 Provider의 인스턴스가 앱이 실행되면 무조건 다 생기나요?
안녕하세요! provider적용하기 강의 듣고 있습니다. 궁금한게, 프로젝트를 진행할 수 록 여러개의 provider를 생성하게 되는데 그럼 이 모든 provider의 인스턴스가 사용하지 않아도 무조건 앱 실행시 다 인스턴스화 되어 메모리에 올라가게 되나요? 제가 객체지향관련 개념이 플러터하면서 처음이라 조금 헷갈립니다. 감사합니다.
-
미해결[코드팩토리] [중급] Flutter 진짜 실전! 상태관리, 캐시관리, Code Generation, GoRouter, 인증로직 등 중수가 되기 위한 필수 스킬들!
RatingPagination 렌더링 부분에서 api 요청이 가지 않고 있습니다.
- 학습 관련 질문을 남겨주세요. 상세히 작성하면 더 좋아요! - 먼저 유사한 질문이 있었는지 검색해보세요. - 서로 예의를 지키며 존중하는 문화를 만들어가요. - 잠깐! 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요.코드팩토리 디스코드https://bit.ly/3HzRzUMFlutter 강의를 구매하시면 코드팩토리 디스코드 서버 플러터 프리미엄 채널에 들어오실 수 있습니다! 디스코드 서버에 들어오시고 저에게 메세지로 강의를 구매하신 이메일을 보내주시면 프리미엄 채널에 등록해드려요! 프리미엄 채널에 들어오시면 모든 질의응답 최우선으로 답변해드립니다! RatingPagination 렌더링 강의에서 http://ip/restaurant/:rid/rating으로 요청이 가지 않고 있습니다! 어떤 부분을 놓치고 있을까요? restaurant_detail_screen.dartclass RestaurantDetailScreen extends ConsumerStatefulWidget { final String id; const RestaurantDetailScreen({required this.id, super.key}); @override ConsumerState<RestaurantDetailScreen> createState() => _RestaurantDetailScreenState(); } class _RestaurantDetailScreenState extends ConsumerState<RestaurantDetailScreen> { @override void initState() { super.initState(); ref.read(restaurantProvider.notifier).getDetail(id: widget.id); } @override Widget build(BuildContext context) { final state = ref.watch(restaurantDetailProvider(widget.id)); final ratingsState = ref.watch(restaurantRatingProvider(widget.id)); if (state == null) { return DefaultLayout( child: Center( child: CircularProgressIndicator(), ), ); } return DefaultLayout( title: '불타는 떡볶이', child: CustomScrollView( slivers: [ renderTop( model: state!, ), if (state is! RestaurantDetailModel) renderLoading(), if (state is RestaurantDetailModel) renderLabel(), if (state is RestaurantDetailModel) renderProduct( products: state.products, ), if(ratingsState is CursorPagination<RatingModel>) renderRatings(models: ratingsState.data), ], ), ); } SliverPadding renderRatings({ required List<RatingModel> models, }){ return SliverPadding( padding: EdgeInsets.symmetric(horizontal: 16.0), sliver: SliverList( delegate: SliverChildBuilderDelegate( (_, index) => RatingCard.fromModel( model: models[index], ), childCount: models.length, ), ) ); } //skeleton 사용 -> 로딩중에는 미리보는것 같은 효과 가져오기 SliverPadding renderLoading() { return SliverPadding( padding: EdgeInsets.symmetric( vertical: 16.0, horizontal: 16.0, ), sliver: SliverList( delegate: SliverChildListDelegate( List.generate( 3, (index) => Padding( padding: const EdgeInsets.only(bottom: 32.0), child: SkeletonParagraph( style: SkeletonParagraphStyle( lines: 5, padding: EdgeInsets.zero, ), ), ), ), ), ), ); } // 일반 위젯을 slivers에 추가하려면 SliverToBoxAdapter로 감싸줘야 한다. SliverToBoxAdapter renderTop({ required RestaurantModel model, }) { return SliverToBoxAdapter( child: RestaurantCard.fromModel( model: model, isDetail: true, ), ); } SliverPadding renderLabel() { return SliverPadding( padding: EdgeInsets.symmetric(horizontal: 16.0), sliver: SliverToBoxAdapter( child: Text( '메뉴', style: TextStyle( fontSize: 18.0, fontWeight: FontWeight.w500, ), ), ), ); } SliverPadding renderProduct({ required List<RestaurantProductModel> products, }) { return SliverPadding( padding: EdgeInsets.symmetric(horizontal: 16.0), sliver: SliverList( delegate: SliverChildBuilderDelegate( (context, index) { final model = products[index]; return Padding( padding: const EdgeInsets.only(top: 16.0), child: ProductCard.fromModel( model: model, ), ); }, childCount: products.length, ), ), ); } } restaurantRatingProviderfinal restaurantRatingProvider = StateNotifierProvider.family<RestaurantRatingStateNotifier, CursorPaginationBase, String>((ref, id){ final repository = ref.watch(restaurantRatingRepositoryProvider(id)); return RestaurantRatingStateNotifier(repository: repository); }); class RestaurantRatingStateNotifier extends StateNotifier<CursorPaginationBase> { final RestaurantRatingRepository repository; RestaurantRatingStateNotifier({ required this.repository, }) : super( CursorPaginationLoading(), ); } restaurantRatingRepositoryProviderfinal restaurantRatingRepositoryProvider = Provider.family<RestaurantRatingRepository, String>((ref, id) { final dio = ref.watch(dioProvider); return RestaurantRatingRepository(dio, baseUrl: 'http://$ip/restaurant/$id/rating'); }); // http://ip/restaurant/:rid/rating @RestApi() abstract class RestaurantRatingRepository implements IBasePaginationRepository<RatingModel>{ factory RestaurantRatingRepository(Dio dio, {String baseUrl}) = _RestaurantRatingRepository; @GET('/') @Headers({ 'accessToken': 'true', }) Future<CursorPagination<RatingModel>> paginate({ @Queries() PaginationParams? paginationParams = const PaginationParams(), //@Queries() -> PaginationParams 클래스의 값들이 자동으로 쿼리로 변환되어서 요청할 때 들어감! }); }