Search code examples
formsflutterpopupalertflutter-form-builder

How to make "Flutter Form Builder" recognize submitted form data from questions in an RFlutter Alert?


I am building a form using the "Flutter Form Builder" package 4.0.2.

Most form questions are on a screen, but the last two are on successive* popups created with the RFlutter Alert package 1.1.0

* By "successive" I mean there is a "Post" button on the bottom of the main form screen (see first image immediately below). When pressed, an RFlutter alert popup appears (see 2nd screenshot image below), with the second-to-last FormBuilderRadioGroup question ("q11"). When the "Next" button is pressed, yet another RFlutter alert popup appears, on which the last FormBuilderRadioGroup question appears ("q12"). The entire Flutter Form Builder form submits when the "Finish Posting" button here is pressed.

screenshot of main screen, before any alert popups appear

screenshot of 1st alert popup

screenshot of 2nd alert popup

The user interface works well, but the problem is nothing from the questions in the alert popups (Q11 and Q12) gets submitted with the form (see console snapshot below - q1, q2 and q3 also have problems, but that's not what I'm asking about here).

screenshot of console

Here is the code (skipped irrelevant parts, for length):

// This is the stateful widget that the main application instantiates, per https://api.flutter.dev/flutter/widgets/Form-class.html
class SandboxWriteReviewScreen extends StatefulWidget {
  // BEGIN code from material_tag_editor
  final String title = 'Material Tag Editor Demo';
// END code from material_tag_editor

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

// This is the private State class that goes with WriteReviewScreen
class _SandboxWriteReviewScreenState extends State<SandboxWriteReviewScreen> {
  var data;
  AutovalidateMode autovalidateMode = AutovalidateMode.always;
  bool readOnly = false;
  bool showSegmentedControl = true;
  //final _newFormbuilderKey = GlobalKey<FormState>();
  final _newnewFormbuilderKey = GlobalKey<FormBuilderState>();

  // above "GlobalKey" lets us generate a unique, app-wide ID that we can associate with our form, per https://fluttercrashcourse.com/blog/realistic-forms-part1
  final ValueChanged _onChanged = (val) => print(val);

  // BEGIN  related to FormBuilderTextField in form below
  final _ageController = TextEditingController(text: '45');
  bool _ageHasError = false;
  // END related to FormBuilderTextField in form below

  String qEleven;
  String qTwelve;

  // BEGIN code from material_tag_editor
  List<String> qOne = [];
  final FocusNode _focusNode = FocusNode();

  onDelete(index) {
    setState(() {
      qOne.removeAt(index);
    });
  }

  // below = reiteration for cons

  List<String> qThree = [];
  //final FocusNode _focusNode = FocusNode();

  uponDelete(index) {
    // NOTE: "uponDelete" for cons vs. "onDelete" for pros
    setState(() {
      qThree.removeAt(index);
    });
  }

// END code from material_tag_editor

  //final _user = User();

  List<bool> isSelected;

  int starIconColor =
      0xffFFB900; // was 0xffFFB900;  0xffD49428 is from this image: https://images.liveauctioneers.com/houses/logos/lg/bartonsauction550_large.jpg?auto=webp&format=pjpg&width=140

