Skip to content

Commit 22ea946

Browse files
committed
Fixed memory leaks by properly disposing of text editing controllers.
1 parent 1bbe5b7 commit 22ea946

8 files changed

+98
-49
lines changed

lib/src/fields/form_builder_color_picker.dart

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,16 @@ extension on Color {
1717
}*/
1818

1919
/// Prefixes a hash sign if [leadingHashSign] is set to `true` (default is `true`).
20-
String toHex({bool leadingHashSign = true}) => '${leadingHashSign ? '#' : ''}'
21-
'${alpha.toRadixString(16).padLeft(2, '0').toUpperCase()}'
22-
'${red.toRadixString(16).padLeft(2, '0').toUpperCase()}'
23-
'${green.toRadixString(16).padLeft(2, '0').toUpperCase()}'
24-
'${blue.toRadixString(16).padLeft(2, '0').toUpperCase()}';
20+
String toHex({bool leadingHashSign = true}) {
21+
/// Converts an rgba value (0-255) into a 2-digit Hex code.
22+
String _hexValue(int rgbaVal) {
23+
assert(rgbaVal == rgbaVal & 0xFF);
24+
return rgbaVal.toRadixString(16).padLeft(2, '0').toUpperCase();
25+
}
26+
27+
return '${leadingHashSign ? '#' : ''}'
28+
'${_hexValue(alpha)}${_hexValue(red)}${_hexValue(green)}${_hexValue(blue)}';
29+
}
2530
}
2631

2732
enum ColorPickerType { ColorPicker, MaterialPicker, BlockPicker }
@@ -145,7 +150,7 @@ class FormBuilderColorPickerField extends FormBuilderField<Color> {
145150
),
146151
enabled: !state.readOnly,
147152
readOnly: state.readOnly,
148-
controller: state.effectiveController,
153+
controller: state._effectiveController,
149154
focusNode: state.effectiveFocusNode,
150155
textAlign: textAlign,
151156
autofocus: autofocus,
@@ -185,8 +190,6 @@ class _FormBuilderColorPickerFieldState
185190
extends FormBuilderFieldState<FormBuilderColorPickerField, Color> {
186191
TextEditingController _effectiveController;
187192

188-
TextEditingController get effectiveController => _effectiveController;
189-
190193
String get valueString => value?.toHex();
191194

192195
Color _selectedColor;
@@ -199,6 +202,15 @@ class _FormBuilderColorPickerFieldState
199202
effectiveFocusNode.addListener(_handleFocus);
200203
}
201204

