I can't set my custom reactive_forms control to null. The value seems to stay in it when the FormGroup has no value in the field. But in the custom control builder it has the field and says it has a value. The binding of the control is not in sync with the form. It only happens after I swap out my form for another form at runtime. Any idea what I can inspect? I have been inspecting my code in the debugger for hours so clearly I am inspecting the wrong thing. The form is definitely empty and the control has a value somehow. Could I somehow forcefully empty the control? form!.resetState({});
does't work. I need the code here builder: (ReactiveFormFieldState<TValue, TValue> field) {
to contain null in the field
.
Custom control:
import 'package:flutter/material.dart';
import 'package:flutter_typeahead/flutter_typeahead.dart';
import 'package:reactive_forms/reactive_forms.dart';
import 'autocomplete_field_view_model.dart';
class AutocompleteField<
TValue,
TViewModel extends AutocompleteFieldViewModel<TValue, TSuggestion>,
TSuggestion> extends ReactiveFormField<TValue, TValue> {
AutocompleteField(
{required String formControlName,
required this.viewModel,
this.onTypedCallback,
this.context,
this.label,
this.form,
this.hint,
bool hasFocus = false,
this.onFocusLost,
Map<String, String> Function(AbstractControl<dynamic>)?
validationMessages,
this.key})
: super(
key: key,
formControlName: formControlName,
validationMessages: validationMessages,
showErrors: (control) => control.invalid,
builder: (ReactiveFormFieldState<TValue, TValue> field) {
viewModel.field = field;
viewModel.setTypeAheadText(field.value);
viewModel.resetFieldCursor();
return FocusScope(
child: Focus(
onFocusChange: (isFocused) {
hasFocus = isFocused;
viewModel.onFocusChange(isFocused, onFocusLost);
// ignore: invalid_use_of_protected_member
field.setState(() {});
},
child: TypeAheadFormField<TSuggestion>(
hideSuggestionsOnKeyboardHide: true,
suggestionsBoxController:
viewModel.suggestionsBoxController,
textFieldConfiguration: TextFieldConfiguration(
controller: viewModel.typeAheadController,
onChanged: (String text) {
if (onTypedCallback != null) {
onTypedCallback(text);
}
viewModel.onTyped(text);
viewModel.resetFieldCursor();
},
autofocus: false,
decoration: InputDecoration(
errorMaxLines: 3,
suffixIconConstraints: const BoxConstraints(
minWidth: 2,
minHeight: 2,
),
suffixIcon: ReactiveStatusListenableBuilder(
formControlName: formControlName,
builder: (context, control, child) {
return control.pending
? Container(
width: 90,
height: 60,
child: Stack(children: [
Positioned(
top: 10,
right: 15,
child: CircularProgressIndicator(
backgroundColor:
Theme.of(context)
.primaryColorDark),
)
]))
: Container(width: 0);
},
),
alignLabelWithHint: true,
labelStyle: TextStyle(
height: 0,
fontSize: hasFocus ? 24 : 18.0,
color: Theme.of(context!).primaryColor),
hintText: hint,
labelText: label,
counterText: ''),
),
suggestionsCallback: viewModel.suggestionsCallback,
suggestionsBoxDecoration:
viewModel.suggestionBoxDecoration(),
itemBuilder: viewModel.itemBuilder,
onSuggestionSelected: (TSuggestion suggestion) =>
viewModel.onSuggestionSelected(suggestion),
)));
});
TViewModel viewModel;
final Key? key;
final BuildContext? context;
final String? label;
final String? hint;
bool? hasFocus;
final void Function()? onFocusLost;
final void Function(String text)? onTypedCallback;
final FormGroup? form;
@override
ReactiveFormFieldState<TValue, TValue> createState() =>
ReactiveFormFieldState<TValue, TValue>();
}
Adding a key solved the issue. After all those hours I thought to try it straight after I asked the question. Damn, will have to remember this one...