flutter_bloc: ^6.0.6 #狀態管理框架
equatable: ^1.2.3 #增強組件相等性判斷
class MainBloc extends Bloc<MainEvent, MainState> {
MainBloc() : super(MainInitial());
@override
Stream<MainState> mapEventToState(
MainEvent event,
) async* {
// TODO: implement mapEventToState
}
}
@immutable
abstract class MainEvent {}
@immutable
abstract class MainState {}
class MainInitial extends MainState {}
class MainBloc extends Bloc<MainEvent, MainState> {
MainBloc() : super(MainState(selectedIndex: 0, isExtended: false));
@override
Stream<MainState> mapEventToState(MainEvent event) async* {
///main_view中添加的事件,會在此處回調,此處處理完數據,將數據yield,BlocBuilder就會刷新組件
if (event is SwitchTabEvent) {
///獲取到event事件傳遞過來的值,咱們拿到這值塞進MainState中
///直接在state上改變內部的值,然後yield,只能觸發一次BlocBuilder,它內部會比較上次MainState對象,如果相同,就不build
yield MainState()
..selectedIndex = event.selectedIndex
..isExtended = state.isExtended;
} else if (event is IsExtendEvent) {
yield MainState()
..selectedIndex = state.selectedIndex
..isExtended = !state.isExtended;
}
}
}
@immutable
abstract class MainEvent extends Equatable{
const MainEvent();
}
///切換NavigationRail的tab
class SwitchTabEvent extends MainEvent{
final int selectedIndex;
const SwitchTabEvent({@required this.selectedIndex});
@override
List<Object> get props => [selectedIndex];
}
///展開NavigationRail,這個邏輯比較簡單,就不用傳參數了
class IsExtendEvent extends MainEvent{
const IsExtendEvent();
@override
List<Object> get props => [];
}
class MainState{
int selectedIndex;
bool isExtended;
MainState({this.selectedIndex, this.isExtended});
}
@immutable
abstract class LoadingState extends Equatable {}
class LoadingInitial extends LoadingState {
@override
List<Object> get props => [];
}
class LoadingBeforeSate extends LoadingState{
///實現相應的欄位信息
@override
List<Object> get props => [];
}
class LoadingFailureState extends LoadingState{
///實現相應的欄位信息
@override
List<Object> get props => [];
}
class LoadingSuccessState extends LoadingState{
///實現相應的欄位信息
@override
List<Object> get props => [];
}
///在View中使用,偽代碼
BlocBuilder<MainBloc, MainState>(builder: (context, state) {
if(state is LoadingBeforeSate){
return Beforewidget(state.XX,..);
} else if(state is LoadingFailureState){
return FailureWidget(state.XX,state.XX,...);
} else if(state is LoadingSuccessState){
return SuccessWidget(state.XX);
} else {
return ErrorWidget(...);
}
})
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MainPage(),
);
}
}
class MainPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
///創建BlocProvider的,表明該Page,我們是用MainBloc,MainBloc是屬於該頁面的Bloc了
return BlocProvider(
create: (BuildContext context) => MainBloc(),
child: BodyPage(),
);
}
}
class BodyPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Bloc')),
body: totalPage(),
);
}
}
Widget totalPage() {
return Row(
children: [
navigationRailSide(),
Expanded(child: Center(
child: BlocBuilder<MainBloc, MainState>(builder: (context, state) {
///看這看這:刷新組件!
return Text("selectedIndex:" + state.selectedIndex.toString());
}),
))
],
);
}
//增加NavigationRail組件為側邊欄
Widget navigationRailSide() {
//頂部widget
Widget topWidget = Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
width: 80,
height: 80,
decoration: BoxDecoration(
shape: BoxShape.circle,
image: DecorationImage(
image: NetworkImage("https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3383029432,2292503864&fm=26&gp=0.jpg"),
fit: BoxFit.fill),
)),
),
);
//底部widget
Widget bottomWidget = Container(
child: BlocBuilder<MainBloc, MainState>(
builder: (context, state) {
return FloatingActionButton(
onPressed: () {
///添加NavigationRail展開,收縮事件
context.bloc<MainBloc>().add(IsExtendEvent());
},
///看這看這:刷新組件!
child: Icon(state.isExtended ? Icons.send : Icons.navigation),
);
},
),
);
return BlocBuilder<MainBloc, MainState>(builder: (context, state) {
return NavigationRail(
backgroundColor: Colors.white12,
elevation: 3,
///看這看這:刷新組件!
extended: state.isExtended,
labelType: state.isExtended ? NavigationRailLabelType.none : NavigationRailLabelType.selected,
//側邊欄中的item
destinations: [
NavigationRailDestination(
icon: Icon(Icons.add_to_queue),
selectedIcon: Icon(Icons.add_to_photos),
label: Text("測試一")),
NavigationRailDestination(
icon: Icon(Icons.add_circle_outline),
selectedIcon: Icon(Icons.add_circle),
label: Text("測試二")),
NavigationRailDestination(
icon: Icon(Icons.bubble_chart),
selectedIcon: Icon(Icons.broken_image),
label: Text("測試三")),
],
//頂部widget
leading: topWidget,
//底部widget
trailing: bottomWidget,
selectedIndex: state.selectedIndex,
onDestinationSelected: (int index) {
///添加切換tab事件
context.bloc<MainBloc>().add(SwitchTabEvent(selectedIndex: index));
},
);
});
}
class MainState {
int selectedIndex;
bool isExtended;
///初始化方法,基礎變量也需要賦初值,不然會報空異常
MainState init() {
return MainState()
..selectedIndex = 0
..isExtended = false;
}
///clone方法,此方法實現參考fish_redux的clone方法
///也是對官方Flutter Login Tutorial這個demo中copyWith方法的一個優化
///Flutter Login Tutorial(https://bloclibrary.dev/#/flutterlogintutorial)
MainState clone() {
return MainState()
..selectedIndex = selectedIndex
..isExtended = isExtended;
}
}
@immutable
abstract class MainEvent {}
///初始化事件,這邊目前不需要傳什麼值
class MainInitEvent extends MainEvent {}
///切換NavigationRail的tab
class SwitchTabEvent extends MainEvent {
final int selectedIndex;
SwitchTabEvent({@required this.selectedIndex});
}
///展開NavigationRail,這個邏輯比較簡單,就不用傳參數了
class IsExtendEvent extends MainEvent {}
class MainBloc extends Bloc<MainEvent, MainState> {
MainBloc() : super(MainState().init());
@override
Stream<MainState> mapEventToState(MainEvent event) async* {
///main_view中添加的事件,會在此處回調,此處處理完數據,將數據yield,BlocBuilder就會刷新組件
if (event is MainInitEvent) {
yield await init();
} else if (event is SwitchTabEvent) {
///獲取到event事件傳遞過來的值,咱們拿到這值塞進MainState中
///直接在state上改變內部的值,然後yield,只能觸發一次BlocBuilder,它內部會比較上次MainState對象,如果相同,就不build
yield switchTap(event);
} else if (event is IsExtendEvent) {
yield isExtend();
}
}
///初始化操作,在網絡請求的情況下,需要使用如此方法同步數據
Future<MainState> init() async {
return state.clone();
}
///切換tab
MainState switchTap(SwitchTabEvent event) {
return state.clone()..selectedIndex = event.selectedIndex;
}
///是否展開
MainState isExtend() {
return state.clone()..isExtended = !state.isExtended;
}
}
class MainPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocProvider(
///在MainBloc上使用add方法,添加初始化事件
create: (BuildContext context) => MainBloc()..add(MainInitEvent()),
child: BodyPage(),
);
}
}
///下方其餘代碼省略.
class CounterCubit extends Cubit<CounterState> {
CounterCubit() : super(CounterInitial());
}
@immutable
abstract class CounterState {}
class CounterInitial extends CounterState {}
class CounterCubit extends Cubit<CounterState> {
CounterCubit() : super(CounterState().init());
}
class CounterState {
///初始化方法
CounterState init() {
return CounterState();
}
///克隆方法,針對於刷新界面數據
CounterState clone() {
return CounterState();
}
}
class CounterState {
int count;
CounterState init() {
return CounterState()..count = 0;
}
CounterState clone() {
return CounterState()..count = count;
}
}
class CounterCubit extends Cubit<CounterState> {
CounterCubit() : super(CounterState().init());
///自增
void increase() => emit(state.clone()..count = ++state.count);
}
class CounterPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (BuildContext context) => CounterCubit(),
child: BlocBuilder<CounterCubit, CounterState>(builder: _counter),
);
}
Widget _counter(BuildContext context, CounterState state) {
return Scaffold(
appBar: AppBar(title: const Text('Cubit範例')),
body: Center(
child: Text('點擊了 ${state.count} 次', style: TextStyle(fontSize: 30.0)),
),
floatingActionButton: FloatingActionButton(
onPressed: () => context.bloc<CounterCubit>().increase(),
child: const Icon(Icons.add),
),
);
}
}