如果网络不存在,我想显示一个错误屏幕。我不使用connectivity软件包,因为我不想连续检查。我只想在处理后端api并显示屏幕时处理异常。我无法捕捉到异常。
我发现了关于套接字异常的this问题和this问题,但似乎都没有帮助。
这就是我所谓的后端api-
callBackendApi() async {
try {
http.Response response = await Future.value(/*api call here*/)
.timeout(Duration(seconds: 90), onTimeout: () {
print('TIME OUT HAPPENED');
});
} catch (exception) {
Fluttertoast.showToast(msg: 'Check internet connection.');
print('Error occurred' + exception.toString());
}
}
答案 0 :(得分:4)
我的解决方案是导入“ dart.io”,以便从try块中捕获SocketException:
iter
答案 1 :(得分:0)
好吧,我不知道我的答案是否会解决您的问题,但是几天前我遇到了您的问题,但就我而言,使用的是Firebase实时数据库。我在问自己,如何保护我的应用程序免受无法连接互联网的网络故障的影响?好吧,我也没有使用连接程序包,所以我以一种您已经尝试使用超时进行网络操作的方法来解决此问题。我将分享两个片段,这些片段具有为解决此类问题而实施的不同方法,并添加了一些注释以尝试解释它们之间的差异。
方法1-在网络请求方法之外设置超时
下面的代码片段是一个简单的firebase数据库请求,其中_viewsRef
是DatabaseReference,而once
方法执行该请求,并向我返回一个有或没有数据的Future。
// get users visualization from realtime database and returns a future
static Future<DataSnapshot> getUserVisualizations({@required String uid}) async {
return _viewsRef.child(uid).limitToLast(50).once();
}
在我的BLoC组件类中,我正在调用以下方法,并将超时设置为返回的将来。
myBlocComponentMethod(){
//.. some work and finally the call
FirebaseUserViewsHelper.getUserVisualizations(uid: _currentUid)
.then(
(dataSnapshot){
if (dataSnapshot.value == null) {
// do some things to handle no data
}
else {
/// handle your data here
});
}
} // setting timeout here is an important point
).timeout( Duration(seconds: Constants.NETWORK_TIMEOUT_SECONDS),
onTimeout: (){
// method to handle a timeout exception and tell to view layer that
// network operation fails
// if we do not implement onTimeout callback the framework will throw a TimeoutException
} );
}
那么这里有什么意义?在这种情况下,如果超时到期且将来未完成,则调用onTimeout回调,并且可以告诉视图层网络操作失败,并向用户显示有关此操作的小部件。但是,即使超时到期,对Firebase数据库的请求也会一次又一次地发生,这就像请求数据库的异步事件保持在dart事件队列上一样。我认为这种行为不利于性能,但是如果您使用带有少许逻辑的StreamBuilder构建UI并编写代码,则当您重新建立互联网连接并使用BloC模式时,所请求的数据将立即可用,UI可以轻松对此做出响应事件,我们不需要提供示例刷新按钮即可让用户再次提出请求。我不知道这是否是实现此行为的正确方法,但它是否有效。
方法2-通过网络请求方法在内部设置超时
在另一个firebase数据库请求方法下
static Future<DataSnapshot> readUserNode( {@required String uid} ) async
=> USERS_REFERENCE.child(uid).once()
.timeout( Duration(seconds: Constants.NETWORK_TIMEOUT_SECONDS ) );
//note: Without timeout callback this line will throw a TimeoutException if the time expires
另一个BLoc组件中的用法:
myBlocComponentMethod2(){
for( String uid in iterable ){
FirebaseUserHelper.readUserNode(uid: uid)
.then( (userSnapshot){
if (userSnapshot.value == null){
// do your stuffs
}
else {
// more stuffs to do
}
}).catchError( (error){
// when timeout expired we will catch the TimeoutException HERE and handling telling
// the UI what we need
} );
}
}
我在这里得到的最大区别是行为。在第二种情况下,由于我在超时过期时将超时放入了request方法中,因此请求事件不再运行,这就像将请求事件从dart事件队列中删除一样。从性能的角度来看这可能很好,但是现在我们需要在UI中提供一个刷新按钮,以便用户再次执行数据以再次从Internet获取数据。
我不知道此解决方法是否可以解决您的问题,因为您介绍了SocketException,但我所描述的情况并非如此,我也不知道您使用的是哪种api来发出请求。无论如何,我希望本文中描述的概念可以帮助您解决问题。
答案 2 :(得分:0)
我这样使用dio:
try {
var formData = FormData.from(Map<String, dynamic>.from(data));
var response = await dio.post(
uri,
data: formData,
);
jsonResponse = json.decode(response.data);
} on DioError catch (e) {
if (DioErrorType.RECEIVE_TIMEOUT == e.type ||
DioErrorType.CONNECT_TIMEOUT == e.type) {
throw CommunicationTimeoutException(
"Server is not reachable. Please verify your internet connection and try again");
} else if (DioErrorType.RESPONSE == e.type) {
// 4xx 5xx response
// throw exception...
} else if (DioErrorType.DEFAULT == e.type) {
if (e.message.contains('SocketException')) {
throw CommunicationTimeoutException('blabla');
}
} else {
throw CommunicationException("Problem connecting to the server. Please try again.");
}
}