dart에서 쓰이는 bloc package가 아닌 flutter에서 쓰이는 flutter_bloc 패키지에 관한 것임
BlocBuilder
bloc과 builder 기능을 사용하기 위한 flutter widget . block builder는 새 상태에 대한 응답으로 위젯 작성 처리, blocBuilder는 streamBuilder와 매우 유사하지만 필요한 보일러 플레이트 코드의 양을 줄기이 위한 더 간단한 api를 가지고 있음! / builder 함수는 잠재적으로 여러번 호출 될 수 있음 / state response => 위젯 return pure function
대화 상자 표시 등과 같은 상태 변경에 대해 수행하려면 blocListener를 참조!
블록 매개 변수를 생략하면, blocBuilder가 blocProvider 및 현재 buildcontext를 사용하여 자동으로 조회를 수행함
BlocBuilder<BlocA, BlocAState>(
// blocProvider, BlocState
builder: (context, state) {
// return widget here based on BlocA's state
}
)
// 단일 위젯으로 범위가 지정, 상위 blocProvider 및
//현재 BuildContext를 통해 access 할 수 없는 bloc 제공하려는 경우에만 bloc 지정
BlocBuilder<BlocA, BlocAState>(
bloc: blocA, // provide the local bloc instance
builder: (context, state) {
// return widget here based on BlocA's state
}
)
// true를 return 하면, 빌더와 상태가 함꼐 호출 => 위잿 재구성
// false일 경우 return 하면, 상태와 함꼐 호출되지 않아 위젯 재구성하지 않음
blocSelector는 blocBuilder와 유사히자만 블록 상태를 기준으로 새 값을 선택하여 업데이트를 필터링할 수 있는 위젯! / 값이 변경되지 않으면 불필요한 빌드 방지 / blocSelector가 builder를 다시 호출 여부는 결정하려면 값이 불변해야함!! => blocSelector 가 blockProvider 및 buildContext를 사용하여 조회를 자동으로 수행
BlocSelector<BlocA, BlocAState, SelectedState>(
selector: (state) {
// return selected state based on the provided state.
},
builder: (context, state) {
// return widget here based on the selected state.
},
)
BlockProvider.of(context)를 통해 자식들에게 bloc을 제공하는 Flutter 위젯!
하위 tree 내의 여러 위젯에 bloc의 단일 instance를 제공할 수 있도록 종속성 주입 위젯(해당 객체를 클래스마다 인스턴스화 즉 생성하는 대신 클래스에 종속 객체를 주입)으로 사용
bloc provider를 사용하여 하위 트리 나머지 부분에 사용할 수 있는 새 블록을 만들어야 함! / BlockProvider는 블록을 만드는 역할을 하므로 block close를 자동으로 함!
기본값으로, blocProvider는 BlocProvidr.of(context)를 통해 bloc을 조회할 떄 Create 실행
위의 동작을 무시하고 즉시 실행하도록 하려면 lazy를 false로 설정
BlocProvider(
lazy: false,
create: (BuildContext context) => BlocA(),
child: ChildA(),
);
//BlocProvider를 사용하여, 위젯 트리의 새 부분에 기존 bloc을 provide 할 수 있음
// 기존 bloc을 새 path에 사용할 수 있어야 할 떄 가장 일반적으로 사용
// blockProvider는 bloc create X => bloc을 자동으로 close하지 않음!
BlocProvider.value(
value: BlocProvider.of<BlocA>(context),
child: ScreenA(),
);
// with extensions
context.read<BlocA>();
// without extensions
BlocProvider.of<BlocA>(context)
MultiBlocProvider는 여러 blocProvider를 하나로 병합하는 widget
MultiBlocProvider는 가독성 향상 / 여러 BlocProvider를 중첩할 필요 x
BlocProvider<BlocA>(
create: (BuildContext context) => BlocA(),
child: BlocProvider<BlocB>(
create: (BuildContext context) => BlocB(),
child: BlocProvider<BlocC>(
create: (BuildContext context) => BlocC(),
child: ChildA(),
)
)
)
// to
MultiBlocProvider(
providers: [
BlocProvider<BlocA>(
create: (BuildContext context) => BlocA(),
),
BlocProvider<BlocB>(
create: (BuildContext context) => BlocB(),
),
BlocProvider<BlocC>(
create: (BuildContext context) => BlocC(),
),
],
child: ChildA(),
)
BlocListener는 BlocWidgetListener와 선택적 Bloc을 사용하고 Bloc의 상태 변화에 따라 Listener를 호출하는 위젯 (snackBar, navigation, dialog에 사용 => 상태 변경당 한번 발생하는 기능!!)
Listener는 BlocBuilder의 builder와 달리 각 상태 변경(initial state는 포함되지 않음)에 대해 한 번만 호출되며 무효함수임! / Bloc 매겨 변수 생략 시 BlocListener가 BlocProvider 및 현재 BuildContext를 사용하여 조회를 자동으로 수행
BlocListener<BlocA, BlocAState>(
listener: (context, state) {
// do stuff here based on BlocA's state
},
child: Container(),
)
//BlocProvider 및 현재 BuildContext를 통해 Access 할 수 없는 bloc을 제공하려는 경우에만 bloc 지정
BlocListener<BlocA, BlocAState>(
bloc: blocA,
listener: (context, state) {
// do stuff here based on BlocA's state
},
child: Container()
)
// function을 선택적으로 호출하려면, listenWhen을 사용하면 된다.
//listen 이전 bloc state와 현재 bloc state를 사용하고 있는 bool을 return 해줌 =>
//true라면 listener와 상태가 같이 호출 / false라면 함꼐 호출되지 않음
BlocListener<BlocA, BlocAState>(
listenWhen: (previousState, state) {
// return true/false to determine whether or not
// to call listener with state
},
listener: (context, state) {
// do stuff here based on BlocA's state
},
child: Container(),
)
여러 BlocListener 위젯을 하나로 병합하는 위젯 / 가독성 향상 , 중첩할 필요 없음
BlocListener<BlocA, BlocAState>(
listener: (context, state) {},
child: BlocListener<BlocB, BlocBState>(
listener: (context, state) {},
child: BlocListener<BlocC, BlocCState>(
listener: (context, state) {},
child: ChildA(),
),
),
)
//to
MultiBlocListener(
listeners: [
BlocListener<BlocA, BlocAState>(
listener: (context, state) {},
),
BlocListener<BlocB, BlocBState>(
listener: (context, state) {},
),
BlocListener<BlocC, BlocCState>(
listener: (context, state) {},
),
],
child: ChildA(),
)
BlocConsumer 는 새로운 상태에 반응하기 위해 builder와 listener를 노출시킴 / 중첩된 blocListener 및 BlocBuilder와 유사하지만 코드의 양을 줄임
BlocConsumer는 UI를 재구성하고 bloc의 상태 변화에 대한 다른 대응을 실행하는 경우에만 사용해야함
BlocConsumer는 필수 BlocWidgetBuilde 및 BlocWidgetListener와 선택적 bloc, BlockBuilderCondition 및 BlocListenerCondition을 사용
bloc 매개 변수 생략시 BlocConsumer는 BlocProvider 및 현재 BuildContext를 사용하여 조회를 자동으로 수행
BlocConsumer<BlocA, BlocAState>(
listener: (context, state) {
// do stuff here based on BlocA's state
},
builder: (context, state) {
// return widget here based on BlocA's state
}
)
//선택적으로 ListenWhen과 buildWhen을 사용할 수 있음
//listenWhen 과 buildWhen은 각각의 bloc state 변경에 대해 호출됌
// 각각 이전 상태와 현재 상태를 취하며, 빌더 및 listener 기능의 호출 여부를 결정하는 모음을 반환
// Bloc Consumer가 초기화되면 이전 상태의 Bloc Consumer가 초기화 됌 / listenWhen과 buildWhen이 구현되지 않는 경우 기본적으로 true로 설정됌
BlocConsumer<BlocA, BlocAState>(
listenWhen: (previous, current) {
// return true/false to determine whether or not
// to invoke listener with state
},
listener: (context, state) {
// do stuff here based on BlocA's state
},
buildWhen: (previous, current) {
// return true/false to determine whether or not
// to rebuild the widget with state
},
builder: (context, state) {
// return widget here based on BlocA's state
}
)
RepositoryProvider는 RepositoryProvider.of(context)을 통해 Child에게 repository를 제공하는 위젯
하위 트리 내의 여러 위젯에 repository의 single instance를 제공할 수 있도록 DI Widget으로 사용 / bloc을 제공하려면 BlocProvider를 사용해야 하지만, RepositoryProvider는 Repository에 대해서만 사용!
RepositoryProvider(
create: (context) => RepositoryA(),
child: ChildA(),
);
// childA에서 다음을 사용하여 repository instance 검색할 수 있음
// with extensions
context.read<RepositoryA>();
// without extensions
RepositoryProvider.of<RepositoryA>(context)
MultiRepositoryProvider는 여러 Repository 위젯을 하나로 병합하는 위젯!
RepositoryProvider<RepositoryA>(
create: (context) => RepositoryA(),
child: RepositoryProvider<RepositoryB>(
create: (context) => RepositoryB(),
child: RepositoryProvider<RepositoryC>(
create: (context) => RepositoryC(),
child: ChildA(),
)
)
)
// to
MultiRepositoryProvider(
providers: [
RepositoryProvider<RepositoryA>(
create: (context) => RepositoryA(),
),
RepositoryProvider<RepositoryB>(
create: (context) => RepositoryB(),
),
RepositoryProvider<RepositoryC>(
create: (context) => RepositoryC(),
),
],
child: ChildA(),
)