I am running into a little issue that I think is caused because I am trying to get a record from the database that could be not there yet!
so right now I have a view that creates a job in database (CreateUpdateJobView)
I would like to display the job applications for that particular job, in the same page. ex: Job A Job A application 1 Job A application 2
right now I am able to get all the job applications to display under the job, which is kinda what I want but not exactly
here is a youtube video explaining my pain(video quality will improve once youtube finishes uploading it): https://www.youtube.com/watch?v=5PrppRPF_wA
class CreateUpdateJobView extends StatefulWidget {
const CreateUpdateJobView({Key? key}) : super(key: key);
@override
_CreateUpdateJobViewState createState() => _CreateUpdateJobViewState();
}
class _CreateUpdateJobViewState extends State<CreateUpdateJobView> {
CloudJobApplication? _jobApplication;
CloudJob? _job;
late final FirebaseCloudStorage _jobsService;
late final TextEditingController _jobDescriptionController;
late final TextEditingController _jobAreaCodeController;
late final TextEditingController _jobStateController;
final currentUser = AuthService.firebase().currentUser!;
@override
void initState() {
_jobsService = FirebaseCloudStorage();
_jobDescriptionController = TextEditingController();
_jobAreaCodeController = TextEditingController();
_jobStateController = TextEditingController();
super.initState();
}
Future<CloudJob> createOrGetExistingJob(BuildContext context) async {
final widgetJob = context.getArgument<CloudJob>();
if (widgetJob != null) {
_job = widgetJob;
_jobDescriptionController.text = widgetJob.jobDescription;
return widgetJob;
}
final existingJob = _job;
if (existingJob != null) {
return existingJob;
}
final userId = currentUser.id;
final newJob = await _jobsService.createNewJob(jobCreatorId: userId);
_job = newJob;
return newJob;
}
@override
void dispose() {
_deleteJobIfTextIsEmpty();
_saveJobIfTextNotEmpty();
_jobDescriptionController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('New Job'),
actions: [
IconButton(
onPressed: (() async {
print('create new job application');
final job = _job;
if (job == null) {
return;
}
final newJobApplication =
await _jobsService.createNewJobApplication(
jobCreatorId: currentUser.id,
jobId: job.documentId,
jobApplicationDescription: job.jobDescription);
_jobApplication = newJobApplication;
}),
icon: const Icon(Icons.add),
)
],
),
body: Column(
children: [
FutureBuilder(
future: createOrGetExistingJob(context),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.done:
_setupTextControllerListener();
// at this point we want to start to listening for changes
return Column(
//mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('Job State'),
Text(_jobStateController.text),
const SizedBox(height: 10),
const Text('Job description'),
TextField(
controller: _jobDescriptionController,
keyboardType: TextInputType.multiline,
maxLines: null,
decoration: const InputDecoration(
hintText: 'Describe job...',
),
),
const SizedBox(height: 10),
]);
default:
return const CircularProgressIndicator();
}
},
),
const SizedBox(height: 100.0),
const Text('Job Applications'),
StreamBuilder(
// ERROR OCCURS RIGHT HERE
stream: _jobsService.getJobApplication(_job.documentId),
// ERROR OCCURS RIGHT HERE
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
case ConnectionState.active:
if (snapshot.hasData) {
final allJobApplications =
snapshot.data as Iterable<CloudJobApplication>;
return JobApplicationsListView(
jobApplications: allJobApplications,
onTap: (job) {
Navigator.of(context).pushNamed(
updateJobApplicationRoute,
arguments: job,
);
},
);
} else {
return const CircularProgressIndicator();
}
default:
return const CircularProgressIndicator();
}
},
),
],
),
);
}
}
here are the streams for getting the job applications::
Stream<Iterable<CloudJobApplication>> allJobApplications() =>
jobApplication.snapshots().map((event) => event.docs
.map((doc) => CloudJobApplication.fromSnapshot(doc))
.where(
(jobApplication) => jobApplication.jobApplicationState == 'new'));
// here is the function i want to use
Stream<Iterable<CloudJobApplication>> getJobApplication(
String jobApplicationId) =>
jobApplication.snapshots().map((event) => event.docs
.map((doc) => CloudJobApplication.fromSnapshot(doc))
.where((jobApplication) =>
jobApplication.documentId == jobApplicationId));
here are the error::
: Error: Property 'documentId' cannot be accessed on 'CloudJob?' because it is potentially null.
lib/…/jobs/create_update_job_view.dart:183
- 'CloudJob' is from 'package:ijob_clone_app/services/cloud/cloud_job.dart' ('lib/services/cloud/cloud_job.dart').
package:ijob_clone_app/…/cloud/cloud_job.dart:1
Try accessing using ?. instead.
stream: _jobsService.getJobApplication(_job.documentId),
I think i might be doing the wrong approach maybe
CodePudding user response:
The problem is that you made _job
Nullable. Consider the following:
- Are you sure that
context.getArgument<CloudJob>()
is not going to return null? In that case, instead of making_job
Nullable like you did, make it a late variable like this:late CloudJob _job
. Next, initialize this variable ininitState()
. - If it can be null or you cannot move the logic into initState(): Do the following with your Widget:
_job.documentId == null ? Text('job is null!') : StreamBuilder(
// ERROR OCCURS RIGHT HERE
stream: _jobsService.getJobApplication(_job.documentId!),
// ERROR OCCURS RIGHT HERE
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
case ConnectionState.active:
if (snapshot.hasData) {
final allJobApplications =
snapshot.data as Iterable<CloudJobApplication>;
return JobApplicationsListView(
jobApplications: allJobApplications,
onTap: (job) {
Navigator.of(context).pushNamed(
updateJobApplicationRoute,
arguments: job,
);
},
);
} else {
return const CircularProgressIndicator();
}
default:
return const CircularProgressIndicator();
}
},on
),
This is not a solution in the final ui but does fix the issue. You can replace the Text I added with a proper error explaining the cause, if there is one.