async/await method throws exception -type int is not a subtype of bool - flutter

I am calling a graphql endpoint which returns success, but I do get an exception on the calling method.
Here is my calling method -
await AmplifyInstance()// this is where I get the exception. Snip below
.createUserOnAzureCosmosDB(user)
.then((result) {
print(result['data']['userPhoneNumber']);
_intlPhoneFieldController.text =
(result['data']['userPhoneNumber'].toString())
.substring(1);
_incrementStep('continueOnProfilePictureWidget');
});
Here is the called method -
Future<dynamic> createUserOnAzureCosmosDB(User user) async {
HttpLink link = GlobalVariables().graphqlEndpoint;
GraphQLClient graphQlClient = GraphQLClient(
link: link,
cache: GraphQLCache(
store: InMemoryStore(),
),
);
try {
QueryResult mutationResult = await graphQlClient.mutate(
//Mutation query here
if (mutationResult.data?['createUser'] != null) {
print('Created user on Cosmos DB');
registerUserStatus['result'] = true;
registerUserStatus['data'] = mutationResult.data?['createUser'];
}
} on ApiException catch (e) {
print('Mutation failed: $e');
registerUserStatus['result'] = false;
registerUserStatus['errorMessage'] = e.message;
}
return registerUserStatus;
}
And the returned registerUserStatus is just an array -
var registerUserStatus = {};
Here is the exception -
UPDATE eamirho3ein
Here is the result of print("result=$result);
I/flutter (14224): result = {result: true, data: {__typename: User, partitionKey: user, userPhoneNumber: 14160000000, userDisplayName: testuser, avatarUrl: www.url.com, createdAt: Today}}

This is not actually an answer, but rather a way to find the answer more easily yourself:
then chains make it increasingly hard to find your problem, because the compiler/debugger/IDE has a harder time pointing you to it. So don't do it.
With async/await available from the beginning, there never has been a reason to use then in any Dart program.
await AmplifyInstance().createUserOnAzureCosmosDB(user).then((result) {
Is equivalent to just writing:
final result = await AmplifyInstance().createUserOnAzureCosmosDB(user);
And then continuing on with the code you had put in the lambda function in the then part. Obviously, you need to remove the closing bracket somewhere too now.
This way, your error will actually pop up where it happens, not at the await of a huge chain that leaves you wondering what the problem might be.

Related

Function expressions can't be named: then(

Im having issues with my code and since I'm new at using Flutter, I have no clue on how to fix it. I was trying to fix the http.get(string) and I kind of did, but now I'm having issues with then(()).
void submitForm(FeedbackForm feedbackForm) async {
try {
await http.get(Uri.parse(URL + feedbackForm.toParams()).then((response)) {
callback(convert.jsonDecode(response.body['status']));
});
} catch (e) {
print(e);
}
}
}
It seems you got a parenthesis missplaced:
await http.get(...).then((response) => callback(...))
The them allows you to use the result of the previous Future, as soon as it becomes available. If you find it confusing you can declare one variable at a time.
final response = await http.get(...);
// Check if response was as expected
await callback();

Riverpod giving a bad state exception when one hits back button on webpage

I'm getting this error in my StateNotifiers when one hits the back button on their webpage. I've isolated it to happening where the longRunningAPI request is below.
Exception has occurred.
"Error: Bad state: Tried to use RunListNotifier after `dispose` was called.
and I have code like this.
final runListController = StateNotifierProvider.autoDispose
.family<RunListNotifier, AsyncValue<List<Run>>, RunListParameter>(
(ref, param) {
return RunListNotifier(read: ref.read, param: param);
});
class RunListNotifier extends StateNotifier<AsyncValue<List<Run>>> {
RunListNotifier({required this.read, required this.param})
: super(AsyncLoading()) {
fetchViaAPI(param);
}
final Reader read;
final RunListParameter param;
void fetchViaAPI(RunListParameter param) async {
state = AsyncLoading();
try {
List<Run> stuff = await read(apiProvider).longRunningAPI(param: param);
state = AsyncData(stuff);
} catch (e) {
state = AsyncError(e);
}
}
}
is it safe to simply do something like this in the catch?
} catch (e) {
if (e.runtimeType.toString() == 'StateError') {
// ignore the error
} else {
state = AsyncError(e);
}
}
I believe you could solve this problem by checking mounted before setting the state after your API call like so:
List<Run> stuff = await read(apiProvider).longRunningAPI(param: param);
if (!mounted) return;
state = AsyncData(stuff);
This simply checks if dispose was called and if so, don't attempt to modify the state.
Another resource that could be useful is adding a cancelToken to your API call and canceling if the provider is disposed.
final longRunningApi = FutureProvider.autoDispose.family<List<Run>, RunListParameter>((ref, param) async {
final cancelToken = CancelToken();
ref.onDispose(cancelToken.cancel);
final api = await ref.watch(apiProvider);
final res = await api.longRunningApi(param, cancelToken);
ref.maintainState = true;
return res;
});
Then you'd have to add the cancelToken to your actual request. A great example of this in the marvel example project by the author of Riverpod can be found here.

How to refactor functional error handling in flutter with dartz

I have two methods
#override
Future<Option<StorageFailure>> init() async {
final root = await getRootDir();
return root.fold(
(failure) => some(failure),
(dir) {
final images = Directory("$dir/images");
final videos = Directory("$dir/videos");
images.create(); // more error handling here (try|either)
videos.create();
},
);
}
#override
Future<Either<StorageFailure, Directory>> getRootDir() async {
try {
final root = await getApplicationDocumentsDirectory();
return right(Directory("${root.path}/files"));
} catch (e) {
return left(StorageFailure(reason: e.toString()));
}
}
On the init method after folding i need to do more error handling but i don't like to nest too much my code. Also i don't know how to return a Failure from the right function.
What would be a better way to chain those values?
The cool thing about Either is, that you can chain actions and take care of error handling just once at the end. If the program fails to get the root directory, the part about creating the media subdirectories is never executed. So there is no need for nested error handling.
I am in the process of learning functional programming myself. So there might be an even better solution, but here is how I would do it:
// I intentionally added all type annotations for better understanding.
Future<Option<StorageFailure>> init() async {
final Either<StorageFailure, Directory> root = await getRootDir();
final Either<StorageFailure, Either<StorageFailure, Success>> result = await root.traverseFuture(createImagesAndVideosSubfolders);
final Either<StorageFailure, Success> flattenedResult = result.flatMap(id);
return flattenedResult.fold((failure) => some(failure), (success) => none());
}
Future<Either<StorageFailure, Success>> createImagesAndVideosSubfolders(Directory dir) async {
try {
await Directory('${dir.path}/images').create();
await Directory('${dir.path}/videos').create();
return right(Success('success'));
} catch (e) {
return left(StorageFailure(reason: e.toString()));
}
}
get the root directory
create the media directories (if getRootDir failed, the root.traverseFuture method just returns the existing StorageFailure)
Flatten the nested Eithers with flatMap and the id function. The traverseFuture method is the map version for futures. If there would be a flatMap equivalent for futures as well, the result would not be wrapped in an either and the last step to flatten the result would not be necessary.
You could simplify the init function even more by just returning Either instead of converting it to an Option.
Like so:
Future<Either<StorageFailure, Success>> init() async {
...
return result.flatMap(id);
}

Dart Flutter, help me understand futures

See this code:
class SomeClass{
String someVariable;
SomeClass();
Future<String> getData () async {
Response response = await get('http://somewebsite.com/api/content');
Map map = jsonDecode(response.body); // do not worry about statuscode, trying to keep it minimal
someVariable = map['firstName'];
return 'This is the first name : $someVariable';
}
}
Now look at main:
void main(){
String someFunction() async {
SomeClass instance = SomeClass(); // creating object
String firstNameDeclaration = await instance.getData().then((value) => value);
return firstNameDeclaration;
}
}
When working with Future, like in the case of firstNameDeclaration why do I have to use .then() method to access the string object, since I am waiting for the function to finish?
When searching on the web, some people use .then() others don't, I am confused.
Kindly help me have a clearer understanding of how Futures and async functions overall work.
Background
Asynchronous operations let your program complete work while waiting for another operation to finish. Here are some common asynchronous operations:
Fetching data over a network.
Writing to a database.
Reading data from a file.
To perform asynchronous operations in Dart, you can use the Future class and the async and await keywords.
When an async function invokes "await", it is converted into a Future, and placed into the execution queue. When the awaited future is complete, the calling function is marked as ready for execution and it will be resumed at some later point. The important difference is that no Threads need to be paused in this model.
Futures vs async-await
When an async function invokes "await", it is converted into a Future, and placed into the execution queue. When the awaited future is complete, the calling function is marked as ready for execution and it will be resumed at some later point. The important difference is that no Threads need to be paused in this model.
async-await is just a a declarative way to define asynchronous functions and use their results into Future and it provides syntactic sugar that help you write clean code involving Futures.
Consider this dart code snipped involving Futures -
Future<String> getData(int number) {
return Future.delayed(Duration(seconds: 1), () {
return 'this is a future string $number.';
});
}
main(){
getData(10).then((data) => {
print(data)
});
}
As you can see when you use Futures, you can use then callback when the function return a future value. This is easy to manage if there is single "then" callback but the situation escalates quickly as soon as there are many nested "then" callbacks for example -
Future<String> getProductCostForUser() {
return getUser().then((user) => {
var uid = user.id;
return getOrder(uid).then((order) => {
var pid = order.productId;
return getProduct(pid).then((product) => {
return product.totalCost;
});
});
});
}
main(){
getProductCostForUser().then((cost) => {
print(cost);
});
}
As you can when there multiple chained "then" callback the code become very hard to read and manage. This problem is solved by "async-await". Above chained "then" callbacks can be simplified by using "async-await" like so -
Future<String> getProductCostForUser() async {
var user = await getUser();
var order = await getOrder(user.uid);
var product = await getProduct(order.productId);
return product.totalCost;
}
main() async {
var cost = await getProductCostForUser();
print(cost);
}
As you can above code is much more readable and easy to understand when there are chained "then" callbacks.
I hope this explains some basic concepts and understanding regarding the "async-await" and Futures.
You can further read about topic and examples here
Basically, you should either use await OR then(). However, Dart guidelines advocates that you should prefer use await over then() :
This code :
Future<int> countActivePlayers(String teamName) {
return downloadTeam(teamName).then((team) {
if (team == null) return Future.value(0);
return team.roster.then((players) {
return players.where((player) => player.isActive).length;
});
}).catchError((e) {
log.error(e);
return 0;
});
}
should be replaced by :
Future<int> countActivePlayers(String teamName) async {
try {
var team = await downloadTeam(teamName);
if (team == null) return 0;
var players = await team.roster;
return players.where((player) => player.isActive).length;
} catch (e) {
log.error(e);
return 0;
}
}
In your case, you should write :
void main(){
Future<String> someFunction() async {
SomeClass instance = SomeClass(); // creating object
String firstNameDeclaration = await instance.getData();
return firstNameDeclaration;
// Or directly : return await instance.getData();
// Or : return instance.getData();
}
}

Dart Completer.complete() results never resolve

I have a function that returns a future which depends on the result of a callback to resolve:
Future connectSocket(String email, String password, {Function onConnectCallback}) async {
var completer = new Completer();
print("Connecting...");
var query = getQueryString(email, password);
socketIO = await SocketIOManager().createInstance(SocketOptions(localDomainWindows, query: query));
socketIO.on("loginError", (data) {
print("Login err");
_connected = false;
connectedCallback();
completer.complete(false);
});
socketIO.onConnect((data) {
print("***CONNECTED***");
_connected = true;
completer.complete(true);
connectedCallback();
});
socketIO.connect();
return completer.future;
}
I can see ***CONNECTED*** printed to the console, and my socket server acknowledges the connection, but the function await-ing the resolution never resumes, it just hangs.
socketConnection.connectSocket(_email, _password)
.then((success) {
print("SUCCESS") // never gets printed
}
The only possible explanation for this is that some code in the callback is blocking your program from continuing because Completer.complete should otherwise always make the future complete.
If it is blocked, however, the event loop will never be able to call your code.
As a bool assignment should never be blocking (_connected = true;), the only part of your function that could be halting your program is connectedCallack();. If you remove or fix it, you should see your future complete.