Provider 기초개념정리 + 예제

2024. 9. 18. 18:17·Flutter/Flutter

Provider란?

Provider는 상태 관리를 쉽게 하게 해준다. 여러 화면에서 같은 상태를 공유할 수 있고, 상태가 변경되면 UI를 자동으로 업데이트한다.

class Counter with ChangeNotifier {
  int _count = 0;

  int get count => _count;

  void increment() {
    _count++;
    notifyListeners();  // 상태 변경 알림
  }
}

ChangeNotifier

상태가 변경될 때 UI에 알림을 보내 다시 그리도록 한다.

class MyModel extends ChangeNotifier {
  int value = 0;

  void increment() {
    value++;
    notifyListeners();  // UI에 상태가 변경되었음을 알림
  }
}

ChangeNotifierProvider

ChangeNotifier를 앱에서 사용할 수 있도록 해주는 도구다.

ChangeNotifierProvider(
  create: (_) => MyModel(),
  child: MyApp(),
);

MultiProvider

여러 상태를 다루고 싶을 때 사용한다.

MultiProvider(
  providers: [
    ChangeNotifierProvider(create: (_) => MyModel1()),
    ChangeNotifierProvider(create: (_) => MyModel2()),
  ],
  child: MyApp(),
);

Provider.of(context)

Provider에 저장된 상태를 가져올 때 사용한다.

int value = Provider.of<MyModel>(context).value;

Consumer

상태가 변경될 때 해당 부분만 다시 그리도록 도와준다.

Consumer<MyModel>(
  builder: (context, model, child) {
    return Text(model.value.toString());
  },
);

context.watch()

상태를 구독하고, 변경이 발생하면 UI를 다시 그린다.

int value = context.watch<MyModel>().value;

context.read()

상태를 한 번만 가져오고, 이후 상태 변경을 감지하지 않는다.

context.read<MyModel>().increment();

notifyListeners()

상태가 변경되면 구독하는 모든 위젯에 알림을 보낸다.

void increment() {
  value++;
  notifyListeners();  // 구독 중인 모든 위젯에 알림
}

성능 최적화

Consumer나 Selector를 사용해 필요한 부분만 다시 그리도록 하여 성능을 최적화한다.

Consumer<MyModel>(
  builder: (context, model, child) {
    return Text(model.value.toString());
  },
);

이 코드들을 통해 상태를 효율적으로 관리하고 성능을 최적화할 수 있다.

 

더 잘 이해하기 위해 버스가 도착하는 시간을 알려주는 버스알림앱을 예로 들어보도록 하자

 

코드 예시 1)

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'bus_view_model.dart';
import 'user_favorite_bus_view_model.dart';

