mirror of
https://github.com/flutter/flutter
synced 2024-10-02 14:34:22 +00:00
- Fixes _DropdownMenuState leaking text controller (#146571)
This commit is contained in:
parent
266cdf0b1e
commit
1002ce4b03
|
@ -434,13 +434,15 @@ class _DropdownMenuState<T> extends State<DropdownMenu<T>> {
|
||||||
double? leadingPadding;
|
double? leadingPadding;
|
||||||
bool _menuHasEnabledItem = false;
|
bool _menuHasEnabledItem = false;
|
||||||
TextEditingController? _localTextEditingController;
|
TextEditingController? _localTextEditingController;
|
||||||
TextEditingController get _textEditingController {
|
|
||||||
return widget.controller ?? (_localTextEditingController ??= TextEditingController());
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
if (widget.controller != null) {
|
||||||
|
_localTextEditingController = widget.controller;
|
||||||
|
} else {
|
||||||
|
_localTextEditingController = TextEditingController();
|
||||||
|
}
|
||||||
_enableFilter = widget.enableFilter;
|
_enableFilter = widget.enableFilter;
|
||||||
filteredEntries = widget.dropdownMenuEntries;
|
filteredEntries = widget.dropdownMenuEntries;
|
||||||
buttonItemKeys = List<GlobalKey>.generate(filteredEntries.length, (int index) => GlobalKey());
|
buttonItemKeys = List<GlobalKey>.generate(filteredEntries.length, (int index) => GlobalKey());
|
||||||
|
@ -448,7 +450,7 @@ class _DropdownMenuState<T> extends State<DropdownMenu<T>> {
|
||||||
|
|
||||||
final int index = filteredEntries.indexWhere((DropdownMenuEntry<T> entry) => entry.value == widget.initialSelection);
|
final int index = filteredEntries.indexWhere((DropdownMenuEntry<T> entry) => entry.value == widget.initialSelection);
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
_textEditingController.value = TextEditingValue(
|
_localTextEditingController?.value = TextEditingValue(
|
||||||
text: filteredEntries[index].label,
|
text: filteredEntries[index].label,
|
||||||
selection: TextSelection.collapsed(offset: filteredEntries[index].label.length),
|
selection: TextSelection.collapsed(offset: filteredEntries[index].label.length),
|
||||||
);
|
);
|
||||||
|
@ -458,8 +460,10 @@ class _DropdownMenuState<T> extends State<DropdownMenu<T>> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
if (widget.controller == null) {
|
||||||
_localTextEditingController?.dispose();
|
_localTextEditingController?.dispose();
|
||||||
_localTextEditingController = null;
|
_localTextEditingController = null;
|
||||||
|
}
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -469,8 +473,8 @@ class _DropdownMenuState<T> extends State<DropdownMenu<T>> {
|
||||||
if (oldWidget.controller != widget.controller) {
|
if (oldWidget.controller != widget.controller) {
|
||||||
if (widget.controller != null) {
|
if (widget.controller != null) {
|
||||||
_localTextEditingController?.dispose();
|
_localTextEditingController?.dispose();
|
||||||
_localTextEditingController = null;
|
|
||||||
}
|
}
|
||||||
|
_localTextEditingController = widget.controller ?? TextEditingController();
|
||||||
}
|
}
|
||||||
if (oldWidget.enableSearch != widget.enableSearch) {
|
if (oldWidget.enableSearch != widget.enableSearch) {
|
||||||
if (!widget.enableSearch) {
|
if (!widget.enableSearch) {
|
||||||
|
@ -489,7 +493,7 @@ class _DropdownMenuState<T> extends State<DropdownMenu<T>> {
|
||||||
if (oldWidget.initialSelection != widget.initialSelection) {
|
if (oldWidget.initialSelection != widget.initialSelection) {
|
||||||
final int index = filteredEntries.indexWhere((DropdownMenuEntry<T> entry) => entry.value == widget.initialSelection);
|
final int index = filteredEntries.indexWhere((DropdownMenuEntry<T> entry) => entry.value == widget.initialSelection);
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
_textEditingController.value = TextEditingValue(
|
_localTextEditingController?.value = TextEditingValue(
|
||||||
text: filteredEntries[index].label,
|
text: filteredEntries[index].label,
|
||||||
selection: TextSelection.collapsed(offset: filteredEntries[index].label.length),
|
selection: TextSelection.collapsed(offset: filteredEntries[index].label.length),
|
||||||
);
|
);
|
||||||
|
@ -601,7 +605,7 @@ class _DropdownMenuState<T> extends State<DropdownMenu<T>> {
|
||||||
trailingIcon: entry.trailingIcon,
|
trailingIcon: entry.trailingIcon,
|
||||||
onPressed: entry.enabled
|
onPressed: entry.enabled
|
||||||
? () {
|
? () {
|
||||||
_textEditingController.value = TextEditingValue(
|
_localTextEditingController?.value = TextEditingValue(
|
||||||
text: entry.label,
|
text: entry.label,
|
||||||
selection: TextSelection.collapsed(offset: entry.label.length),
|
selection: TextSelection.collapsed(offset: entry.label.length),
|
||||||
);
|
);
|
||||||
|
@ -630,7 +634,7 @@ class _DropdownMenuState<T> extends State<DropdownMenu<T>> {
|
||||||
currentHighlight = (currentHighlight! - 1) % filteredEntries.length;
|
currentHighlight = (currentHighlight! - 1) % filteredEntries.length;
|
||||||
}
|
}
|
||||||
final String currentLabel = filteredEntries[currentHighlight!].label;
|
final String currentLabel = filteredEntries[currentHighlight!].label;
|
||||||
_textEditingController.value = TextEditingValue(
|
_localTextEditingController?.value = TextEditingValue(
|
||||||
text: currentLabel,
|
text: currentLabel,
|
||||||
selection: TextSelection.collapsed(offset: currentLabel.length),
|
selection: TextSelection.collapsed(offset: currentLabel.length),
|
||||||
);
|
);
|
||||||
|
@ -649,7 +653,7 @@ class _DropdownMenuState<T> extends State<DropdownMenu<T>> {
|
||||||
currentHighlight = (currentHighlight! + 1) % filteredEntries.length;
|
currentHighlight = (currentHighlight! + 1) % filteredEntries.length;
|
||||||
}
|
}
|
||||||
final String currentLabel = filteredEntries[currentHighlight!].label;
|
final String currentLabel = filteredEntries[currentHighlight!].label;
|
||||||
_textEditingController.value = TextEditingValue(
|
_localTextEditingController?.value = TextEditingValue(
|
||||||
text: currentLabel,
|
text: currentLabel,
|
||||||
selection: TextSelection.collapsed(offset: currentLabel.length),
|
selection: TextSelection.collapsed(offset: currentLabel.length),
|
||||||
);
|
);
|
||||||
|
@ -661,7 +665,7 @@ class _DropdownMenuState<T> extends State<DropdownMenu<T>> {
|
||||||
currentHighlight = null;
|
currentHighlight = null;
|
||||||
controller.close();
|
controller.close();
|
||||||
} else { // close to open
|
} else { // close to open
|
||||||
if (_textEditingController.text.isNotEmpty) {
|
if (_localTextEditingController!.text.isNotEmpty) {
|
||||||
_enableFilter = false;
|
_enableFilter = false;
|
||||||
}
|
}
|
||||||
controller.open();
|
controller.open();
|
||||||
|
@ -677,14 +681,14 @@ class _DropdownMenuState<T> extends State<DropdownMenu<T>> {
|
||||||
final DropdownMenuThemeData defaults = _DropdownMenuDefaultsM3(context);
|
final DropdownMenuThemeData defaults = _DropdownMenuDefaultsM3(context);
|
||||||
|
|
||||||
if (_enableFilter) {
|
if (_enableFilter) {
|
||||||
filteredEntries = filter(widget.dropdownMenuEntries, _textEditingController);
|
filteredEntries = filter(widget.dropdownMenuEntries, _localTextEditingController!);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (widget.enableSearch) {
|
if (widget.enableSearch) {
|
||||||
if (widget.searchCallback != null) {
|
if (widget.searchCallback != null) {
|
||||||
currentHighlight = widget.searchCallback!.call(filteredEntries, _textEditingController.text);
|
currentHighlight = widget.searchCallback!.call(filteredEntries, _localTextEditingController!.text);
|
||||||
} else {
|
} else {
|
||||||
currentHighlight = search(filteredEntries, _textEditingController);
|
currentHighlight = search(filteredEntries, _localTextEditingController!);
|
||||||
}
|
}
|
||||||
if (currentHighlight != null) {
|
if (currentHighlight != null) {
|
||||||
scrollToHighlight();
|
scrollToHighlight();
|
||||||
|
@ -750,12 +754,12 @@ class _DropdownMenuState<T> extends State<DropdownMenu<T>> {
|
||||||
enableInteractiveSelection: canRequestFocus(),
|
enableInteractiveSelection: canRequestFocus(),
|
||||||
textAlignVertical: TextAlignVertical.center,
|
textAlignVertical: TextAlignVertical.center,
|
||||||
style: effectiveTextStyle,
|
style: effectiveTextStyle,
|
||||||
controller: _textEditingController,
|
controller: _localTextEditingController,
|
||||||
onEditingComplete: () {
|
onEditingComplete: () {
|
||||||
if (currentHighlight != null) {
|
if (currentHighlight != null) {
|
||||||
final DropdownMenuEntry<T> entry = filteredEntries[currentHighlight!];
|
final DropdownMenuEntry<T> entry = filteredEntries[currentHighlight!];
|
||||||
if (entry.enabled) {
|
if (entry.enabled) {
|
||||||
_textEditingController.value = TextEditingValue(
|
_localTextEditingController?.value = TextEditingValue(
|
||||||
text: entry.label,
|
text: entry.label,
|
||||||
selection: TextSelection.collapsed(offset: entry.label.length),
|
selection: TextSelection.collapsed(offset: entry.label.length),
|
||||||
);
|
);
|
||||||
|
|
|
@ -8,11 +8,8 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:leak_tracker_testing/leak_tracker_testing.dart';
|
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
// TODO(polina-c): _DropdownMenuState should not be used after disposal, https://github.com/flutter/flutter/issues/145622 [leaks-to-clean]
|
|
||||||
LeakTesting.settings = LeakTesting.settings.withIgnoredAll();
|
|
||||||
|
|
||||||
const String longText = 'one two three four five six seven eight nine ten eleven twelve';
|
const String longText = 'one two three four five six seven eight nine ten eleven twelve';
|
||||||
final List<DropdownMenuEntry<TestMenu>> menuChildren = <DropdownMenuEntry<TestMenu>>[];
|
final List<DropdownMenuEntry<TestMenu>> menuChildren = <DropdownMenuEntry<TestMenu>>[];
|
||||||
|
@ -2070,6 +2067,7 @@ void main() {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
final TextEditingController controller = TextEditingController();
|
final TextEditingController controller = TextEditingController();
|
||||||
|
addTearDown(controller.dispose);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
|
|
Loading…
Reference in a new issue