반응형

 

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}" )
                        ],
                      ),
                    );
                  }
                );
              },
          ),
        ),
    );
  }
}

 

 

참고 자료

 

Flutter 공식 문서

MVVM 패턴

 

이 패턴을 사용하면 Flutter 애플리케이션의 구조를 더 명확하게 만들고, 유지보수와 테스트를 더 쉽게 할 수 있습니다.

반응형

+ Recent posts