1. post_repository.dart
class PostRepository {
const PostRepository();
Future<Map<String, dynamic>> add(Map<String, dynamic> data) async {
Response response = await dio.post("/api/post", data: data);
Map<String, dynamic> body = response.data;
return body;
}
Future<Map<String, dynamic>> update(int id, Map<String, dynamic> data) async {
Response response = await dio.put("/api/post/${id}", data: data);
Map<String, dynamic> body = response.data;
return body;
}
}
- 메서드 작성은 API 문서를 참고했습니다.


2. PostUpdateForm
class PostUpdateForm extends ConsumerWidget {
final _formKey = GlobalKey<FormState>();
final _title = TextEditingController();
final _content = TextEditingController();
Post post;
PostUpdateForm(this.post);
@override
Widget build(BuildContext context, WidgetRef ref) {
PostDetailVM vm = ref.read(postDetailProvider(post.id!).notifier);
return Form(
key: _formKey,
child: ListView(
children: [
CustomTextFormField(
controller: _title,
initValue: "${post.title}",
hint: "Title",
),
const SizedBox(height: smallGap),
CustomTextArea(
controller: _content,
initValue: "${post.content}",
hint: "Content",
),
const SizedBox(height: largeGap),
CustomElevatedButton(
text: "글 수정하기",
click: () {
// 1. 사용자 입력값 받기
// 2. 유효성검사 (생략)
// 3. VM에게 위임
vm.update(post.id!, _title.text.trim(), _content.text.trim());
},
),
],
),
);
}
}
3. PostDetailVM
class PostDetailModel {
Post post;
PostDetailModel({required this.post});
PostDetailModel copyWith({Post? post}) {
return PostDetailModel(post: post ?? this.post);
}
PostDetailModel.fromMap(Map<String, dynamic> map) : post = Post.fromMap(map);
}
final postDetailProvider = NotifierProvider.family
.autoDispose<PostDetailVM, PostDetailModel?, int>(() {
return PostDetailVM();
});
class PostDetailVM extends AutoDisposeFamilyNotifier<PostDetailModel?, int> {
final mContext = navigatorKey.currentContext!;
PostRepository postRepository = const PostRepository();
@override
PostDetailModel? build(id) {
init(id);
return null;
}
Future<void> update(int id, String title, String content) async {
final requestBody = {
"title": title,
"content": content,
};
Map<String, dynamic> responseBody =
await postRepository.update(id, requestBody);
if (!responseBody["success"]) {
ScaffoldMessenger.of(mContext!).showSnackBar(
SnackBar(content: Text("게시글 수정 실패 : ${responseBody["errorMessage"]}")),
);
return;
}
state = PostDetailModel.fromMap(responseBody["response"]);
ref
.read(postListProvider.notifier)
.update(Post.fromMap(responseBody["response"]));
// 수정 중이던 게시글로 이동
Navigator.pop(mContext);
}
- update의 경우 PostDetailVM의 상태(state)와 PostListVM의 상태 모두 갱신해야 합니다.
4. PostWriteForm
class PostWriteForm extends ConsumerWidget {
final _formKey = GlobalKey<FormState>();
final _title = TextEditingController();
final _content = TextEditingController();
PostWriteForm({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
PostListVM vm = ref.read(postListProvider.notifier);
return Form(
key: _formKey,
child: ListView(
shrinkWrap: true,
children: [
CustomTextFormField(
controller: _title,
hint: "Title",
),
const SizedBox(height: smallGap),
CustomTextArea(
controller: _content,
hint: "Content",
),
const SizedBox(height: largeGap),
CustomElevatedButton(
text: "글쓰기",
click: () {
// 1. 사용자 입력값 받기
// 2. 유효성검사 (생략)
// 3. VM에게 위임
vm.add(_title.text.trim(), _content.text.trim());
},
),
],
),
);
}
}
- postDetailProvider는 매개변수로 postId를 받아야 하기 때문에 postListProvider를 호출했습니다.
5. PostListVM
class PostListModel {
// 생략
final postListProvider = NotifierProvider<PostListVM, PostListModel?>(() {
return PostListVM();
});
class PostListVM extends Notifier<PostListModel?> {
final refreshCtrl = RefreshController();
final mContext = navigatorKey.currentContext!;
PostRepository postRepository = const PostRepository();
// build 생략
void add(String title, String content) async {
final requestBody = {
"title": title,
"content": content,
};
Map<String, dynamic> responseBody = await postRepository.add(requestBody);
if (!responseBody["success"]) {
ScaffoldMessenger.of(mContext!).showSnackBar(
SnackBar(content: Text("글쓰기 실패 : ${responseBody["errorMessage"]}")),
);
return;
}
Post post = Post.fromMap(responseBody["response"]);
// 이전 상태 받아오기
PostListModel model = state!;
// 깊은 복사, 새 post가 앞으로
model.posts = [post, ...model.posts];
// 상태 갱신
state = state!.copyWith(posts: model.posts);
// postList 페이지로 이동
Navigator.pop(mContext);
}
void update(Post post) {
PostListModel model = state!;
// 기존 posts 리스트를 순회하면서 id가 일치하는 경우 업데이트된 post로 교체
model.posts = model.posts.map((p) => p.id == post.id ? post : p).toList();
// postList 상태 갱신
state = state!.copyWith(posts: model.posts);
}
}
- ref.read(postListProvider.notifier).update(Post.fromMap(responseBody["response"]));
- PostDetailVM 에서 호출하는 update 메서드를 여기에 적습니다.
Share article