[Flutter] MVVM + 상태관리
Flutter에서 Riverpod을 활용한 MVVM 패턴을 적용하면 상태 관리를 더욱 간결하고 효율적으로 할 수 있습니다.
이번 포스팅에서는 기본적인 MVVM 패턴을 Riverpod을 활용하여 개선하는 과정을 살펴보겠습니다.
1️⃣ MVVM 패턴이란?
👉 UI(View)와 비즈니스 로직(Model)을 ViewModel을 통해 분리하여 코드의 유지보수성과 테스트 용이성을 높이는 패턴
👉 기존의 setState() 방식 대신 Riverpod을 활용하여 전역 상태 관리
2️⃣ MVVM 패턴 구현
이번 구현에서는 Riverpod을 사용하여 MVVM 패턴을 적용할 것입니다.
📌 구조
- Model (todo_item.dart): 데이터 모델을 정의.
- ViewModel (todo_list_view_model.dart): 비즈니스 로직을 담당하고 Riverpod을 활용하여 상태 관리.
- View (todo_list_view.dart): UI를 관리하며 ViewModel과 상호작용.
📌 Step 1: Model 생성 (todo_item.dart)
👉 할 일(Todo) 항목을 정의하는 데이터 모델
// Model
// 1. 우리가 관리 하고 싶은 데이터 (창고 안에 넣을 상품)
class TodoItem {
String title;
bool isDone;
TodoItem({required this.title, this.isDone = false});
// 상태 변경 시 불변 객체를 유지하기 위한 copyWith 메서드 추가
TodoItem copyWith({String? title, bool? isDone}) {
return TodoItem(
title: title ?? this.title,
isDone: isDone ?? this.isDone,
);
}
}
📌 역할
- title: 할 일 제목.
- isDone: 완료 여부.
- 불변 객체 원칙을 유지하기 위해 copyWith 메서드 추가.
📌 Step 2: ViewModel 생성 (todo_list_view_model.dart)
👉 비즈니스 로직을 담당하며, 상태를 Riverpod으로 관리
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../models/todo_item.dart';
// ViewModel - 상태 관리 및 비즈니스 로직을 담당
class TodoListViewModel extends Notifier<List<TodoItem>> {
@override
List<TodoItem> build() {
return [];
}
// 할 일 추가
void addItem(String title) {
state = [...state, TodoItem(title: title)];
}
// 할 일 완료 상태 변경 (불변 객체 유지)
void toggleItem(TodoItem todo) {
state = state
.map((item) => item == todo ? item.copyWith(isDone: !item.isDone) : item)
.toList();
}
}
// Provider - ViewModel을 Riverpod 상태 관리에 등록
final todoListViewModelProvider =
NotifierProvider<TodoListViewModel, List<TodoItem>>(
() => TodoListViewModel(),
);
📌 개선된 점
- Riverpod을 활용하여 상태 관리 (NotifierProvider 사용).
- state = [...state, TodoItem(title: title)]; → 기존 리스트를 변경하지 않고 새로운 객체를 생성하여 업데이트.
- toggleItem에서 불변 객체(copyWith)를 유지하여 안전한 상태 관리.
📌 Step 3: View 생성 (todo_list_view.dart)
👉 ViewModel을 사용하여 UI를 업데이트하는 역할
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../models/todo_item.dart';
import '../view_models/todo_list_view_model.dart';
class TodoListView extends ConsumerWidget {
final TextEditingController _controller = TextEditingController();
TodoListView({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
// ViewModel의 상태를 감시 (자동 업데이트)
final todos = ref.watch(todoListViewModelProvider);
// ViewModel 인스턴스를 가져와서 메서드 실행
final todoNotifier = ref.read(todoListViewModelProvider.notifier);
return Flexible(
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(16.0),
child: TextField(
controller: _controller,
decoration: InputDecoration(
hintText: 'Enter todo item...',
suffixIcon: IconButton(
onPressed: () {
if (_controller.text.isNotEmpty) {
todoNotifier.addItem(_controller.text);
_controller.clear();
}
},
icon: Icon(Icons.add),
),
),
),
),
SizedBox(height: 16.0),
Expanded(
child: ListView.builder(
itemCount: todos.length,
itemBuilder: (context, index) {
final TodoItem item = todos[index];
return ListTile(
title: Text(item.title),
trailing: Checkbox(
value: item.isDone,
onChanged: (value) {
todoNotifier.toggleItem(item);
},
),
);
},
),
)
],
),
);
}
}
📌 개선된 점
- ConsumerWidget을 사용하여 Riverpod 상태를 구독.
- ref.watch(todoListViewModelProvider) → 자동으로 UI 업데이트.
- View에서는 ViewModel의 메서드를 호출하는 역할만 수행.
📌 Step 4: 앱 실행 (main.dart)
👉 ProviderScope를 사용하여 Riverpod을 활성화하고 TodoListView를 표시
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_statement_v01/_mvvm/_04/views/todo_list_view.dart';
void main() {
runApp(ProviderScope(child: TodoApp()));
}
class TodoApp extends StatelessWidget {
const TodoApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: SafeArea(
child: Scaffold(
appBar: AppBar(
title: const Text('TodoList'),
),
body: TodoListView(),
),
),
);
}
}
📌 역할
- ProviderScope를 추가하여 Riverpod 상태 관리 활성화.
- TodoListView()를 Scaffold 내에 배치하여 UI 구성.
📌 MVVM 패턴 적용 후 개선된 점
개선 사항 | 기존 방식 (setState()) | Riverpod 적용 방식 |
상태 관리 | setState()를 통해 직접 관리 | NotifierProvider를 활용하여 전역 상태 관리 |
비즈니스 로직 위치 | View에서 직접 관리 | ViewModel에서 관리 |
UI 업데이트 | setState() 호출 시 전체 위젯 재빌드 | 필요한 부분만 자동 업데이트 |
불변 객체 유지 | 기존 객체를 직접 수정 | copyWith을 활용하여 상태 변경 |
📝 결론
✅ Riverpod을 활용한 MVVM 패턴의 장점
✔ UI와 비즈니스 로직을 완전히 분리하여 유지보수성이 향상됨.
✔ Riverpod을 통해 전역 상태를 효율적으로 관리하여 코드의 가독성이 향상됨.
✔ View는 UI를 렌더링하는 역할만 수행하고, ViewModel에서 모든 상태 관리를 담당하여 구조가 깔끔해짐.
✔ 불변 객체 원칙을 유지하여 예측 가능한 상태 관리 가능.
👉 Riverpod을 활용하면 더 유지보수하기 좋은 구조로 Flutter 애플리케이션을 개발할 수 있습니다! 🚀
MVVM 패턴이 궁금하다면??
[Flutter] MVVM TodoList 만들기
📝 Flutter MV 패턴 & MVVM 패턴 정리Flutter에서 MV (Model-View) 패턴과 MVVM (Model-View-ViewModel) 패턴을 활용하여 코드의 유지보수성과 가독성을 높일 수 있습니다.이번 포스팅에서는 MV → MVVM으로 발전하는
seohong.tistory.com
[Flutter] MVVM 패턴에 대해서 알아 보자
📝 Flutter MVVM 패턴 정리Flutter에서 애플리케이션을 개발할 때, 유지보수성과 확장성을 높이기 위해 MVVM (Model-View-ViewModel) 패턴을 적용할 수 있습니다.이번 포스팅에서는 MVVM 패턴의 개념과 코드
seohong.tistory.com