问题描述
我遵循this approach来管理集团并保存一些代码行。
我想从我的API中获取数据。我想通过initState
方法并调用myBloc.add(MyEvent())
。但是问题是,它只被调用了一次。
我到处搜索并在一些博客上尝试了一些解决方案,这是Github的官方仓库问题,但仍然无法正常工作。我找到了similar question,但是由于我没有使用任何依赖项注入或单例,因此无法找到问题的确切原因和位置,而我的问题仍未得到解决。
这是我到目前为止尝试过的,但仍然无法解决问题:
为清楚起见,请看一下此录音。
最后,这是我的脚本的样子:
leave_bloc.dart
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:Flutter_prismahr/app/data/models/leave_model.dart';
import 'package:Flutter_prismahr/app/data/repositories/leave_repository.dart';
import 'package:Meta/Meta.dart';
part 'leave_event.dart';
part 'leave_state.dart';
class LeaveBloc extends Bloc<LeaveEvent,LeaveState> {
LeaveBloc() : super(LeaveInitial());
final LeaveRepository repository = LeaveRepository();
@override
Stream<LeaveState> mapEventToState(
LeaveEvent event,) async* {
print('TRIGGERED EVENT IS: $event');
if (event is LeaveScreenInitialized) {
yield LeaveLoading();
try {
final response = await repository.fetch();
if (response is List<Leave> && response.isNotEmpty) {
yield LeaveLoaded(data: response);
} else {
yield LeaveEmpty();
}
} catch (e) {
yield LeaveFailure(error: e.toString());
}
}
if (event is LeaveAdded) {
yield LeaveCreated(data: event.data);
}
}
}
leave_event.dart
part of 'leave_bloc.dart';
abstract class LeaveEvent {
const LeaveEvent();
// @override
// List<Object> get props => [];
}
class LeaveScreenInitialized extends LeaveEvent {}
class LeaveAdded extends LeaveEvent {
final Leave data;
const LeaveAdded({@required this.data}) : assert(data != null);
// @override
// List<Object> get props => [data];
@override
String toString() => 'LeaveAdded { data: $data }';
}
leave_state.dart
part of 'leave_bloc.dart';
abstract class LeaveState {
const LeaveState();
// @override
// List<Object> get props => [];
}
class LeaveInitial extends LeaveState {}
class LeaveLoading extends LeaveState {}
class LeaveEmpty extends LeaveState {}
class LeaveLoaded extends LeaveState {
final List<Leave> data;
const LeaveLoaded({@required this.data}) : assert(data != null);
// @override
// List<Object> get props => [data];
@override
String toString() => 'LeaveLoaded { data: $data }';
}
class LeaveFailure extends LeaveState {
final String error;
const LeaveFailure({@required this.error}) : assert(error != null);
// @override
// List<Object> get props => [error];
@override
String toString() => 'LeaveFailure { error: $error }';
}
class LeaveCreated extends LeaveState {
final Leave data;
const LeaveCreated({@required this.data}) : assert(data != null);
// @override
// List<Object> get props => [data];
@override
String toString() => 'LeaveCreated { data: $data }';
}
leave_screen.dart
import 'package:Flutter/material.dart';
import 'package:Flutter_bloc/Flutter_bloc.dart';
import 'package:Flutter_prismahr/app/bloc/leave/leave_bloc.dart';
import 'package:Flutter_prismahr/app/bloc/leave_update/leave_update_bloc.dart';
import 'package:Flutter_prismahr/app/components/empty.dart';
import 'package:Flutter_prismahr/app/data/models/leave_model.dart';
import 'package:Flutter_prismahr/app/routes/routes.dart';
import 'components/leave_list.dart';
import 'components/leave_list_loading.dart';
class LeaveScreen extends StatefulWidget {
LeaveScreen({Key key}) : super(key: key);
@override
_LeaveScreenState createState() => _LeaveScreenState();
}
class _LeaveScreenState extends State<LeaveScreen> {
LeaveBloc _leaveBloc;
LeaveUpdateBloc _leaveUpdateBloc;
List<Leave> _leaves;
@override
void initState() {
print('INIT STATE CALLED');
_leaves = <Leave>[];
_leaveBloc = BlocProvider.of<LeaveBloc>(context);
_leaveUpdateBloc = BlocProvider.of<LeaveUpdateBloc>(context);
_leaveBloc.add(LeaveScreenInitialized());
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
pinned: true,floating: true,title: Text(
'Leave Requests',style: Theme.of(context)
.textTheme
.headline6
.copyWith(fontWeight: FontWeight.w900),),SliverToBoxAdapter(
child: MultiBlocListener(
listeners: [
BlocListener<LeaveBloc,LeaveState>(
listener: (context,state) {
if (state is LeaveLoaded) {
setState(() {
_leaves = state.data;
});
}
if (state is LeaveCreated) {
setState(() {
_leaves.add(state.data);
});
}
},BlocListener<LeaveUpdateBloc,LeaveUpdateState>(
listener: (context,state) {
if (state is LeaveUpdateSuccess) {
int index = _leaves.indexWhere((leave) {
return leave.id == state.data.id;
});
setState(() {
_leaves[index] = state.data;
_leaveUpdateBloc.add(ResetState());
});
}
},],child: BlocBuilder<LeaveBloc,LeaveState>(
builder: (context,state) {
if (state is LeaveLoading) {
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: 20,vertical: 30,child: LeaveListLoading(),);
}
if (state is LeaveEmpty) {
return Padding(
padding: const EdgeInsets.only(top: 100),child: Empty(),);
}
return LeaveList(
data: _leaves,bloc: _leaveUpdateBloc,);
},floatingActionButton: BlocBuilder<LeaveBloc,LeaveState>(
builder: (context,state) {
if (state is! LeaveLoading) {
return FloatingActionButton(
child: Icon(Icons.add),onpressed: () async {
final data = await Navigator.of(context).pushNamed(
Routes.LEAVE_CREATE,);
if (data != null) {
_leaveBloc.add(LeaveAdded(data: data));
}
},);
}
return SizedBox();
},);
}
}
app_router.dart
...
...
import 'package:Flutter_prismahr/app/views/leave/leave_screen.dart';
...
...
class Router {
// Provide a function to handle named routes. Use this function to
// identify the named route being pushed,and create the correct
// screen.
final LeaveBloc _leaveBloc = LeaveBloc();
final LeaveUpdateBloc _leaveUpdateBloc = LeaveUpdateBloc();
final LeaveCreateBloc _leaveCreateBloc = LeaveCreateBloc();
Route<dynamic> generateRoute(RouteSettings settings) {
final RouteArguments args = settings.arguments;
switch (settings.name) {
...
...
case Routes.LEAVE:
return MaterialPageRoute(
builder: (_) => MultiBlocProvider(
providers: [
BlocProvider(create: (context) => _leaveBloc),BlocProvider(create: (context) => _leaveUpdateBloc),BlocProvider(create: (context) => _leaveCreateBloc),child: LeaveScreen(),);
...
...
default:
return MaterialPageRoute(
builder: (_) => Scaffold(
body: Center(
child: Text('No route defined for ${settings.name}'),);
}
}
void dispose() {
_leaveBloc.close();
_leaveUpdateBloc.close();
_leaveCreateBloc.close();
}
}
任何线索?
解决方法
TL; DR
- 请勿在路由级集团访问上使用
BlocProvider(create: (context) => _yourBloc)
。- 改为使用
BlocProvider.value(value: _leaveBloc,child: LeaveScreen())
。- 请勿在UI的
dispose
方法内关闭该方块。
在@Rolly的帮助下,我们设法解决了此问题的主要原因。发生这种情况的原因是,在这种情况下,我使用了路由级别的块访问提供程序,并从UI上的dispose
方法关闭了块。由于这是很久以前的问题,所以我不确定我是否还记得所有事情。但我会尽我所能地解释并解释。
主要原因
将集团视为一扇门。我告诉我的应用程序在用户访问leaveBloc
时进入LeaveScreen
,造成混乱,并为他们提供所需的东西。第一次访问它是因为它处于打开状态,因此它可以正常工作,直到用户按下“后退”按钮并且我的脚本将其关闭,应用程序才知道该怎么做。
当用户返回该页面时,集团已关闭,应用程序尝试敲门,但门无法打开,因此它不知道该怎么做,而是站在那儿等待门关上打开。
解决方案
由于我们正在使用路由级块访问,因此该应用程序不应关闭UI级别内的任何块。而是在路由文件上创建一个dispose函数,并在小部件树顶部附近触发它。像这样:
router.dart
class Router {
final LeaveBloc _leaveBloc = LeaveBloc(
...
);
Route<dynamic> generateRoute(RouteSettings settings) {
case Routes.LEAVE:
return MaterialPageRoute(
builder: (_) =>
BlocProvider.value(value: _leaveBloc,child: LeaveScreen()));
}
void dispose() {
_leaveBloc.close();
// another bloc
// another bloc
}
}
main.dart
void main() async {
...
runApp(MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key key}) : super(key: key);
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final Router _router = Router();
...
@override
void dispose() {
_router.dispose(); // <-- trigger dispose when the user closes the app
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
...
);
}
}
附加说明
请注意,我正在为此更改route
脚本:
case Routes.LEAVE:
return MaterialPageRoute(
builder: (_) => MultiBlocProvider(
providers: [
BlocProvider(create: (context) => _leaveBloc),BlocProvider(create: (context) => _leaveUpdateBloc),BlocProvider(create: (context) => _leaveCreateBloc),],child: LeaveScreen(),),);
对此:
case Routes.LEAVE:
return MaterialPageRoute(
builder: (_) =>
BlocProvider.value(value: _leaveBloc,child: LeaveScreen()));
这是必要的,因为我们需要告诉应用程序我们不想从UI级别关闭块,而是让路由器的dispose方法完成这项工作。这行的bloc库文档中对此进行了正式解释。
在将CounterBloc实例提供给路由时,我们正在使用BlocProvider.value,因为我们不希望BlocProvider处理处置bloc(因为_AppState对此负责)。
参考: bloc library's official documentation (Bloc Access - Named Route Access)
,在initState
中只被调用一次是正常的。创建窗口小部件时,该生命周期挂钩仅执行一次。
如果您希望每次都执行该操作,请导航至详细信息屏幕,然后导航至新的详细信息屏幕,因为这将每次都重新创建您的详细信息小部件并添加您的事件。