Home > Mobile >  flutter/dart: receiver can be null
flutter/dart: receiver can be null

Time:10-11

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 in initState().
  • 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.

  • Related