0

For some weird reason, my local state variable "_jobApplicationState" is not updating.

I see that it is updated in the database, but its not updating on my page. If I leave the record and come back, everything works as expected.

I am driving this functionality by pressing the button 'Send inquiry'.

I took out a bunch of code to make it easy to read.

I got this to work for a minute at somepoint. but I forgot to save:(


class JobApplicationView extends StatefulWidget {
  const JobApplicationView({Key? key}) : super(key: key);

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

// https://youtu.be/VPvVD8t02U8?t=90350
class _JobApplicationViewState extends State<JobApplicationView> {
  CloudJobApplication? _jobApplication;
  final _formKey = GlobalKey<FormState>();
  final currentUser = AuthService.firebase().currentUser!;
  late final FirebaseCloudStorage _firebaseService;
  //
  late String _jobApplicationState;
  //
  late DateTime _jobApplicationStartDate;
  late DateTime _jobApplicationEndDate;
  //
  bool? isJobCreatorSameAsJobApplicator;
  String? _jobCreatorId;
  String? _jobApplicatorId;
  String? _jobDescription;
  List? _jobUserData;
  String? _jobAddress;
  String? _jobType;
  //

  @override
  void initState() {
    super.initState();
    _jobApplicationStartDate = DateTime.now();
    _jobApplicationEndDate = DateTime.now();

    _firebaseService = FirebaseCloudStorage();
    // _jobDescriptionController = TextEditingController();
    // _jobAreaCodeController = TextEditingController();
    // _jobApplicationStateController = TextEditingController();
  }

  //Future<CloudJobApplication>
  createOrGetExistingJob(BuildContext context) async {
    final widgetJobApplication = context.getArgument<CloudJobApplication>();

    if (widgetJobApplication != null) {
      _jobApplication = widgetJobApplication;
      _jobApplicationState = widgetJobApplication.jobApplicationState;
      _jobApplicatorId = widgetJobApplication.jobApplicatorId;
      _jobCreatorId = widgetJobApplication.jobCreatorId;
      _jobDescription = widgetJobApplication.jobApplicationDescription;

      return widgetJobApplication;
    }
    print('ELSE TRIGGERED!');
    return widgetJobApplication;
  }

  void _updateJobField(localStateField, jobColumn, jobColumnValue) async {
    //* localStateField: local field to update so that the build context is refreshed
    //* jobColumn: the name of the column in the db
    //* jobColumnValue: the value for the jobColumn

    setState(() {
      if (localStateField == '_jobApplicationState') {
        _jobApplicationState = jobColumnValue;
      }
    });

    await _firebaseService.updateJobApplicationColumn(
      documentId: _jobApplication?.documentId as String,
      fieldNameColumn: jobColumn,
      fieldNameColumnValue: jobColumnValue,
    );
  }

  sendInqury() {
    print('setting job applications state!');
    print('_jobApplicationState b4:: $_jobApplicationState');

    _updateJobField(_jobApplicationState, jobApplicationStateColumn,
        jobApplicationStateOpen);
    print('_jobApplicationState after:: $_jobApplicationState');
  }

  @override
  void dispose() {
    //_deleteJobIfTextIsEmpty();
    // _jobDescriptionController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('update job application'),
        actions: [],
      ),
      body: FutureBuilder(
        future: createOrGetExistingJob(context),
        builder: (context, snapshot) {
          switch (snapshot.connectionState) {
            case ConnectionState.done:
              return Form(
                key: _formKey,
                child: ListView(
                  padding: const EdgeInsets.all(32.0),
                  children: [
                    //getStateChevrons(_jobApplicationState),
                    const Divider(
                      height: 20,
                      thickness: 5,
                      indent: 0,
                      endIndent: 0,
                      color: Colors.blue,
                    ),
                    Text(_jobApplicationState),
                    TextButton(
                      style: TextButton.styleFrom(
                        foregroundColor: Colors.white,
                        backgroundColor: Colors.blue,
                        padding: const EdgeInsets.all(16.0),
                        textStyle: const TextStyle(fontSize: 20),
                      ),
                      onPressed: sendInqury,
                      child: const Text('Send inquiry'),
                    )
                  ],
                ),
              );

            default:
              return const CircularProgressIndicator();
          }
        },
      ),
    );
  }
}

I figured out the answer, here is the answer code:

import 'dart:developer';
import 'package:flutter/material.dart';
import '../../services/cloud/cloud_job_application.dart';
import '/services/auth/auth_service.dart';
import '/utilities/generics/get_arguments.dart';
import '/services/cloud/firebase_cloud_storage.dart';

class JobApplicationView extends StatefulWidget {
  const JobApplicationView({Key? key}) : super(key: key);

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

// https://youtu.be/VPvVD8t02U8?t=90350
class _JobApplicationViewState extends State<JobApplicationView> {
  CloudJobApplication? _jobApplication;
  late final FirebaseCloudStorage cloudFunctions;
  final _formKey = GlobalKey<FormState>();
  final currentUser = AuthService.firebase().currentUser!;
  // state varibles
  String _jobApplicationState = 'default';
  String _jobApplicationSubState = 'default';
  late final TextEditingController _jobDescriptionController;

