The problem is very simple but I am unable to find a proper solution. I have a search screen and here is what I want to do.
Text
widget 'Your results will appear here'.CircularProgressIndicator
.ListView
or Text
saying 'Found nothing'.CircularProgressIndicator
again.How can I achieve this behavior using Stream
? Following is my code so far:
Widgets
StreamBuilder<List<Model>>(
stream: bloc.searchStream,
builder: (context, snapshot) {
if (snapshot.hasError) {
return Center(
child: Text(
'Error occurred'
),
);
}
if (!snapshot.hasData) {
return Center(
child: Text(
'Your results will appear here'
),
);
}
if (snapshot.hasData && snapshot.data.length < 1) {
return Center(
child: Text(
'Found nothing'
),
);
}
// Where should I put my CircularProgressIndicator?
return ListView.separated(
itemCount: snapshot.data.length,
separatorBuilder: (context, index) => Divider(height: 1),
itemBuilder: (context, index) {
final item = snapshot.data[index];
return ItemWidget(item);
},
);
},
)
BLoC
final _searchStreamController = StreamController<List<Model>>();
Stream<List<Model>> get searchStream => _searchStreamController.stream;
void search(String searchTerm) async {
if (searchTerm.isEmpty) {
_searchStreamController.add(null);
return;
}
final client = HttpClient();
client.method = HttpMethod.POST;
client.endPoint = 'search';
client.addData('query', searchTerm);
try {
final responseStr = await client.execute();
final response = SearchResponse.fromJson(responseStr);
_searchStreamController.add(response.data);
} catch (e) {
_searchStreamController.addError(e);
}
}
I want to display CircularProgressIndicator
every time search(String searchedTerm)
function is called.
Thank you.
Here is what hack I had to do to achieve my requirements. If there is a better way to achieve this, please post an answer and I will mark it as correct.
Hack
class JugarModel<T> {
bool isProcessing;
T data;
JugarModel({this.isProcessing, this.data});
}
And then use this isProcessing
property to check whether to show CircularProgressIndicator
or ListView
. The rest of the code became:
Widget
StreamBuilder<JugarModel>(
stream: bloc.searchStream,
builder: (context, snapshot) {
if (snapshot.hasError) {
return Center(
child: Text(
'Error occurred'
),
);
}
if (!snapshot.hasData) {
return Center(
child: Text(
'Your results will appear here'
),
);
}
if (snapshot.hasData && snapshot.data.data.isEmpty) {
if (snapshot.data.isProcessing) {
return Center(
child: CircularProgressIndicator(),
);
} else {
return Center(
child: Text(
'Found nothing'
),
);
}
}
return ListView.separated(
itemCount: snapshot.data.data.length,
separatorBuilder: (context, index) => Divider(height: 1),
itemBuilder: (context, index) {
final item = snapshot.data.data[index];
return ItemWidget(item);
},
);
},
)
BLoC
final _searchStreamController = StreamController<JugarModel<List<Data>>>();
Stream<JugarModel<List<Data>>> get searchStream => _searchStreamController.stream;
void search(String searchTerm) async {
if (searchTerm.isEmpty) {
_searchStreamController.add(null);
return;
}
final client = HttpClient();
client.method = HttpMethod.POST;
client.endPoint = 'search';
client.addData('query', searchTerm);
// This MAGIC line will call StreamBuilder callback with isProcessing set to true.
_searchStreamController.add(JugarModel<List<Data>>(isProcessing: true, data: List()));
try {
final responseStr = await client.execute();
final response = SearchResponse.fromJson(responseStr);
// And after we've received response from API and parsed it, we're calling StreamBuilder
// callback again with isProcessing set to false.
_searchStreamController.add(JugarModel<List<Data>>(isProcessing: false, data: response.data));
} catch (e) {
_searchStreamController.addError(e);
}
}