text_form_field.dart 6.81 KB
Newer Older
1 2 3 4 5 6 7 8 9
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';

import 'input_decorator.dart';
import 'text_field.dart';
10
import 'theme.dart';
11 12 13

/// A [FormField] that contains a [TextField].
///
14
/// This is a convenience widget that wraps a [TextField] widget in a
15 16 17 18 19 20 21
/// [FormField].
///
/// A [Form] ancestor is not required. The [Form] simply makes it easier to
/// save, reset, or validate multiple fields at once. To use without a [Form],
/// pass a [GlobalKey] to the constructor and use [GlobalKey.currentState] to
/// save or reset the form field.
///
22 23 24 25 26 27 28 29 30
/// When a [controller] is specified, its [TextEditingController.text]
/// defines the [initialValue]. If this [FormField] is part of a scrolling
/// container that lazily constructs its children, like a [ListView] or a
/// [CustomScrollView], then a [controller] should be specified.
/// The controller's lifetime should be managed by a stateful widget ancestor
/// of the scrolling container.
///
/// If a [controller] is not specified, [initialValue] can be used to give
/// the automatically generated controller an initial value.
31
///
32 33
/// For a documentation about the various parameters, see [TextField].
///
34 35 36 37 38 39 40 41
/// See also:
///
///  * <https://material.google.com/components/text-fields.html>
///  * [TextField], which is the underlying text field without the [Form]
///    integration.
///  * [InputDecorator], which shows the labels and other visual elements that
///    surround the actual text editing widget.
class TextFormField extends FormField<String> {
42 43
  /// Creates a [FormField] that contains a [TextField].
  ///
44 45 46 47
  /// When a [controller] is specified, [initialValue] must be null (the
  /// default). If [controller] is null, then a [TextEditingController]
  /// will be constructed automatically and its `text` will be initialized
  /// to [initalValue] or the empty string.
48
  ///
49 50
  /// For documentation about the various parameters, see the [TextField] class
  /// and [new TextField], the constructor.
51 52
  TextFormField({
    Key key,
53
    this.controller,
54
    String initialValue,
55
    FocusNode focusNode,
56
    InputDecoration decoration = const InputDecoration(),
57
    TextInputType keyboardType,
58
    TextCapitalization textCapitalization = TextCapitalization.none,
59
    TextInputAction textInputAction,
60
    TextStyle style,
61 62 63 64 65 66 67
    TextAlign textAlign = TextAlign.start,
    bool autofocus = false,
    bool obscureText = false,
    bool autocorrect = true,
    bool autovalidate = false,
    bool maxLengthEnforced = true,
    int maxLines = 1,
68
    int maxLength,
69
    VoidCallback onEditingComplete,
70
    ValueChanged<String> onFieldSubmitted,
71 72
    FormFieldSetter<String> onSaved,
    FormFieldValidator<String> validator,
73
    List<TextInputFormatter> inputFormatters,
74
    bool enabled,
75
    Brightness keyboardAppearance,
76
    EdgeInsets scrollPadding = const EdgeInsets.all(20.0),
77
  }) : assert(initialValue == null || controller == null),
78
       assert(textAlign != null),
79 80
       assert(autofocus != null),
       assert(obscureText != null),
81
       assert(autocorrect != null),
82
       assert(autovalidate != null),
83
       assert(maxLengthEnforced != null),
84
       assert(scrollPadding != null),
85
       assert(maxLines == null || maxLines > 0),
86
       assert(maxLength == null || maxLength > 0),
87
       super(
88
    key: key,
89
    initialValue: controller != null ? controller.text : (initialValue ?? ''),
90 91
    onSaved: onSaved,
    validator: validator,
92
    autovalidate: autovalidate,
93
    builder: (FormFieldState<String> field) {
94
      final _TextFormFieldState state = field;
95 96
      final InputDecoration effectiveDecoration = (decoration ?? const InputDecoration())
        .applyDefaults(Theme.of(field.context).inputDecorationTheme);
97
      return new TextField(
98
        controller: state._effectiveController,
99
        focusNode: focusNode,
100
        decoration: effectiveDecoration.copyWith(errorText: field.errorText),
101
        keyboardType: keyboardType,
102
        textInputAction: textInputAction,
103
        style: style,
104
        textAlign: textAlign,
105
        textCapitalization: textCapitalization,
106 107
        autofocus: autofocus,
        obscureText: obscureText,
108
        autocorrect: autocorrect,
109
        maxLengthEnforced: maxLengthEnforced,
110
        maxLines: maxLines,
111
        maxLength: maxLength,
112
        onChanged: field.didChange,
113
        onEditingComplete: onEditingComplete,
114
        onSubmitted: onFieldSubmitted,
115
        inputFormatters: inputFormatters,
116
        enabled: enabled,
117
        scrollPadding: scrollPadding,
118
        keyboardAppearance: keyboardAppearance,
119 120 121
      );
    },
  );
122 123 124

  /// Controls the text being edited.
  ///
125 126
  /// If null, this widget will create its own [TextEditingController] and
  /// initialize its [TextEditingController.text] with [initialValue].
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
  final TextEditingController controller;

  @override
  _TextFormFieldState createState() => new _TextFormFieldState();
}

class _TextFormFieldState extends FormFieldState<String> {
  TextEditingController _controller;

  TextEditingController get _effectiveController => widget.controller ?? _controller;

  @override
  TextFormField get widget => super.widget;

  @override
  void initState() {
    super.initState();
    if (widget.controller == null) {
      _controller = new TextEditingController(text: widget.initialValue);
    } else {
      widget.controller.addListener(_handleControllerChanged);
    }
  }

  @override
  void didUpdateWidget(TextFormField oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (widget.controller != oldWidget.controller) {
      oldWidget.controller?.removeListener(_handleControllerChanged);
      widget.controller?.addListener(_handleControllerChanged);

      if (oldWidget.controller != null && widget.controller == null)
        _controller = new TextEditingController.fromValue(oldWidget.controller.value);
      if (widget.controller != null) {
        setValue(widget.controller.text);
        if (oldWidget.controller == null)
          _controller = null;
      }
    }
  }

  @override
  void dispose() {
    widget.controller?.removeListener(_handleControllerChanged);
    super.dispose();
  }

  @override
  void reset() {
    super.reset();
    setState(() {
      _effectiveController.text = widget.initialValue;
    });
  }

  void _handleControllerChanged() {
    // Suppress changes that originated from within this class.
    //
    // In the case where a controller has been passed in to this widget, we
    // register this change listener. In these cases, we'll also receive change
    // notifications for changes originating from within this class -- for
    // example, the reset() method. In such cases, the FormField value will
    // already have been set.
    if (_effectiveController.text != value)
191
      didChange(_effectiveController.text);
192
  }
193
}