Onboarding 화면
지난 번에는 Riverpod의 공식문서를 보며 Riverpod 예제에 대해서 공부했다.
이번에는 직접 구현하면서 개인 프로젝트에 적용했다.
프로젝트의 OnboardingView를 보면 아래와 같이 기획을 하였다.
왼쪽은 화면(UI)이고 오른쪽은 화면은 각 위젯(widget)에 대한 기능 설명이다.
시작하기 버튼은 course와 campus에 초기값이 아닌 다른 값이 들어가면 활성화되면서 색이 변경된다.
Data State Management(campus)
그러면 나는 저번에 배운 Data State management Tool인 Riverpod을 사용하여 Data 상태관리를 해보겠다.
final campusProvider = StateNotifierProvider<CampusNotifier, String>(
(ref) => CampusNotifier(),
);
// campusNotifier 클래스 정의
class CampusNotifier extends StateNotifier<String> {
CampusNotifier() : super('---'); // 초기 상태 설정
void updateSelectedCampus(String campus) {
state = campus; // 상태 업데이트
}
}
- StateNotifierProvider
- StateNotifierProvider는 StateNotifier를 사용하여 상태를 관리하는 provider이다. 이 provider는 두 가지 제네릭 타입을 받는데 첫 번째 타입은 StateNotifier의 클래스 타입 (CampusNotifier). 두 번째 타입은 관리할 상태의 타입 여기서는 (String).
- campusNotifier
- campusNotifier는 StateNotifier<String>을 상속받아 상태를 관리하는 클래스이다. String은 이 notifier가 관리하는 Data State의 타입이다.
- updateSelectedCampus()
- 이 메서드는 입력된 캠퍼스 이름을 받아 상태를 업데이트한다. 내가 따로 setState()를 하지 않아도 state = campus;를 통해 상태를 변경하면, 이 상태를 구독하고 있는 UI가 자동으로 다시 빌드된다.
위의 코드들이 어떠한 역할을 하는지 한번 알아보자.
class OnboardingView extends ConsumerStatefulWidget {
OnboardingView({super.key});
@override
ConsumerState<ConsumerStatefulWidget> createState() => _OnboardingViewState();
}
class _OnboardingViewState extends ConsumerState<OnboardingView>
with WidgetsBindingObserver {
@override
Widget build(BuildContext context) {
final course = ref.watch(courseTextFieldProvider); // course 상태 구독
final campusState = ref.watch(campusProvider); // campus 상태 구독
bool _isActive() {
return course.isNotEmpty && campusState != '---';
}
return Scaffold(
--- 생략
}
}
ConsumerStatefulWidget과 ConsumerState를 사용하여 OnboardingView를 확장시켰다.
이 두 클래스를 사용하면 상태를 쉽게 관리하고 UI를 업데이트할 수 있다.
- ConsumerStatefulWidget
- ConsumerStatefulWidget은 상태를 관리하는 StatefulWidget의 확장 기능을 갖고있다. 이 위젯은 Riverpod의 상태를 구독할 수 있는 기능을 제공한다.
- 상태가 변경될 때 UI를 자동으로 업데이트할 수 있도록 하여 내부 상태를 유지하면서 외부 상태(예: Riverpod provider)를 구독하는 것.
- final campusState = ref.watch(campusProvider); // campus 상태 구독 위의 코드가 그역할을 한다.
나는 GreenFieldCampusPicker 컴포넌트를 만들어서 사용했기다.
아래 이미지는 피그마에서 기획한 GreenFieldCampusPicker Component이다.
그래서 콜백함수안 WidgetRef(ref)를 이용하여 provider(campusProvider)에 접근하고 안에 있는 updateSelectedCampus 메소드를 이용하여 selectedCampus를 업데이트하는 것이다.
GreenFieldCampusPicker(
onCampusSelected: (selectedCampus) {
ref
.read(campusProvider.notifier)
.updateSelectedCampus(selectedCampus);
},
),
Data State Management(course)
나는 onboarding 폴더에 view와 textfield가 있다.
TextField 코드를 보면 onChanged 콜백을 사용하여 value가 변경될때 ref는 Riverpod의 WidgetRef로, provider에 접근할 수 있게 한다. 그래서 courseTextFieldProvider에 있는 setKeyword 메서드를 사용하여 상태를 변경해준다.
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../utilities/design_system/app_colors.dart';
import '../../utilities/design_system/app_texts.dart';
import '../../viewmodels/onboarding_view_model.dart';
class OnboardingTextField extends ConsumerStatefulWidget {
@override
_OnboardingTextFieldState createState() => _OnboardingTextFieldState();
}
class _OnboardingTextFieldState extends ConsumerState<OnboardingTextField> {
late final TextEditingController controller;
@override
void initState() {
super.initState();
controller = TextEditingController(); // 컨트롤러 초기화
}
@override
void dispose() {
controller.dispose(); // 컨트롤러 해제
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: AppColorsTheme.main().gfGray100Color,
borderRadius: BorderRadius.circular(8),
),
child: TextField(
controller: controller,
onChanged: (value) {
ref
.read(courseTextFieldProvider.notifier)
.setKeyword(value);
},
decoration: InputDecoration(
hintText: '학습중이거나 학습완료 강좌 입력.',
hintStyle: TextStyle(color: AppColorsTheme.main().gfGray400Color),
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 12, horizontal: 20),
),
style: AppTextsTheme.main().gfTitle2.copyWith(
color: AppColorsTheme.main().gfBlackColor,
),
),
);
}
}
그렇다면 Provider를 만드는 것도 한번 보자.
OnboardingTextFieldNotifier 클래스를 정의하여 setKeyword메서드를 사용한다.
final courseTextFieldProvider =
StateNotifierProvider<OnboardingTextFieldNotifier, String>(
(ref) => OnboardingTextFieldNotifier(),
);
// OnboardingTextFieldNotifier 클래스 정의
class OnboardingTextFieldNotifier extends StateNotifier<String> {
OnboardingTextFieldNotifier() : super(''); // 초기 상태 설정
void setKeyword(String keyword) {
state = keyword; // 상태 업데이트
}
}
Data State Subscribe(View)
이제 모든 provider를 만들어주었으니 구독하여 Data의 상태를 추적하여 UI에 업데이트 해주자.
final course = ref.watch(courseTextFieldProvider); // course 상태 구독
final campusState = ref.watch(campusProvider); // campus 상태 구독
bool _isActive() {
return course.isNotEmpty && campusState != '---';
}
위의 코드들이 데이터를 구독하여 상태를 갖고 있는것이다.
이제 필요한 모든 코드들을 작성하였으니 시작 버튼의 UI를 Data(course, campus)에 따라 변경해주자.
_isActive() 메서드를 이용하여 Data(course, campus)가 초기값이 아닐때 색 변경과 버튼이 가능하도록 만들어주자.
GreenFieldConfirmButton(
text: "시작하기",
isAble: _isActive(),
textColor: Theme.of(context).appColors.gfWhiteColor,
backGroundColor: _isActive()
? Theme.of(context).appColors.gfMainColor
: Theme.of(context).appColors.gfGray300Color,
onPressed: () async {
final result = await ref
.read(onboardingViewModelProvider.notifier)
.createAuthUser(
tokenState.value?.provider ?? '',
tokenState.value?.idToken ?? '',
tokenState.value?.accessToken ?? '',
);
switch (result) {
case Success():
context.go('/home');
case Failure(exception: final e):
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('로그인 실패'),
content: Text('에러 발생: $e'),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text('확인'),
),
],
),
);
}
},
),
결과
데이터에 변경에 따라 시작하기 버튼이 활성화되고 색이 변경되는 것을 볼 수 있다.
이것을 사용하여 앞으로 Data의 상태를 구독하고 관리해주자.
'Flutter' 카테고리의 다른 글
[Flutter] RiverPod - Loading 상태 처리하기(with Lottie & Skeleton) (0) | 2025.01.14 |
---|---|
[Flutter] Riverpod 학습 - Performing side effects & Passing arguments to your requests (0) | 2025.01.07 |
[Flutter] RiverPod - provider 알아보기(with 네트워크 요청) (0) | 2025.01.06 |
[Flutter] RiverPod 학습 - State management (0) | 2025.01.03 |
[Flutter] [Error Handling] Result Pattern (간편로그인 적용) (0) | 2024.12.26 |