Dart - Cannot catch an asynchronous exception - flutter

Here is a simple example of my problem:
import 'dart:async';
void main() {
try {
Timer(Duration(seconds: 1), () {
throw TimeoutException('1 second has expired');
});
} catch (o) {
print("caught ${o.runtimeType}");
}
}
I imagine the problem is that the timer finishes counting down after the try-catch block terminates, considering that the countdown is asynchronous and initializing the Timer was successful. How can I catch the exception without changing the callback function of the Timer?
In my specific case, I am using flutter_blue and having trouble with the async method BluetoothDevice#connect().
/// Establishes a connection to the Bluetooth Device.
Future<void> connect({
Duration? timeout,
bool autoConnect = true,
}) async {
var request = protos.ConnectRequest.create()
..remoteId = id.toString()
..androidAutoConnect = autoConnect;
Timer? timer;
if (timeout != null) {
timer = Timer(timeout, () {
disconnect();
throw TimeoutException('Failed to connect in time.', timeout);
});
}
await FlutterBlue.instance._channel
.invokeMethod('connect', request.writeToBuffer());
await state.firstWhere((s) => s == BluetoothDeviceState.connected);
timer?.cancel();
return;
}
I called the method like so:
try {
await (device as BluetoothDevice).connect(timeout: Duration(seconds: 1));
} catch (o) {
print("caught ${o.runtimeType}");
}
Considering I wait for BluetoothDevice#connect() with await and timer is cancelled upon a successful connection (at the end of the method) with timer?.cancel();, I do not know why the try-catch is not catching the following TimeoutException:
E/flutter ( 3710): [ERROR:flutter/lib/ui/ui_dart_state.cc(199)] Unhandled Exception: TimeoutException after 0:00:01.000000: Failed to connect in time.
E/flutter ( 3710): #0 BluetoothDevice.connect.<anonymous closure> (package:flutter_blue/src/bluetooth_device.dart:33:9)
E/flutter ( 3710): #1 _rootRun (dart:async/zone.dart:1346:47)
E/flutter ( 3710): #2 _CustomZone.run (dart:async/zone.dart:1258:19)
E/flutter ( 3710): #3 _CustomZone.runGuarded (dart:async/zone.dart:1162:7)
E/flutter ( 3710): #4 _CustomZone.bindCallbackGuarded.<anonymous closure> (dart:async/zone.dart:1202:23)
E/flutter ( 3710): #5 _rootRun (dart:async/zone.dart:1354:13)
E/flutter ( 3710): #6 _CustomZone.run (dart:async/zone.dart:1258:19)
E/flutter ( 3710): #7 _CustomZone.bindCallback.<anonymous closure> (dart:async/zone.dart:1186:23)
E/flutter ( 3710): #8 Timer._createTimer.<anonymous closure> (dart:async-patch/timer_patch.dart:18:15)
E/flutter ( 3710): #9 _Timer._runTimers (dart:isolate-patch/timer_impl.dart:395:19)
E/flutter ( 3710): #10 _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:426:5)
E/flutter ( 3710): #11 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)
E/flutter ( 3710):
I see that the stack trace starts at BluetoothDevice#connect(), but I am not sure what to do about it.
I also tried calling then((_) {}, (o) => print("caught ${o.runtimeType}")) on the Future<void> returned by BluetoothDevice#connect() and then waiting for it inside the try-catch, yet I had no success.
Any ideas?

That connect method from the package you're using is poorly written. You're not doing anything wrong, whoever wrote that code did. If you poke around the GitHub Issues sections of that repository you'll find quite a few issues and pull requests related to this problem like this and the issues/PR it links.
The code in a timer callback exists outside of the function where the timer is instantiated. It's impossible to directly catch an error thrown in a timer callback.
If you want a timeout, don't use the functionality provided by this package, use the native Dart timeout function.
try {
await (device as BluetoothDevice).connect().timeout(Duration(seconds: 1));
} catch (o) {
print("caught ${o.runtimeType}");
}

