Printer
Printer

Reputation: 465

Flutter streamProvider not updating

When a user posts a comment. It stores the comment into the database (Mysql) but the streamProvider is not updating the listview for some reason. I'm able to access the provider and the commentData. But when I post a new comment. through add method. As I said, the listview does not display the new comment which has been sent to the database.

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);
    },
),

Upvotes: 1

Views: 394

Answers (1)

Maurits van Beusekom
Maurits van Beusekom

Reputation: 5989

Looks like you simply convert the out put of the getComments method to a stream. Meaning when you call the intStream method you will get a stream which only emits the results of the getComments method once. There is nothing letting the stream know that more items are added.

I can't guess what kind of database you are using to back this up and which "streaming" capabilities it has but someone needs to let your stream know that a new item has been added. One way to solve this would be something like this:

  1. Declare a StreamController which will act as stream and sink;
  2. In the intStream method, initialize the StreamController with the outcome of the getComments method and return the stream of the StreamController;
  3. After saving the comment to the database, add the comment to the StreamController.

In code this could look something like this:

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);
    }

  }
}

This above code is an example and should work. You might need to tweak it a little bit as mentioned in the comments that are part of the code example. And like I mentioned some databases directly support streaming (e.g. Firebase) which directly return the result of a query as a stream and will automatically add items to the stream when they are added to the database and match the query criteria. I couldn't deduce this from your code though.

Some reading material on working with the StreamController class can be found here:

EDIT:

I updated the logic in the add method to make sure the _streamController if not null.

EDIT 2:

Updated the code to return a stream emitting lists of comments, so we can better facilitate the ListView class.

Upvotes: 1

Related Questions