inblog logo
|
jjack1
    Flutter

    [Flutter] riverpod 통신 with mock

    만들면서 배우는 플러터 앱 프로그래밍
    최재원's avatar
    최재원
    Jul 30, 2025
    [Flutter] riverpod 통신 with mock
    Contents
    1. 프로젝트 생성2. 의존성 추가3. 창고 코드 작성(기본형)1. 창고 데이터 타입2. 창고3. 창고 관리자4. 창고 코드 작성(모델 일체형)1. 창고 데이터 타입2. 창고3. 창고 관리자5. repository 코드 작성1. repository6. 창고 코드 작성(모델 분리형)1. 창고 데이터 타입7. repository 코드 작성

    프로젝트 코드

    https://github.com/flutter-lec/data_sample
    graph LR view --> viewModel viewModel --> repository repository --> viewModel viewModel --> view

    1. 프로젝트 생성

    notion image
    • 안드로이드 스튜디오로 새 플러터 프로젝트 생성

    2. 의존성 추가

    flutter_riverpod: ^2.6.1 dio: ^5.8.0+1 // 통신 라이브러리

    3. 창고 코드 작성(기본형)

    1. 창고 데이터 타입

    /// #### 3. 창고 데이터 타입 /// /// - 페이지이름으로 타입 이름을 작성 /// /// - 클래스 내부에 다른 클래스 같이 생긴 타입이 필요 하면 레코드를 사용 하자 /// - final을 사용해서 변경금지를 강제하자 class Post { final int id; final int userId; final String title; /// named type record List<({int id, String comment, String owner})> replies; Post(this.id, this.userId, this.title, this.replies); }
    • 화면에 사용될 데이터 타입
    • 스프링 서버의 DTO 라고 생각 하면 된다
    • 단일 데이터 타입이 아니라 컬랙션을 사용할 경우 레코드 타입을 사용하자
      • 선택적 매개변수를 사용해 이름이 있는 레코드 타입을 만들자
    • 이름은 페이지 이름

    2. 창고

    /// 2. 창고 /// - 페이지이름VM 으로 타입 이름을 작성 /// - 통신을 받을 동안 null 을 사용해야하니 ? 타입을 사용하자 class PostVM extends Notifier<Post?> { @override Post? build() { init(); // 비동기로 만들어서 일단 null 을 return 하자 return null; } /// 실행 타이밍 /// 1. build 할때 실행 void init() {} }
    • 창고가 생성되면 일단 state 를 null 로 한다
    • null 을 return 하기 때문에 타입에 ? 를 추가하자
    • 나중에 init() 메서드를 호출해서 데이터 통신 후 state 를 변경하자
    • 이름은 페이지 이름 + VM

    3. 창고 관리자

    /// 1. 창고 관리자 /// - 1번 타입 -> 창고 타입 /// - 2버 타입 -> 데이터 타입 final postProvider = NotifierProvider<PostVM, Post?>(() { return PostVM(); });
    • view 에서 watch, read 를 실행하면 창고 관리자가 생성된다
    • 창고 관리자는 생성 되면서 창고가 생성된다
    • 제네릭
        1. 창고 타입
        1. 데이터 타입
    • 이름은 페이지 이름 + Provider
     

    4. 창고 코드 작성(모델 일체형)

    전체코드
    import 'package:data_sample/repository/post_repository.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; /// #### 1. 창고 관리자 /// - 1번 타입 -> 창고 타입 /// /// - 2번 타입 -> 데이터 타입 /// view에서 watch or read 하면 NotifierProvider 이놈이 실행된다 final postProvider = NotifierProvider<PostVM, Post?>(() { return PostVM(); // 비동기로 만들어서 일단 null 을 return 하자 }); /// 2. 창고 /// - 책임 : 데이터 변경 /// - 책임 : 비즈니스 로직(스프링의 서비스) /// - '페이지이름VM' 으로 타입 이름을 작성 /// /// - 통신을 받을 동안 null 을 사용해야하니 ? 타입을 사용하자 class PostVM extends Notifier<Post?> { PostRepository postRepository = PostRepository.instance; // 컴포지션 /// build() 함수는 무조건 동기적으로 작성해야한다 @override Post? build() { init(); return null; // build에서 return 하는 값이 state 값이 된다 } /// 2가지 전략 Future<void> update() async { // 통신 Post post = await postRepository.update(); state = state!.copyWith(title: post.title); // state = post; } /// 실행 타이밍 /// 1. build 할때 실행 Future<void> init() async { // 1. 데이터 받기 Post post = await postRepository.findById(1); // // 2. 파싱 -> repo에서 파싱하면 안씀 // Post post = Post.fromMap(data); state = post; } } /// #### 3. 창고 데이터 타입 /// /// - '페이지이름'으로 타입 이름을 작성 /// /// - 클래스 내부에 다른 클래스 같이 생긴 타입이 필요 하면 레코드를 사용 하자 /// - final을 사용해서 변경금지를 강제하자 class Post { final int? id; final int? userId; final String? title; /// named type record final List<({int id, String comment, String owner})> replies; Post(this.id, this.userId, this.title, this.replies); /// 1. toJson -> 요청 DTO /// 2. fromJson -> 응답 DTO. fromMap 으로 만들거다. (Dio 통신라이브러리가 json -> map 해준다). GPT가 만들어주는 어노테이션 쓰지말자 /// 무엇을(restApi서버 -> dio -> map 데이터) -> 어떻게 (post 데이터) /// 생성자로 만든다 Post Post.fromMap(Map<String, dynamic> map) : id = map["id"], userId = map["userId"], title = map["title"], replies = (map["replies"] != null) ? (map["replies"] as List) .map( (e) => ( id: e["id"] as int, comment: e["comment"] as String, owner: e["owner"] as String, ), ) .toList() : []; /// #### 3. copyWith -> 깊은 복사를 해야함 /// - ? 타입을 사용해서 변경하고 싶은 것만 넣자 Post copyWith({ int? id, int? userId, String? title, List<({int id, String comment, String owner})>? replies, }) { return Post( id ?? this.id, userId ?? this.userId, title ?? this.title, replies ?? this.replies, ); } }

    1. 창고 데이터 타입

    1. 필드 값

    class Post { final int? id; final int? userId; final String? title; /// named type record final List<({int id, String comment, String owner})> replies; Post(this.id, this.userId, this.title, this.replies); ...
    • 필드 이름은 ?타입을 사용해서 통신으로 받지 못하더라도 예외가 발생하지 않게 하자
    • 필드 값들은 final 을 사용해 임의 변경을 강제로 차단하자

    2. fromMap()

    class Post{ ... Post.fromMap(Map<String, dynamic> map) : id = map["id"], userId = map["userId"], title = map["title"], replies = (map["replies"] != null) ? (map["replies"] as List) .map( (e) => ( id: e["id"] as int, comment: e["comment"] as String, owner: e["owner"] as String, ), ) .toList() : []; ...
    • 위 코드 말고 GPT 가 알려주는 코드는 사용하지 말자
    • 무엇을(restApi서버 -> dio -> map 데이터) -> 어떻게 (post 데이터)
    • 원래는 fromJson 유형이다. 통신으로 받아온 json 데이터를 다트의 클래스로 변환 하는 방법을 작성한다
    • dio 라이브러리는 통신을 하고 나서 json → map 으로 변환 해준다
      • 변환 되는 타입은 Map<String, dynamic> 이다
    • 따라서 우리는 map 을 객체로 변환하는 코드를 작성한다
    • 이름이 있는 생성자를 사용해 map 데이터를 클래스로 맵핑한다
      • static 메서드로 만들지 않는 이유는 Post 가 new 되어 있지 않은 상태에서 호출해야 하기 때문
    • dynamic 타입 이기 때문에 형변환이 필요함
      • Dart는 최상위 Map의 단순 필드 접근에서는 비교적 관대하게 추론함
      • 따라서 최상위 변수는 명시적 형변환이 필요 없음
      • 내부 Map은 명시적 형변환이 필요한 이유
        • map["replies"]는 List<dynamic>으로 간주됨
        • e는 dynamic이기 때문에, e["id"] 같은 접근을 할 경우 Dart는 타입 정보를 추론할 수 없음
        • 따라서 e["id"]는 무조건 dynamic으로 취급됨
        • 각 필드를 명시적으로 타입 변환 (as int, as String) 해야 함

    3. copyWith()

    class Post{ ... Post copyWith({ int? id, int? userId, String? title, List<({int id, String comment, String owner})>? replies, }) { return Post( id ?? this.id, userId ?? this.userId, title ?? this.title, replies ?? this.replies, ); } }
    • 깊은 복사
      • 이유 → 모든 데이터가 아니라 일부만 변경했을 때 사용
      • 불변을 위해 객체를 새로 생성한다
    • ? 타입을 사용해 넣고 싶은 값만 넣을 수 있다
     

    2. 창고

    1. 기본 클래스

    class PostVM extends Notifier<Post?> { PostRepository postRepository = PostRepository.instance; // 컴포지션 /// build() 함수는 무조건 동기적으로 작성해야한다 @override Post? build() { init(); return null; // build에서 return 하는 값이 state 값이 된다 } ... }
    • 책임
      • 데이터 변경
      • 서비스 로직
    • 통신에 필요한 repo 를 클래스 내부 변수로 할당해서 컴포지션 한다
      • instance는 싱글톤으로 만들어 졌다(static)
    • build() 메서드를 자동으로 실행한다. 창고가 new 될 때
      • 무조건 실행이기 때문에 동기적으로 작성해야 한다
      • 오버라이드로 구현한다
    • 통신으로 데이터를 변경한다면 일단 null 을 return

    2. init()

    class PostVM extends Notifier<Post?> { ... Future<void> init() async { // 1. 데이터 받기 Post post = await postRepository.findById(1); // // 2. 파싱 -> repo에서 파싱하면 안씀 // Post post = Post.fromMap(data); state = post; } }
    • vm 에 데이터를 초기화 시키는 로직이 들어간다
    • 실행 타이밍은 build 시 실행한다
    • 서버에 데이터를 요청 후 모델을 초기화 한다
    • repo 에서 map 데이터를 그대로 받아와 파싱을 할 수도 있다. repo에서 파싱해서 들고 올 수 도 있다

    3. update()

    class PostVM extends Notifier<Post?> { ... /// 2가지 전략 Future<void> update() async { // 통신 Post post = await postRepository.update(); // 일부 데이터만 받으면 state = state!.copyWith(title: post.title); // 전체 데이터를 받으면 // state = post; } ...
    • 필요한 추가 로직등을 여기에 메서드로 작성할 수 있다
    • 상태 업데이트가 필요하여 update() 메서드를 만들었다
    • repo 에 데이터를 전송한 뒤 응답 받은 데이터를 state 에 할당
      • 변경된 데이터의 응답을 일부만 받을 경우 copyWith() 을 사용한다
      • 변경된 데이터의 응답 전부를 받을 경우 그냥 바로 state 에 할당한다
    • 할당 할 때는 깊은 복사를 사용해야 riverpod 이 상태가 변경된 걸 감지한다

    3. 창고 관리자

    /// #### 1. 창고 관리자 /// - 1번 타입 -> 창고 타입 /// /// - 2번 타입 -> 데이터 타입 /// view에서 watch or read 하면 NotifierProvider 이놈이 실행된다 final postProvider = NotifierProvider<PostVM, Post?>(() { return PostVM(); // 비동기로 만들어서 일단 null 을 return 하자 });
    • view 에서 ConsumerWidget 을 사용해서 WidgetRef ref 에 접근 할 수 있게 되면
      • ref → 모든 viewMV(창고) 에 접근 할 수 있는 객체
    • ref.watch | ref.read 를 호출하면 그 때 NotifierProvider 가 실행이 되면서 창고와 창고 관리자가 초기화 된다
    • 첫 번째 타입 → 창고 타입
    • 두 번째 타입 → 데이터 타입
     

    번외(초보자용)

    초보자용 뷰모델(파싱을 여기서)
    import 'package:data_sample/repository/post_repository.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; /// #### 1. 창고 관리자 /// - 1번 타입 -> 창고 타입 /// /// - 2번 타입 -> 데이터 타입 /// view에서 watch or read 하면 NotifierProvider 이놈이 실행된다 final postProvider = NotifierProvider<PostVM, Post?>(() { return PostVM(); // 비동기로 만들어서 일단 null 을 return 하자 }); /// 2. 창고 /// - 책임 : 데이터 변경 /// - 책임 : 비즈니스 로직(스프링의 서비스) /// - '페이지이름VM' 으로 타입 이름을 작성 /// /// - 통신을 받을 동안 null 을 사용해야하니 ? 타입을 사용하자 class PostVM extends Notifier<Post?> { PostRepository postRepository = PostRepository.instance; // 컴포지션 /// build() 함수는 무조건 동기적으로 작성해야한다 @override Post? build() { init(); return null; // build에서 return 하는 값이 state 값이 된다 } /// 실행 타이밍 /// 1. build 할때 실행 Future<void> init() async { // 1. 데이터 받기 Map<String, dynamic> data = await postRepository.findById(1); // 2. 파싱 Post post = Post.fromMap(data); state = post; } } /// #### 3. 창고 데이터 타입 /// /// - '페이지이름'으로 타입 이름을 작성 /// /// - 클래스 내부에 다른 클래스 같이 생긴 타입이 필요 하면 레코드를 사용 하자 /// - final을 사용해서 변경금지를 강제하자 class Post { int id; int userId; String title; /// named type record List<({int id, String comment, String owner})> replies; Post(this.id, this.userId, this.title, this.replies); /// 1. toJson -> 요청 DTO /// 2. fromJson -> 응답 DTO. fromMap 으로 만들거다. (Dio 통신라이브러리가 json -> map 해준다). GPT가 만들어주는 어노테이션 쓰지말자 /// 무엇을(restApi서버 -> dio -> map 데이터) -> 어떻게 (post 데이터) /// 생성자로 만든다 Post Post.fromMap(Map<String, dynamic> map) : id = map["id"], userId = map["userId"], title = map["title"], replies = (map["replies"] as List) .map((e) => (id: e["id"] as int, comment: e["comment"] as String, owner: e["owner"] as String)) .toList(); /// #### 3. copyWith -> 깊은 복사를 해야함 /// - ? 타입을 사용해서 변경하고 싶은 것만 넣자 Post copyWith({ int? id, int? userId, String? title, List<({int id, String comment, String owner})>? replies, }) { return Post( id ?? this.id, userId ?? this.userId, title ?? this.title, replies ?? this.replies, ); } }
    초보자용 레파지토리(여기서 파싱x)
    /// - 책임 : 서버 접근, db 접근 /// - 책임 : 데이터 파싱 /// 휴대기기 내부 db, 서버 db에 접근해야하기 때문에 각각 repo 를 만들어야 한다 /// 통신을 위한 repo class PostRepository { // const PostRepository(); // const 는 있는지 찾아야 한다. 싱글톤으로 만들면 바로 찾는다 static PostRepository instance = PostRepository._single(); // 싱글톤 생성 PostRepository._single(); // 이름있는 생성자만 만들면 기본생성자를 호출할 수 없다 Future<Map<String, dynamic>> findById(int id) async { // 1. 통신 // 자바스크립트의 setInterval Map<String, dynamic> response = await Future.delayed(Duration(seconds: 5), () { return _mockPost; }); // 2. 파싱 x -> 리턴 // 나중에 파싱에 오류 try catch 처리를 하자 return response; } } // 가짜 데이터 final Map<String, dynamic> _mockPost = { "id": 1, "userId": 3, "title": "제목1", "replies": [ {"id": 1, "comment": "댓글1", "owner": "ssar"}, {"id": 2, "comment": "댓글2", "owner": "cos"}, {"id": 3, "comment": "댓글3", "owner": "love"}, ], };
     
     
     

    1차 유형 DTO 처럼 불변으로 사용하는 방법

    창고 데이터를 DTO 처럼 불변으로 관리하는 방법이다
    단점 변경이 있을 때 마다 copyWith을 사용할 때 여러개의 필드값을 넣어주는 반복을 해야 한다
     
    1차 유형 riverpod
    1차 유형 repo

    5. repository 코드 작성

    전체코드
    import 'package:data_sample/pages/post_vm.dart'; /// - 책임 : 서버 접근, db 접근 /// - 책임 : 데이터 파싱 /// 휴대기기 내부 db, 서버 db에 접근해야하기 때문에 각각 repo 를 만들어야 한다 /// 통신을 위한 repo class PostRepository { // const PostRepository(); // const 는 있는지 찾아야 한다. 싱글톤으로 만들면 바로 찾는다 static PostRepository instance = PostRepository._single(); // 싱글톤 생성 PostRepository._single(); // 이름있는 생성자만 만들면 기본생성자를 호출할 수 없다 Future<Post> findById(int id) async { // 1. 통신 // 자바스크립트의 setInterval Map<String, dynamic> response = await Future.delayed(Duration(seconds: 5), () { return _mockPost; }); // 2. 파싱 x -> 리턴 // 나중에 파싱에 오류 try catch 처리를 하자 Post post = Post.fromMap(response); return post; } Future<Post> update() async { // 1. 통신 코드 Map<String, dynamic> response = await Future.delayed( Duration(seconds: 5), () { return _mockUpdatePost; }, ); Post post = Post.fromMap(response); // 2. 리턴 return post; } } // 가짜 데이터 final Map<String, dynamic> _mockUpdatePost = {"id": 1, "userId": 3, "title": "제목10"}; // 가짜 데이터 final Map<String, dynamic> _mockPost = { "id": 1, "userId": 3, "title": "제목1", "replies": [ {"id": 1, "comment": "댓글1", "owner": "ssar"}, {"id": 2, "comment": "댓글2", "owner": "cos"}, {"id": 3, "comment": "댓글3", "owner": "love"}, ], };

    1. repository

    1. repository

    class PostRepository { // const PostRepository(); // const 는 있는지 찾아야 한다. 싱글톤으로 만들면 바로 찾는다 static PostRepository instance = PostRepository._single(); // 싱글톤 생성 PostRepository._single(); // 이름있는 생성자만 만들면 기본생성자를 호출할 수 없다 ... }
    • 생성자를 const 로 작성해서 단일 repository로 만들어서 공유해서 사용할 수 있다
      • 단점 → heap 메모리에서 해당 데이터를 검색하는 과정이 있다
    • static 으로 싱글톤 방법으로 생성
      • _를 붙여서 private 로 이름이 있는 생성자를 만들고 static 변수를 만들어 바로 초기화 해서 사용한다
      • 이름이 있는 생성자만 존재하면 기본 생성자로 호출 x

    findbById()

    class PostRepository { ... Future<Post> findById(int id) async { // 1. 통신 // 자바스크립트의 setInterval Map<String, dynamic> response = await Future.delayed(Duration(seconds: 5), () { return _mockPost; }); // 2. 파싱 x -> 리턴 // 나중에 파싱에 오류 try catch 처리를 하자 Post post = Post.fromMap(response); return post; } ... }
    • repository 는 스프링의 repository 처럼 사용하면 된다
    • 서버 혹은 DB 에서 데이터를 요청하는 로직이 있다
    • 데이터를 요청하면 원래는 json을 받지만 dio 라이브러리를 사용하면 Map 으로 변환 해준다
    • dio 로 부터 받은 Map 데이터를 이름 있는 생성자 fromMap 을 사용해 파싱한다
    • 파싱된 객체를 리턴 한다
    • 다트도 싱글 스레드이기 때문에 await 를 사용해 비동기 통신을 해야 한다
    • 비동기를 사용하는 함수의 리턴 값은 Future 타입 이다
    • 임시로 delayed를 사용해 통신하는 것처럼 만들어 사용한다
    • 리턴하는 데이터는 mock 데이터다

    update()

    Future<Post> update() async { // 1. 통신 코드 Map<String, dynamic> response = await Future.delayed( Duration(seconds: 5), () { return _mockUpdatePost; }, ); Post post = Post.fromMap(response); // 2. 리턴 return post; }
    • 이것도 마찬가지로 통신 로직이다
    • 업데이트 후 받은 데이터를 fromMap 으로 파싱한다
    • 임시로 delayed를 사용해 통신하는 것처럼 만들어 사용한다
    • 리턴하는 데이터는 mock 데이터다

    mockData

    // 가짜 데이터 final Map<String, dynamic> _mockPost = { "id": 1, "userId": 3, "title": "제목1", "replies": [ {"id": 1, "comment": "댓글1", "owner": "ssar"}, {"id": 2, "comment": "댓글2", "owner": "cos"}, {"id": 3, "comment": "댓글3", "owner": "love"}, ], };
    // 가짜 데이터 final Map<String, dynamic> _mockUpdatePost = {"id": 1, "userId": 3, "title": "제목10"};
    • 실제 데이터를 요청할 수 없는 상황에서 사용
    • 이 mock 데이터를 가지고 가짜 통신 코드를 작성한다
    • 나중에 실제 데이터가 존재하면 데이터 부분만 변경하면 된다

    6. 창고 코드 작성(모델 분리형)

    전체코드
    import 'package:data_sample/model/post.dart'; import 'package:data_sample/model/reply.dart'; import 'package:data_sample/repository/post_repository.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; // 1. 창고 관리자 final postProvider = NotifierProvider<PostVM, PostModel?>(() { return PostVM(); }); // 2. 창고 (비지니스 로직) class PostVM extends Notifier<PostModel?> { PostRepository postRepository = PostRepository.instance; @override PostModel? build() { init(); return null; } Future<void> updateV2() async { // 통신 (제목10) await postRepository.update(); Post prevPost = state!.post; prevPost.update("제목10"); state = state!.copyWith(post: prevPost); } Future<void> update() async { // 통신 final response = await postRepository.update(); Post nextPost = Post.fromMap(response); state = state!.copyWith(post: nextPost); } Future<void> init() async { final response = await postRepository.findById(1); state = PostModel.fromMap(response); } } // 3. 창고 데이터 타입 class PostModel { final Post post; final List<Reply> replies; // 레코드 // 생성자 PostModel({required this.post, required this.replies}); // fromMap 생성자 PostModel.fromMap(Map<String, dynamic> data) : post = Post.fromMap(data), replies = (data["replies"] as List<dynamic>).map((e) => Reply.fromMap(e)).toList(); // copyWith PostModel copyWith({ Post? post, List<Reply>? replies, }) { return PostModel( post: post ?? this.post, replies: replies ?? this.replies, ); } }

    1. 창고 데이터 타입

    1. 필드 값

    class PostModel { final Post post; final List<Reply> replies; // 레코드 // 생성자 PostModel({required this.post, required this.replies}); ... }
    • 이름을 페이지명+Model 로 작성
    • 내부 데이터는 각각의 공유 사용이 가능한 model 을 가져와 사용한다(컴포지션)
    • final 을 사용해 초기화 후 변경을 금지한다
    • 생성자는 선택적 매개변수로 만들어 이름이 보이도록 만든다

    2. fromMap

    class PostModel { ... PostModel.fromMap(Map<String, dynamic> data) : post = Post.fromMap(data), replies = (data["replies"] as List<dynamic>).map((e) => Reply.fromMap(e)).toList(); ... }
    • 이름 있는 생성자
    • map 을 가져와서 각각의 model 로 파싱하고 초기화 한다

    3. copyWith

    class PostModel { ... PostModel copyWith({ Post? post, List<Reply>? replies, }) { return PostModel( post: post ?? this.post, replies: replies ?? this.replies, ); } }
    • 상태 변경을 위한 깊은 복사 메서드
    • 선택적 매개변수를 사용해 필요한 부분만 받는다
    • ?? 연산자를 사용해 왼쪽이 null 이면 오른쪽을 넣는다

    4. Post(model)

    class Post { int? id; int? userId; String? title; void update(String title) { this.title = title; } // 일반 생성자 Post(this.id, this.userId, this.title); // fromMap 생성자 (initializer list 사용) Post.fromMap(Map<String, dynamic> data) : id = data['id'], userId = data['userId'], title = data['title']; }
    • 공유 가능한 데이터는 따로 분리해서 model 로 사용한다
    • 이 model 은 데이터 변경이 가능하기 때문에 ? 타입을 사용한다
    • 스프링의 엔티티처럼 사용하면 된다
    • 상태 변경 메서드를 추가해도 된다
    • 마찬가지로 이름있는 생성자를 사용한다

    5. Reply(model)

    class Reply { int? id; String? comment; String? owner; // 일반 생성자 Reply(this.id, this.comment, this.owner); // fromMap 생성자 Reply.fromMap(Map<String, dynamic> data) : id = data['id'], comment = data['comment'], owner = data['owner']; }
    • 공유 가능한 데이터는 따로 분리해서 model 로 사용한다
    • 이 model 은 데이터 변경이 가능하기 때문에 ? 타입을 사용한다
    • 스프링의 엔티티처럼 사용하면 된다
    • 상태 변경 메서드를 추가해도 된다
    • 마찬가지로 이름있는 생성자를 사용한다

    7. repository 코드 작성

    5번과 동일
    // 서버접근 + 파싱 class PostRepository { static PostRepository instance = PostRepository._single(); PostRepository._single(); Future<Map<String, dynamic>> findById(int id) async { // 1. 통신 코드 final response = await Future.delayed( Duration(seconds: 2), () { return _mockPost; }, ); // 2. 리턴 return response; } Future<Map<String, dynamic>> update() async { // 1. 통신 코드 final response = await Future.delayed( Duration(seconds: 2), () { return _mockUpdatePost; }, ); // 2. 리턴 return response; } } // 가짜 데이터 final Map<String, dynamic> _mockUpdatePost = {"id": 1, "userId": 3, "title": "제목10"}; // 가짜 데이터 final Map<String, dynamic> _mockPost = { "id": 1, "userId": 3, "title": "제목1", "replies": [ {"id": 1, "comment": "댓글1", "owner": "ssar"}, {"id": 2, "comment": "댓글2", "owner": "cos"}, {"id": 3, "comment": "댓글3", "owner": "love"}, ], };
    출처: 만들면서 배우는 플러터 앱 프로그래밍
     
    Share article
    Contents
    1. 프로젝트 생성2. 의존성 추가3. 창고 코드 작성(기본형)1. 창고 데이터 타입2. 창고3. 창고 관리자4. 창고 코드 작성(모델 일체형)1. 창고 데이터 타입2. 창고3. 창고 관리자5. repository 코드 작성1. repository6. 창고 코드 작성(모델 분리형)1. 창고 데이터 타입7. repository 코드 작성

    jjack1

    RSS·Powered by Inblog