MVVM(Model-View-ViewModel) 패턴은 UI와 비즈니스 로직을 분리하는 소프트웨어 아키텍처 패턴입니다. Flutter에서 MVVM 패턴을 사용하면 코드의 가독성과 유지보수성을 높이고, 테스트 용이성을 개선할 수 있습니다.
구성 요소
1. Model
• 데이터 구조와 비즈니스 로직을 포함합니다.
• 데이터 소스(예: 데이터베이스, API)와 상호작용합니다.
2. View
• 사용자 인터페이스를 담당합니다.
• 사용자 입력을 받습니다.
• ViewModel에서 제공하는 데이터를 표시합니다.
3. ViewModel
• View와 Model 간의 중개자 역할을 합니다.
• Model에서 데이터를 가져와서 View에 전달합니다.
• 사용자 입력을 처리하고, Model을 업데이트합니다.
• ChangeNotifier를 상속받아 데이터 변경 시 View에 알립니다.
MVVM 패턴의 장점
1. 유지보수성 향상: UI와 비즈니스 로직이 분리되어 코드를 더 쉽게 관리할 수 있습니다.
2. 재사용성: ViewModel과 Model은 특정 UI에 의존하지 않으므로 재사용이 가능합니다.
3. 테스트 용이성: UI와 독립적으로 ViewModel과 Model을 테스트할 수 있습니다.
4. 유연성: 다양한 플랫폼과 프레임워크에서 사용 가능합니다.
Flutter에서 MVVM 패턴 구현 예제
1. Model
Model 클래스는 데이터를 정의하고, 데이터와 상호작용하는 로직을 포함합니다.
class User {
String username;
String email;
User({required this.username, required this.email});
}
2. ViewModel
ViewModel 클래스는 ChangeNotifier를 상속받아 상태 변화를 알립니다. View와 Model 간의 데이터 처리를 담당합니다.
import 'package:flutter/material.dart';
import 'model/user.dart';
class UserViewModel extends ChangeNotifier {
User _user = User(username: '', email: '');
String get username => _user.username;
String get email => _user.email;
void setUsername(String username) {
_user.username = username;
notifyListeners();
}
void setEmail(String email) {
_user.email = email;
notifyListeners();
}
}
3. View
View 클래스는 사용자 인터페이스를 정의하고, ViewModel과 상호작용합니다.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'viewmodel/user_viewmodel.dart';
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('MVVM Example')),
body: Padding(
padding: EdgeInsets.all(16.0),
child: Column(
children: [
TextField(
onChanged: (value) {
context.read<UserViewModel>().setUsername(value);
},
decoration: InputDecoration(labelText: 'Username'),
),
TextField(
onChanged: (value) {
context.read<UserViewModel>().setEmail(value);
},
decoration: InputDecoration(labelText: 'Email'),
),
SizedBox(height: 20),
Consumer<UserViewModel>(
builder: (context, viewModel, child) {
return Column(
children: [
Text('Username: ${viewModel.username}'),
Text('Email: ${viewModel.email}'),
],
);
},
),
],
),
),
);
}
}
void main() {
runApp(
ChangeNotifierProvider(
create: (_) => UserViewModel(),
child: MaterialApp(home: MyHomePage()),
),
);
}
요약
• Model: 데이터 구조와 비즈니스 로직을 정의합니다.
• View: 사용자 인터페이스를 담당하며, 사용자 입력을 처리합니다.
• ViewModel: Model과 View 간의 중개자로서, 데이터를 처리하고 View에 전달합니다.
다른예시(실습)
파일목록
datasource
- datasource.dart
repository
- album_datasource_repository.dart
viewModel
- album_view_model.dart
view
- album_mvvm_view.dart
datasource.dart
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:flutter_lecture/model/album.dart';
class Datasource {
Future<List<Album>> getAlbumList() async {
final response = await http
.get(Uri.parse("https://jsonplaceholder.typicode.com/albums"));
return jsonDecode(response.body)
.map<Album>((json) => Album.fromJson(json))
.toList();
}
}
album_datasource_repository.dart
import 'package:flutter_lecture/datasource/datasource.dart';
import '../model/album.dart';
class AlbumDatasourceRepository {
final Datasource _datasource = Datasource();
Future<List<Album>> getAlbumList() {
return _datasource.getAlbumList();
}
}
album_view_model.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter_lecture/repository/album_datasource_repository.dart';
import '../model/album.dart';
class AlbumViewModel with ChangeNotifier {
late final AlbumDatasourceRepository _albumRepository;
List<Album> _albumList = List.empty(growable: true);
List<Album> get albumList => _albumList;
AlbumViewModel() {
_albumRepository = AlbumDatasourceRepository();
_getAlbumList();
}
Future<void> _getAlbumList() async{
_albumList = await _albumRepository.getAlbumList();
notifyListeners();
}
}
album_mvvm_view.dart
import 'package:flutter/material.dart';
import 'package:flutter_lecture/model/albums.dart';
import 'package:flutter_lecture/bloc/album_bloc.dart';
import 'package:flutter_lecture/viewModel/album_view_model.dart';
import 'package:provider/provider.dart';
import '../model/album.dart';
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
late List<Album> albumList;
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<AlbumViewModel>(
create: ((context) => AlbumViewModel()),
child: Scaffold(
appBar: AppBar(
title: const Text("test title"),
),
body: Consumer<AlbumViewModel>(
builder: (context, provider, child) {
albumList = provider.albumList;
return ListView.builder(
itemCount: albumList.length,
itemBuilder: (context, index) {
return Container(
padding: const EdgeInsets.all(10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("ID: ${albumList[index].id.toString()}"),
Text("Title: ${albumList[index].title}" )
],
),
);
}
);
},
),
),
);
}
}
참고 자료
• MVVM 패턴
이 패턴을 사용하면 Flutter 애플리케이션의 구조를 더 명확하게 만들고, 유지보수와 테스트를 더 쉽게 할 수 있습니다.
'프론트엔드 > Flutter' 카테고리의 다른 글
flutter todo-app 만들기 (1) | 2024.07.23 |
---|---|
flutter scrollview pagination 예제 및 정리 (2) | 2024.07.22 |
flutter form 제출 및 라우터 이동 (0) | 2024.07.22 |
flutter webview 사용방법 (0) | 2024.07.22 |
flutter local notifications - 알림 (0) | 2024.07.22 |