|
| 1 | +// Copyright 2014 The Flutter Authors. All rights reserved. |
| 2 | +// Use of this source code is governed by a BSD-style license that can be |
| 3 | +// found in the LICENSE file. |
| 4 | + |
| 5 | +// @dart = 2.8 |
| 6 | + |
1 | 7 | import 'package:flutter/services.dart'; |
2 | 8 | import 'package:flutter/widgets.dart'; |
3 | 9 | import 'package:flutter/material.dart'; |
4 | 10 |
|
| 11 | +// import 'input_decorator.dart'; |
| 12 | +// import 'text_field.dart'; |
| 13 | +// import 'theme.dart'; |
5 | 14 |
|
| 15 | +export 'package:flutter/services.dart' show SmartQuotesType, SmartDashesType; |
6 | 16 |
|
7 | | -class GFTextField extends StatefulWidget { |
8 | | - |
9 | | - GFTextField({ |
10 | | - Key key, |
| 17 | +class GFTextField extends FormField<String> { |
| 18 | + GFTextField({ |
| 19 | + Key key, |
11 | 20 | this.controller, |
12 | 21 | String initialValue, |
13 | 22 | FocusNode focusNode, |
@@ -55,58 +64,188 @@ class GFTextField extends StatefulWidget { |
55 | 64 | ScrollPhysics scrollPhysics, |
56 | 65 | Iterable<String> autofillHints, |
57 | 66 | AutovalidateMode autovalidateMode, |
58 | | - }) : assert(initialValue == null || controller == null), |
59 | | - assert(textAlign != null), |
60 | | - assert(autofocus != null), |
61 | | - assert(readOnly != null), |
62 | | - assert(obscuringCharacter != null && obscuringCharacter.length == 1), |
63 | | - assert(obscureText != null), |
64 | | - assert(autocorrect != null), |
65 | | - assert(enableSuggestions != null), |
66 | | - assert(autovalidate != null), |
67 | | - assert( |
68 | | - autovalidate == false || |
69 | | - autovalidate == true && autovalidateMode == null, |
70 | | - 'autovalidate and autovalidateMode should not be used together.' |
71 | | - ), |
72 | | - assert(maxLengthEnforced != null), |
73 | | - assert(scrollPadding != null), |
74 | | - assert(maxLines == null || maxLines > 0), |
75 | | - assert(minLines == null || minLines > 0), |
76 | | - assert( |
77 | | - (maxLines == null) || (minLines == null) || (maxLines >= minLines), |
78 | | - "minLines can't be greater than maxLines", |
79 | | - ), |
80 | | - assert(expands != null), |
81 | | - assert( |
82 | | - !expands || (maxLines == null && minLines == null), |
83 | | - 'minLines and maxLines must be null when expands is true.', |
84 | | - ), |
85 | | - assert(!obscureText || maxLines == 1, 'Obscured fields cannot be multiline.'), |
86 | | - assert(maxLength == null || maxLength > 0), |
87 | | - assert(enableInteractiveSelection != null), |
88 | | - super( |
89 | | - key: key, |
90 | | - initialValue: controller != null ? controller.text : (initialValue ?? ''), |
91 | | - onSaved: onSaved, |
92 | | - validator: validator, |
93 | | - enabled: enabled ?? decoration?.enabled ?? true, |
94 | | - autovalidateMode: autovalidate |
95 | | - ? AutovalidateMode.always |
96 | | - : (autovalidateMode ?? AutovalidateMode.disabled), |
97 | | - builder: (FormFieldState<String> field) { |
98 | | - final _TextFormFieldState state = field as _TextFormFieldState; |
99 | | - final InputDecoration effectiveDecoration = (decoration ?? const InputDecoration()) |
100 | | - .applyDefaults(Theme.of(field.context).inputDecorationTheme);}); |
| 67 | + }) : assert(initialValue == null || controller == null), |
| 68 | + assert(textAlign != null), |
| 69 | + assert(autofocus != null), |
| 70 | + assert(readOnly != null), |
| 71 | + assert(obscuringCharacter != null && obscuringCharacter.length == 1), |
| 72 | + assert(obscureText != null), |
| 73 | + assert(autocorrect != null), |
| 74 | + assert(enableSuggestions != null), |
| 75 | + assert(autovalidate != null), |
| 76 | + assert( |
| 77 | + autovalidate == false || |
| 78 | + autovalidate == true && autovalidateMode == null, |
| 79 | + 'autovalidate and autovalidateMode should not be used together.'), |
| 80 | + assert(maxLengthEnforced != null), |
| 81 | + assert(scrollPadding != null), |
| 82 | + assert(maxLines == null || maxLines > 0), |
| 83 | + assert(minLines == null || minLines > 0), |
| 84 | + assert( |
| 85 | + (maxLines == null) || (minLines == null) || (maxLines >= minLines), |
| 86 | + "minLines can't be greater than maxLines", |
| 87 | + ), |
| 88 | + assert(expands != null), |
| 89 | + assert( |
| 90 | + !expands || (maxLines == null && minLines == null), |
| 91 | + 'minLines and maxLines must be null when expands is true.', |
| 92 | + ), |
| 93 | + assert(!obscureText || maxLines == 1, |
| 94 | + 'Obscured fields cannot be multiline.'), |
| 95 | + assert(maxLength == null || maxLength > 0), |
| 96 | + assert(enableInteractiveSelection != null), |
| 97 | + super( |
| 98 | + key: key, |
| 99 | + initialValue: |
| 100 | + controller != null ? controller.text : (initialValue ?? ''), |
| 101 | + onSaved: onSaved, |
| 102 | + validator: validator, |
| 103 | + enabled: enabled ?? decoration?.enabled ?? true, |
| 104 | + autovalidateMode: autovalidate |
| 105 | + ? AutovalidateMode.always |
| 106 | + : (autovalidateMode ?? AutovalidateMode.disabled), |
| 107 | + builder: (FormFieldState<String> field) { |
| 108 | + final _GFTextFieldState state = field as _GFTextFieldState; |
| 109 | + final InputDecoration effectiveDecoration = (decoration ?? |
| 110 | + const InputDecoration()) |
| 111 | + .applyDefaults(Theme.of(field.context).inputDecorationTheme); |
| 112 | + void onChangedHandler(String value) { |
| 113 | + if (onChanged != null) { |
| 114 | + onChanged(value); |
| 115 | + } |
| 116 | + field.didChange(value); |
| 117 | + } |
| 118 | + |
| 119 | + return TextField( |
| 120 | + controller: state._effectiveController, |
| 121 | + focusNode: focusNode, |
| 122 | + decoration: |
| 123 | + effectiveDecoration.copyWith(errorText: field.errorText), |
| 124 | + keyboardType: keyboardType, |
| 125 | + textInputAction: textInputAction, |
| 126 | + style: style, |
| 127 | + strutStyle: strutStyle, |
| 128 | + textAlign: textAlign, |
| 129 | + textAlignVertical: textAlignVertical, |
| 130 | + textDirection: textDirection, |
| 131 | + textCapitalization: textCapitalization, |
| 132 | + autofocus: autofocus, |
| 133 | + toolbarOptions: toolbarOptions, |
| 134 | + readOnly: readOnly, |
| 135 | + showCursor: showCursor, |
| 136 | + obscuringCharacter: obscuringCharacter, |
| 137 | + obscureText: obscureText, |
| 138 | + autocorrect: autocorrect, |
| 139 | + smartDashesType: smartDashesType ?? |
| 140 | + (obscureText |
| 141 | + ? SmartDashesType.disabled |
| 142 | + : SmartDashesType.enabled), |
| 143 | + smartQuotesType: smartQuotesType ?? |
| 144 | + (obscureText |
| 145 | + ? SmartQuotesType.disabled |
| 146 | + : SmartQuotesType.enabled), |
| 147 | + enableSuggestions: enableSuggestions, |
| 148 | + maxLengthEnforced: maxLengthEnforced, |
| 149 | + maxLines: maxLines, |
| 150 | + minLines: minLines, |
| 151 | + expands: expands, |
| 152 | + maxLength: maxLength, |
| 153 | + onChanged: onChangedHandler, |
| 154 | + onTap: onTap, |
| 155 | + onEditingComplete: onEditingComplete, |
| 156 | + onSubmitted: onFieldSubmitted, |
| 157 | + inputFormatters: inputFormatters, |
| 158 | + enabled: enabled ?? decoration?.enabled ?? true, |
| 159 | + cursorWidth: cursorWidth, |
| 160 | + cursorHeight: cursorHeight, |
| 161 | + cursorRadius: cursorRadius, |
| 162 | + cursorColor: cursorColor, |
| 163 | + scrollPadding: scrollPadding, |
| 164 | + scrollPhysics: scrollPhysics, |
| 165 | + keyboardAppearance: keyboardAppearance, |
| 166 | + enableInteractiveSelection: enableInteractiveSelection, |
| 167 | + buildCounter: buildCounter, |
| 168 | + autofillHints: autofillHints, |
| 169 | + ); |
| 170 | + }, |
| 171 | + ); |
| 172 | + |
| 173 | + /// Controls the text being edited. |
| 174 | + /// |
| 175 | + /// If null, this widget will create its own [TextEditingController] and |
| 176 | + /// initialize its [TextEditingController.text] with [initialValue]. |
| 177 | + final TextEditingController controller; |
| 178 | + |
101 | 179 | @override |
102 | 180 | _GFTextFieldState createState() => _GFTextFieldState(); |
103 | 181 | } |
104 | 182 |
|
105 | | -class _GFTextFieldState extends State<GFTextField> { |
| 183 | +class _GFTextFieldState extends FormFieldState<String> { |
| 184 | + TextEditingController _controller; |
| 185 | + |
| 186 | + TextEditingController get _effectiveController => |
| 187 | + widget.controller ?? _controller; |
| 188 | + |
| 189 | + @override |
| 190 | + GFTextField get widget => super.widget as GFTextField; |
| 191 | + |
| 192 | + @override |
| 193 | + void initState() { |
| 194 | + super.initState(); |
| 195 | + if (widget.controller == null) { |
| 196 | + _controller = TextEditingController(text: widget.initialValue); |
| 197 | + } else { |
| 198 | + widget.controller.addListener(_handleControllerChanged); |
| 199 | + } |
| 200 | + } |
| 201 | + |
106 | 202 | @override |
107 | | - Widget build(BuildContext context) { |
108 | | - return Container( |
109 | | - child: TextFormField(), |
110 | | - ); |
| 203 | + void didUpdateWidget(GFTextField oldWidget) { |
| 204 | + super.didUpdateWidget(oldWidget); |
| 205 | + if (widget.controller != oldWidget.controller) { |
| 206 | + oldWidget.controller?.removeListener(_handleControllerChanged); |
| 207 | + widget.controller?.addListener(_handleControllerChanged); |
| 208 | + |
| 209 | + if (oldWidget.controller != null && widget.controller == null) |
| 210 | + _controller = |
| 211 | + TextEditingController.fromValue(oldWidget.controller.value); |
| 212 | + if (widget.controller != null) { |
| 213 | + setValue(widget.controller.text); |
| 214 | + if (oldWidget.controller == null) _controller = null; |
| 215 | + } |
| 216 | + } |
| 217 | + } |
| 218 | + |
| 219 | + @override |
| 220 | + void dispose() { |
| 221 | + widget.controller?.removeListener(_handleControllerChanged); |
| 222 | + super.dispose(); |
| 223 | + } |
| 224 | + |
| 225 | + @override |
| 226 | + void didChange(String value) { |
| 227 | + super.didChange(value); |
| 228 | + |
| 229 | + if (_effectiveController.text != value) _effectiveController.text = value; |
| 230 | + } |
| 231 | + |
| 232 | + @override |
| 233 | + void reset() { |
| 234 | + super.reset(); |
| 235 | + setState(() { |
| 236 | + _effectiveController.text = widget.initialValue; |
| 237 | + }); |
| 238 | + } |
| 239 | + |
| 240 | + void _handleControllerChanged() { |
| 241 | + // Suppress changes that originated from within this class. |
| 242 | + // |
| 243 | + // In the case where a controller has been passed in to this widget, we |
| 244 | + // register this change listener. In these cases, we'll also receive change |
| 245 | + // notifications for changes originating from within this class -- for |
| 246 | + // example, the reset() method. In such cases, the FormField value will |
| 247 | + // already have been set. |
| 248 | + if (_effectiveController.text != value) |
| 249 | + didChange(_effectiveController.text); |
111 | 250 | } |
112 | 251 | } |
0 commit comments