Я не могу понять, что не так с моим кодом. Что бы я ни пытался, я все равно получаю эту ошибку при попытке подтвердить свой выбор в виджете с множественным выбором. Вот мой код.
class _ParticipantsToSelectState extends State<ParticipantsToSelect> {
static List<Animal> _animals = [
Animal(id: 1, name: "Lion"),
Animal(id: 2, name: "Flamingo"),
Animal(id: 3, name: "Hippo"),
Animal(id: 4, name: "Horse"),
Animal(id: 5, name: "Tiger"),
Animal(id: 6, name: "Penguin"),
Animal(id: 7, name: "Spider"),
Animal(id: 8, name: "Snake"),
Animal(id: 9, name: "Bear"),
Animal(id: 10, name: "Beaver"),
];
final _items = _animals
.map((animal) => MultiSelectItem<Animal>(animal, animal.name))
.toList();
List<Animal?>? _selectedAnimals3 = [];
final _multiSelectKey = GlobalKey<FormFieldState<List<Animal>?>>(); // Update here
@override
void initState() {
_selectedAnimals3 = _animals;
super.initState();
}
@override
Widget build(BuildContext context) {
return Expanded(
child: MultiSelectBottomSheetField<Animal>(
key: _multiSelectKey,
initialChildSize: 0.7,
maxChildSize: 0.95,
title: Text("Animals"),
buttonText: Text("Favorite Animals"),
items: _items,
searchable: true,
validator: (values) {
if (values == null || values.isEmpty) {
return "Required";
}
List<String> names = values.map((e) => e!.name).toList(); // Update here
if (names.contains("Frog")) {
return "Frogs are weird!";
}
return null;
},
onConfirm: (List<Animal?>? values) { // Update here
setState(() {
_selectedAnimals3 = values;
});
_multiSelectKey.currentState?.validate();
},
chipDisplay: MultiSelectChipDisplay(
onTap: (item) {
setState(() {
_selectedAnimals3!.remove(item);
});
_multiSelectKey.currentState?.validate();
},
),
),
);
}
}
Я попытался передать _selectedAnimals3, а также «значения», а затем инициализировать его с помощью _animals, но получил ту же ошибку. Мне нужно, чтобы _selectedAnimals3 заполнялся выбранными элементами при подтверждении.
Кроме того, почему вы объявили _selectedAnimals3 ссылкой, допускающей значение NULL? Он инициализируется ненулевым значением, и вашему методу onConfirm не нужно заменять всю коллекцию, вы можете просто очистить ее.
Итак, List<Animal?> означает список объектов типа Animal, которые могут иметь нулевые значения, тогда как List<Animal>? означает допускающий значение NULL список объектов типа Animal, который может быть либо списком объектов типа Animal, либо нулевым значением.
Хорошо, так почему вы говорите, что не понимаете, что не так с вашим кодом?
На самом деле я начал с List<Animal> _selectedAnimals3 = []; и я думаю, что это было неправильно? Можете ли вы предложить, какие должны быть строки для объявления и onConfirm ?
Это было мое начальное объявление и onConfirm, которые выдавали ту же ошибку. List<Animal> _selectedAnimals3 = []; onConfirm: (значения) { setState(() { _selectedAnimals3 = values .where((element) => element != null) .toList() .cast<Animal>(); }); _multiSelectKey.currentState?.validate(); },
Вашему методу onConfirm не нужно перезаписывать _selectedAnimals3 в аргументах null — просто очистите существующий список, если values равно null.
Спасибо за быстрый ответ, но не могли бы вы предложить код для очистки существующего списка?
Э... .clear() ?
Извините, я не понимаю. Если я использую clear(), он очистит весь список, не так ли? Я хочу, чтобы _selectedAnimals3 сохранял выбранные элементы.
Так что же тогда onConfirm должно быть связано с values?
Он должен заполнить список _selectedAnimals3 выбранными значениями.
Это давно известная проблема в этой библиотеке, чтобы избежать этой проблемы, вы должны сделать параметр типа MultiSelectBottomSheetField<Animal?> обнуляемым.
Ваш код должен выглядеть следующим образом
class _ParticipantsToSelectState extends State<ParticipantsToSelect> {
static final List<Animal> _animals = [
Animal(id: 1, name: "Lion"),
Animal(id: 2, name: "Flamingo"),
Animal(id: 3, name: "Hippo"),
Animal(id: 4, name: "Horse"),
Animal(id: 5, name: "Tiger"),
Animal(id: 6, name: "Penguin"),
Animal(id: 7, name: "Spider"),
Animal(id: 8, name: "Snake"),
Animal(id: 9, name: "Bear"),
Animal(id: 10, name: "Beaver"),
];
final _items = _animals.map((animal) => MultiSelectItem<Animal>(animal, animal.name)).toList();
List<Animal?> _selectedAnimals3 = [];
final _multiSelectKey = GlobalKey<FormFieldState<List<Animal>>>(); // Update here
@override
void initState() {
_selectedAnimals3 = _animals;
super.initState();
}
@override
Widget build(BuildContext context) {
return Expanded(
child: MultiSelectBottomSheetField<Animal?>(
key: _multiSelectKey,
initialChildSize: 0.7,
maxChildSize: 0.95,
title: const Text("Animals"),
buttonText: const Text("Favorite Animals"),
items: _items,
searchable: true,
validator: (values) {
if (values == null || values.isEmpty) {
return "Required";
}
List<String> names = values.map((e) => e!.name).toList(); // Update here
if (names.contains("Frog")) {
return "Frogs are weird!";
}
return null;
},
onConfirm: (List<Animal?> values) {
// Update here
setState(() {
_selectedAnimals3 = values;
});
_multiSelectKey.currentState?.validate();
},
chipDisplay: MultiSelectChipDisplay(
onTap: (item) {
setState(() {
_selectedAnimals3.remove(item);
});
_multiSelectKey.currentState?.validate();
},
),
),
);
}
}
Я предлагаю вам найти другую библиотеку для достижения ваших требований.
Отредактировано
Я разветвил библиотеку, чтобы решить эту проблему. Я видел, что на рассмотрении находится несколько PR, похоже, что разработчик неактивен.
Добавьте изменение pubspecs.yaml со следующей строкой
multi_select_flutter:
git: https://github.com/alexsunderlion/multi_select_flutter.git
И ваш код должен быть следующим.
class _ParticipantsToSelectState extends State<ParticipantsToSelect> {
static final List<Animal> _animals = [
Animal(id: 1, name: "Lion"),
Animal(id: 2, name: "Flamingo"),
Animal(id: 3, name: "Hippo"),
Animal(id: 4, name: "Horse"),
Animal(id: 5, name: "Tiger"),
Animal(id: 6, name: "Penguin"),
Animal(id: 7, name: "Spider"),
Animal(id: 8, name: "Snake"),
Animal(id: 9, name: "Bear"),
Animal(id: 10, name: "Beaver"),
];
final _items = _animals.map((animal) => MultiSelectItem<Animal>(animal, animal.name)).toList();
List<Animal> _selectedAnimals3 = [];
final _multiSelectKey = GlobalKey<FormFieldState<List<Animal>>>(); // Update here
@override
void initState() {
_selectedAnimals3 = _animals;
super.initState();
}
@override
Widget build(BuildContext context) {
return Expanded(
child: MultiSelectBottomSheetField<Animal>(
key: _multiSelectKey,
initialChildSize: 0.7,
maxChildSize: 0.95,
title: const Text("Animals"),
buttonText: const Text("Favorite Animals"),
items: _items,
searchable: true,
validator: (values) {
if (values == null || values.isEmpty) {
return "Required";
}
List<String> names = values.map((e) => e.name).toList(); // Update here
if (names.contains("Frog")) {
return "Frogs are weird!";
}
return null;
},
onConfirm: (List<Animal> values) {
// Update here
setState(() {
_selectedAnimals3 = values;
});
_multiSelectKey.currentState?.validate();
},
chipDisplay: MultiSelectChipDisplay(
onTap: (item) {
setState(() {
_selectedAnimals3.remove(item);
});
_multiSelectKey.currentState?.validate();
},
),
),
);
}
}
Спасибо, Алекс. Я подтверждаю, что это работает, когда параметр типа MultiSelectBottomSheetField<Animal?> принимает значение NULL. Не могли бы вы предложить другую библиотеку, которая предлагает раскрывающийся список множественного выбора с функцией поиска из коробки?
Я разветвил библиотеку и изменил ее, чтобы решить проблему null safety. Пожалуйста, просмотрите мои изменения в ответе. Вы можете использовать мою вилку или вилку из моего репо для вашего использования.
Спасибо Алекс за исправление проблемы в оригинальной библиотеке. Я переключился на ваш и обновил свой код в соответствии с вашим предложением в части «Отредактировано», и все работает так, как ожидалось. Хорошая работа !
Я надеюсь, что этот ответ может быть принят 😜.
Позвольте спросить вас, как вы думаете, в чем разница между List<Animal?> и List<Animal>??