205+
@override
206+
void dispose() {
207+
// Dispose the _effectiveController when initState created it
208+
if (null == widget.controller) {
209+
_effectiveController.dispose();
210+
}
211+
super.dispose();
212+
}
213+
202214
Future<void> _handleFocus() async {
203215
if (effectiveFocusNode.hasFocus && !readOnly) {
204216
await Future.microtask(

lib/src/fields/form_builder_date_range_picker.dart

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ class FormBuilderDateRangePicker extends FormBuilderField<List<DateTime>> {
147147
keyboardType: keyboardType,
148148
obscureText: obscureText,
149149
onEditingComplete: onEditingComplete,
150-
controller: state.effectiveController,
150+
controller: state._effectiveController,
151151
autocorrect: autocorrect,
152152
autofocus: autofocus,
153153
buildCounter: buildCounter,
@@ -193,8 +193,6 @@ class FormBuilderDateRangePickerState
193193
extends FormBuilderFieldState<FormBuilderDateRangePicker, List<DateTime>> {
194194
TextEditingController _effectiveController;
195195

196-
TextEditingController get effectiveController => _effectiveController;
197-
198196
@override
199197
void initState() {
200198
_effectiveController =
@@ -203,6 +201,15 @@ class FormBuilderDateRangePickerState
203201
super.initState();
204202
}
205203

204+
@override
205+
void dispose() {
206+
// Dispose the _effectiveController when initState created it
207+
if (null == widget.controller) {
208+
_effectiveController.dispose();
209+
}
210+
super.dispose();
211+
}
212+
206213
Future<void> _handleFocus() async {
207214
if (effectiveFocusNode.hasFocus) {
208215
_hideKeyboard();
@@ -237,7 +244,7 @@ class FormBuilderDateRangePickerState
237244
return '';
238245
}
239246
if (value.length == 1) {
240-
return '${format(value[0])}';
247+
return format(value[0]);
241248
}
242249
return '${format(value[0])} - ${format(value[1])}';
243250
}
@@ -266,10 +273,4 @@ class FormBuilderDateRangePickerState
266273
super.reset();
267274
_setTextFieldString();
268275
}
269-
270-
@override
271-
void dispose() {
272-
_effectiveController?.dispose();
273-
super.dispose();
274-
}
275276
}

lib/src/fields/form_builder_date_time_picker.dart

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ class FormBuilderDateTimePicker extends FormBuilderField<DateTime> {
255255
readOnly: true,
256256
enabled: state.readOnly ? false : enabled,
257257
autocorrect: autocorrect,
258-
controller: state.textFieldController,
258+
controller: state._textFieldController,
259259
focusNode: state.effectiveFocusNode,
260260
inputFormatters: inputFormatters,
261261
keyboardType: keyboardType,
@@ -291,7 +291,6 @@ class FormBuilderDateTimePicker extends FormBuilderField<DateTime> {
291291

292292
class _FormBuilderDateTimePickerState
293293
extends FormBuilderFieldState<FormBuilderDateTimePicker, DateTime> {
294-
TextEditingController get textFieldController => _textFieldController;
295294
TextEditingController _textFieldController;
296295

297296
// DateTime stateCurrentValue;
@@ -311,6 +310,15 @@ class _FormBuilderDateTimePickerState
311310
initialValue == null ? '' : dateFormat.format(initialValue);
312311
}
313312

313+
@override
314+
void dispose() {
315+
// Dispose the _textFieldController when initState created it
316+
if (null == widget.controller) {
317+
_textFieldController.dispose();
318+
}
319+
super.dispose();
320+
}
321+
314322
// Hack to avoid manual editing of date - as is in DateTimeField library
315323
/*Future<void> _handleFocus() async {
316324
if (effectiveFocusNode.hasFocus) {
@@ -319,7 +327,7 @@ class _FormBuilderDateTimePickerState
319327
}*/
320328

321329
DateFormat _getDefaultDateTimeFormat() {
322-
var appLocale = widget.locale ?? Localizations.localeOf(context);
330+
final appLocale = widget.locale ?? Localizations.localeOf(context);
323331
switch (widget.inputType) {
324332
case InputType.time:
325333
return DateFormat.Hm(appLocale.toString());
@@ -340,7 +348,7 @@ class _FormBuilderDateTimePickerState
340348
newValue = await _showDatePicker(context, currentValue) ?? currentValue;
341349
break;
342350
case InputType.time:
343-
var newTime = await _showTimePicker(context, currentValue);
351+
final newTime = await _showTimePicker(context, currentValue);
344352
newValue =
345353
newTime != null ? DateTimeField.convert(newTime) : currentValue;
346354
break;
@@ -355,7 +363,7 @@ class _FormBuilderDateTimePickerState
355363
throw 'Unexpected input type ${widget.inputType}';
356364
break;
357365
}
358-
var finalValue = newValue ?? currentValue;
366+
final finalValue = newValue ?? currentValue;
359367
didChange(finalValue);
360368
return finalValue;
361369
}
@@ -481,6 +489,6 @@ class _FormBuilderDateTimePickerState
481489
@override
482490
void patchValue(dynamic val) {
483491
super.patchValue(val);
484-
textFieldController.text = val == null ? '' : dateFormat.format(val);
492+
_textFieldController.text = val == null ? '' : dateFormat.format(val);
485493
}
486494
}

lib/src/fields/form_builder_location_field.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,12 @@ class _FormBuilderLocationFieldState
282282
effectiveFocusNode.addListener(_handleFocus);
283283
}
284284

285+
@override
286+
void dispose() {
287+
_controller.dispose();
288+
super.dispose();
289+
}
290+
285291
Future<void> _handleFocus() async {
286292
if (effectiveFocusNode.hasFocus && !readOnly) {
287293
await Future.microtask(

lib/src/fields/form_builder_phone_field.dart

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ class FormBuilderPhoneField extends FormBuilderField<String> {
218218

219219
class _FormBuilderPhoneFieldState
220220
extends FormBuilderFieldState<FormBuilderPhoneField, String> {
221-
TextEditingController _effectiveController = TextEditingController();
221+
TextEditingController _effectiveController;
222222
Country _selectedDialogCountry;
223223

224224
String get fullNumber {
@@ -233,14 +233,21 @@ class _FormBuilderPhoneFieldState
233233
@override
234234
void initState() {
235235
super.initState();
236-
if (widget.controller != null) {
237-
_effectiveController = widget.controller;
238-
}
236+
_effectiveController = widget.controller ?? TextEditingController();
239237
_selectedDialogCountry = CountryPickerUtils.getCountryByIsoCode(
240238
widget.defaultSelectedCountryIsoCode);
241239
_parsePhone();
242240
}
243241

242+
@override
243+
void dispose() {
244+
// Dispose the _effectiveController when initState created it
245+
if (null == widget.controller) {
246+
_effectiveController.dispose();
247+
}
248+
super.dispose();
249+
}
250+
244251
Future<void> _parsePhone() async {
245252
print('initialValue: $initialValue');
246253
if (initialValue != null && initialValue.isNotEmpty) {

lib/src/fields/form_builder_signature_pad.dart

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ class FormBuilderSignaturePad extends FormBuilderField<Uint8List> {
7777
child: GestureDetector(
7878
onVerticalDragUpdate: (_) {},
7979
child: Signature(
80-
controller: state.effectiveController,
80+
controller: state._controller,
8181
width: width,
8282
height: height,
8383
backgroundColor: backgroundColor,
@@ -89,7 +89,7 @@ class FormBuilderSignaturePad extends FormBuilderField<Uint8List> {
8989
Expanded(child: SizedBox()),
9090
TextButton.icon(
9191
onPressed: () {
92-
state.effectiveController.clear();
92+
state._controller.clear();
9393
field.didChange(null);
9494
},
9595
label: Text(
@@ -113,26 +113,33 @@ class FormBuilderSignaturePad extends FormBuilderField<Uint8List> {
113113

114114
class _FormBuilderSignaturePadState
115115
extends FormBuilderFieldState<FormBuilderSignaturePad, Uint8List> {
116-
SignatureController get effectiveController =>
117-
widget.controller ?? _controller;
118-
119-
final SignatureController _controller = SignatureController();
116+
SignatureController _controller;
120117

121118
@override
122119
void initState() {
123120
super.initState();
124-
effectiveController.addListener(() async {
121+
_controller = widget.controller ?? SignatureController();
122+
_controller.addListener(() async {
125123
requestFocus();
126-
var _value = await effectiveController.toImage() != null
127-
? await effectiveController.toPngBytes()
124+
var _value = await _controller.toImage() != null
125+
? await _controller.toPngBytes()
128126
: null;
129127
didChange(_value);
130128
});
131129
}
132130

131+
@override
132+
void dispose() {
133+
// Dispose the _controller when initState created it
134+
if (null == widget.controller) {
135+
_controller.dispose();
136+
}
137+
super.dispose();
138+
}
139+
133140
@override
134141
void reset() {
135-
effectiveController?.clear();
142+
_controller?.clear();
136143
super.reset();
137144
}
138145

lib/src/fields/form_builder_text_field.dart

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -462,16 +462,17 @@ class _FormBuilderTextFieldState
462462
@override
463463
void initState() {
464464
super.initState();
465-
if (widget.controller == null) {
466-
_controller = TextEditingController(text: initialValue);
467-
} else {
468-
widget.controller.addListener(_handleControllerChanged);
469-
}
465+
_controller =
466+
widget.controller ?? TextEditingController(text: initialValue);
467+
_controller.addListener(_handleControllerChanged);
470468
}
471469

472470
@override
473471
void dispose() {
474-
_controller?.dispose();
472+
// Dispose the _controller when initState created it
473+
if (null == widget.controller) {
474+
_controller.dispose();
475+
}
475476
super.dispose();
476477
}
477478

lib/src/fields/form_builder_typeahead.dart

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ class FormBuilderTypeAhead<T> extends FormBuilderField<T> {
317317
return TypeAheadField<T>(
318318
textFieldConfiguration: textFieldConfiguration.copyWith(
319319
enabled: !state.readOnly,
320-
controller: state.typeAheadController,
320+
controller: state._typeAheadController,
321321
style: state.readOnly
322322
? theme.textTheme.subtitle1.copyWith(
323323
color: theme.disabledColor,
@@ -335,10 +335,10 @@ class FormBuilderTypeAhead<T> extends FormBuilderField<T> {
335335
suggestionsBox,
336336
onSuggestionSelected: (T suggestion) {
337337
if (selectionToTextTransformer != null) {
338-
state.typeAheadController.text =
338+
state._typeAheadController.text =
339339
selectionToTextTransformer(suggestion);
340340
} else {
341-
state.typeAheadController.text =
341+
state._typeAheadController.text =
342342
suggestion != null ? suggestion.toString() : '';
343343
}
344344
if (onSuggestionSelected != null) {
@@ -377,15 +377,22 @@ class _FormBuilderTypeAheadState<T>
377377
extends FormBuilderFieldState<FormBuilderTypeAhead<T>, T> {
378378
TextEditingController _typeAheadController;
379379

380-
TextEditingController get typeAheadController => _typeAheadController;
381-
382380
@override
383381
void initState() {
384382
super.initState();
385383
_typeAheadController = widget.controller ??
386384
TextEditingController(text: widget.initialValue?.toString());
387385
}
388386

387+
@override
388+
void dispose() {
389+
// Dispose the _typeAheadController when initState created it
390+
if (null == widget.controller) {
391+
_typeAheadController.dispose();
392+
}
393+
super.dispose();
394+
}
395+
389396
@override
390397
void reset() {
391398
super.reset();

0 commit comments

Comments
 (0)