iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

TextField

Flutter TextField: controllers, focus, decoration, validation, and the patterns for clean forms.

Flutter — TextField

EXAMPLE
import 'package:flutter/material.dart';

# ===== Basic =====
TextField(
  decoration: InputDecoration(
    labelText: 'Email',
    hintText: 'you@example.com',
    border: OutlineInputBorder(),
  ),
  onChanged: (value) => print(value),
);

# ===== Controller (recommended for non-trivial forms) =====
class _FormState extends State<MyForm> {
  final _emailCtrl = TextEditingController();
  final _passwordCtrl = TextEditingController();

  @override
  void dispose() {
    _emailCtrl.dispose();
    _passwordCtrl.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Column(children: [
      TextField(
        controller: _emailCtrl,
        keyboardType: TextInputType.emailAddress,
        decoration: const InputDecoration(labelText: 'Email'),
      ),
      TextField(
        controller: _passwordCtrl,
        obscureText: true,
        decoration: const InputDecoration(labelText: 'Password'),
      ),
      ElevatedButton(
        onPressed: () => print('email: ${_emailCtrl.text}'),
        child: const Text('Sign in'),
      ),
    ]);
  }
}

# ===== Focus =====
final focusNode = FocusNode();

TextField(focusNode: focusNode, ...);

# Focus next field on submit:
TextField(
  textInputAction: TextInputAction.next,
  onSubmitted: (_) => FocusScope.of(context).requestFocus(passwordFocus),
);

# Auto-focus on mount:
TextField(autofocus: true)

# ===== Form + validation =====
final _formKey = GlobalKey<FormState>();

Form(
  key: _formKey,
  child: Column(children: [
    TextFormField(
      decoration: const InputDecoration(labelText: 'Email'),
      validator: (v) {
        if (v == null || !v.contains('@')) return 'invalid email';
        return null;
      },
    ),
    TextFormField(
      decoration: const InputDecoration(labelText: 'Password'),
      obscureText: true,
      validator: (v) => v != null && v.length >= 8 ? null : 'min 8 chars',
    ),
    ElevatedButton(
      onPressed: () {
        if (_formKey.currentState!.validate()) {
          // submit
        }
      },
      child: const Text('Sign in'),
    ),
  ]),
);

# ===== InputDecoration tour =====
InputDecoration(
  labelText: 'Email',
  hintText: 'you@example.com',
  helperText: 'We never share this',
  errorText: hasError ? 'Required' : null,
  prefixIcon: const Icon(Icons.email),
  suffixIcon: IconButton(icon: const Icon(Icons.clear), onPressed: () => _emailCtrl.clear()),
  border: const OutlineInputBorder(),
  filled: true,
  fillColor: Colors.grey.shade100,
)

# ===== Keyboard types =====
keyboardType: TextInputType.emailAddress
keyboardType: TextInputType.number
keyboardType: TextInputType.phone
keyboardType: TextInputType.url
keyboardType: TextInputType.multiline,
maxLines: 5,

# ===== Input formatters =====
import 'package:flutter/services.dart';

TextField(
  keyboardType: TextInputType.number,
  inputFormatters: [
    FilteringTextInputFormatter.digitsOnly,
    LengthLimitingTextInputFormatter(10),
  ],
);

# ===== Patterns to internalise =====
// - TextEditingController for non-trivial fields; dispose() in State.dispose()
// - TextFormField + Form for validation flows
// - textInputAction + FocusNode chain for keyboard 'Next' / 'Done'
// - inputFormatters for masks (phone, credit card, money)

# ===== Pitfalls =====
// - Forgetting to dispose controllers -> memory leaks
// - Reading TextField.value vs controller.text inconsistently
// - autocorrect / enableSuggestions left on for passwords (leaks)
// - Tight Stack + Keyboard insets without resizeToAvoidBottomInset

Why it matters

TextField + TextEditingController + InputDecoration + Form. Dispose controllers, validate with TextFormField, chain focus with textInputAction, and reach for inputFormatters when input needs masks. The patterns are small; the cleanliness pays off in every form screen.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
TextField(
    decoration: const InputDecoration(border: OutlineInputBorder(), labelText: 'Name'),
    onChanged: (v) => print(v),
)
Try it Yourself »

Discussion

Loading…