Flutter Provider

Flutter Provider 상세 소개 (Flutter Provider Detailed Introduction)

1. Provider란? (What is Provider?)

Provider는 Flutter에서 공식적으로 권장하는 상태 관리 패턴입니다.

  • 상태 관리: UI가 데이터 변경을 감지하고 자동으로 업데이트됨.
  • 의존성 주입: 객체를 효율적으로 공유할 수 있음.
  • Flutter의 기본 철학과 잘 맞음: InheritedWidget을 기반으로 하여 성능이 우수함.

💡 Bloc과 비교하면 코드가 간결하고, GetX보다 공식적인 지원을 받는 것이 장점입니다.


2. Provider 설치 (Installing Provider)

pubspec.yaml 파일에 provider 패키지를 추가합니다.

dependencies:
  provider: latest_version  # 최신 버전 사용

최신 버전 확인: pub.dev Provider

설치 후 flutter pub get 실행!


3. Provider 기본 사용법 (Basic Usage of Provider)

3.1 ChangeNotifier를 이용한 상태 관리 (Using ChangeNotifier)

1️⃣ 상태 클래스 생성

import 'package:flutter/material.dart';

class CounterProvider extends ChangeNotifier {
  int _counter = 0;

  int get counter => _counter; // Getter

  void increment() {
    _counter++;
    notifyListeners(); // UI 업데이트 요청
  }
}

💡 notifyListeners()를 호출하면 Provider를 구독하는 모든 UI가 업데이트됩니다.


2️⃣ Provider 등록 (main.dart)

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'counter_provider.dart'; // 위에서 만든 Provider

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => CounterProvider(),
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomeScreen(),
    );
  }
}

💡 ChangeNotifierProvider를 사용하여 CounterProvider를 앱 전역에서 사용할 수 있도록 등록합니다.


3️⃣ UI에서 Provider 사용

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'counter_provider.dart';

class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final counterProvider = Provider.of<CounterProvider>(context);

    return Scaffold(
      appBar: AppBar(title: Text('Provider 예제')),
      body: Center(
        child: Text('카운터: ${counterProvider.counter}', style: TextStyle(fontSize: 24)),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: counterProvider.increment,
        child: Icon(Icons.add),
      ),
    );
  }
}

💡 Provider.of<CounterProvider>(context)를 사용하여 상태를 가져오고, 버튼을 클릭하면 상태가 업데이트됩니다.

장점: notifyListeners() 덕분에 UI가 자동으로 업데이트됨.
단점: context가 필요하여 코드가 길어질 수 있음.


3.2 Consumer를 이용한 상태 관리 (Using Consumer)

Consumer를 사용하면 Provider.of보다 효율적으로 UI를 빌드할 수 있습니다.

class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Consumer 예제')),
      body: Center(
        child: Consumer<CounterProvider>(
          builder: (context, counterProvider, child) {
            return Text('카운터: ${counterProvider.counter}', style: TextStyle(fontSize: 24));
          },
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => context.read<CounterProvider>().increment(),
        child: Icon(Icons.add),
      ),
    );
  }
}

장점: Consumer를 사용하면 필요한 부분만 리빌드되어 성능이 향상됨.
단점: Consumer가 많아지면 코드 가독성이 떨어질 수 있음.


3.3 Selector를 이용한 최적화 (Using Selector for Optimization)

Selector는 특정 값이 변경될 때만 UI를 업데이트하도록 최적화할 수 있습니다.

class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Selector 예제')),
      body: Center(
        child: Selector<CounterProvider, int>(
          selector: (context, provider) => provider.counter, // 특정 값만 감지
          builder: (context, counter, child) {
            return Text('카운터: $counter', style: TextStyle(fontSize: 24));
          },
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => context.read<CounterProvider>().increment(),
        child: Icon(Icons.add),
      ),
    );
  }
}

장점: 불필요한 UI 리빌드를 방지하여 성능 향상.
단점: 코드가 다소 길어질 수 있음.


4. MultiProvider 사용 (Using MultiProvider)

여러 개의 Provider를 동시에 사용해야 할 경우 MultiProvider를 사용하면 됩니다.

void main() {
  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (context) => CounterProvider()),
        ChangeNotifierProvider(create: (context) => AnotherProvider()),
      ],
      child: MyApp(),
    ),
  );
}

장점: 여러 개의 Provider를 쉽게 관리 가능.
단점: MultiProvider가 많아지면 코드가 길어질 수 있음.


5. 라우팅과 Provider (Navigation with Provider)

새로운 화면으로 이동할 때 Provider의 상태를 유지하려면 ChangeNotifierProvider.value를 사용합니다.

Navigator.push(
  context,
  MaterialPageRoute(
    builder: (context) => ChangeNotifierProvider.value(
      value: Provider.of<CounterProvider>(context, listen: false),
      child: NextScreen(),
    ),
  ),
);

장점: 새로운 화면에서도 기존 Provider 상태를 유지할 수 있음.
단점: listen: false를 설정해야 성능이 저하되지 않음.


6. 의존성 관리 (Dependency Injection with Provider)

Provider는 ProxyProvider를 이용하여 다른 Provider의 데이터를 의존성으로 주입할 수 있습니다.

ChangeNotifierProvider(create: (context) => UserProvider()),
ProxyProvider<UserProvider, AuthProvider>(
  update: (context, userProvider, authProvider) =>
      AuthProvider(userProvider.user),
)

장점: Provider 간의 의존 관계를 설정할 수 있음.


7. 결론 (Conclusion)

기능ProviderGetXBloc
사용법보통 ⭕쉬움 ✅어려움 ❌
성능우수 ✅빠름 ✅느림 ❌
공식 지원있음 ✅없음 ❌있음 ✅
의존성 관리있음 ✅있음 ✅있음 ✅

Provider는 공식적으로 지원되는 상태 관리 패턴이며, GetX보다 코드가 정형화되어 있어 유지보수에 유리합니다.
하지만, 코드가 길어질 수 있어 소규모 프로젝트에서는 GetX, 대규모 프로젝트에서는 Provider를 사용하는 것이 좋습니다. 🚀

Leave a Reply

Your email address will not be published. Required fields are marked *