Flutter : Provider 와 Riverpod

2025. 1. 14. 21:00

Flutter에서 상태관리는 상태변화를 감지해서 UI를 업데이트하고 데이터를 동기화하는 등 많은 부분을 관리하는데 필수적입니다. 상태관리를 하는데 여러가지 방법이 있는데, 오늘은 많이 사용되는 setState, Provider, RiverPod에 대해서 알아보겠습니다.

 

1. setState

setState는 Flutter에서 기본으로 제공하는 상태 관리 방법으로 StateFulWidget을 통해 사용할 수 있습니다. 

 

import 'package:flutter/material.dart';

class CounterApp extends StatefulWidget {
  @override
  _CounterAppState createState() => _CounterAppState();
}

class _CounterAppState extends State<CounterApp> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('setState 예제'),
      ),
      body: Center(
        child: Text('버튼이 눌린 횟수: $_counter'),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

void main() => runApp(MaterialApp(home: CounterApp()));

 

위의 코드는 setState를 사용한 예시입니다. setState는 변화를 감지하면 flutter에게 변화를 알려서 업데이트합니다.

보시면 statefulWidget를 사용하는 상태에서 변화가 있는 _counter를 setState안에서 관리하면서 counter값을 UI에 적용하고 있습니다. 이렇게 상태를 관리할 수 있습니다.

 

단점

하지만 flutter의 기본 상태관리 기능이기 때문에 몇가지 단점이 있습니다.

  • setState는 상태가 감지되면 build함수를 호출하게 됩니다. 그렇기 때문에 불필요하게 많은 랜더링이 되어 앱의 성능이 느려질 수 있습니다.
  • setState는 위젯안에서만 국한되어, 상태를 여러 위젯이 공유해야하는 상태에서는 구조가 복잡해집니다.
  • 비동기 처리를 할때 여러가지 setState가 있을경우 상태가 중첩되어 이상이 생길 수 있다.

이러한 문제들을 해결하기 위해서 사용되는 상태관리 패키지들이 provider와 riverpod 입니다.

 

 

 

Provider

provider는 의존성 주입을 통해서 상태를 관리하는 라이브러리입니다.

 

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

class Counter with ChangeNotifier {
  int _count = 0;

  int get count => _count;

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Provider 예제')),
        body: Center(
          child: Consumer<Counter>(
            builder: (context, counter, child) {
              return Text('버튼이 눌린 횟수: ${counter.count}');
            },
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            Provider.of<Counter>(context, listen: false).increment();
          },
          tooltip: 'Increment',
          child: Icon(Icons.add),
        ),
      ),
    );
  }
}

 

ChangeNotifier를 통해서 setState처럼 상태를 변화하는 클래스를 생성해줍니다. 이를 ChangeNotifierProvider를 통해서 앱이 상태를 받게 됩니다. 그리고 나서 Provider.of() 혹은 Consumer를 상용해서 상태를 위젯에 전달하여 UI를 업데이트 합니다.

 

단점

  • provider도 changeNotifier를 사용할때 해당하는 상태를 확인하는 모든 위젯이 다시 렌더링하게는 문제가 있습니다.
  • 테스트가 불가능해서 독립적인 기능을 확인하기 어렵습니다.
  • 위젯의 생명주기를 관리하지 않기 때문에 따로 관리를 해주어야하는 문제가 있습니다.

 

Riverpod

Riverpod은 provider의 발전되 형태의 상태관리 라이브러이입니다. provider의 기능을 사용하면서 더 안전하고 유연하면서 많은 기능을 지원합니다.

 

특징

  • Null Safety: Riverpod는 Dart의 null safety를 완벽하게 지원하여, 런타임 오류를 줄이고 코드의 안정성을 높입니다.
  • 전역 상태 관리: 애플리케이션의 어느 곳에서든 상태를 쉽게 접근하고 관리할 수 있습니다.
  • 테스트 용이성: 상태를 독립적으로 관리할 수 있어, 테스트가 용이합니다. Provider의 상태를 Mocking하기 쉬워, 유닛 테스트가 간편합니다.
  • 비동기 처리 지원: 비동기 작업을 쉽게 처리할 수 있는 FutureProvider와 StreamProvider를 제공합니다.

 

void main() {
  runApp(
    ProviderScope(
      child: MyApp(),
    ),
  );
}

 

사용할때는 가장먼저 MyApp()을 ProviderScope로 감쌉니다.

 

class Counter extends StateNotifier<int> {
  Counter() : super(0); // 초기 상태

  void increment() {
    state++; // 상태 변경
  }
}

final counterProvider = StateNotifierProvider<Counter, int>((ref) {
  return Counter();
});

class CounterScreen extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider); // 상태 구독

    return Scaffold(
      appBar: AppBar(
        title: Text('Riverpod 카운터 예제'),
      ),
      body: Center(
        child: Text('버튼이 눌린 횟수: $count'),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          ref.read(counterProvider.notifier).increment(); // 상태 변경
        },
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

 

상태를 관리하는StateNotifier를 상속 받는 클래스를 만들고 이를 StateNotifierProvider로 정의 합니다.

그리고 상태를 ConsumerWidget안에서 ref.watch나 ref.read를 통해서 상태를 구독하고 상태를 변경합니다.

 

단점

  • 학습 곡선 : riverpod을 사용하기 위해서는 상태관리, provider 등 여러가지 알아야하는 것이 있어서 학습 곡선이 있다는 것이 단점입니다.
  • Boilerplate 코드 : 사용하기 위해서 설정해야하는 코드들이 다수 있어서 간단한 프로젝트에서는 불필요하게 코드가 많아질 수 있습니다.

 

 

 

오늘은 이렇게 상태를 하는 방법과 상태관리를 위해서 사용하는 라이브러리 몇가지를 알아보았습니다. 상태관리는 필수는 아니지만 상태가 변화가 발생할 경우 반드시 필요해집니다. 그렇기 때문에 위의 방법을 알아두시면 도움이 될 것입니다. 또한 다른 방법들도 공부해보시면 도움이 될 것입니다.

'Flutter' 카테고리의 다른 글

폰트 적용  (0) 2025.02.06
flutter Statefulwigets 생명주기  (0) 2025.01.15
Clean Architecture  (0) 2025.01.13
Firebase 쉽게 연결하기  (1) 2024.12.20
Navigation VS GoRouter  (0) 2024.12.20

BELATED ARTICLES

more