Related

Unhandled Exception: [firebase_functions/internal] INTERNAL

I've a Flutter App Project which contains levels of permission, so, my main objective with this is allow an manager delete a user, making him not allowed anymore to login, deleting from FirebaseAuth.
I've a permission called "Deactivated" to prevent the user from logging in, but i really want to remove from FirebaseAuth too.
The only way I've found is using the Firebase Cloud Functions, so I tried to do very similar to FlutterFire Docs
When i execute using PostMan, it works perfectly, the user test is deleted as expected:
METHOD: Post
Url: https://us-central1-<DATABASE_NAME>.cloudfunctions.net/deleteUserByEmail/
Body -> Raw:
{
"userEmail": "test#gmail.com"
}
My index.js
const functions = require("firebase-functions");
const admin = require('firebase-admin');
admin.initializeApp();
exports.deleteUserByEmail = functions.https.onRequest(async (request, response) => {
try {
const userEmail = request.body.userEmail;
admin.auth().getUserByEmail(userEmail)
.then(userRecord => {
const uid = userRecord.uid
return admin.auth().deleteUser(uid)
.then(() => {
console.log("Successfully deleted user");
response.status(200).send('Delete User');
return
})
}).catch(error => {
throw new functions.https.HttpsError('invalid-argument', error.HttpsError);
});
} catch (err) {
throw new functions.https.HttpsError('invalid-argument', "Error passing arguments into");
}
})
Part of my code that calls the code Flutter code:
HttpsCallable callable =
FirebaseFunctions.instance.httpsCallable('deleteUserByEmail');
try {
final resp = await callable.call(<String, dynamic>{
"userEmail" : userModel.email,
});
} on FirebaseFunctionsException catch (e) {
debugPrint(e.toString());
}
Once i tap the button, i receive this error code:
E/flutter (20323): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: [firebase_functions/internal] INTERNAL
E/flutter (20323):
E/flutter (20323): #0 StandardMethodCodec.decodeEnvelope
package:flutter/…/services/message_codecs.dart:653
E/flutter (20323): #1 MethodChannel._invokeMethod
package:flutter/…/services/platform_channel.dart:296
E/flutter (20323): <asynchronous suspension>
E/flutter (20323): #2 MethodChannelHttpsCallable.call
package:cloud_functions_platform_interface/…/method_channel/method_channel_https_callable.dart:23
E/flutter (20323): <asynchronous suspension>
E/flutter (20323): #3 HttpsCallable.call
package:cloud_functions/src/https_callable.dart:49
E/flutter (20323): <asynchronous suspension>
E/flutter (20323):
E/flutter (20323): #0 StandardMethodCodec.decodeEnvelope
package:flutter/…/services/message_codecs.dart:653
E/flutter (20323): #1 MethodChannel._invokeMethod
package:flutter/…/services/platform_channel.dart:296
E/flutter (20323): <asynchronous suspension>
E/flutter (20323): #2 MethodChannelHttpsCallable.call
package:cloud_functions_platform_interface/…/method_channel/method_channel_https_callable.dart:23
After some research I've found that some people had that problem when their code wasn't inside a tryCatch block, but mine was since ever...
I've tried to start admin.initializeApp() parsing the credentials and databaseURL even though my FlutterFire CLI is configured, but didn't work too.

"Unhandled Exception" but I DO handle it! It's a mystery (for me)

