Вопрос или проблема
У меня есть два виджета Автозаполнения, и мне нужно, чтобы их текстовые поля располагались рядом. Кроме того, я хочу, чтобы optionsViewBuilder для обоих занимал всю ширину от начала первого текстового поля до конца второго. Для достижения этого я использовал CompositedTransformTarget и CompositedTransformFollower, и это работает идеально, как задумано.
Однако логика не работает идеально. Я сталкиваюсь с проблемой со вторым TextField: только область непосредственно под этим TextFieldSelectable и скроллируемая, в то время как область непосредственно под первым TextField невыбираемая и нескроллируемая для второго optionsViewBuilder.
Только область в черном квадрате доступна для выбора или прокрутки для второго виджета автозаполнения, как показано на этом фото
class MyWidget extends StatelessWidget {
const MyWidget({super.key});
@override
Widget build(BuildContext context) {
final LayerLink layerLink = LayerLink();
final List<String> fruits = [
'Apple',
'Banana',
'Cherry',
'Grapes',
];
final List<String> vegetables = [
'Carrot',
'Broccoli',
'Spinach',
'Potato',
];
return Column(
children: [
SizedBox(
height: 200,
),
CompositedTransformTarget(
link: layerLink,
child: Row(
children: [
Flexible(
child: Autocomplete(
optionsBuilder: (textEditingValue) => fruits,
optionsViewBuilder: (context, onSelected, options) =>
CompositedTransformFollower(
targetAnchor: Alignment.bottomLeft,
link: layerLink,
child: MyAutocompleteOptionsViewBuilder<String>(
options: options,
onSelected: onSelected,
),
),
),
),
Flexible(
child: Autocomplete(
optionsBuilder: (textEditingValue) => vegetables,
optionsViewBuilder: (context, onSelected, options) =>
CompositedTransformFollower(
targetAnchor: Alignment.bottomLeft,
link: layerLink,
child: MyAutocompleteOptionsViewBuilder<String>(
options: options,
onSelected: onSelected,
),
),
),
),
],
),
),
],
);
}
}
class MyAutocompleteOptionsViewBuilder<T> extends StatelessWidget {
const MyAutocompleteOptionsViewBuilder({
super.key,
required this.options,
required this.onSelected,
});
final Iterable<T> options;
final void Function(T) onSelected;
@override
Widget build(BuildContext context) {
return Align(
alignment: Alignment.topLeft,
child: Material(
elevation: 4.0,
color: Colors.white,
child: ConstrainedBox(
constraints: BoxConstraints(
maxHeight: MediaQuery.of(context).size.height * 0.2,
),
child: ListView.builder(
shrinkWrap: true,
padding: EdgeInsets.zero,
itemCount: options.length,
itemBuilder: (BuildContext context, int index) {
final option = options.elementAt(index);
return ListTile(
title: Text(option.toString()),
onTap: () {
onSelected(option);
},
);
},
),
),
),
);
}
}
Ответ или решение
Чтобы решить проблему с возможностью выбора опций в двух виджетах Autocomplete в Flutter, когда они располагаются бок о бок и используют CompositedTransformTarget
и CompositedTransformFollower
, нужно учесть несколько ключевых моментов, касающихся архитектуры виджетов и их взаимодействия.
Проблема
Из описания видно, что область, которая отвечает за отображение опций для второго текстового поля, ограничена только ниже самого поля. Это вызвано тем, что вы используете один и тот же LayerLink
для обоих виджетов, что приводит к тому, что CompositedTransformFollower
для второго виджета перекрывает (или аффектит) поведение первого, из-за чего область взаимодействия оказывается разделенной.
По сути, каждый из ваших CompositedTransformFollower
использует один и тот же LayerLink
. Это считается источником вашей проблемы. Для каждого текстового поля вам нужно создать отдельный LayerLink
, чтобы их выпадающие списки не конфликтовали между собой.
Решение
Создайте два отдельных LayerLink
— один для каждого виджета Autocomplete. Вот как вы можете изменить ваш код, чтобы это реализовать:
class MyWidget extends StatelessWidget {
const MyWidget({super.key});
@override
Widget build(BuildContext context) {
final LayerLink fruitLayerLink = LayerLink();
final LayerLink vegetableLayerLink = LayerLink();
final List<String> fruits = ['Apple', 'Banana', 'Cherry', 'Grapes'];
final List<String> vegetables = ['Carrot', 'Broccoli', 'Spinach', 'Potato'];
return Column(
children: [
SizedBox(height: 200),
Row(
children: [
Flexible(
child: Autocomplete(
optionsBuilder: (textEditingValue) => fruits,
optionsViewBuilder: (context, onSelected, options) => CompositedTransformFollower(
link: fruitLayerLink,
targetAnchor: Alignment.bottomLeft,
child: MyAutocompleteOptionsViewBuilder<String>(
options: options,
onSelected: onSelected,
),
),
),
),
Flexible(
child: Autocomplete(
optionsBuilder: (textEditingValue) => vegetables,
optionsViewBuilder: (context, onSelected, options) => CompositedTransformFollower(
link: vegetableLayerLink,
targetAnchor: Alignment.bottomLeft,
child: MyAutocompleteOptionsViewBuilder<String>(
options: options,
onSelected: onSelected,
),
),
),
),
],
),
],
);
}
}
class MyAutocompleteOptionsViewBuilder<T> extends StatelessWidget {
const MyAutocompleteOptionsViewBuilder({
super.key,
required this.options,
required this.onSelected,
});
final Iterable<T> options;
final void Function(T) onSelected;
@override
Widget build(BuildContext context) {
return Align(
alignment: Alignment.topLeft,
child: Material(
elevation: 4.0,
color: Colors.white,
child: ConstrainedBox(
constraints: BoxConstraints(maxHeight: MediaQuery.of(context).size.height * 0.2),
child: ListView.builder(
shrinkWrap: true,
padding: EdgeInsets.zero,
itemCount: options.length,
itemBuilder: (BuildContext context, int index) {
final option = options.elementAt(index);
return ListTile(
title: Text(option.toString()),
onTap: () {
onSelected(option);
},
);
},
),
),
),
);
}
}
Заключение
Теперь код использует отдельные LayerLink
для каждого из полей Autocomplete, что позволяет пользователю взаимодействовать с выпадающими списками без каких-либо проблем. Важно помнить, что разделение логики отображения между разными виджетами является ключевым моментом при использовании анимации и взаимодействий в Flutter.
Примечания
- Убедитесь, что версии ваших зависимостей и самой библиотеки Flutter актуальны.
- Тестируйте на разных устройствах, чтобы убедиться в корректности работы.
Следуя приведенным рекомендациям, вы сможете добиться полного взаимодействия с автозаполнением в обеих текстовых полях, обеспечивая отличное пользовательское взаимодействие и продуктивность вашего приложения.