Проблемы FutureBuilder с загрузкой новых данных

Я пытаюсь реализовать кнопку воспроизведения, которая при нажатии будет воспроизводить выбранный аудиофайл из ListTile.

Вот код для получения пути к файлу из плитки списка:

Future getPath() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    var selectedCard = prefs.getInt('selectedIndex');
    var selectedPath = prefs.getString('selectedPath') ?? "";
    
    return selectedPath;
  }

Вот где встроена кнопка воспроизведения:

Widget build(BuildContext context) {
    final AudioPlayerController audioController =
        Provider.of<AudioPlayerController>(context, listen: true);

    return FutureBuilder(
        future: getPath(),
        builder: ((context, snapshot) {
          if (snapshot.hasData) {
            String path = snapshot.data.toString();
            var apc = AudioPlayerController(path.toString());
            return StreamProvider(
                initialData: apc.playingState ?? PlayState.ready,
                create: (_) => apc.playingStateStream,
                builder: (context, child) {
                  return Container(
                      child: FloatingActionButton(
                          heroTag: "Play",
                          backgroundColor: AppColor.AppLightBlueColor,
                          foregroundColor: AppColor.AppDarkBlueColor,
                          focusColor: AppColor.AppLightBlueColor,
                          splashColor: AppColor.AppLightBlueColor,
                          elevation: 0,
                          onPressed: () async {

                            if (apc.playingState == PlayState.playing) {
                              await apc.pause();
                            } else if (apc.playingState == PlayState.ready) {
                              await apc.setSource(path);
                              await apc.play();
                            } else if (apc.playingState == PlayState.paused) {
                              await apc.resume();
                            }
                          },
                          child: StreamBuilder(
                              stream: apc.playingStateStream,
                              builder: (context, snapshot) {
                                switch (snapshot.data) {
                                  case PlayState.playing:
                                    return const Icon(
                                      Icons.pause,
                                      size: 50,
                                    );
                                  case PlayState.paused:
                                    return const Icon(
                                      Icons.play_arrow_rounded,
                                      size: 50,
                                    );
                                  default:
                                    debugPrint("Default");
                                    // stderr.writeln(snapshot);
                                    return const Icon(
                                      Icons.play_arrow_rounded,
                                      size: 50,
                                    );
                                }
                              })));

Вот откуда берется путь в ListTile:

              shape: RoundedRectangleBorder(
                borderRadius: BorderRadius.circular(15.0),
              ),
              elevation: 0,
              child: Column(
                children: [
                  Container(
                      decoration: BoxDecoration(
                          borderRadius: borderRadius,
                          color: selectedIndex == index
                              ? AppColor.HighlightTileColor
                              : AppColor.AppLightBlueColor),
                      alignment: Alignment.center,
                      height: MediaQuery.of(context).size.height / 9,
                      child: ListTile(
                        selected: selectedIndex == index,
                        shape:
                            RoundedRectangleBorder(borderRadius: borderRadius),
                        onTap: () async {
                          SharedPreferences prefs =
                              await SharedPreferences.getInstance();
                          prefs.setInt('selectedIndex', index);
                          prefs.setString('selectedPath', file1);
                          setState(() {
                            selectedIndex = index;
                            // selectedPath = file1;
                          });

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

Пытался реализовать setState внутри функции getPath(), но это создало бесконечный цикл

Поделитесь кодом для вашего ListTile. Похоже, вы сохраняете индекс и путь к выбранному файлу в общих настройках. Итак, в методе onTap вашего ListTile сохраните индекс и путь выбранного элемента ListTile в общих настройках.

Subash 18.01.2023 19:30

@Subash Вы правы, моя проблема заключается в обновлении кнопки воспроизведения, чтобы воспроизвести правильную плитку из общего выбора предпочтений.

dstoner 18.01.2023 19:38

Извините, я должен был уточнить, вот как код был ранее. Futurebuilder до сих пор не обновляет @Subash

dstoner 18.01.2023 19:43

проблема может заключаться в том, что вы вызвали setState из виджета, содержащего ListTile, но это не привело к перестроению виджета с futureBuilder. Включите полные виджеты для лучшего понимания.

Subash 18.01.2023 19:49

Нет. future: getPath(), Не строить будущее как будущее: параметр в FutureBuilder. Посмотрите первые несколько абзацев документации FutureBuilder или посмотрите мое видео на youtu.be/sqE-J8YJnpg

Randal Schwartz 19.01.2023 06:01
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
5
59
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

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

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

late Future<String> _getPathFuture;

В функции initState присвойте ей начальное значение:

@override
void initState() {
  super.initState();
  _getPathFuture =  getPath();
}

Вы также должны изменить getPath, чтобы указать тип возвращаемого значения:

Future<String> getPath() async {
  SharedPreferences prefs = await SharedPreferences.getInstance();
  var selectedCard = prefs.getInt('selectedIndex');
  var selectedPath = prefs.getString('selectedPath') ?? "";
    
  return Future.value(selectedPath);
}

Теперь используйте эту переменную в FutureBuilder, здесь также установите тип String:

return FutureBuilder<String>(
  future: _getPathFuture,
  ...
);

После этого, как только вы убедитесь, что новые значения сохранены в общих настройках, вызовите setState, например:

setState(() {
  _getPathFuture = getPath();
});

Два комментария:

  1. Запись в общие настройки является асинхронной. Чтобы убедиться, что операция записи завершена, используйте await prefs.setInt....

  2. Поскольку чтение общих настроек выполняется синхронно, весь FutureBuilder нужен только потому, что SharedPreferences.getInstance() является асинхронным. Но нет никакой реальной причины читать его значение каждый раз, когда вы обращаетесь к нему. Вы можете сохранить это значение где-нибудь в своем приложении при его запуске, даже в функции main, сохранить его, чтобы вы могли получить к нему доступ из своего приложения.

Какой отличный ответ. респект вам

venir 18.01.2023 23:42

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