I made a https call in cloud functions and threw an error if a condition fails, in flutter i handled it successfully and it shows me this : PlatformException(functionsError, Cloud function failed with exception., {code: FAILED_PRECONDITION, details: null, message: Error: Your card was declined.})
but i want to know how i can get just the message part to display to the user? so far i already tried e.message but that didnt work
CloudFunctions(app: Firebase.app(), region: 'asia-southeast2')
.getHttpsCallable(functionName: 'addPayment')
.call({
'paymentMethodId': paymentMethod.id,
'userid': FirebaseAuth.instance.currentUser.uid,
}).catchError((e) => print('ERROR $e'));
this is my function
The PlatformException class has the following properties:
code → String An error code. final
details → dynamic Error details, possibly null. final
hashCode → int The hash code for this object. [...] read-only,
inherited
message → String A human-readable error message, possibly null.
final
runtimeType → Type A representation of the runtime type of the
object. read-only, inherited
To handle it you would do:
.catchError((e) {
if (e is PlatformException){
// Show e.details['message']
}else{
print('ERROR $e');
}
});
.catchError((error) {
String errorMessage = error.toString();
if (error is PlatformException &&
error.message != null &&
error.message!.isNotEmpty) {
errorMessage = error.message!;
}
}
Related
I am trying to write unit tests for a flutter app and I can't get this one test case to work correctly.
Here is the function returning Future<Either<WeatherData, DataError>>:
#override
Future<Either<WeatherData, DataError>> fetchWeatherByCity({required String city}) async {
try {
var response = await apiService.fetchWeatherByCity(city: city);
if (response.statusCode == 200) {
return Left(WeatherData.fromJson(jsonDecode(response.body)));
} else {
return Right(DataError(title: "Error", description: "Desc", code: 0, url: "NoUrl"));
}
} catch (error) {
AppException exception = error as AppException;
return Right(DataError(
title: exception.title, description: exception.description, code: exception.code, url: exception.url));
}
}
Here is the code where I am trying to write the unit test:
sut = WeatherRepositoryImpl(apiService: mockWeatherApiService);
test(
"get weather by city DataError 1 - Error 404 ",
() async {
when(mockWeatherApiService.fetchWeatherByCity(city: "city"))
.thenAnswer((_) async => Future.value(weatherRepoMockData.badResponse));
final result = await sut.fetchWeatherByCity(city: "city");
verify(mockWeatherApiService.fetchWeatherByCity(city: "city")).called(1);
expect(result, isInstanceOf<DataError>);
verifyNoMoreInteractions(mockWeatherApiService);
},
);
When I run this specific test, I receive this error:
Expected: <Instance of 'DataError'>
Actual: Right<WeatherData, DataError>:<Right(Instance of 'DataError')>
Which: is not an instance of 'DataError'
What I am not getting here? What should I be expecting from the function for the test to pass successfully?
You are directly using the result which is actually a wrapper and has a type of Either<WeatherData, DataError>.
You need to unwrap the value using the fold method on the result and then expect accordingly, So in your code you can do something like this to make it work:
final result = await sut.fetchWeatherByCity(city: "city");
result.fold(
(left) => fail('test failed'),
(right) {
expect(result, isInstanceOf<DataError>);
});
verifyNoMoreInteractions(mockWeatherApiService);
Hope this helps.
You need to either make the expected value a Right(), or extract the right side of the actual value. Doing either of those will match, but as it is, you're comparing a wrapped value with an unwrapped value.
I am new to dart/flutter programming. I have been trying to analyze the content of a file and return the result but couldn't. The code actually works when in the main function, but as soon as I take it out it doesn't work anymore. I have read the dart tutorial on futures, async, await, and streams as well as watched YouTube videos but still couldn't solve the problem. I believe my problem revolves around those concepts. Here is my code:
Future<String> r(String name) async {
var f = File(name);
var lines = f.readAsLines();
await lines
.then((line) => line.forEach((element) {
if (element.contains(RegExp(r'^hello'))) {
return element;
}
}))
.onError((error, stackTrace) => 'An error occured');
}
I was getting 2 errors:
The function name 'r' was underlined:
The body might complete normally, causing 'null' to be returned, but the return type is a potentially non-nullable type.
Try adding either a return or a throw statement at the end.
The variable 'element' was underlined:
The return type 'String' isn't a 'void', as required by the closure's context.
Thanks.
Your first error says that "Whatever happens, You have to return a String"
Your second error says that you are trying to return a String in a void function, As you see List. Foreach is a void function and you can't just return something in it because the return statement matches with the closest Function
So I rewrited you're code as following
Future<String> r(String name) async {
try {
var f = File(name);
var lines = await f.readAsLines();
for (var element in lines)
if (element.contains(RegExp(r'^hello'))) {
return element;
}
return "not found";
} catch (e) {
return 'An error occurred: $e';
}
}
Since you are using File and File.readAsLines I think it would be better to just wrap everything inside a try catch bloc k.
I'm creating a news application, and I get my data by JSON and in each running my app get me a crash on (response HTTP) I can't understand it:
Exception has occurred. FormatException (FormatException: Unexpected character (at character 1
<HTML></HTML> ^ )
Code:
import 'package:http/http.dart' as http;
import 'dart:convert';
class Category {
int id;
String title;
Category({
this.id,
this.title
});
static Future<List<Category>> getCategories() async {
http.Response response = await http.get("url JSON"); // <- Here carash
Map<String, dynamic> map = json.decode(response.body);
List<Category> list = [];
for(var map in map['categories']){
list.add(
Category(id: map['id'], title: map['title'])
);
}
return list;
}
}
You're doing something wrong with request I think and the api does not handle that error and throws the error as html as I guess. Anyway, the way you are decoding is not the proper way.
Few points to keep in mind when you are doing http request:
Most importantly study data type (HTML / JSON) and structure of the
data. By printing to the console or great way to use curl request or postman.
Always make sure to wrap your json.decode() with a try - catch
block, When there is a error, you can properly see what's going on
with your code.
Make sure that your response status code is 200. And handle your data occordingly.
And before iterate over the decoded response, ensure that it contains
value.
If you don't know the type or if you are not
sure, without using static type to assign decoded response body use
final (Dont always use dynamic type, my suggestion for only for situations like this). This will help you with FormatException like the situation you are facing.
Last but not least, when you want to check error messages with status code. As example,
when checking expired jwt, you are getting 500 as status code.
So only checking status code is not enough in that situation
because of 500 means internal server error.
Ex:
List list = [];
try {
final decodedBody = json.decode(response.body); // when you have error messages with response, you can also check message with status code
debugPrint(decodedBody?.toString());
if (response.statusCode == 200) {
if (decodedBody != null && map['categories'] != null) { // you can also check the data type you want here ex: (map is List)
for(var map in map['categories']){
list.add(Category(id: map['id'], title: map['title']));
}
// Below is another thrilled way to do this
// list = decodedBody.map['categories']((item) => Category(id: item['id'], title: item['title'])).toList();
}
}
} catch(e, _) {
debugPrint(e.toString());
}
Wish this helpful. Follow above steps and if there any other error let me know.
The response that you are receiving is not a JSON and so your JSON can't be decoded.
Your server is returning some HTML tags and this means you might be requesting to the wrong URL, please check the URL in PostMan or Browser and see response.
I need to trap all of the listed PurchasesErrorCode error codes in my Flutter app so I can respond to them accordingly.
Currently I can only trap "userCancelled", for everything else I can only report the information returned in the standard PlatformException code, message and details properties, without knowing what they will contain.
try {
// Code to make purchase..
} on PlatformException catch (e) {
if (!(e.details as Map)["userCancelled"]) {
// Here I need a comprehensive switch statement so I can
// retry where appropriate/control what messages the user sees
String reason = '';
(e.details as Map).forEach((k,v) => reason += '$k => $v');
showError(context, 'Error', '${e.code} : ${e.message}');
} else {
showError(context, 'Purchase Cancelled', 'Your purchase was not completed, you have not been charged.');
}
}
These codes are exposed in IOS/Swift and Android/Kotlin but I can't get them in Flutter/Dart - what am I missing?
I developed the RevenueCat Flutter plugin and I created an issue on GitHub some time ago to track this (https://github.com/RevenueCat/purchases-flutter/issues/3). I am sorry there is some room for improvement in our Flutter error handling.
When we send the platform exceptions we pass the error code as an String:
result.error(error.getCode().ordinal() + "", error.getMessage(), userInfoMap);
Too bad we can't just pass an int as the first parameter and we have to pass a String, I guess we could pass it in the userInfoMap. But for now, since we are not providing the enum with the error codes yet, you would have to do something like this in your code:
enum PurchasesErrorCode {
UnknownError,
PurchaseCancelledError,
StoreProblemError,
PurchaseNotAllowedError,
PurchaseInvalidError,
ProductNotAvailableForPurchaseError,
ProductAlreadyPurchasedError,
ReceiptAlreadyInUseError,
InvalidReceiptError,
MissingReceiptFileError,
NetworkError,
InvalidCredentialsError,
UnexpectedBackendResponseError,
ReceiptInUseByOtherSubscriberError,
InvalidAppUserIdError,
OperationAlreadyInProgressError,
UnknownBackendError,
InsufficientPermissionsError
}
try {
} on PlatformException catch (e) {
PurchasesErrorCode errorCode = PurchasesErrorCode.values[int.parse(e.code)];
switch (errorCode) {
case PurchasesErrorCode.UnknownError:
case PurchasesErrorCode.PurchaseCancelledError:
case PurchasesErrorCode.StoreProblemError:
// Add rest of cases
}
}
When you do e.details, you also get access to a readableErrorCode containing the name of the error code; and an underlyingErrorMessage, that can hopefully help you debug any issue.
I hope that helps
The issue mentioned on the previous answer was solved, after version 1.0.0 you can handle like this:
try {
PurchaserInfo purchaserInfo = await Purchases.purchasePackage(package);
} on PlatformException catch (e) {
var errorCode = PurchasesErrorHelper.getErrorCode(e);
if (errorCode == PurchasesErrorCode.purchaseCancelledError) {
print("User cancelled");
} else if (errorCode == PurchasesErrorCode.purchaseNotAllowedError) {
print("User not allowed to purchase");
}
}
I have the following code that I am using to log a user in silently
try {
result = await GoogleSignIn().signInSilently().catchError((x) {
print(x);
});
} catch (e) {
print(e);
}
If the user cannot sign in silently, it results in an error.
PlatformException (PlatformException(sign_in_required, com.google.android.gms.common.api.ApiException: 4: 4: , null))
The problem I am having is that I cannot seem to catch the exception. catchError nor the catch block are being hit. How do I catch this error?
Do the following in your method
try {
result = await GoogleSignIn().signInSilently(suppressErrors: false).catchError((x) {
print(x);
});
} catch (e) {
print(e);
}
By default suppressErrors = true suppressing the error messages you want to catch.
Looking at the source code
The SignInSilently method is used to suppress error messages thus not throwing the exception you want to catch.
From the docs of this method:
/// When [suppressErrors] is set to `false` and an error occurred during sign in
/// returned Future completes with [PlatformException] whose `code` can be
/// either [kSignInRequiredError] (when there is no authenticated user) or
/// [kSignInFailedError] (when an unknown error occurred).
Full Method
/// Attempts to sign in a previously authenticated user without interaction.
///
/// Returned Future resolves to an instance of [GoogleSignInAccount] for a
/// successful sign in or `null` if there is no previously authenticated user.
/// Use [signIn] method to trigger interactive sign in process.
///
/// Authentication process is triggered only if there is no currently signed in
/// user (that is when `currentUser == null`), otherwise this method returns
/// a Future which resolves to the same user instance.
///
/// Re-authentication can be triggered only after [signOut] or [disconnect].
///
/// When [suppressErrors] is set to `false` and an error occurred during sign in
/// returned Future completes with [PlatformException] whose `code` can be
/// either [kSignInRequiredError] (when there is no authenticated user) or
/// [kSignInFailedError] (when an unknown error occurred).
Future<GoogleSignInAccount> signInSilently({bool suppressErrors = true}) {
final Future<GoogleSignInAccount> result = _addMethodCall('signInSilently');
if (suppressErrors) {
return result.catchError((dynamic _) => null);
}
return result;
}
Reference
Google Sign In Plugin Repo
Method Source Code Starting Line 283
This might help.
Checklists :
Did not register a sha fingerprint.
Make sure to have my "support email" set.
Enable the Google Sign-in method.
The catchError block is actually getting hit if you use suppressErrors = false
Try the following code and see what it prints on your console.
result = await GoogleSignIn().signInSilently(suppressErrors: false).
catchError((x) {
print("inside catchError");
});
The problem is the print(e); line in the catch statement. 'e' is not a string so an error occurs. I don't know why but no breakpoints are hit in the catch statement for this error and no error is output to the console. If I put a catch statement around a function that calls the login code ie.
void tryLogin(){
try{
myLoginLogic();
} catch(e) {
print(e);
}
}
Then I do get an error message
Unhandled Exception: type '_TypeError' is not a subtype of type 'String'
So to be clear the correct code would be
try {
result = await GoogleSignIn().signInSilently().catchError((e) {
print(e.toString());
});
} catch (e) {
print(e.toString());
}
Why code doesn't break on error and no message is written to the console for the original function I don't know. Also what I don't understand is why, even when the catch statement is written without errors, in debug mode the code breaks on the signInSilently() line.