Как я могу гарантировать, что элементы внутри выпадающего меню могут быть выбраны только один раз во Flutter?

class CardWidget extends StatefulWidget {
  final String title;
  final Color color;
  final IconData icon;
  const CardWidget({
    super.key,
    required this.title,
    required this.color,
    required this.icon,
  });

  @override
  State<CardWidget> createState() => _CardWidgetState();
}

class _CardWidgetState extends State<CardWidget> {    

  final List<String> items = [
        'Key 1',
        'Key 2',
        'Key 3',
        'Key 4',
      ];
      String? selectedValue;
 @override
  Widget build(BuildContext context) {
         return SizedBox(
          height: 200,
          width: 200,
          child: Padding(
            padding: const EdgeInsets.all(8.0),
            child: Card(
              elevation: 10,
              shape: RoundedRectangleBorder(
                borderRadius: BorderRadius.circular(20.0),
              ),
              color: widget.color,
              child: Column(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                children: [
                  Icon(
                    widget.icon,
                    color: Colors.white,
                    size: 32,
                  ),
                  Center(
                    child: Text(
                      widget.title,
                      style: GoogleFonts.poppins(
                          color: Colors.white,
                          fontSize: 14,
                          fontWeight: FontWeight.bold),
                    ),
                  ),
                  DropdownButtonHideUnderline(
                    child: DropdownButton2(
                      isExpanded: true,
                      hint: Row(
                        children: [
                          Expanded(
                            child: Center(
                              child: Text(
                                'Select a Key',
                                style: dropdownTitle,
                                overflow: TextOverflow.ellipsis,
                              ),
                            ),
                          ),
                        ],
                      ),
                      items: items
                          .map((item) => DropdownMenuItem<String>(
                                value: item,
                                child: Center(
                                  child: Text(
                                    item,
                                    style: dropdownItems,
                                    overflow: TextOverflow.ellipsis,
                                  ),
                                ),
                              ))
                          .toList(),
                      value: selectedValue,
                      onChanged: (value) {
                        setState(() {
                          selectedValue = value as String;
                        });
                      },
                      icon: const Icon(
                        Icons.arrow_forward_ios_outlined,
                      ),
                      iconSize: 20,
                      iconEnabledColor: Color.fromARGB(255, 31, 10, 88),
                      iconDisabledColor: Colors.grey,
                      buttonHeight: 40,
                      buttonWidth: 135,
                      buttonPadding: const EdgeInsets.only(
                        left: 15,
                        right: 15,
                      ),
                      buttonDecoration: BoxDecoration(
                        borderRadius: BorderRadius.circular(14),
                        border: Border.all(
                          color: Colors.black26,
                        ),
                        color: Color.fromARGB(255, 255, 255, 255),
                      ),
                      buttonElevation: 2,
                      itemHeight: 40,
                      itemPadding: const EdgeInsets.only(left: 14, right: 14),
                      dropdownMaxHeight: 200,
                      dropdownWidth: 200,
                      dropdownPadding: null,
                      dropdownDecoration: BoxDecoration(
                        borderRadius: BorderRadius.circular(10),
                        color: Colors.white,
                      ),
                      dropdownElevation: 8,
                      scrollbarRadius: const Radius.circular(10),
                      scrollbarThickness: 6,
                      scrollbarAlwaysShow: true,
                      offset: const Offset(-20, 0),
                    ),
                  ),

CardWidget, который я создал.

return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: const [
              CardWidget(
                      title: 'Mute',
                      color: Color.fromARGB(255, 211, 38, 25),
                      icon: Icons.volume_off_outlined,
                    ),
                CardWidget(
                      title: 'Mute',
                      color: Color.fromARGB(255, 211, 38, 25),
                      icon: Icons.volume_off_outlined,
                    ),
              CardWidget(
                      title: 'Mute',
                      color: Color.fromARGB(255, 211, 38, 25),
                      icon: Icons.volume_off_outlined,
                    ),
               CardWidget(
                      title: 'Mute',
                      color: Color.fromARGB(255, 211, 38, 25),
                      icon: Icons.volume_off_outlined,
                    ),
              ],
            ),

Домашняя страница, на которой я использую CardWidget.

Я могу вызвать свой список и выбрать элементы внутри списка.

Однако, как видно на изображении, он может быть активным, даже если во всех раскрывающихся списках выбрана «клавиша 1». Я имею дело, например, с тем, что «ключ 1» выбран в первом раскрывающемся списке, если «ключ 1» выбран во втором раскрывающемся списке, он просто покажет его там. Во втором раскрывающемся списке не должно быть написано «ключ 1» под меню, оно должно быть написано в последнем выбранном. Оставьте первое поле пустым.

вы используете какой-то пакет для DropdownButtonWidget

Yeasin Sheikh 17.11.2022 13:01

Я использую pub.dev/packages/dropdown_button2 этот пакет.

ckot 17.11.2022 13:09

Можете ли вы включить образец тела лесов со всеми. кажется его не хватает

Yeasin Sheikh 17.11.2022 13:15

Я добавил весь свой код.

ckot 17.11.2022 13:44

На эшафоте будет 5 дропдаунов?

Yeasin Sheikh 17.11.2022 13:48

Будет 4 штуки. Я показал, как определил один виджет CardWidget на главной странице, чтобы не писать тот же код. Как и на втором фото, есть дизайн карты с 4 выпадающими меню рядом. Есть выпадающие с ключевыми номерами от 1 до 4 каждый.

ckot 17.11.2022 13:51

Просто добавьте CardWidget 4 раза, и он отобразит изображение на второй фотографии.

ckot 17.11.2022 13:52

Я снова отредактировал свой код.

ckot 17.11.2022 13:54

Хорошо, понял, позвольте мне посмотреть, что я могу сделать для вас

Yeasin Sheikh 17.11.2022 13:55

Вопрос супер крутой

Yeasin Sheikh 17.11.2022 14:15

Я пытался в течение 3-4 часов, но я не мог найти ответ.

ckot 17.11.2022 14:18

Я начал с карты, но это было немного хаотично для понимания другими, а затем преобразовать в класс модели, чтобы сделать его читабельным.

Yeasin Sheikh 17.11.2022 14:21
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
12
58
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Удален выбранный элемент из раскрывающегося списка других: Я использую Valunotifer для упрощения фрагмента, и логика

onChanged: (value) {
    final index =
        cardsNotifier.value.indexWhere((element) => element.id == id);

    for (int i = 0; i < cardsNotifier.value.length; i++) {
      if (index != i && cardsNotifier.value[i].selectedItem == value) {
        cardsNotifier.value[i].selectedItem = null;
      }
    }
    cardsNotifier.value[index].selectedItem = value;

    cardsNotifier.value = cardsNotifier.value.toList();
  },

Тестовый виджет

class FA extends StatefulWidget {
  const FA({super.key});

  @override
  State<FA> createState() => _FAState();
}

class CardHelper {
  final int id;
  String? selectedItem;
  CardHelper({
    required this.id,
    this.selectedItem,
  });
}

ValueNotifier<List<CardHelper>> cardsNotifier = ValueNotifier(List.generate(
    4,
    (index) => CardHelper(
          id: index,
        )));

class _FAState extends State<FA> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Row(children: [
        for (int i = 0; i < cardsNotifier.value.length; i++)
          CardWidget(
            title: 'Mute',
            id: cardsNotifier.value[i].id,
          ),
      ]),
    );
  }
}

class CardWidget extends StatelessWidget {
  final String title;

  final int id;

  const CardWidget({
    super.key,
    required this.id,
    required this.title,
  });

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: 200,
      width: 200,
      child: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Card(
          elevation: 10,
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(20.0),
          ),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: [
              itemBuilder(),
            ],
          ),
        ),
      ),
    );
  }

  ValueListenableBuilder<List<CardHelper>> itemBuilder() {
    return ValueListenableBuilder<List<CardHelper>>(
      valueListenable: cardsNotifier,
      builder: (context, data, child) => DropdownButtonHideUnderline(
        child: DropdownButton2(
          isExpanded: true,
          hint: Row(
            children: [
              Expanded(
                child: Center(
                  child: Text(
                    'Select a Key',
                    overflow: TextOverflow.ellipsis,
                  ),
                ),
              ),
            ],
          ),
          items: ['Key 1', 'Key 2', 'Key 3', 'Key 4']
              .map((item) => DropdownMenuItem<String>(
                    value: item,
                    child: Center(
                      child: Text(
                        item,
                        overflow: TextOverflow.ellipsis,
                      ),
                    ),
                  ))
              .toList(),
          value: data[id].selectedItem,
          onChanged: (value) {
            final index =
                cardsNotifier.value.indexWhere((element) => element.id == id);

            for (int i = 0; i < cardsNotifier.value.length; i++) {
              if (index != i && cardsNotifier.value[i].selectedItem == value) {
                cardsNotifier.value[i].selectedItem = null;
              }
            }
            cardsNotifier.value[index].selectedItem = value;

            cardsNotifier.value = cardsNotifier.value.toList();
          },
          icon: const Icon(
            Icons.arrow_forward_ios_outlined,
          ),
          iconSize: 20,
        ),
      ),
    );
  }
}

