How to Use Flutter Text Field: A Complete Guide for Beginners

Flutter text field is an essential widget that allows users to enter text in a mobile app. As a beginner, understanding how to use text fields in your Flutter app is crucial. 

In this blog post, you'll learn everything you need to know about Flutter text fields, from creating a basic text field to implementing advanced features such as input validation and formatting. 

SimpleTextField Widget in Flutter

The SimpleTextField widget is a custom text input field in Flutter that provides additional features and customizations beyond the standard TextField widget. 

It has properties for setting text input type, hint text, error text, prefix icon, text color, background color, and border radius, among others. It also has validation capabilities that can be used to check user input and provide confirmation or error messages.


Properties:

onChanged: A callback function that is called every time the text in the TextField changes.


controller: An object that can be used to control the text in the TextField. This can be useful for things like setting the initial value of the TextField, or programmatically changing the text.


textEditingController: A more powerful version of the controller property that allows you to also access the current selection and set the selection range.


autofillHints: A list of strings that specify the types of data that the TextField can autofill. For example, if you specify "email" as an autofill hint, then the user's email address may be automatically filled in if their device has that information saved.


textInputType: Specifies the type of data that the TextField is intended to receive. For example, you can specify that the TextField should only accept numbers, or that it should accept a URL.


autoFocus: If true, the TextField will automatically be focused when the widget is built.


obscureText: If true, the text entered into the TextField will be obscured (i.e., shown as dots or asterisks).


textInputAction: Specifies the type of action that the keyboard should take when the user presses the keyboard's action button. For example, you can specify that the keyboard should submit the form, or that it should move to the next TextField.


focusNode: An object that represents the current focus state of the TextField. This can be used to programmatically set or clear focus.


prefixIconData: An icon that will be displayed to the left of the TextField.


hintText: Text that is displayed in the TextField when it is empty to give the user an idea of what should be entered.


labelText: A label that is displayed above the TextField. This is typically used to give more context or information about what should be entered into the TextField.


errorText: Text that is displayed below the TextField when the input is invalid.


helperText: Text that is displayed below the TextField to provide additional help or guidance to the user.


showLabelAboveTextField: If true, the labelText will be displayed above the TextField. If false, it will be displayed inside the TextField.


floatingLabelBehavior: Specifies how the labelText should behave when the TextField has focus.


fillColor: The background color of the TextField.


accentColor: The color of the accent used by the TextField. This is used for things like the cursor and the focus highlight.


textColor: The color of the text in the TextField.


borderRadius: The radius of the border of the TextField.


validator: A function that is called to validate the input in the TextField.


showConfirmation: If true, a check mark will be displayed to the right of the TextField when the input is valid.


showError: If true, an error icon will be displayed to the right of the TextField when the input is invalid.


verticalPadding: The amount of padding that should be applied vertically to the TextField.


horizontalPadding: The amount of padding that should be applied horizontally to the TextField.



  • Use this helper widget for text field : 
util / common_text_field.dart



class SimpleTextField extends StatefulWidget {
  const SimpleTextField(
      {this.onChanged,
      this.controller,
      this.textEditingController,
      this.autofillHints,
      this.textInputType,
      this.autoFocus = false,
      this.obscureText = false,
      this.textInputAction,
      this.focusNode,
      this.prefixIconData,
      this.hintText,
      this.labelText,
      this.errorText,
      this.helperText,
      this.showLabelAboveTextField = false,
      this.floatingLabelBehavior = FloatingLabelBehavior.auto,
      this.fillColor,
      this.accentColor,
      this.textColor = Colors.black,
      this.borderRadius = 6,
      this.validator,
      this.showConfirmation = true,
      this.showError = true,
      this.verticalPadding = 20,
      this.horizontalPadding = 12,
      Key? key})
      : super(key: key);

  final Function(String)? onChanged;
  final TextEditingController? textEditingController;
  final Iterable? autofillHints;
  final TextInputType? textInputType;
  final bool autoFocus;
  final bool obscureText;
  final TextInputAction? textInputAction;
  final FocusNode? focusNode;
  final IconData? prefixIconData;
  final String? hintText;
  final String? labelText;
  final String? errorText;
  final TextEditingController? controller;

  /// Text placed below the text field
  final String? helperText;
  final bool showLabelAboveTextField;
  final FloatingLabelBehavior floatingLabelBehavior;
  final Color? fillColor;
  final Color? accentColor;
  final Color textColor;
  final double borderRadius;
  final bool Function(String)? validator;
  final bool showConfirmation;
  final bool showError;
  final double verticalPadding;
  final double horizontalPadding;

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

class SimpleTextFieldState extends State {
  late FocusNode focusNode;
  late TextEditingController textEditingController;
  late bool hasConfirmation;
  late bool hasError;
  late bool hasFocus;

