Skip to content

Commit 67e6cad

Browse files
authored
Restorable CupertinoTextFormFieldRow (#144541)
## Description This PR makes `CupertinoTextFormFieldRow` restorable. The implementation is based on flutter/flutter#78835 which made `FormField` and `TextFormField` restorable. ## Related Issue Fixes flutter/flutter#144504. ## Tests Adds 4 tests.
1 parent e73e7e2 commit 67e6cad

File tree

2 files changed

+339
-56
lines changed

2 files changed

+339
-56
lines changed

packages/flutter/lib/src/cupertino/text_form_field_row.dart

Lines changed: 88 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ class CupertinoTextFormFieldRow extends FormField<String> {
157157
color: CupertinoColors.placeholderText,
158158
),
159159
EditableTextContextMenuBuilder? contextMenuBuilder = _defaultContextMenuBuilder,
160+
super.restorationId,
160161
}) : assert(initialValue == null || controller == null),
161162
assert(obscuringCharacter.length == 1),
162163
assert(maxLines == null || maxLines > 0),
@@ -186,50 +187,54 @@ class CupertinoTextFormFieldRow extends FormField<String> {
186187
prefix: prefix,
187188
padding: padding,
188189
error: (field.errorText == null) ? null : Text(field.errorText!),
189-
child: CupertinoTextField.borderless(
190-
controller: state._effectiveController,
191-
focusNode: focusNode,
192-
keyboardType: keyboardType,
193-
decoration: decoration,
194-
textInputAction: textInputAction,
195-
style: style,
196-
strutStyle: strutStyle,
197-
textAlign: textAlign,
198-
textAlignVertical: textAlignVertical,
199-
textCapitalization: textCapitalization,
200-
textDirection: textDirection,
201-
autofocus: autofocus,
202-
toolbarOptions: toolbarOptions,
203-
readOnly: readOnly,
204-
showCursor: showCursor,
205-
obscuringCharacter: obscuringCharacter,
206-
obscureText: obscureText,
207-
autocorrect: autocorrect,
208-
smartDashesType: smartDashesType,
209-
smartQuotesType: smartQuotesType,
210-
enableSuggestions: enableSuggestions,
211-
maxLines: maxLines,
212-
minLines: minLines,
213-
expands: expands,
214-
maxLength: maxLength,
215-
onChanged: onChangedHandler,
216-
onTap: onTap,
217-
onEditingComplete: onEditingComplete,
218-
onSubmitted: onFieldSubmitted,
219-
inputFormatters: inputFormatters,
220-
enabled: enabled ?? true,
221-
cursorWidth: cursorWidth,
222-
cursorHeight: cursorHeight,
223-
cursorColor: cursorColor,
224-
scrollPadding: scrollPadding,
225-
scrollPhysics: scrollPhysics,
226-
keyboardAppearance: keyboardAppearance,
227-
enableInteractiveSelection: enableInteractiveSelection,
228-
selectionControls: selectionControls,
229-
autofillHints: autofillHints,
230-
placeholder: placeholder,
231-
placeholderStyle: placeholderStyle,
232-
contextMenuBuilder: contextMenuBuilder,
190+
child: UnmanagedRestorationScope(
191+
bucket: field.bucket,
192+
child: CupertinoTextField.borderless(
193+
restorationId: restorationId,
194+
controller: state._effectiveController,
195+
focusNode: focusNode,
196+
keyboardType: keyboardType,
197+
decoration: decoration,
198+
textInputAction: textInputAction,
199+
style: style,
200+
strutStyle: strutStyle,
201+
textAlign: textAlign,
202+
textAlignVertical: textAlignVertical,
203+
textCapitalization: textCapitalization,
204+
textDirection: textDirection,
205+
autofocus: autofocus,
206+
toolbarOptions: toolbarOptions,
207+
readOnly: readOnly,
208+
showCursor: showCursor,
209+
obscuringCharacter: obscuringCharacter,
210+
obscureText: obscureText,
211+
autocorrect: autocorrect,
212+
smartDashesType: smartDashesType,
213+
smartQuotesType: smartQuotesType,
214+
enableSuggestions: enableSuggestions,
215+
maxLines: maxLines,
216+
minLines: minLines,
217+
expands: expands,
218+
maxLength: maxLength,
219+
onChanged: onChangedHandler,
220+
onTap: onTap,
221+
onEditingComplete: onEditingComplete,
222+
onSubmitted: onFieldSubmitted,
223+
inputFormatters: inputFormatters,
224+
enabled: enabled ?? true,
225+
cursorWidth: cursorWidth,
226+
cursorHeight: cursorHeight,
227+
cursorColor: cursorColor,
228+
scrollPadding: scrollPadding,
229+
scrollPhysics: scrollPhysics,
230+
keyboardAppearance: keyboardAppearance,
231+
enableInteractiveSelection: enableInteractiveSelection,
232+
selectionControls: selectionControls,
233+
autofillHints: autofillHints,
234+
placeholder: placeholder,
235+
placeholderStyle: placeholderStyle,
236+
contextMenuBuilder: contextMenuBuilder,
237+
),
233238
),
234239
);
235240
},
@@ -272,19 +277,45 @@ class CupertinoTextFormFieldRow extends FormField<String> {
272277
}
273278

