StreamBuilder updates ListView but observe duplicate entries

20 hours ago 1
ARTICLE AD BOX

My use case is that on pressing a button on the UI, an object starts emitting (finite) data which I want it to be appended in a ListView until data contains a token saying it should clear all contents.

To achieve this I have the object utilising a Stream for sending data, which I subscribe to it in the UI via a StreamBuilder wrapping a ListView.

The problem is that my test code below shows duplicate entries into the ListView.

For testing purposes the code below contains also a Text() to be controlled by another StreamBuilder. I also subscribe to the Stream and logging to the console when data is received. Neither of these have duplicates.

import 'package:flutter/material.dart'; import 'dart:async'; Stream<String> resultsStream() async* { for (int i = 1; i <= 10; i++) { await Future.delayed(Duration(seconds: 1)); yield "result: ${i}"; } } Stream<String> messagesStream() async* { for (int i = 1; i <= 10; i++) { await Future.delayed(Duration(seconds: 1)); yield "message: ${i}"; } } void main() { StreamSubscription<String>? subscription = messagesStream().listen( (data) { print('subscription/console data: $data'); }, onError: (error) { print('Error: $error'); }, onDone: () { print('Stream is done (closed)!'); }, cancelOnError: false, // Don't cancel subscription if an error occurs ); runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', home: const Scaffold(body: SearchResultsWidget(passdata: {})), ); } } class SearchResultsWidget extends StatefulWidget { final Map<String, dynamic> passdata; const SearchResultsWidget({super.key, required this.passdata}); @override State<SearchResultsWidget> createState() => _SearchResultsWidgetState(); } class _SearchResultsWidgetState extends State<SearchResultsWidget> { @override Widget build(BuildContext context) { return Column( children: [ const SizedBox(height: 35.0), Text("messages"), Container( child: SizedBox( height: 100, child: StreamBuilder( stream: messagesStream(), builder: (context, snapshot) { if (!snapshot.hasData) { return Center(child: CircularProgressIndicator()); } else { print("text/message got data: ${snapshot.data}"); return Text(snapshot.data!); } }, ), ), ), /// list view Text("rsults"), Container( child: SizedBox( height: 100, child: StreamBuilder( stream: resultsStream(), builder: (context, snapshot) { if (!snapshot.hasData) { return Center(child: CircularProgressIndicator()); } else { return ListView.builder( itemBuilder: (context, index) { print("listview/results got data: ${snapshot.data}"); return Text(snapshot.data.toString()); }, ); } }, ), ), ), ], ); } }

I must be doing something wrong with the ListView, can anyone offer advice on how to avoid the duplicates in it?

I also have a doubt in that, AFAIU, the StreamBuilder recreates the ListView each time. Is this the most efficient way to do this?

Read Entire Article