게시글 목록을 불러오겠습니다.
post_list_vm 만들기
게시글 목록 페이지의 View Model을 만들어줘야 합니다.
final postListProvider = NotifierProvider<PostListVM, PostListModel?>(() {
return PostListVM();
});
class PostListVM extends Notifier<PostListModel?> {
@override
PostListModel? build() {
init();
return null;
}
Future<void> init() async {
}
}
만들 때 해당 페이지에서만 사용하는 model이기 때문에 생성자를 만들어줘야 합니다.
class PostListModel {
bool isFirst;
bool isLast;
int pageNumber;
int size;
int totalPage;
List<Post> posts;
}
초기에는 null값을 받도록 만들어 줄 수도 있습니다.
하는 김에 Post, User class 만들기
class User {
int? id;
String? username;
String? imgUrl;
User.fromMap(Map<String, dynamic> map)
: this.id = map["id"],
this.username = map["username"],
this.imgUrl = map["imgUrl"];
}
class Post {
int? id;
String? title;
String? content;
DateTime? createdAt;
DateTime? updatedAt;
int? bookmarkCount;
bool? isBookmark;
User? user;
Post.fromMap(Map<String, dynamic> map)
: id = map["id"],
title = map["title"],
content = map["content"],
createdAt = DateFormat("yyyy-mm-dd").parse(map["createdAt"]),
updatedAt = DateFormat("yyyy-mm-dd").parse(map["createdAt"]),
bookmarkCount = map["bookmarkCount"],
isBookmark = map["isBookmark"],
user = User.fromMap(map["user"]);
}
.fromMap 메서드가 있습니다.
이 메서드는 응답받은 데이터를 받아서 ViewModel로 사용할 수 있도록 변경해주는 메서드입니다.
딴길로 샛다. 다시 통신으로 진행하자
post_list_vm에서 build가 실행 될 때,
init이 실행 됩니다. 초기에 실행되면서 init을 통해 repostitory에 통신을 요청합니다.
Future<void> init(int page) async {
Map<String, dynamic> responseBody = await postRepo.findAll(page: page); << 통신요청
if (!responseBody["success"]) {
ScaffoldMessenger.of(mContext!).showSnackBar(
SnackBar(
content: Text("게시글 목록보기 실패 : ${responseBody["errorMessage"]}")),
);
return;
}
state = PostListModel.fromMap(responseBody["response"]);
}
통신에서 (repository)
class PostRepository {
const PostRepository();
//Map<String, dynamic>
Future<Map<String, dynamic>> findAll({int page = 0}) async {
Response response =
await dio.get("/api/post", queryParameters: {"page": page});
Map<String, dynamic> body = response.data;
return body;
}
}
문서를 보면 게시글목록을 요청할 때는

파라미터를 요구하고 있습니다.
이때는 dio의 요청을 보낼 때 queryParameters를 적어줄 수 있습니다.
await dio.get("/api/post", queryParameters: {"page": page}); <<
통신이 끝나고 응답이 오면
응답이 오면 VM에서 후 처리를 진행합니다.
응답 상태가 불량이면
if (!responseBody["success"]) {
ScaffoldMessenger.of(mContext!).showSnackBar(
SnackBar(
content: Text("게시글 목록보기 실패 : ${responseBody["errorMessage"]}")),
);
return;
}
게시글 목록보기 실패를 띄워줍니다.
성공한다면
state = PostListModel.fromMap(responseBody["response"]);
만들어 두었던 fromMap을 사용하여 상태를 변경시켜 줍니다.
화면에서는 ref.watch를 통해 상태를 관찰하고 있기 때문에 자동으로 변경이 됩니다.
ViewModel 전체코드
import 'package:flutter/material.dart';
import 'package:flutter_blog/data/repository/post_repository.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../../data/model/post.dart';
import '../../../../main.dart';
class PostListModel {
bool isFirst;
bool isLast;
int pageNumber;
int size;
int totalPage;
List<Post> posts;
PostListModel.fromMap(Map<String, dynamic> map)
: isFirst = map["isFirst"],
isLast = map["isLast"],
pageNumber = map["pageNumber"],
size = map["size"],
totalPage = map["totalPage"],
posts = (map["posts"] as List<dynamic>)
.map((e) => Post.fromMap(e))
.toList();
// 묵시적 형변환 List<dynamic>으로 list타입이 된다. & map<String,dynamic>으로 받게된다. 이걸 Post.fromMap으로 하나씩 리스트에 넣는다.
}
class PostList_Post {}
class PostList_User {}
final postListProvider = NotifierProvider<PostListVM, PostListModel?>(() {
return PostListVM();
});
class PostListVM extends Notifier<PostListModel?> {
final mContext = navigatorKey.currentContext!;
PostRepository postRepo = const PostRepository();
@override
PostListModel? build() {
init(0);
return null;
}
Future<void> init(int page) async {
Map<String, dynamic> responseBody = await postRepo.findAll(page: page);
if (!responseBody["success"]) {
ScaffoldMessenger.of(mContext!).showSnackBar(
SnackBar(
content: Text("게시글 목록보기 실패 : ${responseBody["errorMessage"]}")),
);
return;
}
state = PostListModel.fromMap(responseBody["response"]);
}
}
View 전체코드
import 'package:flutter/material.dart';
import 'package:flutter_blog/ui/pages/post/detail_page/post_detail_page.dart';
import 'package:flutter_blog/ui/pages/post/list_page/post_list_vm.dart';
import 'package:flutter_blog/ui/pages/post/list_page/wiegets/post_list_item.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class PostListBody extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
PostListModel? model = ref.watch(postListProvider);
if (model == null) {
return Center(child: CircularProgressIndicator());
} else {
return ListView.separated(
itemCount: model.posts.length,
itemBuilder: (context, index) {
return InkWell(
onTap: () {
Navigator.push(
context, MaterialPageRoute(builder: (_) => PostDetailPage()));
},
child: PostListItem(model.posts[index]),
);
},
separatorBuilder: (context, index) {
return const Divider();
},
);
}
}
}
Share article