  @override
  void initState() {
    super.initState();
    cloudFunctions = FirebaseCloudStorage();
    _jobDescriptionController = TextEditingController();
  }

  //Future<CloudJobApplication>
  getExistingJobApplication(BuildContext context) async {
    log('getExistingJobApplication()');

    if (_jobApplicationState == 'default') {
      var widgetJobApplication = context.getArgument<CloudJobApplication>();

      log('first time openning job application, returning server data');
      _jobApplication = widgetJobApplication;
      _jobApplicationState =
          widgetJobApplication?.jobApplicationState as String;
      _jobDescriptionController.text =
          widgetJobApplication?.jobApplicationDescription as String;
      return widgetJobApplication;
    } else {
      log('job application has been updated, returnnig local data');
      return cloudFunctions.getJobApplication(_jobApplication!.documentId);
    }
  }

  @override
  void dispose() {
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('update job application'),
        actions: [],
      ),
      body: FutureBuilder(
        future: getExistingJobApplication(context),
        builder: (context, snapshot) {
          switch (snapshot.connectionState) {
            case ConnectionState.done:
              return Form(
                key: _formKey,
                child: ListView(padding: const EdgeInsets.all(32.0), children: [
                  Text(_jobApplicationState),
                  Text(_jobDescriptionController.text),
                  const Divider(
                    height: 20,
                    thickness: 5,
                    indent: 0,
                    endIndent: 0,
                    color: Colors.blue,
                  ),

                  TextFormField(
                    controller: _jobDescriptionController,
                    maxLines: 5,
                    decoration: InputDecoration(
                      // enabled: _jobState == jobStateNew ? true : false,
                      hintText: "The toilet wont flush",
                      filled: true,
                      // fillColor: _jobState == jobStateNew ? Colors.white  : Colors.grey,
                      label: Container(
                        decoration: BoxDecoration(
                          borderRadius: BorderRadius.circular(16.0),
                          color: Colors.white,
                        ),
                        child: Row(
                          mainAxisSize: MainAxisSize.min,
                          children: const [
                            Padding(padding: EdgeInsets.only(left: 8.0)),
                            Icon(Icons.info_outline),
                            Padding(
                              padding: EdgeInsets.only(left: 8.0, right: 8.0),
                              child: Text("Job description"),
                            ),
                          ],
                        ),
                      ),
                    ),
                    validator: (str) =>
                        str == '' ? "Job description can't be empty" : null,
                  ),
                  TextButton(
                      onPressed: () async {
                        setState(() {
                          _jobApplicationState = 'Open';
                        });
                        await cloudFunctions.updateJobApplication(
                          documentId: _jobApplication?.documentId as String,
                          jobDescription: _jobDescriptionController.text,
                          jobApplicationState: 'Open',
                        );
                      },
                      child: const Text('update state')),

                  //
                ]),
              );

            default:
              return const CircularProgressIndicator();
          }
        },
      ),
    );
  }
}
Juan Casas
  • 268
  • 2
  • 13
  • Presumably `_updateJobField(_jobApplicationState, jobApplicationStateColumn,` should be `_updateJobField('_jobApplicationState', jobApplicationStateColumn,`. Surely, you'd just run it in the debugger or add sufficient log lines to figure it out. – Richard Heap Oct 24 '22 at 23:42
  • thats not it. but thanks for helping – Juan Casas Oct 24 '22 at 23:45
  • 1
    that is becuase you are using future builder. if you want your ui to be updated on realtime when data changes then you should be using stream builder not a future builder which is a one time fetch. – john Oct 24 '22 at 23:47
  • I have another page, which is used for updating/creating records, and it works fine with future builder, so I dont think thats it either – Juan Casas Oct 24 '22 at 23:55

1 Answers1

1

You should separate the UI and logic -> create a jobApplication Model.

Pack all your logic into a ChangeNotifier and notifyListeners on change.

This is also better for performance because it only rebuilds needed parts of the UI.

I can recommend using a ChangeNotifierProvider.

class JobApplicationProvider extends ChangeNotifier {
  JobApplication jobapplication = BasicParam.standard;

  void setJobApplication(json) async {
    jobapplication = JobApplication.fromJson(json);
    notifyListeners();
  }
}

And in the build Method use it like this:

Widget build(BuildContext context) {
  JobApplicationProvider jobApplication= Provider.of(context);
  return Text(jobApplication.state);
}
damianhog
  • 36
  • 7
  • I figured out what was going on. the job application was grabbing the data from the context passed to it. i did a bit of logic to see if the default values were updated, if they were, then grab the new info from the server – Juan Casas Oct 25 '22 at 18:18
  • ok just kidding. it works on the surface. the issue is, now i have to update the local state variables manually. it should be updating the ui when the data from the server comes in. im going to make a new post with more information. thanks for your solution but its a bit complex for me. I will try to apply it though. thank you!! – Juan Casas Oct 26 '22 at 17:38