사이드로 시작한 프로젝트에서 사용한 blocSelector 를 정리한다.
일반적으로 블록 패턴을 사용할때에는 BlocBuilder / BlocListener / BlocSelector 등을 많이 사용하는 것 같다.
그중 특정 State의 특정 값을 감지해서 위젯을 리빌드하는 블록 셀렉터를 기록한다.
전체 코드는 다음에서 확인 가능하다.
https://github.com/jaemanc/freight_front
BlocListener는 인자로 Bloc과 State를 받는다.
BlocListener<BlocType, StateType>(
bloc: myBloc,
listener: (BuildContext context, StateType state) {
// 상태 변화에 대한 리스너 작성
// state에는 현재의 bloc 상태가 전달됩니다.
},
child: YourWidget(),
);
반면 BlocSelector는 State의 특정 값만 선택해서 감지한다.
BlocSelector<BlocType, StateType, SelectedType>(
selector: (StateType state) {
// 선택적으로 추출하고자 하는 부분의 데이터를 리턴
return state.selectedData;
},
builder: (BuildContext context, SelectedType selectedData) {
// 추출된 데이터를 사용하여 화면을 업데이트
return YourWidget(selectedData: selectedData);
},
);
자주 변경되는 상태라면 BlocListener에 비해 위젯 빌드의 횟수를 줄일 수 있고, 불필요한 리렌더링을 줄여 줄 수 있다는 장점이 있다.
1. 스크린에서 보여주어야 할 위젯 작성.
2. Bloc에서 API 통신 이후 state 변경
이벤트를 통해 블록에서 API를 호출한다.
state는 loadSuccess 로 변경해준다.
3. 변경된 state를 BlocSelector 가 감지하고 위젯을 다시 빌드한다.
글은 편하게 썼지만 state의 불변성, 감지하는 객체의 어느 부분을 바꿔야 하는 지 확인하는 과정에서 삽질을 많이 했다.
핵심은 변경해야 하는 State의 불변성을 유지해 주는 것.
따라서 final 로 작성한다.
그래도 state의 감지가 어렵다면,
BlocObserver를 사용하는 것이 유용하다.
BlocObserver를 사용 할 경우, 블록을 통한 상태 변화를 감지해준다.
main에서
observer를 세팅만 해주면 감지해준다.
사인업 페이지에서는 initial status를 감지했다.
입력하고 회원가입을 처리하면,
로그인 성공 위젯을 빌드하고, state의 상태도 loadSuccess로 변경된것을 감지한 로그가 프린트된다.
다음은 스크린 코드이다.
class _SignUpFormState extends State<SignUpForm> {
AuthenticationBloc get authenticationbloc =>
context.read<AuthenticationBloc>();
final _formKey = GlobalKey<FormState>();
String? _id;
String? _contact;
String? _email;
String? _name;
@override
Widget build(BuildContext context) {
double screenHeight = MediaQuery.of(context).size.height;
double screenWidth = MediaQuery.of(context).size.width;
return BlocSelector<AuthenticationBloc, AuthenticationState, AuthenticationStateStatus>(
selector: (state) {
logger.i(' ${state} ');
return state.status;
}, builder: ((context, state) {
logger.i(' ${state}');
return state != AuthenticationStateStatus.loadSuccess
? Column(children: <Widget>[
Row(children: [_exitBtn()]),
Align(
alignment: Alignment.center,
child: Form(
key: _formKey,
child: Column(
children: <Widget>[
Container(
margin: EdgeInsets.only(
top: screenHeight * 0.1,
bottom: screenHeight * 0.1),
child: const Image(image: AppImages.mainTruck),
),
SizedBox(
width: screenWidth * 0.5,
child: Column(
children: [
TextFormField(
decoration: InputDecoration(labelText: 'ID'),
validator: (value) {
if (value == null || value == '') {
return 'ID를 입력해주세요.';
}
return null;
},
onSaved: (value) {
_id = value;
},
),
TextFormField(
decoration:
InputDecoration(labelText: 'Contact'),
validator: (value) {
if (value == null || value == '') {
return 'Contact를 입력해주세요.';
}
return null;
},
onSaved: (value) {
_contact = value;
},
),
TextFormField(
decoration:
InputDecoration(labelText: 'E-mail'),
validator: (value) {
if (value == null || value == '') {
return 'E-mail을 입력해주세요.';
}
return null;
},
onSaved: (value) {
_email = value;
},
),
TextFormField(
decoration:
InputDecoration(labelText: 'Name'),
validator: (value) {
if (value == null || value == '') {
return '이름을 입력해주세요.';
}
return null;
},
onSaved: (value) {
_name = value;
},
),
],
),
),
SizedBox(height: 16),
ElevatedButton(
onPressed: () => {
if (_formKey.currentState!.validate())
{
_formKey.currentState!.save(),
// call SignUp API
authenticationbloc.add(Registration(
UserEntity(
userId: _id,
contact: _contact,
email: _email,
name: _name,
isLogin: false))),
}
},
child: Text('Sign Up!'),
),
SizedBox(height: 16),
ElevatedButton(
onPressed: () => {
authenticationbloc.add(GuestLoginEvent()),
setState(() {
AuthenticationStateStatus.loadSuccess;
})
},
child: Text('Guest'))
],
))),
]) : Text('로그인 성공했습니다!');
}));
}
'MOBILE' 카테고리의 다른 글
[ERROR] flutter sdk null safety (0) | 2023.09.21 |
---|---|
[ERROR] / No apps connected. sendng "reload" to all React ... (0) | 2022.06.26 |
react native expo 환경 설정 (0) | 2022.02.01 |