title: How to properly use GetX with TextField and some server side validation?
body: <p>I'm using <code>get</code> package for my project's state management. But i'm confused by the implementation for form inputs and validation since i couldn't find any example for it in the documentation. I have some questions regarding to this issue.</p>
  <ol>
  <li>Should i create separate controller for each action? like <code>PageOneController</code> and <code>PageOneFormController</code>?</li>
  <li>How to prevent the parent from rebuilding the child when the state changes?</li>
  <li>How to trigger rebuild on data addition inside the <code>Model().obs</code> object?</li>
  </ol>
  <p>As I mentioned above at point number 1, i found that using separate controller is a bit repetitive and unnecessary, but using same controller at multiple places prevents me to reset the state when i leave the child page since the controller was only destroyed when I leave the page which has initialized the state. To prevent any confusion from my explanation, please have a look at the illustration below.</p>
  <p><a href="https://i.stack.imgur.com/LqKhB.gif" rel="noreferrer"><img src="https://i.stack.imgur.com/LqKhB.gif" alt="visual explanation" /></a></p>
  <p>At point number 2, as we know that the <code>TextField</code> widget accepts <code>errorText</code> for displaying an error message which is only accept a string. With this package in mind, when I try to change state of the error by the <code>onChanged: (value) {}</code> event, it rebuilds the entire widget everytime i type a value inside it which causing the input indicator stayed at the beginning.</p>
  <p>In this case, point number 2 is no more happened but now it won't update the error state at all and it keep showing the error message, even when i typed a new value onto it.</p>
  <p>Please help, here is my script:</p>
  <p><code>education_info_create_page.dart</code></p>
  <pre class="lang-dart prettyprint-override"><code>import 'package:flutter/material.dart';
  import 'package:get/get.dart';
  import 'package:prismahrfinal/app/controllers/account_info/education_info_controller.dart';
  import 'package:prismahrfinal/app/ui/widgets/form_input.dart';
  
  class EducationInfoCreatePage extends GetWidget&lt;EducationInfoController&gt; {
    @override
    Widget build(BuildContext context) {
      return Scaffold(
        body: CustomScrollView(
          slivers: &lt;Widget&gt;[
            SliverAppBar(
              title: Text('Add Education Info'),
              floating: true,
            ),
            SliverToBoxAdapter(
              child: Padding(
                padding: const EdgeInsets.symmetric(vertical: 30, horizontal: 20),
                child: Column(
                  children: &lt;Widget&gt;[
                    Obx(
                      () =&gt; FormInput(
                        autofocus: true,
                        label: 'Institution name',
                        focusNode: controller.institutionFN,
                        keyboardType: TextInputType.text,
                        textInputAction: TextInputAction.next,
                        errorText: controller.institutionError,
                        onChanged: (value) {
                          controller.institutionError = null;
                          controller.institution = value;
                        },
                        onSubmitted: (_) {
                          controller.graduationMonthFN.requestFocus();
                        },
                      ),
                    ),
                    Obx(
                      () =&gt; FormInput(
                        label: 'Graduation month',
                        focusNode: controller.graduationMonthFN,
                        keyboardType: TextInputType.number,
                        textInputAction: TextInputAction.next,
                        errorText: controller.graduationMonthError,
                        onChanged: (value) {
                          controller.graduationMonthError = null;
                          controller.graduationMonth = int.parse(value);
                        },
                        onSubmitted: (_) {
                          controller.graduationYearFN.requestFocus();
                        },
                      ),
                    ),
                    Obx(
                      () =&gt; FormInput(
                        label: 'Graduation Year',
                        focusNode: controller.graduationYearFN,
                        keyboardType: TextInputType.number,
                        textInputAction: TextInputAction.next,
                        errorText: controller.graduationYearError,
                        onChanged: (value) {
                          controller.graduationYearError = null;
                          controller.graduationYear = int.parse(value);
                        },
                        onSubmitted: (_) {
                          controller.qualificationFN.requestFocus();
                        },
                      ),
                    ),
                    Obx(
                      () =&gt; FormInput(
                        label: 'Qualification',
                        focusNode: controller.qualificationFN,
                        keyboardType: TextInputType.text,
                        textInputAction: TextInputAction.next,
                        errorText: controller.qualificationError,
                        onChanged: (value) {
                          controller.qualificationError = null;
                          controller.qualification = value;
                        },
                        onSubmitted: (_) {
                          controller.locationFN.requestFocus();
                        },
                      ),
                    ),
                    Obx(
                      () =&gt; FormInput(
                        label: 'Location',
                        focusNode: controller.locationFN,
                        keyboardType: TextInputType.text,
                        textInputAction: TextInputAction.next,
                        errorText: controller.locationError,
                        onChanged: (value) {
                          controller.locationError = null;
                          controller.location = value;
                        },
                        onSubmitted: (_) {
                          controller.fieldOfStudyFN.requestFocus();
                        },
                      ),
                    ),
                    Obx(
                      () =&gt; FormInput(
                        label: 'Field of study',
                        focusNode: controller.fieldOfStudyFN,
                        keyboardType: TextInputType.text,
                        textInputAction: TextInputAction.next,
                        errorText: controller.fieldOfStudyError,
                        onChanged: (value) {
                          controller.fieldOfStudyError = null;
                          controller.fieldOfStudy = value;
                        },
                        onSubmitted: (_) {
                          controller.majorsFN.requestFocus();
                        },
                      ),
                    ),
                    Obx(
                      () =&gt; FormInput(
                        label: 'Majors',
                        focusNode: controller.majorsFN,
                        keyboardType: TextInputType.text,
                        textInputAction: TextInputAction.next,
                        errorText: controller.majorsError,
                        onChanged: (value) {
                          controller.majorsError = null;
                          controller.majors = value;
                        },
                        onSubmitted: (_) {
                          controller.finalScoreFN.requestFocus();
                        },
                      ),
                    ),
                    Obx(
                      () =&gt; FormInput(
                        label: 'Final Score',
                        focusNode: controller.finalScoreFN,
                        keyboardType: TextInputType.text,
                        textInputAction: TextInputAction.next,
                        errorText: controller.finalScoreError,
                        onChanged: (value) {
                          controller.finalScoreError = null;
                          controller.finalScore = double.parse(value);
                        },
                        onSubmitted: (_) {
                          controller.additionalInfoFN.requestFocus();
                        },
                      ),
                    ),
                    Obx(
                      () =&gt; FormInput(
                        label: 'Additional Info (optional)',
                        focusNode: controller.additionalInfoFN,
                        keyboardType: TextInputType.multiline,
                        maxLines: 5,
                        textInputAction: TextInputAction.go,
                        errorText: controller.additionalInfoError,
                        onChanged: (value) {
                          controller.additionalInfoError = null;
                          controller.additionalInfo = value;
                        },
                        onSubmitted: (_) {
                          controller.add();
                        },
                      ),
                    ),
                  ],
                ),
              ),
            ),
          ],
        ),
        floatingActionButton: FloatingActionButton(
          child: Icon(Icons.send, color: Colors.white),
          shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
          backgroundColor: Theme.of(context).primaryColor,
          tooltip: 'Add Education Info',
          onPressed: controller.add,
        ),
      );
    }
  }
  </code></pre>
  <p><code>education_info_controller.dart</code></p>
  <pre class="lang-dart prettyprint-override"><code>import 'package:flutter/material.dart';
  import 'package:get/get.dart';
  import 'package:meta/meta.dart';
  import 'package:pretty_json/pretty_json.dart';
  import 'package:prismahrfinal/app/data/models/account_info/education_info.dart';
  import 'package:prismahrfinal/app/data/models/account_info/education_info_error.dart';
  import 'package:prismahrfinal/app/data/repositories/account_info/education_info_repository.dart';
  
  class EducationInfoController extends GetxController {
    EducationInfoController({@required this.repository})
        : assert(repository != null);
  
    final EducationInfoRepository repository;
    final FocusNode _institutionFN = FocusNode();
    final FocusNode _graduationMonthFN = FocusNode();
    final FocusNode _graduationYearFN = FocusNode();
    final FocusNode _qualificationFN = FocusNode();
    final FocusNode _locationFN = FocusNode();
    final FocusNode _fieldOfStudyFN = FocusNode();
    final FocusNode _majorsFN = FocusNode();
    final FocusNode _finalScoreFN = FocusNode();
    final FocusNode _additionalInfoFN = FocusNode();
  
    final Rx&lt;ListEducationInfo&gt; data = ListEducationInfo().obs;
    final Rx&lt;EducationInfo&gt; education = EducationInfo().obs;
    final Rx&lt;EducationInfoError&gt; errors = EducationInfoError().obs;
  
    @override
    void onInit() =&gt; fetchDataFromApi();
  
    void fetchDataFromApi() async {
      data.value = await repository.getData();
      if (data.value == null) {
        Get.snackbar(&quot;Error&quot;, &quot;Can't connect to server&quot;);
      }
    }
  
    void add() async {
      this._unfocus();
      debugPrint(prettyJson(education.value.toJson()));
  
      final response = await repository.add(education.value.toJson());
      if (response == null) {
        Get.snackbar(&quot;Error&quot;, &quot;Can't connect to server&quot;);
        return;
      } else if (response is EducationInfoError) {
        errors.value = response;
        return;
      }
  
      data.value.educations.add(response);
    }
  
    void _unfocus() {
      this.institutionFN.unfocus();
      this.graduationMonthFN.unfocus();
      this.graduationYearFN.unfocus();
      this.qualificationFN.unfocus();
      this.locationFN.unfocus();
      this.fieldOfStudyFN.unfocus();
      this.majorsFN.unfocus();
      this.finalScoreFN.unfocus();
      this.additionalInfoFN.unfocus();
    }
  
    // Getters -- Focus Nodes
    get institutionFN =&gt; this._institutionFN;
    get graduationMonthFN =&gt; this._graduationMonthFN;
    get graduationYearFN =&gt; this._graduationYearFN;
    get qualificationFN =&gt; this._qualificationFN;
    get locationFN =&gt; this._locationFN;
    get fieldOfStudyFN =&gt; this._fieldOfStudyFN;
    get majorsFN =&gt; this._majorsFN;
    get finalScoreFN =&gt; this._finalScoreFN;
    get additionalInfoFN =&gt; this._additionalInfoFN;
  
    // Getters -- Values
    get institution =&gt; this.education.value.institution;
    get graduationMonth =&gt; this.education.value.graduationMonth;
    get graduationYear =&gt; this.education.value.graduationYear;
    get qualification =&gt; this.education.value.qualification;
    get location =&gt; this.education.value.location;
    get fieldOfStudy =&gt; this.education.value.fieldOfStudy;
    get majors =&gt; this.education.value.majors;
    get finalScore =&gt; this.education.value.finalScore;
    get additionalInfo =&gt; this.education.value.additionalInfo;
  
    // Getters -- Errors
    get institutionError =&gt; this.errors.value.institution?.first;
    get graduationMonthError =&gt; this.errors.value.graduationMonth?.first;
    get graduationYearError =&gt; this.errors.value.graduationYear?.first;
    get qualificationError =&gt; this.errors.value.qualification?.first;
    get locationError =&gt; this.errors.value.location?.first;
    get fieldOfStudyError =&gt; this.errors.value.fieldOfStudy?.first;
    get majorsError =&gt; this.errors.value.majors?.first;
    get finalScoreError =&gt; this.errors.value.finalScore?.first;
    get additionalInfoError =&gt; this.errors.value.additionalInfo?.first;
  
    // Setters -- Values
    set institution(value) =&gt; this.education.value.institution = value;
    set graduationMonth(value) =&gt; this.education.value.graduationMonth = value;
    set graduationYear(value) =&gt; this.education.value.graduationYear = value;
    set qualification(value) =&gt; this.education.value.qualification = value;
    set location(value) =&gt; this.education.value.location = value;
    set fieldOfStudy(value) =&gt; this.education.value.fieldOfStudy = value;
    set majors(value) =&gt; this.education.value.majors = value;
    set finalScore(value) =&gt; this.education.value.finalScore = value;
    set additionalInfo(value) =&gt; this.education.value.additionalInfo = value;
  
    // Setters -- Errors
    set institutionError(value) =&gt; this.errors.value.institution = value;
    set graduationMonthError(value) =&gt; this.errors.value.graduationMonth = value;
    set graduationYearError(value) =&gt; this.errors.value.graduationYear = value;
    set qualificationError(value) =&gt; this.errors.value.qualification = value;
    set locationError(value) =&gt; this.errors.value.location = value;
    set fieldOfStudyError(value) =&gt; this.errors.value.fieldOfStudy = value;
    set majorsError(value) =&gt; this.errors.value.majors = value;
    set finalScoreError(value) =&gt; this.errors.value.finalScore = value;
    set additionalInfoError(value) =&gt; this.errors.value.additionalInfo = value;
  }
  </code></pre>
  <p><code>app_pages.dart</code></p>
  <pre class="lang-dart prettyprint-override"><code>import 'package:get/get.dart';
  import 'package:prismahrfinal/app/bindings/education_info_binding.dart';
  import 'package:prismahrfinal/app/bindings/employment_info_binding.dart';
  import 'package:prismahrfinal/app/bindings/personal_info_binding.dart';
  import 'package:prismahrfinal/app/ui/android/account_info/education_info/education_info.dart';
  import 'package:prismahrfinal/app/ui/android/account_info/education_info/education_info_create.dart';
  import 'package:prismahrfinal/app/ui/android/account_info/employment_info/employment_info.dart';
  import 'package:prismahrfinal/app/ui/android/account_info/personal_info/personal_info.dart';
  import 'package:prismahrfinal/app/ui/android/account_info/personal_info/personal_info_edit.dart';
  import 'package:prismahrfinal/app/ui/android/home.dart';
  import 'package:prismahrfinal/app/ui/android/login.dart';
  import 'package:prismahrfinal/app/ui/android/account_info.dart';
  part './app_routes.dart';
  
  abstract class AppPages {
    static final pages = [
      GetPage(
        name: Routes.EDUCATION_INFO,
        page: () =&gt; EducationInfoPage(),
        binding: EducationInfoBinding(),
      ),
      GetPage(
        name: Routes.EDUCATION_INFO_CREATE,
        page: () =&gt; EducationInfoCreatePage(),
      ),
    ];
  }
  </code></pre>