  late bool _obscureText; // Add this variable

  @override
  void initState() {
    super.initState();

    _obscureText = widget.obscureText; // Initialize it from widget property

    hasFocus = false;
    textEditingController =
        widget.textEditingController ?? TextEditingController();
    // hasConfirmation = textEditingController.text != null ? isValid : false;
    hasConfirmation = isValid;
    // hasError = textEditingController != null ? !isValid : false;
    hasError = !isValid;
    focusNode = widget.focusNode ?? FocusNode();

    focusNode.addListener(() {
      setState(() {
        hasFocus = focusNode.hasPrimaryFocus;
        bool valid = isValid;
        hasConfirmation = valid;
        hasError = !valid;
      });
    });
  }

  bool get isValid {
    if (hasValidator) {
      return widget.validator!(textEditingController.text);
    }
    return false;
  }

  bool get hasValidator {
    return widget.validator != null;
  }

  @override
  Widget build(BuildContext context) {
    ThemeData currentTheme = Theme.of(context);

    OutlineInputBorder buildFocusedBorder() {
      if (hasValidator) {
        if (hasConfirmation && widget.showConfirmation) {
          return OutlineInputBorder(
              borderSide: const BorderSide(color: Colors.green, width: 1.25),
              borderRadius: BorderRadius.circular(widget.borderRadius));
        } else if (hasError) {
          return OutlineInputBorder(
            borderSide: const BorderSide(color: Colors.red, width: 1.25),
            borderRadius: BorderRadius.circular(widget.borderRadius),
          );
        }
      }
      return OutlineInputBorder(
        borderSide: BorderSide(
            color: widget.accentColor ?? currentTheme.primaryColor,
            width: 1.25),
        borderRadius: BorderRadius.circular(widget.borderRadius),
      );
    }

    OutlineInputBorder buildEnabledBorder() {
      if (hasValidator) {
        if (hasConfirmation) {
          return OutlineInputBorder(
            borderSide: const BorderSide(color: Colors.green),
            borderRadius: BorderRadius.circular(widget.borderRadius),
          );
        } else if (hasError) {
          return OutlineInputBorder(
            borderSide: const BorderSide(color: Colors.red),
            borderRadius: BorderRadius.circular(widget.borderRadius),
          );
        }
      }
      return OutlineInputBorder(
        borderRadius: BorderRadius.circular(widget.borderRadius),
        borderSide: BorderSide(
          color: Colors.grey[400]!,
        ),
      );
    }

    TextStyle? buildLabelStyle() {
      if (hasFocus) {
        return TextStyle(color: widget.accentColor);
      } else {
        return null;
      }
    }

    StatelessWidget? buildSuffixIcon() {
      if (hasValidator) {
        if (hasConfirmation) {
          return const Icon(Icons.check, color: Colors.green);
        } else if (hasError) {
          return const Icon(
            Icons.error,
            color: Colors.red,
            size: 24,
          );
        }
      } else if (widget.obscureText) {
        // Add this condition
        return GestureDetector(
          onTap: () {
            setState(() {
              _obscureText = !_obscureText;
            });
          },
          child: Icon(
            _obscureText ? Icons.visibility : Icons.visibility_off,
            color: hasFocus ? widget.accentColor : Colors.grey[400],
          ),
        );
      }
      return null;
    }

    return TextSelectionTheme(
      data: TextSelectionThemeData(
        selectionColor: widget.accentColor?.withOpacity(.33) ??
            currentTheme.primaryColor.withOpacity(.33),
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          if (widget.labelText != null && widget.showLabelAboveTextField) ...[
            Text(
              widget.labelText!,
              style: TextStyle(
                fontWeight: FontWeight.w500,
                color: hasFocus ? currentTheme.primaryColor : Colors.grey[700],
              ),
            ),
            const SizedBox(height: 6),
          ],
          TextField(
            focusNode: focusNode,
            controller: textEditingController,
            autofillHints: widget.autofillHints,
            keyboardType: widget.textInputType,
            autofocus: widget.autoFocus,
            onChanged: (val) {
              setState(() {
                hasError = false;
                hasConfirmation = isValid;
              });
              if (widget.onChanged != null) {
                widget.onChanged!(val);
              }
            },
            style: TextStyle(color: widget.textColor),
            cursorColor: widget.textColor,
            obscureText: widget.obscureText && _obscureText,
            // Use the local variable
            textInputAction: widget.textInputAction,
            decoration: InputDecoration(
              contentPadding: EdgeInsets.symmetric(
                  vertical: widget.verticalPadding,
                  horizontal: widget.horizontalPadding),
              isDense: true,
              hintText: widget.hintText,
              hintStyle: TextStyle(color: widget.textColor.withOpacity(.45)),
              labelText:
                  widget.showLabelAboveTextField ? null : widget.labelText,
              labelStyle: buildLabelStyle(),
              errorText: widget.errorText != null && hasError && hasValidator
                  ? widget.errorText
                  : null,
              floatingLabelBehavior: widget.floatingLabelBehavior,
              fillColor: widget.fillColor,
              filled: widget.fillColor != null,
              focusedBorder: buildFocusedBorder(),
              enabledBorder: buildEnabledBorder(),
              border: OutlineInputBorder(
                borderRadius: BorderRadius.circular(widget.borderRadius),
              ),
              /*prefixIcon: widget.prefixIconData != null
                  ? Padding(
                      padding: const EdgeInsets.only(left: 12.0, right: 8),
                      child: Icon(
                        widget.prefixIconData,
                        color: hasFocus
                            ? widget.accentColor
                            : widget.textColor.withOpacity(.6),
                        size: 20,
                      ),
                    )
                  : null,*/
              prefixIcon: widget.prefixIconData != null
                  ? Padding(
                      padding: const EdgeInsets.only(left: 12.0, right: 8),
                      child: ShaderMask(
                        shaderCallback: (bounds) {
                          return const LinearGradient(
                            colors: [
                              Color(0xffFF512F),
                              Color(0xfffd6caf),
                            ],
                          ).createShader(bounds);
                        },
                        blendMode: BlendMode.srcATop,
                        child: Icon(
                          widget.prefixIconData,
                          color: Colors.white,
                        ),
                      ),
                    )
                  : null,
              prefixIconConstraints:
                  const BoxConstraints(minHeight: 24, minWidth: 24),
              suffixIcon: buildSuffixIcon(),
            ),
          ),
          if (widget.helperText != null) ...[
            const SizedBox(height: 6),
            Text(
              widget.helperText!,
              style: TextStyle(
                color: Colors.grey[600],
              ),
            )
          ]
        ],
      ),
    );
  }
}

