플러터는 앱의 UI와 데이터 간의 동기화를 효율적으로 처리하기 위해 상태관리가 중요하다. Flutter는 위젯 트리를 기반으로 동작하기 때문에 상태가 변경되면 UI를 다시 빌드해야 하는데 이를 올바르게 상태를 관리하지 않으면 UI가 데이터와 불일치하거나 예상치 못한 오류가 발생할 수 있다.
Flutter 상태 관리 패키지
Flutter의 상태 관리는 크게 5가지 방법이 있는데 setState , GetX , Provider, Bloc , Riverpod 등이 있다.
1. setState
setState는 StatefulWidget의 기본적인 방법 중 하나이다. 이 방법은 주로 간단한 로컬 상태의 변화를 처리할 때 사용된다. setState는 해당 위젯의 상태가 변경되었음을 프레임워크에 알리고, UI를 갱신하기 위해 위젯의 build 메서드를 다시 호출하게 한다.
- setState가 있는 class의 전체 build 메서드가 다시 실행
- 모든 위젯이 재빌드
void _increase() {
setState(() {
_count++;
});
}
2. GetX
컨트롤러를 통해 상태를 관리하며 상태가 변경되면 UI가 자동으로 업데이트된다. 상태 관리뿐만 아니라 라우팅, 의존성 주입 등 다양한 기능 제공한다.
- GetX 컨트롤러를 통해 상태를 관리
- 상태 변화를 observable 변수로 정의
- 컨트롤러의 상태를 업데이트
- 변경을 감지하여 UI를 자동으로 재구성
import 'package:get/get.dart';
class CounterController extends GetxController {
// 상태 변화를 observable 변수로 정의
var counter = 0.obs;
void increment() => counter++;
}
class CounterScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Get.put()을 통해 CounterController를 의존성 주입
final CounterController controller = Get.put(CounterController());
return Scaffold(
body: Center(
// GetX가 상태 변화를 감지하여 UI를 자동으로 재구성
child: Obx(() => Text('Counter: ${controller.counter}')),
),
floatingActionButton: FloatingActionButton(
onPressed: controller.increment, // 버튼 클릭 시 상태를 업데이트
child: Icon(Icons.add),
),
);
}
}
3. Provider
의존성을 주입하여 상태의 변화를 효과적으로 관리하고 UI에 쉽게 반영할 수 있게 해준다. 데이터와 로직을 위젯 트리와 분리하여 보다 효율적으로 관리 가능하다.
- ChangeNotifier를 사용해 상태 변화를 알리고, Provider를 통해 해당 데이터를 위젯 트리에 주입
- UI에서 변경 사항을 Consumer 또는 context.watch로 감지
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class CounterProvider extends ChangeNotifier {
int _counter = 0;
int get counter => _counter;
void increment() {
_counter++;
notifyListeners();
}
}
void main() {
runApp(
ChangeNotifierProvider(
create: (_) => CounterProvider(),
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: CounterScreen(),
);
}
}
class CounterScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
final counterProvider = Provider.of<CounterProvider>(context);
return Scaffold(
body: Center(
child: Text('Counter: ${counterProvider.counter}'),
),
floatingActionButton: FloatingActionButton(
onPressed: counterProvider.increment,
child: Icon(Icons.add),
),
);
}
}
4. Bloc
Stream 기반 상태 관리 도구이다. 앱의 비즈니스 로직과 UI를 명확히 분리하고 이벤트(입력) → 상태(출력)의 패턴을 따른다.
- 이벤트(Event)를 전달하면 비즈니스 로직을 처리하고, 새로운 상태(State)를 반환
- UI는 상태 변화를 구독하고 이를 기반으로 갱신
import 'package:flutter_bloc/flutter_bloc.dart';
// 1. **비즈니스 로직을 처리하고 새로운 상태를 반환**
class CounterCubit extends Cubit<int> {
// 2. 초기 상태를 0으로 설정
CounterCubit() : super(0);
// 3. 이벤트 처리 - 상태를 1 증가시킴
void increment() => emit(state + 1); // emit()으로 새로운 상태를 전달
}
class CounterScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocProvider(
// 4. BlocProvider로 CounterCubit을 제공 (상태 관리를 위한 의존성 주입)
create: (_) => CounterCubit(),
child: Scaffold(
body: Center(
// 5. BlocBuilder로 상태를 구독하고, 상태 변화에 따라 UI를 갱신
child: BlocBuilder<CounterCubit, int>(
builder: (context, count) {
// 6. 상태 값(count)을 사용하여 UI 업데이트
return Text('Counter: $count');
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => context.read<CounterCubit>().increment(), // 7. 버튼 클릭 시 increment 이벤트 발생
child: Icon(Icons.add),
),
),
);
}
}
5. Riverpod
Provider 패키지를 기반으로 구축된 라이브러리이다. Provider의 주요 단점을 해결하고 보다 유연하고 안전한 상태 관리를 제공한다. 전역 상태 관리가 가능하며, Lazy Loading과 다양한 범위의 상태 관리를 지원한다.
- 상태를 Provider 객체로 정의
- ConsumerWidget이나 ref.watch를 통해 상태를 구독하고 변경
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter/material.dart';
// 1. **상태를 Provider 객체로 정의**
final counterProvider = StateProvider<int>((ref) => 0); // StateProvider를 사용하여 상태를 정의
class CounterScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
// 2. **ConsumerWidget이나 ref.watch를 통해 상태를 구독하고 변경**
final counter = ref.watch(counterProvider); // ref.watch()를 사용하여 상태를 구독하고, 상태 값 가져오기
return Scaffold(
body: Center(
// 3. 상태를 UI에 반영
child: Text('Counter: $counter'), // 상태(counter)를 표시
),
floatingActionButton: FloatingActionButton(
// 4. 상태 변경 - 버튼 클릭 시 상태를 증가
onPressed: () => ref.read(counterProvider.notifier).state++, // 상태를 업데이트하는 코드
child: Icon(Icons.add),
),
);
}
}
'개발 > flutter' 카테고리의 다른 글
[Flutter] draggable_list 패키지 개발 (0) | 2025.01.24 |
---|---|
[Flutter] 기본 패키지 불러오지 못할 때 발생한 에러 해결 방법 (0) | 2025.01.19 |
[Flutter] cached_network_image (1) | 2025.01.17 |
[Flutter] Supabase Realtime (0) | 2025.01.16 |
[Flutter] get_it (0) | 2024.09.19 |