1

To validate the whole Form in flutter, a GloabalKey<FormState> must be provided. It looks fine when buttons for interaction with the form contained inside of form, but when, for example, form is a child of AlertDialog, the key has to be passed from the dialog widget, and it doesn't look good. Is there any better solution for obtaining FormState from a parent widget?

Here's the example:

main

void main() {
  runApp(
    const MaterialApp(
      home: InitialScreen(),
    ),
  );
}

Initial Screen

class InitialScreen extends StatelessWidget {
  const InitialScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      floatingActionButton: FloatingActionButton(
        child: const Icon(Icons.add),
        onPressed: () {
          showDialog<void>(
            context: context,
            builder: (BuildContext context) {
              return FormDialog();
            },
          );
        },
      ),
    );
  }
}

Form Dialog

class FormDialog extends StatelessWidget {
  FormDialog({super.key});

  GlobalKey<FormState> myFormState = GlobalKey<FormState>();

  @override
  Widget build(BuildContext context) {
    return AlertDialog(
      title: const Text('Create new item'),
      content: MyForm(
        formCurrentState: myFormState,
      ),
      actions: <Widget>[
        TextButton(
          style: TextButton.styleFrom(
            textStyle: Theme.of(context).textTheme.labelLarge,
          ),
          child: const Text('Add'),
          onPressed: () {
            if (myFormState.currentState!.validate()) {
              print("All fine");
              Navigator.of(context).pop();
            } else {
              print("Error");
            }
          },
        ),
        TextButton(
          style: TextButton.styleFrom(
            textStyle: Theme.of(context).textTheme.labelLarge,
          ),
          child: const Text('Cancel'),
          onPressed: () {
            Navigator.of(context).pop();
          },
        ),
      ],
    );
  }
}

My Form

class MyForm extends StatefulWidget {
  const MyForm({super.key, required this.formCurrentState});

  final GlobalKey<FormState> formCurrentState;

  @override
  State<MyForm> createState() => _MyFormState();
}

class _MyFormState extends State<MyForm> {
  GlobalKey<FormState>? formCurrentState;

  @override
  void initState() {
    super.initState();
    formCurrentState = widget.formCurrentState;
  }

  @override
  Widget build(BuildContext context) {
    return Form(
      key: formCurrentState,
      child: TextFormField(
        validator: (value) {
          if (value == "") {
            return "Please, enter some text";
          } else {
            return null;
          }
        },
      ),
    );
  }
}
Timur Khr
  • 15
  • 5

1 Answers1

0

I think there is no better solution to get FormState from parent widget.

If you don't want to make another widget (MyForm), try this code:

class FormDialog extends StatelessWidget {
  FormDialog({super.key});

  GlobalKey<FormState> myFormState = GlobalKey<FormState>();

  @override
  Widget build(BuildContext context) {
    return AlertDialog(
      title: const Text('Create new item'),
      content: Form(
        key: formCurrentState,
        child: TextFormField(
          validator: (value) {
            if (value == "") {
              return "Please, enter some text";
            } else {
              return null;
            }
          },
        ),
      ),
      actions: <Widget>[
        TextButton(
          style: TextButton.styleFrom(
            textStyle: Theme.of(context).textTheme.labelLarge,
          ),
          child: const Text('Add'),
          onPressed: () {
            if (myFormState.currentState!.validate()) {
              print("All fine");
              Navigator.of(context).pop();
            } else {
              print("Error");
            }
          },
        ),
        TextButton(
          style: TextButton.styleFrom(
            textStyle: Theme.of(context).textTheme.labelLarge,
          ),
          child: const Text('Cancel'),
          onPressed: () {
            Navigator.of(context).pop();
          },
        ),
      ],
    );
  }
}
My Car
  • 4,198
  • 5
  • 17
  • 50
  • The problem is that i want my form to be a separate widget, because i may have several forms in my app, that would share same logic, but would be inside of different screens or widgets (e.g. one of such forms could be in AlertDialog, but another form could be on its own screen) – Timur Khr Jan 14 '23 at 08:29
  • @TimurKhr If so, I don't think there is a better solution to get the `FormState` from the parent widget – My Car Jan 14 '23 at 08:31