목차

  1. MVVM 패턴
    1. MVVM 패턴이란?
    2. MVVM 패턴 구조
  2. 사용된 기능 설명
  3. 사용 예시

 

 

✅ MVVM 패턴

  1. MVVM 패턴이란?
    • MVVMModel-View-ViewModel의 약자로, UI와 비즈니스 로직을 깔끔하게 분리하는 패턴
  2. MVVM 패턴 구조
    • Model: 데이터 구조와 변환 (= 데이터 설계)
    • DataSource: 실제 데이터 소스 접근(API/DB)
    • Repository: 데이터 저장소로, DataSource와 ViewModel 중계
    • ViewModel: View의 상태 관리, 데이터 가공 등 View의 비즈니스 로직을 담당
    • View: 화면 표시, 사용자 이벤트 처리, ViewModel 구독

 

 

 

✅ 사용된 기능 설명

  • ChangeNotifierProvider : Provider 패키지에서 제공하는 상태 제공자. ChangeNotifier를 생성하고, 하위 위젯 트리에서 접근 가능하도록 제공
  • Consumer<T> : Provider에서 제공한 데이터를 구독하는 위젯으로 상태가 바뀌면 자동으로 rebuild. 특정 타입(T)의 Provider만 구독 가능
  • notifyListeners() : ChangeNotifier를 상속한 ViewModel 클래스에서 상태가 바뀌었음을 구독자(View)에 알림.
    이를 통해 Flutter의 UI를 자동으로 다시 그리게(rebuild) 함

 

 

 

✅ 사용 예시

 

1️⃣ model

 

 

2️⃣ dataSource

 

 

3️⃣ repository

 

 

4️⃣ viewModel

 

 

5️⃣ view



 

6️⃣ 테스트

  • HTTP 에서 JSON 데이터 받아올 때, iOS 정책에 의해 에러 발생
  • chrome으로 실행 시, 데이터 정상 작동

HTTP 에서 JSON 데이터 받아올 때, iOS 정책에 의해 에러 발생
chrome으로 실행 시, 데이터 정상 작동

 

 

 

 

✔️ 사용된 코드

1️⃣ model

class Album {
  int? userId;
  int? id;
  String? title;

  Album({this.userId, this.id, this.title});

  factory Album.fromJson(Map<String, dynamic> json) {
    return Album(userId: json['userId'], id: json['id'], title: json['title']);
  }
}

 

2️⃣ dataSource

import 'dart:convert';
import 'package:http/http.dart' as http;
import '../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();
  }
}

 

3️⃣ repository

import '../dataSource/dataSource.dart';
import '../model/album.dart';

class AlbumRepository{
  final DataSource _dataSource = DataSource();

  Future<List<Album>> getAlbumList(){
    return _dataSource.getAlbumList();
  }
}

 

4️⃣ viewModel

import 'package:flutter/material.dart';
import '../model/album.dart';
import '../repository/albumRepository.dart';

class AlbumViewModel with ChangeNotifier{
  late final AlbumRepository _albumRepository;
  List<Album> _albumList = List.empty(growable: true);
  List<Album> get albumList => _albumList;

  AlbumViewModel(){
    _albumRepository = AlbumRepository();
    _getAlbumList();
  }

  Future<void> _getAlbumList() async{
    _albumList = await _albumRepository.getAlbumList();
    notifyListeners();  // ChangeNotifier를 상속한 ViewModel 클래스에서 상태가 바뀌었음을 View에 알림
  }
}

 

5️⃣ view

import "package:flutter/material.dart";
import "package:provider/provider.dart";
import "../model/album.dart";
import "../viewModel/albumViewModel.dart";

class AlbumView extends StatefulWidget {
  const AlbumView({super.key});

  @override
  State<AlbumView> createState() => _AlbumViewState();
}

class _AlbumViewState extends State<AlbumView> {
  late List<Album> albumList;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("MVVM 실습"),
      ),
       body: Consumer<AlbumViewModel>(   // Consumer<T> : Provider에서 제공한 데이터를 구독하는 위젯으로 상태가 바뀌면 자동으로 rebuild. 특정 타입(T)의 Provider만 구독 가능
        builder: (context, provider, child){
          albumList = provider.albumList;
          return ListView.builder(
            itemCount: albumList.length,
            itemBuilder: (context, index){
              return Container(
                padding: const EdgeInsets.all(15),
                child: Text("${albumList[index].id} : ${albumList[index].title}"),
              );
            }
          );
        }
      )
    );
  }
}

+ Recent posts