Flutter: слушатель CupertinoPicker BottomSheet для onClose?

Я просматриваю галерею Flutter в поисках кода, связанного с CupertinoPicker.

Вот соответствующий фрагмент кода:

        child: new GestureDetector(
          // Blocks taps from propagating to the modal sheet and popping.
//          onTap: () { },
          child: new SafeArea(
            child: new CupertinoPicker(
              scrollController: scrollController,
              itemExtent: _kPickerItemHeight,
              backgroundColor: CupertinoColors.white,
              onSelectedItemChanged: (int index) {
                setState(() {
                  print(_selectedItemIndex);
                  _selectedItemIndex = index;
                });
              },
              children: new List<Widget>.generate(coolColorNames.length, (int index) {
                return new Center(child:
                  new Text(coolColorNames[index]),
                );
              }),
            ),
          ),
        ),

Теперь мне нужен обратный вызов / слушатель, когда закрывается CupertinoPicker, другими словами, когда пользователь сделал свой выбор и его выбор окончательный, мне нужно знать, каков его окончательный выбор.

Пример использования здесь заключается в том, что я хочу выполнить обратный вызов api на основе его окончательного выбора при закрытии нижнего листа.

На данный момент я могу получить значения только по мере того, как средство выбора запускается пользователем, поскольку есть только обратный вызов для onSelectedItemChanged. См. Гифку ниже.

введите описание изображения здесь

Я вижу, что у виджета BottomSheet есть обратный вызов onClosing: https://docs.flutter.io/flutter/material/BottomSheet-class.html

Но я не понимаю, как я могу получить его экземпляр для использования, поскольку образец Flutter Gallery вызывает нижний лист, используя следующий код, и нет способа получить нижний лист из кода:

      new GestureDetector(
        onTap: () async {
          await showModalBottomSheet<void>(
            context: context,
            builder: (BuildContext context) {
              return _buildBottomPicker();
            },
          );
        },
        child: _buildMenu(),
      ),

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

РЕДАКТИРОВАТЬ. На основе решения Реми я добавил код Navigator.of (context) .pop (value) в обратный вызов onTap. Однако CupertinoPicker не является постоянным, поэтому, если пользователь касается за пределами средства выбора, средство выбора закрывается и возвращается нулевое значение:

  Widget _buildBottomPicker() {
    final FixedExtentScrollController scrollController =
        new FixedExtentScrollController(initialItem: _selectedItemIndex);

    return new Container(
      height: _kPickerSheetHeight,
      color: CupertinoColors.white,
      child: new DefaultTextStyle(
        style: const TextStyle(
          color: CupertinoColors.black,
          fontSize: 22.0,
        ),
        child: new GestureDetector(
          // Blocks taps from propagating to the modal sheet and popping.
          onTap: () { Navigator.of(context).pop(_selectedItemIndex);},
          child: new SafeArea(
            child: new CupertinoPicker(
              scrollController: scrollController,
              itemExtent: _kPickerItemHeight,
              backgroundColor: CupertinoColors.white,
              onSelectedItemChanged: (int index) {
                setState(() {
//                  print(_selectedItemIndex);
//                  Navigator.of(context).pop(index);
                  _selectedItemIndex = index;
                });
              },
              children: new List<Widget>.generate(coolColorNames.length, (int index) {
                return new Center(child:
                  new Text(coolColorNames[index]),
                );
              }),
            ),
          ),
        ),
      ),
    );
  }

person Simon    schedule 17.04.2018    source источник
comment
Чтобы поймать касания за пределами средства выбора, используйте WillPopScope   -  person Pavel    schedule 24.09.2019


Ответы (4)


На самом деле это намного проще, чем вы думали.

showDialog и его аналоги (включая showModalBottomSheet) возвращают Future, который содержит результат. Итак, вы можете сделать следующее:

final selectedId = await showModalBottomSheet<int>(...);

Единственное требование - при добавлении модального окна / диалога / маршрута / чего-то еще, вы делаете Navigator.of(context).pop(value), чтобы отправить это значение.

person Rémi Rousselet    schedule 17.04.2018
comment
Реми, не могли бы вы сказать мне, где разместить код Navigator.of (context) .pop (value)? Я просто хочу, чтобы появилось средство выбора, пользователь должен выбрать значение, а затем, когда пользователь закрывает средство выбора, должен быть определен окончательный selectedId. Я не ухожу со страницы, поэтому я не понимаю, где я должен разместить навигатор. - person Simon; 17.04.2018
comment
Где-то внутри _buildBottomPicker. Но у меня нет этой части - person Rémi Rousselet; 17.04.2018
comment
модальные окна / диалоги - это маршруты, отображаемые поверх других маршрутов. Таким образом, они также создаются / удаляются с помощью Navigator - person Rémi Rousselet; 17.04.2018
comment
Я добавил код к вопросу и добавил часть кода Navigator в метод onTap, но для этого требуется, чтобы пользователь нажал на элемент, который он хочет выбрать из средства выбора, и навигатор не будет вызываться, если пользователь прокручивает средство выбора для его выбора, а затем закрывает средство выбора, щелкнув за пределами средства выбора в серой области экрана. Есть ли способ вызвать навигатор, когда сборщик закрывается пользователем, когда он касается серой части экрана? - person Simon; 17.04.2018
comment
Обычно пользователь хочет отменить свое действие при нажатии в тени, поэтому вы можете пересмотреть свое решение. Но вы можете использовать WillPopScope для onWillPop обратного вызова, хотя это не оптимально - person Rémi Rousselet; 17.04.2018
comment
Большое вам спасибо за это, искал, как вернуть значение, используя это будущее, возвращаемое функцией - person Ralph; 24.11.2019

нравится:

future.whenComplete(() => requestRefresh());
person wslaimin    schedule 06.09.2019

Как сказано в предыдущем ответе, необходимое значение возвращается в виде Future. Хотя это правда, мне не было очевидно, как это реализовать. В моем случае я хотел отфильтровать список задач по списку категорий на том же экране. Итак, используя ключ int, возвращаемый onSelectedItemChanged, я смог отфильтровать вот так ...

Widget _buildCategoryPicker(BuildContext context, _todoBloc) {
final FixedExtentScrollController scrollController =
    FixedExtentScrollController(initialItem: _selectedIndex);

return GestureDetector(
  onTap: () async {
    await showCupertinoModalPopup<String>(
      context: context,
      builder: (BuildContext context) {
        return _buildBottomPicker(    
          CupertinoPicker(
            (...)
            onSelectedItemChanged: (int index) {
              setState(() => _selectedIndex = index);
            },
            children: (...),
          ), // CupertinoPicker
        );
      },
    ); // await showCupertinoModalPopup

   // Filters the todoList.
    _todoBloc.filter.add(categories[_selectedIndex]);
   },
   child: _buildMenu(
     ...
   ),
 );
}

Вам не нужно устанавливать финал для вашего showCupertinoModalPopup или чего-то подобного ...

final selectedId = await showCupertinoModalPopup<String>(...);
person Jeff Frazier    schedule 23.02.2019

person    schedule
comment
Пожалуйста, предоставьте хотя бы основную информацию о коде, как он работает, как решает проблему ... - person Ruli; 19.01.2021