Удалить выбранный элемент из выпадающего списка

Я использую вспомогательный класс для упрощения

class CardHelper {
  final int id;
  final List<String> items;
  final String? selectedItem;

  CardHelper({
    required this.id,
    required this.items,
    this.selectedItem,
  });
}

А виджет CardWidget принимает данные в качестве входных данных и обеспечивает обратный вызов с выбранным элементом.

class CardWidget extends StatefulWidget {
  final List<String> items;
  final Function(String?) selectedItemCallback;
   .....
  const CardWidget({
    super.key,
    required this.items,

И onChanged будет

items: widget.items
    .map((item) => DropdownMenuItem<String>(
          value: item,
          child: Center(
            child: Text(
              item,
              overflow: TextOverflow.ellipsis,
            ),
          ),
        ))
    .toList(),
value: selectedValue,
onChanged: (value) {
  widget.selectedItemCallback(value);

Теперь для родительского виджета мы создадим список для карт

  final cards = List.generate(
      4,
      (index) =>
          CardHelper(id: index, items: ['Key 1', 'Key 2', 'Key 3', 'Key 4']));

И остальная логика лежит

for (int i = 0; i < cards.length; i++)
    CardWidget(
      title: 'Mute',
      color: Color.fromARGB(255, 211, 38, 25),
      icon: Icons.volume_off_outlined,
      items: cards[i].items,
      selectedItemCallback: (p0) {
        for (int j = 0; j < cards.length; j++) {
          if (i == j) continue;
          cards[j].items.remove(p0);
        }
        setState(() {});
      },
    ),

Воспроизвести виджет


void main(List<String> args) {
  runApp(MaterialApp(home: FA()));
}

class FA extends StatefulWidget {
  const FA({super.key});

  @override
  State<FA> createState() => _FAState();
}

class CardHelper {
  final int id;
  final List<String> items;
  final String? selectedItem;

  CardHelper({
    required this.id,
    required this.items,
    this.selectedItem,
  });
}

class _FAState extends State<FA> {
  final cards = List.generate(
      4,
      (index) =>
          CardHelper(id: index, items: ['Key 1', 'Key 2', 'Key 3', 'Key 4']));

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Row(
        children: [
          for (int i = 0; i < cards.length; i++)
            CardWidget(
              title: 'Mute',
              color: Color.fromARGB(255, 211, 38, 25),
              icon: Icons.volume_off_outlined,
              items: cards[i].items,
              selectedItemCallback: (p0) {
                for (int j = 0; j < cards.length; j++) {
                  if (i == j) continue;
                  cards[j].items.remove(p0);
                }
                setState(() {});
              },
            ),
        ],
      ),
    );
  }
}

class CardWidget extends StatefulWidget {
  final List<String> items;
  final String title;
  final Color color;
  final IconData icon;
  final Function(String?) selectedItemCallback;
  const CardWidget({
    super.key,
    required this.items,
    required this.title,
    required this.color,
    required this.icon,
    required this.selectedItemCallback,
  });

  @override
  State<CardWidget> createState() => _CardWidgetState();
}

class _CardWidgetState extends State<CardWidget> {
  String? selectedValue;
  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: 200,
      width: 200,
      child: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Card(
          elevation: 10,
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(20.0),
          ),
          color: widget.color,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: [
              Icon(
                widget.icon,
                color: Colors.white,
                size: 32,
              ),
              Center(
                child: Text(
                  widget.title,
                ),
              ),
              DropdownButtonHideUnderline(
                child: DropdownButton2(
                  isExpanded: true,
                  hint: Row(
                    children: [
                      Expanded(
                        child: Center(
                          child: Text(
                            'Select a Key',
                            overflow: TextOverflow.ellipsis,
                          ),
                        ),
                      ),
                    ],
                  ),
                  items: widget.items
                      .map((item) => DropdownMenuItem<String>(
                            value: item,
                            child: Center(
                              child: Text(
                                item,
                                overflow: TextOverflow.ellipsis,
                              ),
                            ),
                          ))
                      .toList(),
                  value: selectedValue,
                  onChanged: (value) {
                    widget.selectedItemCallback(value);
                    setState(() {
                      selectedValue = value as String;
                    });
                  },
                  icon: const Icon(
                    Icons.arrow_forward_ios_outlined,
                  ),
                  iconSize: 20,
                  iconEnabledColor: Color.fromARGB(255, 31, 10, 88),
                  iconDisabledColor: Colors.grey,
                  buttonHeight: 40,
                  buttonWidth: 135,
                  buttonPadding: const EdgeInsets.only(
                    left: 15,
                    right: 15,
                  ),
                  buttonDecoration: BoxDecoration(
                    borderRadius: BorderRadius.circular(14),
                    border: Border.all(
                      color: Colors.black26,
                    ),
                    color: Color.fromARGB(255, 255, 255, 255),
                  ),
                  buttonElevation: 2,
                  itemHeight: 40,
                  itemPadding: const EdgeInsets.only(left: 14, right: 14),
                  dropdownMaxHeight: 200,
                  dropdownWidth: 200,
                  dropdownPadding: null,
                  dropdownDecoration: BoxDecoration(
                    borderRadius: BorderRadius.circular(10),
                    color: Colors.white,
                  ),
                  dropdownElevation: 8,
                  scrollbarRadius: const Radius.circular(10),
                  scrollbarThickness: 6,
                  scrollbarAlwaysShow: true,
                  offset: const Offset(-20, 0),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Также есть опция enabled на DropdownMenuItem, которая может отключать состояние с отображением элементов.

Yeasin Sheikh 17.11.2022 14:24

Очень удачное решение. Большое спасибо. Но где-то есть проблема. Например, допустим, для первой карты выбран ключ 1. Ключ 1 должен появиться на второй карточке. Если пользователь выбирает ключ 1 для второй карты, ключ 1 теперь должен быть записан под второй картой. Но с этим кодом я могу выбрать только 1 ключ один раз.

ckot 17.11.2022 14:30

вы хотите переопределить значение, если пользователь выбирает 1-й ключ во втором поле, которое выбирается?

Yeasin Sheikh 17.11.2022 16:32

Нет, я хочу, чтобы все значения были действительны во всех выпадающих списках. Если пользователь выбирает «ключ 1» на первой карточке и хочет выбрать «ключ 1» на второй карточке, ключ 1 на первой карточке следует удалить вместо текста по умолчанию «выберите ключ» или он должен быть пустым.

ckot 17.11.2022 16:54

хорошо, последний выбранный будет иметь приоритет, я думаю, у меня есть?

Yeasin Sheikh 17.11.2022 16:56

я скоро до тебя доберусь

Yeasin Sheikh 17.11.2022 17:17

Да, вы правильно думаете. Я жду вашего ответа. Большое спасибо.

ckot 17.11.2022 21:46

Обновлено, занят финалом семестра 😅

Yeasin Sheikh 18.11.2022 07:48

Хорошо. Желаю вам успехов на экзаменах.

ckot 18.11.2022 21:54

тестовая часть обновления (вверху), которая должна ответить на ваш пост

Yeasin Sheikh 19.11.2022 05:47

Большое спасибо, это именно то решение, которое я хотел. Я просто хочу кое-что спросить. Например, поскольку заголовок созданного вами cardWidget находится в цикле for, он присваивает всем этим карточкам заголовок «Mute». Но названия каждой из этих карт я дам отдельно. Как я могу это сделать?

ckot 19.11.2022 08:05

Вы можете составить список, а затем передать как muteList[i], или лучше создать поле в классе модели

Yeasin Sheikh 19.11.2022 08:09

Другие вопросы по теме