Flutter&Dart, when I try to send an udp packet and the screen of the emulator is turned off the sending fails. Ok.
The mystery is I cannot catch the failing in any way.
Udp socket send() is not async so try&catch is correct. If I add await nothing changes. If I try to append .catchError it is not permitted because it is not an async method.
try {
print(_udpSocket.toString()); //It prints Instance of '_RawDatagramSocket'
_udpSocket!.send(_packet,_serverAddress!,Server_UdpPort); //Send packet
} catch (exception) {
print("aaaaaaaaaaaa"); //It never prints aaaaa, it never executes this code
print(exception.toString());
}
Error in console:
[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: SocketException: Send failed (OS Error: Operation not permitted, errno = 1), address = 0.0.0.0, port = 0
E/flutter (25939): #0 _NativeSocket.send (dart:io-patch/socket_patch.dart:1213:34)
E/flutter (25939): #1 _RawDatagramSocket.send (dart:io-patch/socket_patch.dart:2460:15)
E/flutter (25939): #2 ClassServerConnection._handleOutcomingUdpStreamPacketsQueueEvent_async (package:meet2/2_1_4_ClassServerConnection.dart:1129:29)
E/flutter (25939): #3 ClassServerConnection._handleOutcomingUdpStreamPackets.<anonymous closure> (package:meet2/2_1_4_ClassServerConnection.dart:1090:93)
E/flutter (25939): #4 ClassQueue.callEvent.<anonymous closure> (package:meet2/2_1_2_ClassQueue.dart:78:68)
E/flutter (25939): #5 new Future.delayed.<anonymous closure> (dart:async/future.dart:424:39)
E/flutter (25939): #6 Timer._createTimer.<anonymous closure> (dart:async-patch/timer_patch.dart:18:15)
E/flutter (25939): #7 _Timer._runTimers (dart:isolate-patch/timer_impl.dart:398:19)
E/flutter (25939): #8 _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:429:5)
E/flutter (25939): #9 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:192:12)

How to convert the type Future<List<T>> to <List<T>> in fultter?

everyone! I was developing e-commerce flutter app with doofinder APIs.
But I faced a thorny problem. I tried to get data from doofinder(it's just search service) API then present to screen. I added screen-shots.
Future<List<Product>> fetchProduct(query) async {
var response = await http.get(
Uri.parse(
'https://eu1-search.doofinder.com/5/search?hashid=30a5f&query=$query'),
// Send authorization headers to the backend.
headers: {'Authorization': 'c59dadc5d822ca2b134f170'},
);
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
print(jsonDecode(response.body)['results'].toList().runtimeType);
return jsonDecode(response.body)['results'].toList().cast<List<Product>>();
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load album');
}
}
then,
onChanged: (_) => EasyDebounce.debounce(
'tFMemberController',
const Duration(milliseconds: 800),
() {
isSearchStarted =
textController!.text.isNotEmpty &&
textController!.text.trim().length > 0;
print('isSearchStarted $isSearchStarted');
if (isSearchStarted) {
print('${textController!.text.trim()}');
searchedProducts =
fetchProduct(textController!.text)
as List<Product>;
print(searchedProducts);
}
setState(() {});
},
),
And this is error log.
E/flutter ( 5295): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: type 'Future<List<Product>>' is not a subtype of type 'List<Product>' in type cast
E/flutter ( 5295): #0 _SearchPageState.build.<anonymous closure>.<anonymous closure> (package:s4s_mobileapp/search/page_search.dart:151:41)
E/flutter ( 5295): #1 EasyDebounce.debounce.<anonymous closure> (package:easy_debounce/easy_debounce.dart:44:22)
E/flutter ( 5295): #2 Timer._createTimer.<anonymous closure> (dart:async-patch/timer_patch.dart:18:15)
E/flutter ( 5295): #3 _Timer._runTimers (dart:isolate-patch/timer_impl.dart:398:19)
E/flutter ( 5295): #4 _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:429:5)
E/flutter ( 5295): #5 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:192:12)
E/flutter ( 5295):
I/flutter ( 5295): List<dynamic>
E/flutter ( 5295): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: type 'CastList<dynamic, List<Product>>' is not a subtype of type 'FutureOr<List<Product>>'
E/flutter ( 5295): #0 fetchProduct (package:s4s_mobileapp/search/page_search.dart:41:58)
E/flutter ( 5295): <asynchronous suspension>
E/flutter ( 5295):
This makes me crazy.
I want you to take a closer look at the pictures below and find a suitable solution please.
Change
jsonDecode(response.body)['results'].toList().cast<List<Product>>();
to this:
jsonDecode(response.body)['results'].toList().cast<Product>();
The cast method already knows that you are working with lists and only wants to know the type of the elements, but not the type of the list itself.
EDIT: You also need to change:
searchedProducts = fetchProduct(textController!.text) as List<Product>;
to this:
searchedProducts = fetchProduct(textController!.text) as Future<List<Product>>;
You have to work with futures as your result is processed asynchronously. In the widget tree you have to use FutureBuilder which takes a future and builds your list as you want.

flutter zone vs future

I read about Zone and have used Futures in many languages. I know about Event Loop and that Dart is single threaded. However, when I write following code I could not differentiate how differently it will work and when to use one over another.
What is the difference between zone and future?
For example here:
runZoned(() async {
// Do Something
}, onError: (e, stackTrace) {
print(e);
});
Vs
someAsyncCall().catchError((e) {
print(e);
});
Futures error handling
Zones
What is a Future
Edit 1: I used runZonedGuarded instaed runZoned because runZoned.onError is deprecated.
Flutter : 'onError' is deprecated on runZoned function
Hi! With runZoned you can basically Handling asynchronous errors that are commonly caused by futures (http request, etc). The concept is similar to a try-catch in synchronous code. With Future you cannot do that.
runZoned example:
runZonedGuarded(() {
_timerError();
}, (error, stack) {
print('Uncaught error runZoneGuard: $error');
});
resullt:
I/flutter (13567): Uncaught error runZoneGuard: asynchronous error
Future example:
someCall().then((value) {
_timerError();
}, onError: (value) {
print('Uncaught error onError: $value');
}).catchError((e) {
print('Uncaught error catchError: $e');
});
result:
E/flutter (13567): [ERROR:flutter/lib/ui/ui_dart_state.cc(198)] Unhandled Exception: asynchronous error
E/flutter (13567): #0 _MyHomePageState._timerError.<anonymous closure> (package:async_study/main.dart:60:7)
E/flutter (13567): #1 _rootRun (dart:async/zone.dart:1418:47)
E/flutter (13567): #2 _CustomZone.run (dart:async/zone.dart:1328:19)
E/flutter (13567): #3 _CustomZone.runGuarded (dart:async/zone.dart:1236:7)
E/flutter (13567): #4 _CustomZone.bindCallbackGuarded.<anonymous closure> (dart:async/zone.dart:1276:23)
E/flutter (13567): #5 _rootRun (dart:async/zone.dart:1426:13)
E/flutter (13567): #6 _CustomZone.run (dart:async/zone.dart:1328:19)
E/flutter (13567): #7 _CustomZone.bindCallback.<anonymous closure> (dart:async/zone.dart:1260:23)
E/flutter (13567): #8 Timer._createTimer.<anonymous closure> (dart:async-patch/timer_patch.dart:18:15)
E/flutter (13567): #9 _Timer._runTimers (dart:isolate-patch/timer_impl.dart:398:19)
E/flutter (13567): #10 _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:429:5)
E/flutter (13567): #11 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:192:12)
And the '_timerError()' method used to trhow a async error:
void _timerError() {
Timer.run(() {
throw "asynchronous error";
});
}

Initialize Dio with persistent cookie at the start of the program

I'm using Dio in my app and I want it to save cookies. It turns out I have to download three additional packages to do that: cookie_jar, dio_cookie_manager and path_provider.
I need to:
get application document directory:
Directory docDir = await getApplicationDocumentsDirectory();
pass it to PersistCookieJar
PersistCookieJar(storage: FileStorage(appDocDir.path + '/.cookies/'));
and add it to interceptors:
dio.interceptors.add(CookieManager(_cookieJar));
My problem is with the first part. For some reason, getApplicationDocumentsDirectory(); throws an error when not used inside a StatefulWidget widget.
I have two questions:
Why on earth would getting a path require a StatefulWidget?
How can I do what I want? I need to initialize Dio as early as possible so that by the time the UI loads, the user is already (or almost) logged in. Plus, I don't want to put the logic of initiating the client object inside the UI code, that might for example cause it to be initiated multiple times whenever the widget is created.
What my code looks like right now:
Future<void> initStuff() async {
initLocator();
Directory docDir = await getApplicationDocumentsDirectory(); // throws
print('docDir = ${docDir.path}');
Client cli = Client(docDir);
locator.registerSingleton<Client>(cli);
// ...
}
void main() async {
await initStuff();
runApp(MyApp());
}
Client constructor:
Client(Directory appDocDir) {
final _cookieJar = PersistCookieJar(storage: FileStorage(appDocDir.path + '/.cookies/'));
dio = Dio()
..options.baseUrl = Utils.SERVER_ADDRESS
..options.sendTimeout = 5000
..options.receiveTimeout = 5000
..options.connectTimeout = 5000
..interceptors.add(CookieManager(_cookieJar));
}
The error I'm getting:
E/flutter (24953): [ERROR:flutter/lib/ui/ui_dart_state.cc(199)] Unhandled Exception: Null check operator used on a null value
E/flutter (24953): #0 MethodChannel.binaryMessenger
package:flutter/…/services/platform_channel.dart:142
E/flutter (24953): #1 MethodChannel._invokeMethod
package:flutter/…/services/platform_channel.dart:148
E/flutter (24953): #2 MethodChannel.invokeMethod
package:flutter/…/services/platform_channel.dart:331
E/flutter (24953): #3 MethodChannelPathProvider.getApplicationDocumentsPath
package:path_provider_platform_interface/src/method_channel_path_provider.dart:50
E/flutter (24953): #4 getApplicationDocumentsDirectory
package:path_provider/path_provider.dart:138
E/flutter (24953): #5 initStuff
package:proj/main.dart:20
E/flutter (24953): #6 main
package:proj/main.dart:63
E/flutter (24953): #7 _runMainZoned.<anonymous closure>.<anonymous closure> (dart:ui/hooks.dart:142:25)
E/flutter (24953): #8 _rootRun (dart:async/zone.dart:1354:13)
E/flutter (24953): #9 _CustomZone.run (dart:async/zone.dart:1258:19)
E/flutter (24953): #10 _runZoned (dart:async/zone.dart:1789:10)
E/flutter (24953): #11 runZonedGuarded (dart:async/zone.dart:1777:12)
E/flutter (24953): #12 _runMainZoned.<anonymous closure> (dart:ui/hooks.dart:138:5)
E/flutter (24953): #13 _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:283:19)
E/flutter (24953): #14 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)
OK: Thank you for updating your post.
The error is: Unhandled Exception: Null check operator used on a null value
It's coming from the 3rd party library call getApplicationDocumentsDirectory(), and it's occurring because you're calling a (currently uninitialized) value with null-safety enabled (a Good Thing!).
SUGGESTIONS:
Using a StatefulWidget is a bit more work - but it makes sense here. It also happens to resolve the problem for you, correct?
You might try making docDir nullable (e.g. String? path). This is exactly what the getApplicationDocument page shows:
https://pub.dev/documentation/path_provider/latest/path_provider/getApplicationDocumentsDirectory.html
Future<Directory> getApplicationDocumentsDirectory() async {
final String? path = await _platform.getApplicationDocumentsPath();
if (path == null) {
throw MissingPlatformDirectoryException(
'Unable to get application documents directory');
}
return Directory(path);
}
Finally, check out these links:
https://stackoverflow.com/a/67990442/421195
https://stackoverflow.com/a/68876902/421195
Run this in main() before everything else:
WidgetsFlutterBinding.ensureInitialized();