5

I'm wondering if I can pass a function as a validator. I tried, but got no results.

  Widget Field(String changedValue, String label, bool isTextObscured) {
    return TextFormField(
      decoration: InputDecoration(labelText: label),
      validator: checkFieldEmpty,
    );
  }

  checkFieldEmpty(String fieldContent) {
    if(fieldContent.isEmpty) {
      return 'Ce champ est obligatoire.';
    }
    return null;
  }
gabhar
  • 187
  • 1
  • 4
  • 14
  • Yes, you can, but `fieldContent` must be nullable. Replace `String` with `String?`. Also, make the return type of your `checkFieldEmpty` function `String?` as is said in the answers below. – Stewie Griffin Jun 15 '21 at 20:51

2 Answers2

11

Perhaps the function return type should be String? so that it fits the validator prototype!

  Widget Field(String changedValue, String label, bool isTextObscured) {
    return TextFormField(
      decoration: InputDecoration(labelText: label),
      validator: checkFieldEmpty,
    );
  }

 String? checkFieldEmpty(String? fieldContent) { //<-- add String? as a return type
    if(fieldContent.isEmpty) {
      return 'Ce champ est obligatoire.';
    }
    return null;
  }

A more appropriate way of doing this in Flutter

Remember, flutter is a declarative language. That is, you build your app by composing a widget tree. Here, you are using a function to return a Widget. That is breaking this rule. Instead, you should declare your own custom widget that implements the TextField Widget. Here's how:

1. Declare your custom widget

// Declare your CustomTextField as a Stateless/Stateful Widget
class MyCustomTextField extends StatelessWidget {
  // Declare your custom vars, including your validator function
  final String? changedValue; 
  final String? label; 
  final bool? isTextObscured;
  final String? Function(String?)? validator;

  const MyCustomTextField({
    Key? key, 
    this.changedValue, 
    this.label,
    this.isTextObscured,
    this.validator,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return TextFormField(
      decoration: InputDecoration(labelText: label),
      validator: validator,
    );
  }
}

2. Use your custom widget

Now you can use this custom widget as a child of any other widget:

class ParentWidget extends StatelessWidget {
  const ParentWidget({Key? key}) : super(key: key);

  // This is your custom validator function and can leave
  // anywhere ;)
  Stirng? customValidtaor(String? fieldContent) => fieldContent.isEmpty? 'Ce champ est obligatoire.': null

  @override
  Widget build(BuildContext context) {
    return MyCustomTextField(
      label: 'Some label'
      // declare the validator here...
      // valiator: (fieldContent) => fieldContent.isEmpty? 'Ce champ est obligatoire.': null
      // or use your custom validator function
      validator: customValidator,
    );
  }
}

By doing this, you are respecting Flutter's best practices by using Widget composition ;)

Younss AIT MOU
  • 871
  • 7
  • 14
  • I'm afraid it still doesn't work... "The argument type 'String? Function(String)' can't be assigned to the parameter type 'String? Function(String?)?'." – gabhar Jun 15 '21 at 21:09
  • yup just update the function:`String? function(String? fieldContent){/*...*/}` sorry I missed the argument type. I have updated my response accordingly ;) – Younss AIT MOU Jun 15 '21 at 21:28
0

Add String as a return type for checkFieldEmpty() method, see below

instead this:

checkFieldEmpty(String fieldContent) {}

use this:

String checkFieldEmpty(String fieldContent) {}

Full Code Example:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final appTitle = 'Form Validation Demo';

    return MaterialApp(
      title: appTitle,
      home: Scaffold(
        appBar: AppBar(title: Text(appTitle)),
        body: MyCustomForm(),
      ),
    );
  }
}

// Create a Form widget.
class MyCustomForm extends StatefulWidget {
  @override
  MyCustomFormState createState() {
    return MyCustomFormState();
  }
}

// Create a corresponding State class.
// This class holds data related to the form.
class MyCustomFormState extends State<MyCustomForm> {
  // Create a global key that uniquely identifies the Form widget
  // and allows validation of the form.
  //
  // Note: This is a GlobalKey<FormState>,
  // not a GlobalKey<MyCustomFormState>.
  final _formKey = GlobalKey<FormState>();

  Widget textField(String changedValue, String label, bool isTextObscured) {
    return TextFormField(
      decoration: InputDecoration(labelText: label),
      validator: checkFieldEmpty,
    );
  }

  String checkFieldEmpty(String fieldContent) {
    if (fieldContent.isEmpty) return 'Ce champ est obligatoire.';
    return null;
  }

  @override
  Widget build(BuildContext context) {
    // Build a Form widget using the _formKey created above.
    return Form(
      key: _formKey,
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          textField('changedValue', 'Enter Password', false),
          ElevatedButton(
            onPressed: () {
              // Validate returns true if the form is valid, or false otherwise.
              if (_formKey.currentState.validate()) {
                // If the form is valid,
                // perform further actions here
              }
            },
            child: Text('Submit'),
          ),
        ],
      ),
    );
  }
}
Kartik Patel
  • 557
  • 3
  • 11
  • validator method signature should have `String?` parameter and return types. Like `String? checkFieldEmpty(String? v)` – oleksa Jul 21 '22 at 06:50