I have 3 buttons at the top of screen – Latest, Saved & My Feed
I am using MultiBlocProvider where I am loading categories (works fine) and then Latest posts (loading fine initially).
Problem arises when clicking on Saved or My Feed to load their posts.
Here is code:
Job_post_state.dart
<code>enum JobPostEvent { latest, saved, myFeed }
enum JobPostStatus { initial, loading, loaded, empty, failed }
class JobPostState {
final List<PostDataModel> jobPosts;
final JobPostStatus status;
JobPostState({required this.jobPosts, this.status = JobPostStatus.initial});
JobPostState copyWith({List<PostDataModel>? jobPosts, JobPostStatus? status}) {
return JobPostState(jobPosts: jobPosts ?? [], status: status ?? this.status);
}
}
</code>
<code>enum JobPostEvent { latest, saved, myFeed }
enum JobPostStatus { initial, loading, loaded, empty, failed }
class JobPostState {
final List<PostDataModel> jobPosts;
final JobPostStatus status;
JobPostState({required this.jobPosts, this.status = JobPostStatus.initial});
JobPostState copyWith({List<PostDataModel>? jobPosts, JobPostStatus? status}) {
return JobPostState(jobPosts: jobPosts ?? [], status: status ?? this.status);
}
}
</code>
enum JobPostEvent { latest, saved, myFeed }
enum JobPostStatus { initial, loading, loaded, empty, failed }
class JobPostState {
final List<PostDataModel> jobPosts;
final JobPostStatus status;
JobPostState({required this.jobPosts, this.status = JobPostStatus.initial});
JobPostState copyWith({List<PostDataModel>? jobPosts, JobPostStatus? status}) {
return JobPostState(jobPosts: jobPosts ?? [], status: status ?? this.status);
}
}
job_post_bloc.dart
<code>class JobPostBloc extends Bloc<JobPostEvent, JobPostState> {
final JobPostRepository jobPostRepository;
JobPostBloc({required this.jobPostRepository}) : super(JobPostState(status: JobPostStatus.initial, jobPosts: [])) {
on<JobPostEvent>((event, emit) async {
emit(state.copyWith(status: JobPostStatus.loading));
print('bloc event: $event');
switch (event) {
case JobPostEvent.latest:
try {
final jobPosts = await jobPostRepository.getJobPosts();
emit(state.copyWith(
jobPosts: jobPosts,
status: jobPosts.isEmpty ? JobPostStatus.empty : JobPostStatus
.loaded)
);
} catch (e) {
emit(state.copyWith(status: JobPostStatus.failed));
}
break;
case JobPostEvent.saved:
try {
final jobPosts = await Sqlite().fetchPosts();
emit(state.copyWith(
jobPosts: jobPosts,
status: jobPosts.isEmpty ? JobPostStatus.empty : JobPostStatus
.loaded)
);
} catch (e) {
emit(state.copyWith(status: JobPostStatus.failed));
}
break;
case JobPostEvent.myFeed:
try {
final jobPosts = await jobPostRepository.getJobPosts();
emit(state.copyWith(
jobPosts: jobPosts,
status: jobPosts.isEmpty ? JobPostStatus.empty : JobPostStatus
.loaded)
);
} catch (e) {
emit(state.copyWith(status: JobPostStatus.failed));
}
break;
}
});
}
</code>
<code>class JobPostBloc extends Bloc<JobPostEvent, JobPostState> {
final JobPostRepository jobPostRepository;
JobPostBloc({required this.jobPostRepository}) : super(JobPostState(status: JobPostStatus.initial, jobPosts: [])) {
on<JobPostEvent>((event, emit) async {
emit(state.copyWith(status: JobPostStatus.loading));
print('bloc event: $event');
switch (event) {
case JobPostEvent.latest:
try {
final jobPosts = await jobPostRepository.getJobPosts();
emit(state.copyWith(
jobPosts: jobPosts,
status: jobPosts.isEmpty ? JobPostStatus.empty : JobPostStatus
.loaded)
);
} catch (e) {
emit(state.copyWith(status: JobPostStatus.failed));
}
break;
case JobPostEvent.saved:
try {
final jobPosts = await Sqlite().fetchPosts();
emit(state.copyWith(
jobPosts: jobPosts,
status: jobPosts.isEmpty ? JobPostStatus.empty : JobPostStatus
.loaded)
);
} catch (e) {
emit(state.copyWith(status: JobPostStatus.failed));
}
break;
case JobPostEvent.myFeed:
try {
final jobPosts = await jobPostRepository.getJobPosts();
emit(state.copyWith(
jobPosts: jobPosts,
status: jobPosts.isEmpty ? JobPostStatus.empty : JobPostStatus
.loaded)
);
} catch (e) {
emit(state.copyWith(status: JobPostStatus.failed));
}
break;
}
});
}
</code>
class JobPostBloc extends Bloc<JobPostEvent, JobPostState> {
final JobPostRepository jobPostRepository;
JobPostBloc({required this.jobPostRepository}) : super(JobPostState(status: JobPostStatus.initial, jobPosts: [])) {
on<JobPostEvent>((event, emit) async {
emit(state.copyWith(status: JobPostStatus.loading));
print('bloc event: $event');
switch (event) {
case JobPostEvent.latest:
try {
final jobPosts = await jobPostRepository.getJobPosts();
emit(state.copyWith(
jobPosts: jobPosts,
status: jobPosts.isEmpty ? JobPostStatus.empty : JobPostStatus
.loaded)
);
} catch (e) {
emit(state.copyWith(status: JobPostStatus.failed));
}
break;
case JobPostEvent.saved:
try {
final jobPosts = await Sqlite().fetchPosts();
emit(state.copyWith(
jobPosts: jobPosts,
status: jobPosts.isEmpty ? JobPostStatus.empty : JobPostStatus
.loaded)
);
} catch (e) {
emit(state.copyWith(status: JobPostStatus.failed));
}
break;
case JobPostEvent.myFeed:
try {
final jobPosts = await jobPostRepository.getJobPosts();
emit(state.copyWith(
jobPosts: jobPosts,
status: jobPosts.isEmpty ? JobPostStatus.empty : JobPostStatus
.loaded)
);
} catch (e) {
emit(state.copyWith(status: JobPostStatus.failed));
}
break;
}
});
}
job_list_screen.dart
<code>Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider(
create: (context) => StateBloc(JobPostRepository())
..add(FetchCategories()),
lazy: false,
),
BlocProvider(
create: (context) => JobPostBloc(jobPostRepository: JobPostRepository())
..add(JobPostEvent.latest),
lazy: false,
),
],
child: Scaffold(
backgroundColor: AppColor.appWhite,
body: Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
children: [
/// Top bar with search & user button
Common().sizeBox(40),
AppTopBar(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (builder) => const ProfileScreen()
)
);
},
showLogin: true,
),
const Divider(height: 1, color: AppColor.greyBottomBorder),
/// Latest Jobs, Saved Jobs, My Feed & Filter
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
/// *Problem is here when tapped on each button, JobPostBloc event is not called*
/// *I want to change the post based on button tapped*
/// *Latest button to display posts from API & Saved button to display locally saved posts*
/// ***** onTapped(String text) is called for each button -- shared below*****
/// Latest button
_buildTextButtonWithDivider(AppText.btnLatestJob, displayLatestDivider),
/// Saved button
_buildTextButtonWithDivider(AppText.btnSavedJob, displaySavedDivider),
/// My feed button
_buildTextButtonWithDivider(AppText.btnMyFeed, displayMyFeedDivider),
IconButton(onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (builder) => AdvanceSearchScreen(),
fullscreenDialog: true
)
);
}, icon: AppImageWidget(imageName: AppImage.postFilter),
style: IconButton.styleFrom(
minimumSize: Size(30, 30),
maximumSize: Size(35, 35),
),
),
],
),
BlocBuilder<StateBloc, StateState>(
builder: (context, snapshot) {
if (snapshot is StateLoading) {
return const Center(child: CircularProgressIndicator(),);
} else if (snapshot is StateError) {
return Center(child: Text(snapshot.errorMessage),);
} else if (snapshot is CategoryLoaded) {
return categoryChipList(snapshot.categories);
}
return const Center(child: Text('No category found'),);
}
),
Expanded(
child: BlocBuilder<JobPostBloc, JobPostState>(
builder: (context, snapshot) {
switch (snapshot.status) {
case JobPostStatus.loading:
return const Center(child: CircularProgressIndicator(),);
case JobPostStatus.failed:
return Center(child: Text('Error:'));
case JobPostStatus.loaded:
return ListView.builder(
padding: EdgeInsets.zero,
physics: const BouncingScrollPhysics(),
itemCount: snapshot.jobPosts.length ?? 0,
itemBuilder: (context, index) {
PostDataModel post = snapshot.jobPosts[index];
return JobCard(
isTapped: false,
isBookmarkTapped: false,
post: post,
onTap: () {
print('push tapped');
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => JobDetailView(jobModel: post),
),
);
},
postDeletedSetter: (result) {},
);
}
);
case JobPostStatus.empty:
return const Center(
child: Text('Search for job'),
);
default:
return const Center(
child: Text('Search for job'),
);
}
}
),
),
],
),
),
),
);
}
</code>
<code>Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider(
create: (context) => StateBloc(JobPostRepository())
..add(FetchCategories()),
lazy: false,
),
BlocProvider(
create: (context) => JobPostBloc(jobPostRepository: JobPostRepository())
..add(JobPostEvent.latest),
lazy: false,
),
],
child: Scaffold(
backgroundColor: AppColor.appWhite,
body: Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
children: [
/// Top bar with search & user button
Common().sizeBox(40),
AppTopBar(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (builder) => const ProfileScreen()
)
);
},
showLogin: true,
),
const Divider(height: 1, color: AppColor.greyBottomBorder),
/// Latest Jobs, Saved Jobs, My Feed & Filter
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
/// *Problem is here when tapped on each button, JobPostBloc event is not called*
/// *I want to change the post based on button tapped*
/// *Latest button to display posts from API & Saved button to display locally saved posts*
/// ***** onTapped(String text) is called for each button -- shared below*****
/// Latest button
_buildTextButtonWithDivider(AppText.btnLatestJob, displayLatestDivider),
/// Saved button
_buildTextButtonWithDivider(AppText.btnSavedJob, displaySavedDivider),
/// My feed button
_buildTextButtonWithDivider(AppText.btnMyFeed, displayMyFeedDivider),
IconButton(onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (builder) => AdvanceSearchScreen(),
fullscreenDialog: true
)
);
}, icon: AppImageWidget(imageName: AppImage.postFilter),
style: IconButton.styleFrom(
minimumSize: Size(30, 30),
maximumSize: Size(35, 35),
),
),
],
),
BlocBuilder<StateBloc, StateState>(
builder: (context, snapshot) {
if (snapshot is StateLoading) {
return const Center(child: CircularProgressIndicator(),);
} else if (snapshot is StateError) {
return Center(child: Text(snapshot.errorMessage),);
} else if (snapshot is CategoryLoaded) {
return categoryChipList(snapshot.categories);
}
return const Center(child: Text('No category found'),);
}
),
Expanded(
child: BlocBuilder<JobPostBloc, JobPostState>(
builder: (context, snapshot) {
switch (snapshot.status) {
case JobPostStatus.loading:
return const Center(child: CircularProgressIndicator(),);
case JobPostStatus.failed:
return Center(child: Text('Error:'));
case JobPostStatus.loaded:
return ListView.builder(
padding: EdgeInsets.zero,
physics: const BouncingScrollPhysics(),
itemCount: snapshot.jobPosts.length ?? 0,
itemBuilder: (context, index) {
PostDataModel post = snapshot.jobPosts[index];
return JobCard(
isTapped: false,
isBookmarkTapped: false,
post: post,
onTap: () {
print('push tapped');
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => JobDetailView(jobModel: post),
),
);
},
postDeletedSetter: (result) {},
);
}
);
case JobPostStatus.empty:
return const Center(
child: Text('Search for job'),
);
default:
return const Center(
child: Text('Search for job'),
);
}
}
),
),
],
),
),
),
);
}
</code>
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider(
create: (context) => StateBloc(JobPostRepository())
..add(FetchCategories()),
lazy: false,
),
BlocProvider(
create: (context) => JobPostBloc(jobPostRepository: JobPostRepository())
..add(JobPostEvent.latest),
lazy: false,
),
],
child: Scaffold(
backgroundColor: AppColor.appWhite,
body: Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
children: [
/// Top bar with search & user button
Common().sizeBox(40),
AppTopBar(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (builder) => const ProfileScreen()
)
);
},
showLogin: true,
),
const Divider(height: 1, color: AppColor.greyBottomBorder),
/// Latest Jobs, Saved Jobs, My Feed & Filter
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
/// *Problem is here when tapped on each button, JobPostBloc event is not called*
/// *I want to change the post based on button tapped*
/// *Latest button to display posts from API & Saved button to display locally saved posts*
/// ***** onTapped(String text) is called for each button -- shared below*****
/// Latest button
_buildTextButtonWithDivider(AppText.btnLatestJob, displayLatestDivider),
/// Saved button
_buildTextButtonWithDivider(AppText.btnSavedJob, displaySavedDivider),
/// My feed button
_buildTextButtonWithDivider(AppText.btnMyFeed, displayMyFeedDivider),
IconButton(onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (builder) => AdvanceSearchScreen(),
fullscreenDialog: true
)
);
}, icon: AppImageWidget(imageName: AppImage.postFilter),
style: IconButton.styleFrom(
minimumSize: Size(30, 30),
maximumSize: Size(35, 35),
),
),
],
),
BlocBuilder<StateBloc, StateState>(
builder: (context, snapshot) {
if (snapshot is StateLoading) {
return const Center(child: CircularProgressIndicator(),);
} else if (snapshot is StateError) {
return Center(child: Text(snapshot.errorMessage),);
} else if (snapshot is CategoryLoaded) {
return categoryChipList(snapshot.categories);
}
return const Center(child: Text('No category found'),);
}
),
Expanded(
child: BlocBuilder<JobPostBloc, JobPostState>(
builder: (context, snapshot) {
switch (snapshot.status) {
case JobPostStatus.loading:
return const Center(child: CircularProgressIndicator(),);
case JobPostStatus.failed:
return Center(child: Text('Error:'));
case JobPostStatus.loaded:
return ListView.builder(
padding: EdgeInsets.zero,
physics: const BouncingScrollPhysics(),
itemCount: snapshot.jobPosts.length ?? 0,
itemBuilder: (context, index) {
PostDataModel post = snapshot.jobPosts[index];
return JobCard(
isTapped: false,
isBookmarkTapped: false,
post: post,
onTap: () {
print('push tapped');
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => JobDetailView(jobModel: post),
),
);
},
postDeletedSetter: (result) {},
);
}
);
case JobPostStatus.empty:
return const Center(
child: Text('Search for job'),
);
default:
return const Center(
child: Text('Search for job'),
);
}
}
),
),
],
),
),
),
);
}
<code>void onTapped(String text) {
switch (text) {
case AppText.btnLatestJob:
context.read<JobPostBloc>().add(JobPostEvent.latest);
displayLatestDivider = true;
displaySavedDivider = false;
displayMyFeedDivider = false;
break;
case AppText.btnSavedJob:
context.read<JobPostBloc>().add(JobPostEvent.saved);
displayLatestDivider = false;
displaySavedDivider = true;
displayMyFeedDivider = false;
break;
case AppText.btnMyFeed:
context.read<JobPostBloc>().add(JobPostEvent.myFeed);
displayLatestDivider = false;
displaySavedDivider = false;
displayMyFeedDivider = true;
break;
}
}
</code>
<code>void onTapped(String text) {
switch (text) {
case AppText.btnLatestJob:
context.read<JobPostBloc>().add(JobPostEvent.latest);
displayLatestDivider = true;
displaySavedDivider = false;
displayMyFeedDivider = false;
break;
case AppText.btnSavedJob:
context.read<JobPostBloc>().add(JobPostEvent.saved);
displayLatestDivider = false;
displaySavedDivider = true;
displayMyFeedDivider = false;
break;
case AppText.btnMyFeed:
context.read<JobPostBloc>().add(JobPostEvent.myFeed);
displayLatestDivider = false;
displaySavedDivider = false;
displayMyFeedDivider = true;
break;
}
}
</code>
void onTapped(String text) {
switch (text) {
case AppText.btnLatestJob:
context.read<JobPostBloc>().add(JobPostEvent.latest);
displayLatestDivider = true;
displaySavedDivider = false;
displayMyFeedDivider = false;
break;
case AppText.btnSavedJob:
context.read<JobPostBloc>().add(JobPostEvent.saved);
displayLatestDivider = false;
displaySavedDivider = true;
displayMyFeedDivider = false;
break;
case AppText.btnMyFeed:
context.read<JobPostBloc>().add(JobPostEvent.myFeed);
displayLatestDivider = false;
displaySavedDivider = false;
displayMyFeedDivider = true;
break;
}
}