  @override
  void initState() {
    //isSelected = [true, false];
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(

Cont'd (skipped AppBar):

body: SingleChildScrollView(
        child: Container(
          child: Builder(
            builder: (context) => FormBuilder(
              // was "builder: (context) => Form("
              key: _newnewFormbuilderKey,
              initialValue: {
                'date': DateTime.now(),
              },
              child: Padding(
                padding: const EdgeInsets.all(14.0),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: <Widget>[
                    SizedBox(
                      height: 12.0,
                    ),
                    RichText(
                      text: TextSpan(
                        style: TextStyle(
                          color: Colors.blue,
                        ),
                        children: <TextSpan>[
                          TextSpan(
                            text:
                                'Q1 via TagEditor', // was 'What are 3 good or positive things about the house, property or neighborhood?', //  [ 1 ​]
                            style: TextStyle(
                              fontWeight: FontWeight.bold,
                              fontSize: 16.0,
                            ),
                          ),
                          TextSpan(
                            text: '  (optional)',
                            style: TextStyle(
                              fontWeight: FontWeight.normal,
                              fontStyle: FontStyle.italic,
                              fontSize: 14.0,
                              color: Colors.black54,
                            ), // was 'misleading or inaccurate?',
                          ),
                        ],
                      ),
                    ),
                    // BEGIN code from material_tag_editor
                    Padding(
                      padding: const EdgeInsets.only(top: 16.0),
                      child: TagEditor(
                        length: qOne.length,
                        delimiters: [
                          ','
                        ], // was delimiters: [',', ' '],  Also tried "return" ('\u2386',) and '\u{2386}'
                        hasAddButton: true,
                        textInputAction: TextInputAction
                            .next, // moves user from one field to the next!!!!
                        autofocus: false,
                        maxLines: 1,

                        // focusedBorder: OutlineInputBorder(
                        //   borderSide: BorderSide(color: Colors.lightBlue),
                        //   borderRadius: BorderRadius.circular(20.0),
                        // ),
                        inputDecoration: const InputDecoration(
                          // below was "border: InputBorder.none,"
                          isDense: true,
                          border: OutlineInputBorder(
                            borderRadius: const BorderRadius.all(
                              const Radius.circular(20.0),
                            ),
                          ),
                          focusedBorder: OutlineInputBorder(
                            borderSide: BorderSide(color: Colors.lightBlue),
                            borderRadius: const BorderRadius.all(
                              const Radius.circular(20.0),
                            ),
                            // above is per https://github.com/flutter/flutter/issues/5191
                          ),
                          labelText: 'separate,  with,  commas',
                          labelStyle: TextStyle(
                            fontStyle: FontStyle.italic,
                            backgroundColor:
                                Color(0x65dffd02), // was Color(0xffDDFDFC),
                            color: Colors.black87, // was Color(0xffD82E6D),
                            fontSize: 14,
                          ),
                        ),
                        onTagChanged: (value) {
                          setState(() {
                            qOne.add(value);
                          });
                        },
                        tagBuilder: (context, index) => _Chip(
                          index: index,
                          label: qOne[index],
                          onDeleted: onDelete,
                        ),
                      ),
                    ),
                    // END code from material_tag_editor

                    SuperDivider(),

Cont'd (skipped Q2 - Q9):

SuperDivider(),
                    RichText(
                      text: TextSpan(
                        style: TextStyle(
                          color: Colors.blue,
                        ),
                        children: <TextSpan>[
                          TextSpan(
                            text:
                                'Q10 - via FormBuilder\'s FormBuilderTextField', // [ 9 ​]
                            style: TextStyle(
                              fontWeight: FontWeight.bold,
                              fontSize: 16.0,
                            ),
                          ),
                          TextSpan(
                            text: '  (optional)',
                            style: TextStyle(
                              fontWeight: FontWeight.normal,
                              fontStyle: FontStyle.italic,
                              fontSize: 14.0,
                              color: Colors.black54,
                            ), // was 'misleading or inaccurate?',
                          ),
                        ],
                      ),
                    ),
                    GavTextField(
                      maxCharLength: 1200,
                      fieldAttribute: 'qTen',
                      fieldLabelText:
                          'Be honest & kind.', // was 'Be honest, but kind.',
                    ),
                    SuperDivider(),
                    Padding(
                      padding: const EdgeInsets.symmetric(vertical: 16.0),
                      child: Row(
                        mainAxisAlignment: MainAxisAlignment.center,
                        children: [
                          ElevatedButton(
                            style: ElevatedButton.styleFrom(
                                primary: Colors.purple,
                                padding: EdgeInsets.symmetric(
                                    horizontal: 50, vertical: 20),
                                textStyle: TextStyle(
                                    fontSize: 20, fontWeight: FontWeight.bold)),
                            onPressed: () {
                              Alert(
                                context: context,
                                style: alertStyle,
                                title: 'Two quick things...',
                                desc: ' ',
                                content: Column(
                                  children: <Widget>[
                                    RichText(
                                      text: TextSpan(
                                        style: TextStyle(
                                          color: Colors.blue,
                                        ),
                                        children: <TextSpan>[
                                          TextSpan(
                                            text:
                                                'Q11 - via FormBuilder\'s FormBuilderRadioGroup', // [ 10 ​]
                                            style: TextStyle(
                                              fontWeight: FontWeight.bold,
                                              fontSize: 16.0,
                                            ),
                                          ),
                                        ],
                                      ),
                                    ),
                                    FormBuilderRadioGroup(
                                      name: 'qEleven',
                                      decoration: const InputDecoration(
                                        border: InputBorder.none,
                                      ),
                                      orientation: OptionsOrientation.vertical,
                                      onChanged: _onChanged,
                                      options: [
                                        FormBuilderFieldOption(
                                          value: 'N',
                                          child: RichText(
                                            text: TextSpan(
                                              style: TextStyle(
                                                color: Colors.black,
                                              ),
                                              children: <TextSpan>[
                                                TextSpan(
                                                  text: 'No', // [ 10 ​]
                                                  style: TextStyle(
                                                    fontWeight: FontWeight.w500,
                                                    fontSize: 16.0,
                                                  ),
                                                ),
                                              ],
                                            ),
                                          ),
                                        ),
                                        FormBuilderFieldOption(
                                          value: 'YesSometimes',
                                          child: RichText(
                                            text: TextSpan(
                                              style: TextStyle(
                                                color: Colors.black,
                                              ),
                                              children: <TextSpan>[
                                                TextSpan(
                                                  text:
                                                      'Yes, sometimes', // [ 10 ​]
                                                  style: TextStyle(
                                                    fontWeight: FontWeight.w500,
                                                    fontSize: 16.0,
                                                  ),
                                                ),
                                              ],
                                            ),
                                          ),
                                        ),
                                        FormBuilderFieldOption(
                                          value: 'YesDaily',
                                          child: RichText(
                                            text: TextSpan(
                                              style: TextStyle(
                                                color: Colors.black,
                                              ),
                                              children: <TextSpan>[
                                                TextSpan(
                                                  text: 'Yes, daily', // [ 10 ​]
                                                  style: TextStyle(
                                                    fontWeight: FontWeight.w500,
                                                    fontSize: 16.0,
                                                  ),
                                                ),
                                              ],
                                            ),
                                          ),
                                        ),
                                      ],
                                    ),
                                  ],
                                ),
                                buttons: [
                                  DialogButton(
                                    onPressed: () {
                                      Alert(
                                          context: context,
                                          style: alertStyle,
                                          title: 'And lastly:',
                                          desc: '',
                                          content: Column(
                                            children: <Widget>[
                                              Row(
                                                mainAxisAlignment:
                                                    MainAxisAlignment.start,
                                                children: [
                                                  RichText(
                                                    text: TextSpan(
                                                      style: TextStyle(
                                                        color: Colors.blue,
                                                      ),
                                                      children: <TextSpan>[
                                                        TextSpan(
                                                          text:
                                                              ' Q12 - via FormBuilder\'s', // [ 10 ​]

And finally (skipped Q12):

buttons: [
                                            DialogButton(
                                              // BEGIN submit form when button pressed per 4.0.2 Readme and video https://www.youtube.com/watch?v=7FBELQq808M
                                              onPressed: () {
                                                _newnewFormbuilderKey
                                                    .currentState
                                                    .save();
                                                if (_newnewFormbuilderKey
                                                    .currentState
                                                    .validate()) {
                                                  print(_newnewFormbuilderKey
                                                      .currentState.value);
                                                  print(
                                                    '  >>> Q1\'s value via separate print: {$qOne}',
                                                  );
                                                  print(
                                                    '  >>> Q3\'s value via separate print: {$qThree}',
                                                  );
                                                  print(
                                                    '   >>> Q11\'s value via separate print: {$qEleven}',
                                                  );
                                                  print(
                                                    '   >>> Q12\'s value via separate print: {$qTwelve}',
                                                  );
                                                  //print(_newFormbuilderKey.currentState.privacyChoice.value);
                                                } else {
                                                  print("validation failed");
                                                }
                                              },

                                              // END submit form when button pressed per 4.0.2 Readme and video https://www.youtube.com/watch?v=7FBELQq808M

                                              child: Text(
                                                "Finish Posting",
                                                style: TextStyle(
                                                  color: Colors.white,
                                                  fontSize: 20,
                                                ),
                                              ),
                                            )
                                          ]).show();
                                    },
                                    child: Text(
                                      "Next >",
                                      style: TextStyle(
                                        color: Colors.white,
                                        fontSize: 20,
                                      ),
                                    ),
                                  )
                                ],
                              ).show();
                            },
                            child: Text(
                              'Post',
                              style: TextStyle(
                                color: Colors.white,
                                fontSize: 20,
                              ),
                            ),
                          ),
                        ],
                      ),
                    ),

Thank you!


Solution

  • The flutter form builder package automatically fetches the ancestor Form to add field values in theirs. But since your form fields are now in dialog, they are not guaranteed to be inside the parent layout from which the dialog was invoked.

    Ref from flutter documentation (Link)

    This function takes a builder which typically builds a Dialog widget. 
    Content below the dialog is dimmed with a ModalBarrier. The widget returned by the
    builder does not share a context with the location that showDialog is originally
    called from. Use a StatefulBuilder or a custom StatefulWidget if the dialog needs
    to update dynamically.
    

    You can instead save the value of these fields in the state of your stateful widget and them combine all values later for future operations. Since you are already saving them in a variable qEleven and qTwelve you can use it later.

    Here is the snippet of code doing this for qEleven (compare with this section of code in the question above):

    FormBuilderRadioGroup(
                                          name: 'qEleven',
                                          decoration: const InputDecoration(
                                            border: InputBorder.none,
                                          ),
                                          orientation: OptionsOrientation.vertical,
                                          onChanged: (val) {
                                            print(val);
                                            qEleven = val;
                                          },
                                          options: [
                                            FormBuilderFieldOption(
                                              value: 'N',
                                              child: RichText(
                                                text: TextSpan(
                                                  style: TextStyle(
                                                    color: Colors.black,
                                                  ),
                                                  children: <TextSpan>[
                                                    TextSpan(
                                                      text: 'No', // [ 10 ​]
                                                      style: TextStyle(
                                                        fontWeight: FontWeight.w500,
                                                        fontSize: 16.0,
                                                      ),
                                                    ),
                                                  ],
                                                ),
                                              ),
                                            ),
    

    And here is a screenshot that highlights the differences between to code in the question (left) and the code based on the answer (right):

    screenshot showing code differences

    Another thing that can be done, is to modify form builder package to also accept state, but it might break in case you are accessing an unmounted state.