class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // Provider를 사용해 BusViewModel과 UserFavoriteBusViewModel을 주입받음
    final busViewModel = Provider.of<BusViewModel>(context);  
    final favoriteBusViewModel = Provider.of<UserFavoriteBusViewModel>(context);

    return Scaffold(
      appBar: AppBar(title: Text('즐겨찾기한 버스')),
      body: Column(
        children: [
          // 버튼 클릭 시 BusViewModel의 fetchBusStops() 호출 -> API 호출
          ElevatedButton(
            onPressed: () {
              busViewModel.fetchBusStops(); // API 호출
            },
            child: Text('버스 도착 정보 불러오기'),
          ),
          // 즐겨찾기된 버스 리스트 출력
          Expanded(
            child: ListView.builder(
              itemCount: favoriteBusViewModel.favoriteBuses.length,
              itemBuilder: (context, index) {
                final bus = favoriteBusViewModel.favoriteBuses[index];
                return ListTile(
                  title: Text('${bus.busNumber}번 버스 - ${bus.stopName}'),
                  subtitle: Text('도착 시간: ${bus.arrivalTime}'),
                  // 즐겨찾기 삭제 버튼
                  trailing: IconButton(
                    icon: Icon(Icons.delete),
                    onPressed: () {
                      favoriteBusViewModel.removeFavoriteBus(bus); // 즐겨찾기 삭제
                    },
                  ),
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}

 

  • Provider 주입: Provider.of<T>(context)를 사용해 BusViewModel과 UserFavoriteBusViewModel을 각각 주입받음.
  • API 호출: 버튼 클릭 시 busViewModel.fetchBusStops()를 호출해 버스 도착 정보를 가져오는 API 호출.
  • 즐겨찾기 목록: favoriteBusViewModel.favoriteBuses에서 즐겨찾기된 버스 리스트를 가져와 화면에 표시.
  • 즐겨찾기 삭제: 삭제 버튼 클릭 시 favoriteBusViewModel.removeFavoriteBus(bus)로 즐겨찾기된 버스를 삭제.

 

2) 메인의 코드를 봐보자 

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'bus_view_model.dart';
import 'user_favorite_bus_view_model.dart';
import 'home_screen.dart';
import 'bus_list_screen.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // MultiProvider로 여러 ViewModel을 제공
    return MultiProvider(
      providers: [
        // BusViewModel을 주입하여 상태 관리
        ChangeNotifierProvider(create: (_) => BusViewModel()),
        // UserFavoriteBusViewModel을 주입하여 즐겨찾기 상태 관리
        ChangeNotifierProvider(create: (_) => UserFavoriteBusViewModel()),
      ],
      child: MaterialApp(
        title: 'Bus API Example',
        home: HomeScreen(), // 초기 화면을 HomeScreen으로 설정
        routes: {
          '/bus-list': (context) => BusListScreen(), // '/bus-list' 경로 설정
        },
      ),
    );
  }
}

 

 

  • MultiProvider: BusViewModel과 UserFavoriteBusViewModel 두 개의 상태(ViewModel)를 제공.
  • ChangeNotifierProvider: 각 ViewModel을 ChangeNotifierProvider로 주입하여 상태 관리.
  • MaterialApp: 앱의 초기 화면을 HomeScreen으로 설정하고, /bus-list 경로를 설정해 화면 간 이동 가능하게 함.

 


궁금증, 프로바이더가 MVVM에 쓰이는 기본 상태관리 툴이라고 했는데, 그렇다면 ViewModel 자체에 프로바이더를 씌우는게 당연한가?


< 이야기를 나눠 본 결과 >

보통은 그렇게 쓴다고 한다. 보통 RxSwift에서 쓰는 형태로 생각하면 될 것 같다. 상태는 보통 Data라고 생각해 상태관리하면 그 안에 있는 데이터만 바뀌어야한다고 생각했다. 편협한 생각이었을지도. MVVM에 안맞는 생각이었던거같은데, 좀 더 고민해볼 여지가 있는듯하다.

댓글로 다르게 생각하는, 혹시 첨언을 해주실 여러분의 의견을 정말 환영하며

'Flutter > Flutter' 카테고리의 다른 글

build method 와 render tree를 모르고 hot reload를 논하지마라  (4) 2024.09.25
플러터 프로젝트에서 DI 주입은 어떻게 이루어지는가  (1) 2024.09.19
1. 앱에 어울리는 애니메이션 구현하기 <Lottie편>  (1) 2024.09.16
mainColor를 const로 고정해주고 싶었을 뿐인데  (0) 2024.08.30
위젯 만들 때 마다 계속 나오는 @override와 super.key는 뭐지?  (2) 2024.08.26
'Flutter/Flutter' 카테고리의 다른 글
  • build method 와 render tree를 모르고 hot reload를 논하지마라
  • 플러터 프로젝트에서 DI 주입은 어떻게 이루어지는가
  • 1. 앱에 어울리는 애니메이션 구현하기 <Lottie편>
  • mainColor를 const로 고정해주고 싶었을 뿐인데
복복씨
복복씨
개발자여, 사고하라 !
  • 복복씨
    정리노트
    복복씨
  • 전체
    오늘
    어제
    • 분류 전체보기 (119) N
      • 개발새발자 (22) N
        • 의 삶 (7)
        • 의 회고 (9)
        • 의 낙서장 (6) N
        • 영어 (0)
      • Flutter (39)
        • 새싹 (5)
        • Dart (8)
        • Flutter (15)
        • iOS 에서 Flutter 로 전환하며 (2)
        • 챗지피티랑놀.기 (3)
        • 하루 한 입 플러터 (2)
      • CS (7)
        • 짤막지식 (6)
      • IOS (6)
        • Swift (0)
        • UIKit (1)
        • SwitUI (4)
      • 머신러닝-딥러닝 (28)
        • 논문리뷰 (3)
        • study (16)
        • Kaggle (9)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    시그널링데이터
    schedulemicrotask
    dart
    플러터
    FLUTTER
    unawaited
    veo3
    깊은참조
    asmr 프롬프트
    핫 리로드
    부트캠프
    IOS
    코드 결합도
    flutter 애니메이션
    swiftui 플러터
    새싹 용산
    futurerecord2
    expando
    용산캠
    getit
    새싹 플러터
    runzonedguarded
    플러터 di
    멋쟁이 사자처럼
    유리과일
    유리과일 자르기
    플러터 새싹
    사그널링서버
    새싹
    ai asmr
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.0
복복씨
Provider 기초개념정리 + 예제
상단으로

티스토리툴바