Flutter StreamProvider не обновляется

#flutter

#флаттер

Вопрос:

Когда пользователь публикует комментарий. Он сохраняет комментарий в базе данных (Mysql), но StreamProvider по какой-то причине не обновляет listview. Я могу получить доступ к поставщику и данным комментариев. Но когда я публикую новый комментарий. через метод добавления. Как я уже сказал, listview не отображает новый комментарий, который был отправлен в базу данных.

 class CommentModel {
    final int reportId;
    final String text;

    const CommentModel(this.reportId, this.text);
}
 
 class CommentProvider {
  Stream<List<CommentModel>> intStream(int reportId) {
    return Stream.fromFuture(getComments(reportId));
  }

  Future<List<CommentModel>> getComments(int reportId) async {
    final comments = await _fetchComments(reportId);
    final List<CommentModel> messages = List<CommentModel>();
    for (int i = 0; i < comments.length; i  ) {
      messages.add(CommentModel(reportId, comments[i]["text"])));
    }

    return messages;
  }

  Future<void> add(CommentModel data) async {
    await _postComment(data.reportId, data.text);
  }
}
 
 MultiProvider(
    providers: [
        ChangeNotifierProvider<CommentProvider>(create: (_) => CommentProvider()),
        StreamProvider<List<CommentModel>>(
                create: (_) => CommentProvider().intStream(int.tryParse(reportData["id"])),
                initialData: null,
        ),
    ],
    child: CardCommentWidget(
        reportId: int.tryParse(reportData["id"]),
    ),
),
 
 final commentData = Provider.of<List<CommentModel>>(context);
ListView.builder(
    key: PageStorageKey("commentsScroll"),
    shrinkWrap: true,
    itemCount: commentData.length,
    itemBuilder: (BuildContext context, int index) {
        final comment = commentData[index];
        return Text(comment.text);
    },
),
 

Ответ №1:

Похоже, вы просто преобразуете вывод getComments метода в поток. Это означает, что при вызове intStream метода вы получите поток, который выдает результаты getComments метода только один раз. Ничто не сообщает потоку, что добавлены дополнительные элементы.

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

  1. Объявите a StreamController , который будет действовать как поток и приемник;
  2. В intStream методе инициализируйте StreamController с результатом getComments метода и верните поток StreamController ;
  3. После сохранения комментария в базе данных добавьте комментарий в StreamController .

В коде это может выглядеть примерно так:

 class CommentProvider {
  final StreamController<List<CommentModel>> _streamController;

  Stream<List<CommentModel>> intStream(int reportId) {
    // Initialize a new instance of the StreamController
    // and emit each comment when someone starts listening
    // to the stream.
    if (_streamController == null) {
      _streamController = StreamController<List<CommentModel>>
        .broadcast(
          onListen: () async => await getComments(reportId),
          onErrror: (error) { 
            // Handle error here... 
          },
        );
    }

    return _streamController.stream;
  }

  Future<List<CommentModel>> getComments(int reportId) async {
    final comments = await _fetchComments(reportId);
    final List<CommentModel> messages = List<CommentModel>();
    for (int i = 0; i < comments.length; i  ) {
      messages.add(CommentModel(reportId, comments[i]["text"])));
    }

    return messages;
  }

  Future<void> add(CommentModel data) async {
    await _postComment(data.reportId, data.text);

    // Emit the updated list containing the added 
    // comment on the stream.
    if (_streamController != null) {
      final comments = await getComments(data.reportId);
      _streamController?.add(comments);
    }

  }
}
 

Приведенный выше код является примером и должен работать. Возможно, вам потребуется немного изменить его, как указано в комментариях, которые являются частью примера кода. И, как я уже упоминал, некоторые базы данных напрямую поддерживают потоковую передачу (например, Firebase), которые напрямую возвращают результат запроса в виде потока и автоматически добавляют элементы в поток, когда они добавляются в базу данных и соответствуют критериям запроса. Однако я не смог вывести это из вашего кода.

Некоторые материалы для чтения по работе с StreamController классом можно найти здесь:

Редактировать:

Я обновил логику в add методе, чтобы убедиться _streamController , что если нет null .

РЕДАКТИРОВАТЬ 2:

Обновлен код, чтобы возвращать поток, выдающий списки комментариев, чтобы мы могли лучше упростить ListView класс.

Комментарии:

1. Здравствуйте, спасибо, что помогли мне. Возникла некоторая проблема, например, add ошибка now null.

2. @printer, вы правы. Я забыл проверить _streamController , инициализирован ли он перед вызовом add функции на нем. Это может произойти при вызове add перед инициализацией StreamController с использованием intStream метода. В этих случаях мы можем просто игнорировать вызов add _streamController , поскольку мы знаем, что никто все равно не слушает.

3. Ура, чувак, теперь нет нулевой ошибки. Но у меня есть вопрос, поскольку initStream не возвращает список комментариев. Как я могу получить доступ ко всем комментариям в моем Listview.builder . (Я использую Mysql в качестве базы данных)

4. @Printer, вы правы, в этом случае было бы лучше вернуть список комментариев, поэтому я обновил пример кода, чтобы возвращать список вместо отдельных элементов. Единственным недостатком сейчас является то, что при добавлении элемента вы снова будете извлекать комментарии из базы данных.

5. Приветствую, приятель, я думаю, у меня есть четкое представление о том, как это работает сейчас 🙂