Reputation: 2341
I have a search bar, when characters are entered it sends a request to a BLoC, which then requests data from a Future.
Here is the BLoC
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:stockrails/models/semantics/error-type-model.dart';
import 'package:stockrails/models/user-interface/search-query-model.dart';
import 'package:stockrails/services/apis/stock-data.dart';
import 'package:stockrails/shared/constants/messages.dart';
part 'search_event.dart';
part 'search_state.dart';
class SearchBloc extends Bloc<SearchEvent, SearchState> {
SearchBloc() : super(SearchInitial());
// Connection to api service
StockData _data = StockData();
@override
Stream<SearchState> mapEventToState(
SearchEvent event,
) async* {
// : Initial search state - no text has been entered, no loading
if (event is SearchIntializeEvent) {
yield SearchInitial();
}
// : Querying search state - text is being entered, loading next
if (event is SearchQueryingEvent) {
// Yields call
yield* _mapAPICallToEvent(event.query);
}
}
// + ----------- Functions -------------
Stream<SearchState> _mapAPICallToEvent(String query) async* {
// : Yield loading screen based on prior state
if (state is SearchErrorState)
yield SearchErrorLoadingState();
else
yield SearchLoadingState(query: query);
// : Attempts to call method
List<SearchQueryModel> _result;
try {
_result = await _data.searchQuery(query);
} on TimeoutException {
_result = null;
} catch (err) {
_result = [];
}
// : If it can't then it defaults to using the unknownErrorMessage
if (_result == null)
yield SearchErrorState(errorMessage: Messages.unknownErrorMessage, errorType: ErrorType.errorTypes[0]);
// : Else if there's no content in the response
else if (_result.length == 0)
yield SearchErrorState(errorMessage: query, errorType: ErrorType.errorTypes[1]);
// : If there's no errors return loaded!
else {
yield SearchLoadedState(searchQueryModelList: _result, query: query);
}
}
}
And here is the Future that grabs data
// Grabs data from endpoint and returns or errors
// If it times out it will throw a TimeoutException
Future _dataFetch(String url, String query, String token) async {
try {
// Build out auth header
final authHeader = 'Bearer $token';
final response = await http
.get(url, headers: <String, String>{
'query': query,
'authorization': authHeader,
})
.timeout(Duration(seconds: 10))
.catchError((error) {
throw error;
});
// Makes sure response is okay
if (response.statusCode == 200) {
return json.decode(response.body);
}
// If status code is unknown, prints an error
else {
throw Exception(Messages.unknownErrorMessage);
}
} on TimeoutException catch (error) {
throw error;
} on SocketException catch (error) {
throw error;
} catch (error) {
throw error;
}
}
I've been banging my head against a wall trying to figure out a solution to make the BLoC await
for the Future but also to allow SearchInitializeEvent
to cancel any requests being made.
Because what's happening is on a bad network, the request is trying to go through no matter what and won't stop until it gets a response.
I know you can't cancel Futures, I tried both CancelableOperation
and CancelableCompletion
or whatever it's called.
I'm guessing I have to set up my Future as a Stream instead so I can close it. I'm open to any suggestions. Thank you in advance!
Upvotes: 9
Views: 13096
Reputation: 55
you can use the client.close() to abort the connection :
var client = http.Client();
var uri = Uri.https(host, url, params);
var request = http.Request("get", uri);
http.StreamedResponse response = await client.send(request);
if (condition) //if user needs to abort the request
client.close();
Upvotes: 0
Reputation: 10453
There's a limitation in dart for breaking Streams. It's something that can't be done outside the "loop". This is issue was discussed in detail here:
https://github.com/dart-lang/sdk/issues/42717
https://github.com/felangel/bloc/issues/1472
You may want to look into this from a different perspective, like overriding the changes made by the recent request. Or if the request results are something that's displayed on screen, then consider not displaying the results instead.
Upvotes: 3