0

I am running into state not initialize issue. The code works fine after the job has been created. If the job has not been created, then i get a error screen for like two seconds, then everything works fine.

I am pretty sure that it’s the widget loading before the _jobDocumentId is set but I can’t fix it!

video link: https://youtu.be/2xuuPAwW9g0

Error code:

════════ Exception caught by widgets library ═══════════════════════════════════
LateInitializationError: Field '_jobDocumentId@41263735' has not been initialized.
The relevant error-causing widget was
CreateUpdateJobView
lib/main.dart:26
════════════════════════════════════════════════════════════════════════════════
I/flutter ( 6024): TEXT CONTROLLER
I/flutter ( 6024): TEXT CONTROLLER OUTSIDE
class CreateUpdateJobView extends StatefulWidget {
  const CreateUpdateJobView({Key? key}) : super(key: key);

  @override
  _CreateUpdateJobViewState createState() => _CreateUpdateJobViewState();
}

// https://youtu.be/VPvVD8t02U8?t=90350
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!;
  late final _jobDocumentId; // THIS IS THE ERROR VARIABLE

  @override
  void initState() {
    //_jobDocumentId = null;  
    _jobsService = FirebaseCloudStorage();
    _jobDescriptionController = TextEditingController();
    _jobAreaCodeController = TextEditingController();
    _jobStateController = TextEditingController();

    super.initState();
  }

  void _jobDescriptionControllerListener() async {
    // update database live when the job is being updated
    final job = _job;
    if (job == null) {
      return;
    }
    await _jobsService.updateJob(
      documentId: job.documentId,
      jobDescription: _jobDescriptionController.text,
      //jobState: _jobStateController.text,
    );
  }

  void _setupTextControllerListener() {
    final job = _job;
    if (job != null) {
      _jobStateController.text = job.jobState;
      print('TEXT CONTROLLER');
    }
    print('TEXT CONTROLLER OUTSIDE');
    _jobDescriptionController.removeListener(_jobDescriptionControllerListener);
    _jobDescriptionController.addListener(_jobDescriptionControllerListener);

    _jobAreaCodeController.removeListener(_jobDescriptionControllerListener);
    _jobAreaCodeController.addListener(_jobDescriptionControllerListener);

    _jobStateController.removeListener(_jobDescriptionControllerListener);
    _jobStateController.addListener(_jobDescriptionControllerListener);
  }


// I THINK THIS IS WHERE THE ISSUE IS!
  Future<CloudJob> createOrGetExistingJob(BuildContext context) async {
    final widgetJob = context.getArgument<CloudJob>();

    if (widgetJob != null) {
      _job = widgetJob;
      _jobDescriptionController.text = widgetJob.jobDescription;
      setState(() {
        _jobDocumentId = widgetJob.documentId;
      });
      return widgetJob;
    }

    final existingJob = _job;
    if (existingJob != null) {
      setState(() {
        _jobDocumentId = existingJob.documentId;
      });
      return existingJob;
    }

    // new job

    final userId = currentUser.id;
    final newJob = await _jobsService.createNewJob(jobCreatorId: userId);

    setState(() {
      _jobDocumentId = newJob.documentId;
    });
    _job = newJob;
    return newJob;
  }

  void _deleteJobIfTextIsEmpty() {
    final job = _job;
    if (_jobDescriptionController.text.isEmpty && job != null) {
      _jobsService.deleteJob(documentId: job.documentId);
    }
  }

  void _saveJobIfTextNotEmpty() async {
    final job = _job;
    if (job != null && _jobDescriptionController.text.isNotEmpty) {
      await _jobsService.updateJob(
        documentId: job.documentId,
        jobDescription: _jobDescriptionController.text,
        //jobState: _jobStateController.text,
      );
    }
  }

  @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),
                        // const Text('Area code'),
                        // TextField(
                        //   controller: _jobDescriptionController,
                        //   keyboardType: TextInputType.multiline,
                        //   maxLines: null,
                        //   decoration: const InputDecoration(
                        //     hintText: 'Describe job...',
                        //   ),
                        // ),
                      ]);
                default:
                  return const CircularProgressIndicator();
              }
            },
          ),
          _jobDocumentId == null ? Text('null') : Text(_jobDocumentId)
        ],
      ),
    );
  }
}
Juan Casas
  • 268
  • 2
  • 13
  • When you declare a variable as `late`, you're promising that you'll give it a value before you ever read from the variable. Your `_jobDocumentId` can't be a `late` variable, as there's no way that it'll be initialized before the `_jobDocumentId == null ? Text('null') : Text(_jobDocumentId)` runs for the first time. It should instead be a `String?` as far as I can tell. – Frank van Puffelen Oct 09 '22 at 23:04

0 Answers0