Flutter

[Flutter] Provider + API + MVVM 패턴 적용

devyong 2024. 7. 2. 21:28

Flutter는 선언형 UI를 사용하고있기에 상태관리가 매우 중요하다.

기존 xml로 화면을 그린 안드로이드는 textView에 setText();하면 텍스트가 반영되는 구조였지만

선언형 UI에서는 dataBinding이 되어있지만 데이터 변경을 위젯에 알려야한다고 봐야한다.

 

setState()는 위젯전체를 그리는 방식으로 많은 리소스를 사용하여 비효율적이다.

또한 로직에 따라서 setState()가 무수히 많이 사용되는 경우가 있을 수 있다.

 

setState를 굳이 사용하지 않고 상태관리를할 수 있는 대표적인 패키지는 GetX, Provider, BLoC, RiverPod가 있다.

오늘은 Provider와 MVVM패턴의 결합을 알아 볼 것이다.

(Model, View, ViewModel구조를 사용하며 Repository, DataSource등을 구현하지 않음)

 

 

1. 외부 패키지 provider 추가

provider: ^6.0.5

 

 

2. ViewModel 클래스 생성

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import '../Model/itemListData.dart';

class itemListViewModel extends ChangeNotifier {

  late itemListData itemList;

  parsingData() async {

    final Uri parsingData = Uri.parse("API호출 URI");
    final http.Response response = await http.get(parsingData);

    Map<String, dynamic> itemListMap = jsonDecode((utf8.decode(response.bodyBytes)));
    itemList = ItemListData.fromJson(itemListMap);

    notifyListeners();

  }
}

 

앱 매인화면에는 상품리스트 목록이 노출될 예정이다.

itemListData는 dataModel이며, API파싱값을 json-> dart로 사용한다.

 

ChangeNofitier가 있어야 notifyListeners()가 가능하며 notifyListeners()는 provider위젯에 상태를 변화시킨다.

즉, itemListViewModel.parsingData()함수로 API호출 및 상태관리를 한번에 하는 것이다.

 

 

3. Model 클래스 생성

class itemListData {
  int? itemCount;
  List<productItem>? item;

  itemListData({this.itemCount, this.item});

  factory itemListData.fromJson(Map<String, dynamic> json) {
    return itemListData(
      itemCount: json['data']['itemCount'],
      item: (json['data']['list'] as List<dynamic>).map((item) => productItem.fromJson(item as Map<String, dynamic>)).toList(),
    );
  }
}

class productItem {
  int? no;
  String? title;
  String? thumb;
  String? id;
  int? price;
  String? url;

  productItem(
      {this.no,
      this.title,
      this.thumb,
      this.id,
      this.price,
      this.url});

  factory productItem.fromJson(Map<String, dynamic> json) {
    return productItem(
        no: json['no'],
        title: json['title'],
        thumb: json['thumb'],
        id: json['id'],
        price: json['price'],
        url: json['url']);
  }
}

 

다른예제는 int Count를 상태관리하는 예제가 많았지만, 나는 API response를 상태관리하고자한다.

(model클래스는 상태관리할 자료형에 맞춰서 수정하시길바랍니다.)

 

4.  provider사용을 위해 viewModel을 추가

runApp(
  MultiProvider(
    providers: [
      ChangeNotifierProvider(create: (BuildContext context) => itemListViewModel()),
      //
    ],
    child: const MyApp(),
  ),
);

해당 배열에 여러개의 provider추가가 가능하며, 누락시 런타임에서 에러가 발생할 수 있다.

 

5. 함수호출 및 데이터가져오기

 

(1) initState()에 함수로 itemList를 가져오기

await context.read<itemListViewModel>().parsingData();
itemList = context.read<itemListViewModel>().itemList;

 

(2) Widget build에서 적용

 

Text(
  // Provider 내에서 관리하는 count 데이터 관찰
    '${context.watch<itemListViewModel>().itemList.count}’
)

 

 

여기까지면 Provider, API, MVVM 적용이 완료되었다.