Я пытаюсь реализовать раскрывающийся список в своем приложении Flutter так, чтобы при его открытии, если выбранный элемент не виден, он автоматически прокручивался, чтобы сделать этот элемент видимым. Однако пока мне это не удалось. Может ли кто-нибудь помочь мне с этим?
Что я пробовал до сих пор:
Использование ScrollController: я попытался прикрепить ScrollController к представлению раскрывающегося списка и вызвать animateTo для прокрутки к выбранному элементу, но действие прокрутки не сработало должным образом. Программная прокрутка: пробовал программную прокрутку с использованием различных методов, таких как ScrollController.jumpTo и ScrollController.animateTo, но мне не удалось сделать элемент видимым при открытии раскрывающегося списка. Добавление задержки: добавлены задержки с использованием Future.delayed перед попыткой прокрутки, думая, что список, возможно, еще не полностью отображен, но все равно безуспешно.
@override
Widget build(BuildContext context) {
if (widget.isLoading) {
return Center(child: CircularProgressIndicator());
}
return DropdownSearch<String>(
key: dropdownSearchKey,
enabled: widget.isEnabled,
items: widget.isEnabled ? itemLabels : [],
selectedItem: selectedItem.isEmpty || !widget.isEnabled ? widget.hint : selectedItem,
compareFn: (left, right) => left == right,
onChanged: (selectedItem) {
setState(() {
this.selectedItem = selectedItem ?? '';
});
String? key = reverseMap[selectedItem ?? ''];
if (key != null) {
widget.onChanged({'key': key, 'value': selectedItem ?? ''});
} else {
widget.onChanged({'key': '', 'value': ''});
}
},
dropdownButtonProps: DropdownButtonProps(
icon: Transform.scale(
scale: 1.6,
child: Icon(
Icons.arrow_drop_down,
color: widget.isEnabled ? Theme.of(context).primary : Colors.grey, //icon
),
),
),
popupProps: PopupProps.dialog(
// scrollbarProps: ,
showSelectedItems: true,
showSearchBox: widget.isEnabled,
//+++++++++++++++++++++++++++++++++++++
searchFieldProps: TextFieldProps(
controller: editTextController,
decoration: InputDecoration(
labelText: 'Search',
labelStyle: Theme.of(context).textTheme.headlineSmall!.copyWith(
letterSpacing: 0.0,
fontSize: 16.0,
color: widget.isEnabled ? Theme.of(context).lightCoral : Colors.grey,
fontWeight: FontWeight.w500,
),
contentPadding: EdgeInsets.only(left: 8),
suffixIcon: IconButton(
icon: Icon(Icons.clear),
onPressed: () {
if (editTextController.text.isEmpty) {
dropdownSearchKey.currentState?.closeDropDownSearch();
editTextController.clear();
_clearSelection();
widget.onClear();
} else {
editTextController.clear();
}
},
),
),
),
//+++++++++++++++++++++++++++++++++++++
dialogProps: DialogProps(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18.0),
),
backgroundColor: Colors.white,
),
itemBuilder: (context, item, isSelected) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8), //selecao
decoration: BoxDecoration(
color: isSelected ? Theme.of(context).secondary : Colors.transparent,
),
child: Text(
item,
style: Theme.of(context).textTheme.headlineSmall!.copyWith(
letterSpacing: 0.1,
fontSize: 18.0,
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
// color: Theme.of(context).primary,
color: isSelected ? Colors.white : Theme.of(context).primary,
),
),
);
},
onDismissed: () {
editTextController.clear();
},
containerBuilder: (context, popupWidget) {
return Column(
children: [
Expanded(child: popupWidget),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.white,
textStyle: TextStyle(
fontSize: 18,
),
padding: EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
),
onPressed: () {
dropdownSearchKey.currentState?.closeDropDownSearch();
editTextController.clear();
_clearSelection();
widget.onClear();
},
child: Text("Cancel"),
),
),
],
);
},
),
dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
filled: true,
fillColor: Colors.white,
labelStyle: TextStyle(
color: widget.isEnabled ? Theme.of(context).primary : Colors.grey,
),
),
baseStyle: Theme.of(context).textTheme.headlineSmall!.copyWith(
letterSpacing: 0.0,
fontSize: 16.0,
color: widget.isEnabled ? Theme.of(context).primary : Colors.grey,
),
),
);
}
У меня возникла аналогичная проблема пару дней назад. Это страница чата, которую необходимо прокрутить до конца после загрузки сообщения.
I first tried delay 50ms, I belive it's not enough for flutter rendering all Message widget, always stop in somewhere middle ofListview
.
when the delay is over 300ms, the scrollController.animateTo
always take me to the end of Listview
.
In Your situation it may works when your delay is longer.
void scroll2End() {
Future.delayed(const Duration(milliseconds: 400), () {
scrollController.animateTo(
scrollController.position.maxScrollExtent * 2,
duration: const Duration(milliseconds: 300),
curve: Curves.easeOut,
);
});
}
Я также узнал, что scrollController.animateTo.duration
повлияет на прокрутку до завершения действия. Слишком короткий duration
также приведет к остановке в середине ListView.builder
. У меня есть изображение и текст в Listview.builder
, с scrollController.animateTo.duration(seconds: 2)
все работает нормально.
вы приняли мой ответ, значит, отсрочка работает?