UdaraWanasinghe
UdaraWanasinghe

Reputation: 2862

Flutter TextFormField onSave() doesn't get called after successful validation

I have a weird problem in flutter TextFormField. I implemented form validation in TextFormField. But onSaved() function doesn't get called after successful validation.

First I created basic Widget using TextFormField

--- In AppWidgets class ---

  static Widget buildTextFormField(
    String labelText,
    String helperText,
    IconData prefixIcon, {
    Widget suffixIcon,
    bool obscureText = false,
    TextInputType keyboardType = TextInputType.text,
    TextInputAction textInputAction = TextInputAction.none,
    FocusNode focusNode,
    ValueChanged<String> onFieldSubmitted,
    TextEditingController controller,
    FormFieldValidator<String> validator,
    FormFieldSetter<String> onSaved,
    bool isLightTheme = false,
  }) {
    return Theme(
      data: isLightTheme
          ? AppThemesLight.textFormFieldThemeData
          : AppThemesDark.textFormFieldThemeData,
      child: TextFormField(
        controller: controller,
        validator: validator,
        onSaved: onSaved,
        keyboardType: keyboardType,
        textInputAction: textInputAction,
        focusNode: focusNode,
        onFieldSubmitted: onFieldSubmitted,
        obscureText: obscureText,
        decoration: InputDecoration(
          filled: true,
          fillColor: isLightTheme
              ? AppColorsLight.textFieldFillColor
              : AppColorsDark.textFieldFillColor,
          labelText: labelText,
          helperText: helperText,
          border: OutlineInputBorder(
            borderRadius: BorderRadius.all(
              Radius.circular(AppDimensions.textFieldBorderRadius),
            ),
          ),
          prefixIcon: Icon(
            prefixIcon,
            color: isLightTheme
                ? AppColorsLight.primaryTextColor
                : AppColorsDark.primaryTextColor,
          ),
          suffixIcon: suffixIcon,
        ),
      ),
    );
  }

From that, created email text from field.

  static Widget buildEmailTextFormField(LoginState loginState) {
    return AppWidgets.buildTextFormField(
      'Email address',
      'Your email address',
      Icons.email,
      keyboardType: TextInputType.emailAddress,
      textInputAction: TextInputAction.next,
      focusNode: loginState.focusNodes[0],
      onFieldSubmitted: (String value) {
        print('submitted $value');
        loginState.onFocusChanged(index: 0);
      },
      validator: (String email) {
        print('validator $email');
        return InputValidators.validateEmail(email);
      },
      onSaved: (String email) {
        print('saved $email');
        loginState.email = email;
      },
    );
  }

Here is the email validator I used.

  static String validateEmail(String email) {
    Pattern pattern =
        r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';
    RegExp regex = new RegExp(pattern);
    if (email.isEmpty)
      return 'Email can\'t be empty';
    else if (!regex.hasMatch(email))
      return 'Enter valid email address';
    else
      return null;
  }

I tested above code by putting print statement inside onSaved() function, but it's not printing after successful validation.

Upvotes: 28

Views: 38725

Answers (3)

Joao Victor
Joao Victor

Reputation: 600

I was struggling with this issue and yet I don't found my answer but I solved yours

import 'package:flutter/material.dart';

/// Flutter code sample for [Form].

void main() => runApp(const FormExampleApp());

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('Form Sample')),
        body: const FormExample(),
      ),
    );
  }
}

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

  @override
  State<FormExample> createState() => _FormExampleState();
}

class _FormExampleState extends State<FormExample> {
  final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final TextEditingController textController = TextEditingController();
  @override
  Widget build(BuildContext context) {
    return Form(
      key: _formKey,
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          TextFormField(
            decoration: const InputDecoration(
              hintText: 'Enter your email',
            ),
            onSaved: (value) {
              print('VALUE $value');
            },
            validator: (String? value) {
              if (value == null || value.isEmpty) {
                return 'Please enter some text';
              }
              return null;
            },
          ),
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 16.0),
            child: ElevatedButton(
              onPressed: () {
                // Validate will return true if the form is valid, or false if
                // the form is invalid.
                if (_formKey.currentState!.validate()) {
                  // Process data.
                  _formKey.currentState!.save();
                }
              },
              child: const Text('Submit'),
            ),
          ),
        ],
      ),
    );
  }
}

But my personal issue is that I've build the input like a component so I will need to create a component for form that use it and ok probably this is more easy to solve, anyway I like full code that's why I'm sharing this simple print .

Upvotes: 0

saigopi.me
saigopi.me

Reputation: 14938

Did you call this method formKey.currentState.save() ?

in my case i forgot to call this after adding this it has worked.

Upvotes: 4

UdaraWanasinghe
UdaraWanasinghe

Reputation: 2862

The onSaved() function won't be called automatically after successful validation. We have to call _formKey.currentState.save() manually to save our variables.

Form(
  key: key,
  child: TextFormField(
    onSaved: (val) {
      print('saved');
    },
    validator: (val) {
      print('validating');
    },
  ),
),
RaisedButton(
  child: Text('Click me'),
  onPressed: () {
    if (key.currentState.validate()) {
      key.currentState.save();
      print('valid');
    }
  },
),

Upvotes: 64

Related Questions