Bloc 範例 => GITHUB Bloc
Cubit 範例 => GITHUB Cubit
我們來看這張圖,簡單來說,他就是 UI 觸發一個事件,傳到了 bloc
後,處理邏輯,處理好後,丟出一個狀態。當 UI 接收到對應的狀態,給予對應的 UI。
Bloc 這個套件有兩種寫法。
這邊一般會先用一個 IPostEvent
然後後面的繼承他這樣子。前面加上一個 I 是因為這樣我比較好知道他是 abstract class。
Equatable 在前幾 Day 7 有講過。
abstract class IPostState {}
class PostLoading extends IPostState {}
class PostSuccess extends IPostState {
final List<PostModel> postList;
PostSuccess( this.postList);
}
PostCubit
丟了一個建構子,是一個 IPostRepository
,用來請求資料的。super
是用來設定初始值的。(這邊預設的狀態是在 PostLoading)。emit
代表,輸出狀態。預設一剛開始就是 PostLoading
因此,在 fetchData
、sort
結束後,狀態將會是 PostSuccess
。enum SortState { userId, id, title, body }
class PostCubit extends Cubit<IPostState> {
IPostRepository _repository;
List<PostModel> postList = [];
PostCubit(IPostRepository repository)
: _repository = repository,
super(PostLoading());
Future<void> fetchData() async {
postList = await _repository.fetchData();
sort(SortState.id);
}
void sort(SortState sortBy) {
switch (sortBy) {
case SortState.userId:
postList.sort((a, b) => a.userId.compareTo(b.userId));
break;
case SortState.id:
postList.sort((a, b) => a.id.compareTo(b.id));
break;
case SortState.title:
postList.sort((a, b) => a.title.compareTo(b.title));
break;
case SortState.body:
postList.sort((a, b) => a.body.compareTo(b.body));
break;
}
emit(PostSuccess([...postList]));
}
}
Cubit
的初始化和後面會講到的 Bloc
一樣,和 Provider
類似。MultiBlocProvider
元件是一個初始化的共享裝置元件。
這邊我們在 MaterialApp
外面建立了一個 MultiBlocProvider
,代表只要在 MaterialApp
裡面都可以取到 PostCubit
的資料。
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<PostCubit>(
create: (BuildContext context) => PostCubit(
PostRepository(PostService()),
),
),
],
child: MaterialApp(
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Cubit Sample'),
),
);
}
}
Cubit
的觸發事件不會向 bloc
有一個專門的 Event
,而是直接呼叫 PostCubit
裡面的 sort()
方法。
context.read<PostCubit>().sort(value);
Cubit
的 BlocBuilder
和 Bloc
一樣
BlocBuilder<PostCubit, IPostState>(
builder: (_, state) {
// 如果種態勢 PostSuccess 的話
if (state is PostSuccess) {
return Widget;
}
// else{} //預設值。
return Center(child: CircularProgressIndicator());
},
);
這邊引用 flutter_bloc 文件範例
BlocBuilder<BlocA, BlocAState>(
builder: (context, state) {
// return widget here based on BlocA's state
}
)
// or
// 你不想要共享資料的話,可以放在這。
BlocBuilder<BlocA, BlocAState>(
bloc: blocA, // provide the local bloc instance
builder: (context, state) {
// return widget here based on BlocA's state
}
)
我會這樣說,BlocBuilder
只是用來回應不同狀態下對應的 widgets,BlocListener 則是用來在不同狀態 "do things "。比方說跳頁
、showSnackBar
等等...,你可以考範例 showsnackbar、Navigator
如果狀態是 PostSuccess
,那麼我就回傳一個 ListView.builder
,其他的則是回傳一個 CircularProgressIndicator
。
class _MyListView extends StatelessWidget {
const _MyListView({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return BlocBuilder<PostCubit, IPostState>(
builder: (_, state) {
if (state is PostSuccess) {
return ListView.builder(
itemCount: state.postList.length,
itemBuilder: (context, index) {
PostModel item = state.postList[index];
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(16)),
color: Colors.white,
border: Border.all(color: Colors.blueAccent, width: 2.0)),
margin: EdgeInsets.all(8),
padding: EdgeInsets.all(8),
child: RichText(
text: TextSpan(
style: DefaultTextStyle.of(context).style,
children: <TextSpan>[
TextSpan(
text: item.id.toString() + ". " + item.title,
style: TextStyle(fontSize: 18, color: Colors.red),
),
TextSpan(
text: '\n' + item.body,
style: TextStyle(fontWeight: FontWeight.bold),
),
TextSpan(
text: "\nUser ID:" + item.userId.toString(),
style: TextStyle(fontSize: 18),
),
],
),
));
},
);
}
return Center(child: CircularProgressIndicator());
},
);
}
}
class MyHomePage extends StatefulWidget {
final String title;
const MyHomePage({Key? key, required this.title}) : super(key: key);
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
void initState() {
super.initState();
context.read<PostCubit>().fetchData();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
actions: <Widget>[
PopupMenuButton<SortState>(
icon: Icon(Icons.more_vert),
itemBuilder: (context) => [
PopupMenuItem(
child: Text('使用 userId 排序'),
value: SortState.userId,
),
PopupMenuItem(
child: Text('使用 id 排序'),
value: SortState.id,
),
PopupMenuItem(
child: Text('使用 title 排序'),
value: SortState.title,
),
PopupMenuItem(
child: Text('使用 body 排序'),
value: SortState.body,
)
],
onSelected: (SortState value) {
context.read<PostCubit>().sort(value);
},
)
],
),
body: _MyListView());
}
}
Bloc 則是把事件分開需要有 Event
、Bloc
、State
作為事件。
這邊順便介紹一個 Android Studio Bloc 套件
part of 'post_bloc.dart';
abstract class IPostEvent {}
/// cal Api 的事件
class FetchPostData extends IPostEvent {}
/// 排序的事件
class SortPostEvent extends IPostEvent {
final SortState sortState;
SortPostEvent({required this.sortState});
}
enum SortState { userID, id, title, body }
class PostBloc extends Bloc<IPostEvent, IPostState> {
final IPostRepository _repository;
List<PostModel> postList = [];
/// 設定初始狀態 super(這裡要放初始狀態)
PostBloc({required IPostRepository repository}) : _repository = repository,super(PostLoading());
@override
Stream<IPostState> mapEventToState(IPostEvent event) async* {
// 如果是件事 FetchPostData
if (event is FetchPostData) {yield* _fetchData(event);}
// 如果是件事 SortPostEvent
else if(event is SortPostEvent){yield* _sortState(event);}
}
Stream<IPostState> _fetchData(FetchPostData event) async* {
// 請求 API
postList = await _repository.fetchData();
// 觸發另一個排序事件 SortPostEvent
add(SortPostEvent(sortState: SortState.id));
}
// 自定義的 method
Stream<IPostState> _sortState(SortPostEvent event) async* {
_sort(event.sortState);
yield PostSuccess(postList);
}
// sort method
Future<void> _sort(SortState sortState)async {
switch (sortState) {
case SortState.title:
postList.sort((a, b) => a.title.compareTo(b.title));
break;
case SortState.id:
postList.sort((a, b) => a.id.compareTo(b.id));
break;
case SortState.userID:
postList.sort((a, b) => a.userId.compareTo(b.userId));
break;
case SortState.body:
postList.sort((a, b) => a.body.compareTo(b.body));
break;
}
}
}
這邊的 State
和 Cubit
一樣。
State 的程式碼 => GITHUB
這邊初始化設定和 Cubit
一樣。
初始化的程式碼 => GITHUB
bloc
是使用 add()
裡面放 event 的 class
/// cubit
context.read<PostCubit>().sort(value);
/// bloc
context.read<PostBloc>().add(SortPostEvent(sortState: value));