274279
class _CupertinoTextFormFieldRowState extends FormFieldState<String> {
275-
TextEditingController? _controller;
280+
RestorableTextEditingController? _controller;
276281

277-
TextEditingController? get _effectiveController =>
278-
_cupertinoTextFormFieldRow.controller ?? _controller;
282+
TextEditingController get _effectiveController =>
283+
_cupertinoTextFormFieldRow.controller ?? _controller!.value;
279284

280285
CupertinoTextFormFieldRow get _cupertinoTextFormFieldRow =>
281286
super.widget as CupertinoTextFormFieldRow;
282287

288+
@override
289+
void restoreState(RestorationBucket? oldBucket, bool initialRestore) {
290+
super.restoreState(oldBucket, initialRestore);
291+
if (_controller != null) {
292+
_registerController();
293+
}
294+
// This makes sure to update the internal [FormFieldState] value to sync up with
295+
// text editing controller value.
296+
setValue(_effectiveController.text);
297+
}
298+
299+
void _registerController() {
300+
assert(_controller != null);
301+
registerForRestoration(_controller!, 'controller');
302+
}
303+
304+
void _createLocalController([TextEditingValue? value]) {
305+
assert(_controller == null);
306+
_controller = value == null
307+
? RestorableTextEditingController()
308+
: RestorableTextEditingController.fromValue(value);
309+
if (!restorePending) {
310+
_registerController();
311+
}
312+
}
313+
283314
@override
284315
void initState() {
285316
super.initState();
286317
if (_cupertinoTextFormFieldRow.controller == null) {
287-
_controller = TextEditingController(text: widget.initialValue);
318+
_createLocalController(widget.initialValue != null ? TextEditingValue(text: widget.initialValue!) : null);
288319
} else {
289320
_cupertinoTextFormFieldRow.controller!.addListener(_handleControllerChanged);
290321
}
@@ -298,13 +329,14 @@ class _CupertinoTextFormFieldRowState extends FormFieldState<String> {
298329
_cupertinoTextFormFieldRow.controller?.addListener(_handleControllerChanged);
299330

300331
if (oldWidget.controller != null && _cupertinoTextFormFieldRow.controller == null) {
301-
_controller =
302-
TextEditingController.fromValue(oldWidget.controller!.value);
332+
_createLocalController(oldWidget.controller!.value);
303333
}
304334

305335
if (_cupertinoTextFormFieldRow.controller != null) {
306336
setValue(_cupertinoTextFormFieldRow.controller!.text);
307337
if (oldWidget.controller == null) {
338+
unregisterFromRestoration(_controller!);
339+
_controller!.dispose();
308340
_controller = null;
309341
}
310342
}
@@ -322,18 +354,18 @@ class _CupertinoTextFormFieldRowState extends FormFieldState<String> {
322354
void didChange(String? value) {
323355
super.didChange(value);
324356

325-
if (value != null && _effectiveController!.text != value) {
326-
_effectiveController!.text = value;
357+
if (value != null && _effectiveController.text != value) {
358+
_effectiveController.text = value;
327359
}
328360
}
329361

330362
@override
331363
void reset() {
332364
// Set the controller value before calling super.reset() to let
333365
// _handleControllerChanged suppress the change.
334-
_effectiveController!.text = widget.initialValue!;
366+
_effectiveController.text = widget.initialValue!;
335367
super.reset();
336-
_cupertinoTextFormFieldRow.onChanged?.call(_effectiveController!.text);
368+
_cupertinoTextFormFieldRow.onChanged?.call(_effectiveController.text);
337369
}
338370

339371
void _handleControllerChanged() {
@@ -344,8 +376,8 @@ class _CupertinoTextFormFieldRowState extends FormFieldState<String> {
344376
// notifications for changes originating from within this class -- for
345377
// example, the reset() method. In such cases, the FormField value will
346378
// already have been set.
347-
if (_effectiveController!.text != value) {
348-
didChange(_effectiveController!.text);
379+
if (_effectiveController.text != value) {
380+
didChange(_effectiveController.text);
349381
}
350382
}
351383
}

0 commit comments

Comments
 (0)