Contents
1. 게시글 목록 보기패키지 구조

1. 게시글 목록 보기
1) PostListBody
post_list_body.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 {
print("body 로드 완료");
return ListView.separated(
itemCount: model.posts.length,
itemBuilder: (context, index) {
return InkWell(
onTap: () {
Navigator.push(
context, MaterialPageRoute(builder: (_) => PostDetailPage()));
},
child: PostListItem(post: model.posts[index]),
);
},
separatorBuilder: (context, index) {
return const Divider();
},
);
}
}
}
- ListView.separated
- separatorBuilder를 통해서 item 사이 구분 선을 넣을 수 있습니다.
- InkWell
- 터치 가능한 영역을 만들어주는 위젯입니다.
2) PostListModel
post_list_model.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"],
// Map<String, dynamic> 이니까 dynamic으로 인식 중이라 묵시적 형변환 후 .map 사용 가능
posts = (map["posts"] as List<dynamic>)
.map((e) => Post.fromMap(e))
.toList();
}
final postListProvider = NotifierProvider<PostListVM, PostListModel?>(() {
return PostListVM();
});
class PostListVM extends Notifier<PostListModel?> {
final mContext = navigatorKey.currentContext!;
PostRepository postRepository = const PostRepository();
@override
PostListModel? build() {
init(0);
return null; // watch로 볼 예정이라 반환값 필요 x
}
Future<void> init(int page) async {
Map<String, dynamic> responseBody =
await postRepository.findAll(page: page);
// 리스트 로딩 실패 시
if (!responseBody["success"]) {
ScaffoldMessenger.of(mContext!).showSnackBar(
SnackBar(
content: Text("게시글 목록 보기 실패 : ${responseBody["errorMessage"]}")),
);
return;
}
state = PostListModel.fromMap(responseBody["response"]);
}
}
- class PostListModel
- PostListBody에만 쓰이는 데이터를 모아둔 객체입니다.
- int 타입의 경우 double값이 들어오지 않는지 체크해야 합니다.
- List<Post> posts;
- 자바와 달리 inner class 문법이 없기 때문에 따로 class를 만들어야 합니다.
- PostListModel.fromMap(Map<String, dynamic> map)
- posts = (map["posts"] as List<dynamic>).map
- map["posts"] 는 dynamic 타입이라 .map 메서드를 쓰기 위해 명시적으로 캐스팅함으로써, Dart 컴파일러가 이 값이 실제로는 리스트라는 것을 알 수 있게 합니다.
:
→ 이니셜라이저는 클래스의 생성자를 의미하며, 객체를 생성할 때 초기화하는 방법을 정의합니다.- PostListModel 클래스의 이니셜라이저는 fromMap 팩토리 생성자입니다.
- init(0);
- 시작 페이지는 0페이지라서 0을 파라미터로 넣습니다.
post.dart
class Post {
int? id;
String? title;
String? content;
DateTime? createdAt;
DateTime? updatedAt;
int? bookmarkCount;
User? user;
bool? isBookmark; // post detail에서 쓰기 위해 추가
Post.fromMap(Map<String, dynamic> map)
// 필드 이름 겹치는 거 없으니까 this. 생략 가능
: id = map["id"],
title = map["title"],
content = map["content"],
createdAt = DateFormat("yyyy-mm-dd").parse(map["createdAt"]),
updatedAt = DateFormat("yyyy-mm-dd").parse(map["updatedAt"]),
bookmarkCount = map["bookmarkCount"],
isBookmark = map["isBookmark"],
user = User.fromMap(map["user"]);
}
user.dart
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"];
}
3) PostRepository
post_repository.dart
class PostRepository {
const PostRepository();
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.get 요청에서 쿼리 파라미터는 /api/post?page=0 로 적지 말고 queryParameters 속성에 추가하는 것이 좋습니다.
Share article