Post
import 'package:flutter_blog/data/models/user.dart';
class Post {
// TODO - (댓글은 추후 추가)
int id; // 게시물 ID
String title;
String content;
DateTime createdAt; // 생성일시
DateTime updatedAt; // 수정일시
User user; // 작성자 (관계형 데이터)
int bookmarkCount; // 북마크 수
// 현재 사용자의 북마크 여부 (로그인 상태에 따라 달라짐)
bool? isBookmark;
// post.createdAt.year - 2025
// post.createdAt.month - 5
// DateTime.now().difference(post.createdAt);
Post({
required this.id,
required this.title,
required this.content,
required this.createdAt,
required this.updatedAt,
required this.user,
required this.bookmarkCount,
this.isBookmark,
});
// User.fromMap(..)
// 문자열을 DateTime 형변환 처리 해주어야 한다.
Post.fromMap(Map<String, dynamic> data)
: id = data['id'],
title = data['title'],
content = data['content'],
createdAt = DateTime.parse(data['createdAt']),
updatedAt = DateTime.parse(data['updatedAt']),
isBookmark = data['isBookmark'],
user = User.fromMap(data['user']),
bookmarkCount = data['bookmarkCount'];
Post copyWith({
int? id,
String? title,
String? content,
User? user,
DateTime? createdAt,
DateTime? updatedAt,
int? bookmarkCount,
}) {
return Post(
id: id ?? this.id,
title: title ?? this.title,
content: content ?? this.content,
user: user ?? this.user,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
bookmarkCount: bookmarkCount ?? this.bookmarkCount,
);
}
// 디버깅 용
@override
String toString() {
return 'Post{id: $id, title: $title, content: $content, createdAt: $createdAt, updatedAt: $updatedAt, user: $user, bookmarkCount: $bookmarkCount, isBookmark: $isBookmark}';
}
}
PostUpdateModel
// 서버 통신 요청 결과 --->
// 통신 -- 로딩 -- 응답 (성공, 실패)
// 게시글 수정 모델
import 'package:flutter_blog/data/models/post.dart';
import 'package:flutter_blog/data/models/repository/post_repository.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:logger/logger.dart';
import 'package:validators/validators.dart';
class PostUpdateModel {
final bool? isLoading;
final String? error;
//final Post? updatePost;
PostUpdateModel({this.isLoading, this.error});
PostUpdateModel copyWith({bool? isLoading, String? error}) {
return PostUpdateModel(
isLoading: isLoading ?? this.isLoading,
error: error ?? this.error,
);
}
@override
String toString() {
return 'PostUpdateModel{isLoading: $isLoading, error: $error}';
}
} // end of PostUpdateModel
// 게시글 수정 비즈니스 모델 (메뉴얼)
class PostUpdateNotifier extends AutoDisposeNotifier<PostUpdateModel> {
@override
PostUpdateModel build() {
ref.onDispose(
() {
Logger().d("PostUpdateNotifier 파괴됨");
},
);
return PostUpdateModel();
}
Future<Map<String, dynamic>> updatePost(Post post) async {
try {
state = state.copyWith(isLoading: true);
Map<String, dynamic> response = await PostRepository().updateOne(post);
if (response["success"]) {
state = state.copyWith(isLoading: false);
return {"success": true, "message": "게시글이 성공적으로 수정 됨"};
} else {
state =
state.copyWith(isLoading: false, error: response["errorMessage"]);
return {"success": false, "message": response["errorMessage"]};
}
} catch (e) {
state = state.copyWith(isLoading: false);
return {"success": false, "errorMessage": "수정 중 오류 발생"};
}
}
}
// 일반 NotifierProvider
final counterProvider = NotifierProvider<CounterNotifier, int>(() {
return CounterNotifier();
});
// 문제: 한 번 생성되면 앱이 종료될 때까지 메모리에 남아있음
// - 화면을 벗어나도 상태가 유지됨
// - 메모리 누수 가능성
// - 불필요한 리소스 점유
AutoDisposeNotifier는 "일회성 또는 화면별 상태 관리"에 적합한 메모리 효율적인 Provider입니다.
- 사용해야 하는 경우: 화면별 폼, API 호출, 일회성 작업
- 사용하지 말아야 하는 경우: 전역 상태, 캐시, 여러 화면에서 공유하는 데이터
- 장점: 자동 메모리 관리, 리소스 절약, 깨끗한 상태 초기화
- 주의사항: 상태가 자동으로 사라지므로 지속적으로 유지해야 하는 데이터에는 부적합
만약 게시글별로 독립적인 수정 상태가 필요하다면
// 각 게시글 ID별로 독립적인 수정 상태 관리
final postUpdateProvider = AutoDisposeNotifierProvider.family<
PostUpdateNotifier, PostUpdateModel, int>(() {
return PostUpdateNotifier();
});
SnackBarUtil
import 'package:flutter/material.dart';
class SnackBarUtil {
/// 성공 메시지 표시 (녹색 배경)
static void showSuccess(BuildContext context, String message) {
if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(message),
backgroundColor: Colors.green,
duration: Duration(seconds: 2),
),
);
}
/// 에러 메시지 표시 (빨간색 배경)
static void showError(BuildContext context, String message) {
if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(message),
backgroundColor: Colors.red,
duration: Duration(seconds: 3),
),
);
}
/// 일반 정보 메시지 표시 (기본 배경)
static void showInfo(BuildContext context, String message) {
if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(message),
duration: Duration(seconds: 2),
),
);
}
/// 경고 메시지 표시 (주황색 배경)
static void showWarning(BuildContext context, String message) {
if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(message),
backgroundColor: Colors.orange,
duration: Duration(seconds: 2),
),
);
}
}'Flutter' 카테고리의 다른 글
| 레코드 문법과 tyepdef 에 대해 알아 보자. (2) | 2025.09.05 |
|---|---|
| 블로그 프로젝트 - 게시글 상세 보기 구현 하기 (1) | 2025.09.05 |
| 블로그 프로젝트 - 게시글 쓰기 구현 하기 ( Riverpod 상태관리 ) (0) | 2025.09.04 |
| 블로그 프로젝트 - 게시글 목록 구현 하기 (0) | 2025.09.04 |
| 블로그 프로젝트 - 로그아웃 기능 연결 및 UI 수정 (0) | 2025.09.04 |