How to use SimpleTextField widget demo:

view/ demo_text_field.dart



import 'package:flutter/material.dart';

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

  @override
  Widget build(BuildContext context) {
    TextEditingController controller = TextEditingController();

    return Scaffold(
        body: SingleChildScrollView(
          child: Center(
            child: Padding(
              padding: const EdgeInsets.all(50),
              child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  crossAxisAlignment: CrossAxisAlignment.center,
                  mainAxisSize: MainAxisSize.max, // set mainAxisSize to max
                  children: [
                    
                    
                    
                    SimpleTextField(
                      controller: controller,
                      textInputAction : TextInputAction.go,
                      labelText: 'Default Simple TextField',
                    ),
                    Container(height: 40),
                   
                   
                   
                   SimpleTextField(
                      obscureText: true,
                      controller: controller,
                      labelText: 'Password TextField',
                    ),
                    Container(height: 40),
                    
                    
                    
                    const SimpleTextField(
                      hintText: 'Floating Label with Accent Color set',
                      labelText: 'Floating Label',
                      floatingLabelBehavior: FloatingLabelBehavior.always,
                      textColor: Colors.black,
                      accentColor: Colors.orange,
                    ),
                    Container(height: 40),
                    
                    
                    
                    const SimpleTextField(
                      labelText: 'With Label Above TextField',
                      showLabelAboveTextField: true,
                      textColor: Colors.black,
                      accentColor: Colors.green,
                    ),
                    Container(height: 40),
                    
                    
                    
                    SimpleTextField(
                      labelText: 'With Helper Text',
                      helperText:
                          "I provide a description about this text field",
                      showLabelAboveTextField: true,
                      textColor: Colors.black,
                      accentColor: Colors.red[900],
                    ),
                    Container(height: 40),
                    
                    
                    
                    Column(
                      children: [
                        const Text(
                          "With Confirmation & Error",
                          style: TextStyle(fontSize: 16),
                        ),
                        SimpleTextField(
                          textEditingController:
                              TextEditingController(text: 'change me'),
                          validator: (val) => val == "change me",
                        ),
                      ],
                    ),
                    Container(height: 40),
                    
                    
                    
                    Column(
                      children: const [
                        Text(
                          "With Prefixed Icon",
                          style: TextStyle(fontSize: 16),
                        ),
                        SimpleTextField(
                          hintText: "Search...",
                          prefixIconData: Icons.search,
                          accentColor: Colors.indigo,
                        ),
                      ],
                    ),
                  ]),
            ),
          ),
        ));
  }
}

..

Comments