Skip to content

FormBuilderState: setInternalFieldValue #24

@charlieforward9

Description

@charlieforward9

Is there an existing issue for this?

  • I have searched the existing issues

Package/Plugin version

0.5.0 + #23

Platforms

  • Android
  • iOS
  • Linux
  • MacOS
  • Web
  • Windows

Flutter doctor

Flutter doctor

Minimal code example

Code sample
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';

import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:form_builder_cupertino_fields/form_builder_cupertino_fields.dart';
import 'package:intl/intl.dart';

class ProfileForm extends StatefulWidget {
  const ProfileForm({super.key});

  @override
  State<ProfileForm> createState() => _ProfileFormState();
}

class _ProfileFormState extends State<ProfileForm> {
  final _formKey = GlobalKey<FormBuilderState>();
  TextEditingController firstNameController = TextEditingController();
  TextEditingController lastNameController = TextEditingController();
  TextEditingController emailController = TextEditingController();
  TextEditingController dobController =
      TextEditingController(text: DateTime.now().toIso8601String());
  TextEditingController heightController = TextEditingController();
  TextEditingController weightController = TextEditingController();

  void handleAutofill() {
    setState(() {
      firstNameController.text = "Sample";
      lastNameController.text = "Profile";
      emailController.text = "[email protected]";

      //FIXME: Form not updating the non-textual fields

      // _formKey.currentState?.setInternalFieldValue("Birth Date", "2000-01-01");
      // _formKey.currentState?.setInternalFieldValue("Height", "60\"");
      // _formKey.currentState?.setInternalFieldValue("Weight", "160\"");
      dobController.text = "2000-01-01";
      heightController.text = "60";
      weightController.text = "160";
    });
  }

  void handleFormSubmit() {
    //TODO <are you sure this data is correct?> modal
    try {
      if (_formKey.currentState?.validate() ?? false) {
        _formKey.currentState?.save();
        print("Done");

        setState(() {
          firstNameController.clear();
          lastNameController.clear();
          dobController.clear();
          heightController.clear();
          emailController.clear();
        });
      }
    } catch (e) {
      print(e);
      rethrow;
    }
  }

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: SingleChildScrollView(
        clipBehavior: Clip.none,
        padding: EdgeInsets.only(
          bottom: MediaQuery.of(context).viewInsets.bottom,
        ),
        child: FormBuilder(
          key: _formKey,
          autovalidateMode: AutovalidateMode.onUnfocus,
          child: Column(
            mainAxisSize: MainAxisSize.min,
            //TODO: add a reset button
            crossAxisAlignment: CrossAxisAlignment.center,

            children: [
              const Text(
                "New Patient Profile Form",
                textAlign: TextAlign.center,
              ),
              FormBuilderCupertinoTextField(
                name: "First Name",
                controller: firstNameController,
                keyboardType: TextInputType.name,
                placeholder: "First Name",
                validator: (value) => value == null || value.isEmpty
                    ? "Please enter a first name"
                    : null,
              ),
              FormBuilderCupertinoTextField(
                name: "Last Name",
                controller: lastNameController,
                keyboardType: TextInputType.name,
                placeholder: "Last Name",
                validator: (value) => value == null || value.isEmpty
                    ? "Please enter a last name"
                    : null,
              ),
              FormBuilderCupertinoTextField(
                name: "Email",
                controller: emailController,
                keyboardType: TextInputType.emailAddress,
                placeholder: "Email",
                validator: (value) => value == null ||
                        value.isEmpty ||
                        !value.contains('@') ||
                        !value.contains('.')
                    //TODO: add a regex for email validation
                    ? "Please enter an email"
                    : null,
              ),
              FormBuilderCupertinoDateTimePicker(
                name: "Birth Date",
                controller: dobController,
                inputType: InputType.date,
                format: DateFormat("MMM dd, yyyy"),
                initialValue:
                    DateTime.tryParse(dobController.text) ?? DateTime.now(),
                prefix: const Text("Date of Birth"),
                textAlign: TextAlign.center,
                onChanged: (date) {
                  // the user should only be able to put the date, and this value should be interpreted at UTC and never converted to locale time
                  setState(() => dobController.text =
                      date?.toUtc().toIso8601String().split('T').first ?? '');
                },
                validator: (value) {
                  //if its within the last 10 years its probably a mistake
                  final min = DateTime.now().year - value!.year < 10;
                  final max = DateTime.now().year - value.year > 120;

                  if (min || max) {
                    return 'Please select a valid birth date';
                  }
                  return null;
                },
              ),
              FormBuilderCupertinoSlider(
                name: "Height",
                min: 36,
                max: 99,
                divisions: 63,
                initialValue: double.tryParse(heightController.text) ?? 60,
                prefix: const Text(
                  "Height ",
                ),
                onChanged: (value) =>
                    setState(() => heightController.text = value.toString()),
                minValueWidget: (string) => Container(),
                maxValueWidget: (string) => Container(),
                valueWidget: (value) {
                  final v = int.parse(value);
                  final meters = v * 0.0254;
                  //convert to XftXXin (m)
                  return Text(
                    "${v ~/ 12}ft${v % 12}in / ${meters.toStringAsFixed(2)} m",
                  );
                },
              ),
              FormBuilderCupertinoSlider(
                name: "Weight",
                min: 70,
                max: 400,
                divisions: 330,
                initialValue: double.tryParse(weightController.text) ?? 100,
                prefix: const Text(
                  "Weight ",
                ),
                onChanged: (value) =>
                    setState(() => weightController.text = value.toString()),
                minValueWidget: (string) => Container(),
                maxValueWidget: (string) => Container(),
                valueWidget: (value) {
                  final v = int.parse(value);
                  // Since this is now for weight, we'll convert to kg instead of height calculations
                  final kg = v * 0.453592;
                  // Show weight in lbs and kg
                  return Text(
                    "$v lbs / ${kg.toStringAsFixed(2)} kg",
                  );
                },
              ),
              Padding(
                padding: const EdgeInsets.all(8.0),
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                  children: [
                    kDebugMode
                        ? CupertinoButton(
                            onPressed: handleAutofill,
                            child: const Text(
                              "Autofill",
                            ))
                        : Container(),
                    CupertinoButton(
                      onPressed: handleFormSubmit,
                      child: const Text("Next"),
                    ),
                  ],
                ),
              )
            ],
          ),
        ),
      ),
    );
  }
}

Current Behavior

Clicking Autofill will only update the state of the FormBuilderCupertinoTextField's and none other. Clicking save afterwards will show validation errors for the FormBuilderCupertinoDateTimePicker, even though the text is being updated, and the FormBuilderCupertinoSegmentedControl/FormBuilderCupertinoSlider do not even show an updated positioning at all.

Expected Behavior

State should be updated with controller.value or controller.text or setInternalFieldValue

Steps To Reproduce

  1. run ProfileForm in a Flutter app
  2. Click AutoFill
  3. Observe inactivity of sliders (BUG)
  4. Click Next
  5. Observe validation errors (BUG)

Aditional information

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    Projects

    Status

    Ready

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions