Is there a way to skip await if it's too long? (Flutter) - flutter

I use async await in main so the user has to wait in the splash screen before entering the app.
void main() async {
await Firebase.initializeApp();
String? x;
await FirebaseDatabase.instance.ref().child("data").once().then((snapshot) {
Map data = snapshot.snapshot.value as Map;
x = jsonEncode(data);
});
return ChangeNotifierProvider<DataModel>.value(
value: DataModel(data: x),
child: MaterialApp()
);
}
If there are users entering the app without an internet connection, they will be stuck on the splash screen forever. If there are also users with slow internet connection, they will be stuck on the splash screen longer.
So no matter what the internet connection problem is, I want to set a maximum of 5 seconds only to be in the await, if it exceeds, skip that part and go straight into the app.

Pass your Firebase api call to a function, make the method return future,
Future<String?> getData() async {
await FirebaseDatabase.instance.ref().child("data").once().then((snapshot) {
Map data = snapshot.snapshot.value as Map;
return jsonEncode(data);
});
}
Then on main method, attach a timeout with that method you created
void main() async {
await Firebase.initializeApp();
String? x = await getData().timeout(Duration(seconds: 5), onTimeout: () {
return null };
return ChangeNotifierProvider<DataModel>.value(
value: DataModel(data: x),
child: MaterialApp()
);
}
So if you api takes more then 5 seconds of time, then it exit and return null

you can User Race condition or Future.timeout
Race condition using future.any
final result = await Future.any([
YourFunction(),
Future.delayed(const Duration(seconds: 3))
]);
in above code one who finish first will give result,
so if your code takes more than 3 sec than other function will return answer

Related

Flutter - an async function returns before really finishing?

I have a function scanAndConnect() that should scan for BLE devices and connect to the device with the specified service ID. This function should be async and should return Future.
The problem is that scanAndConnect() prints 99999 and returns without waiting for flutterReactiveBle.statusStream.listen() to finish although I use await before it.
Future scanAndConnect(Uuid serviceId, Uuid charctId) async {
StreamSubscription<BleStatus>? bleStatusStreamSubscription;
StreamSubscription<DiscoveredDevice>? deviceStreamSubscription;
Stream<DiscoveredDevice> stream;
bleStatusStreamSubscription =
await flutterReactiveBle.statusStream.listen((bleStatus) async {
print("new listen ${bleStatus.toString()}");
if (bleStatus == BleStatus.ready) {
await bleStatusStreamSubscription!.cancel();
connectionStatus = BLEConnectionStatus.Connecting;
stream = await flutterReactiveBle.scanForDevices(
withServices: [serviceId],
scanMode: ScanMode.lowLatency,
);
}
});
print("9999999");
}
....
Future connectToDevice() async {
await ble.scanAndConnect(BLE_SERVICE_UUID, BLE_CHAR_UUID)
print("Statement after await in main");
setState(() {
loading = false;
print("Changing state to ${loading.toString()}");
});
}
This is the output I get in Xcode:
flutter: 9999999
flutter: Statement after await in main
flutter: Changing state to false
flutter: new listen BleStatus.unknown
flutter: new listen BleStatus.ready
How can I make scanAndConnect doesn't return before really finishing?
According to the documentation, FlutterReactiveBle.scanForDevices() returns a Stream, not a Future, so await will not work here. You can use
await for
listen()
await stream.first()
to wait for data from a Stream.

Upload multiple images to Firebase Storage AT ONCE

There are some questions posted about uploading multiple files to Firebase, but all the solutions I came across use a forEach loop or something similar to upload one by one. However, if I'm uploading files that depend on each other (say, my app requires both of them to exist to function correctly), this could be an issue because one of the uploads could succeed and the other fail.
I thought there must be a way to do something similar to a batch write in Firestore but for uploading files in Firebase Storage.
Is there any way to do that or is the loop method the only way?
(I'm using Flutter, so if possible I would appreciate it if any code provided as an answer is written in Dart)
Well, you could use Future.wait() to upload your files simultaneously without waiting for any of them to complete before you begin other.
To explain further, I made the following code snippet, please follow along:
void main() {
Future.wait([
uploadImage1(),
uploadImage2(),
uploadImage3(),
]);
}
Future<void> uploadImage1() async {
await Future.delayed(Duration(seconds: 3), () {
print('1');
});
}
Future<void> uploadImage2() async {
throw Exception();
}
Future<void> uploadImage3() async {
await Future.delayed(Duration(seconds: 3), () {
print('3');
});
}
I made three async operations, and added them all to Future.wait list, so all are executed/triggered at the same time, doesn't matters if any of them fails. Try the code on dart pad and you will see the output appears at the same time, the output would look like:
1
3
: ExceptionError: Exception
On the other hand, if you want to wait for one operation to successfully finish, then you can use await on a method that returns Future, so it will wait until the operation is successfully completed.
void main() async {
await uploadImage1();
await uploadImage2();
await uploadImage3();
}
Future<void> uploadImage1() async {
await Future.delayed(Duration(seconds: 3), () {
print('1');
});
}
Future<void> uploadImage2() async {
throw Exception();
}
Future<void> uploadImage3() async {
await Future.delayed(Duration(seconds: 3), () {
print('3');
});
}
The output would be:
1
: ExceptionError: Exception

How to store data after 24 hours in flutter? / How to update UI after some time when app is closed/killed in flutter?

I am making an app with flutter. I want to store data after 24 hours and update UI in app.
I try with Timer.periodic() but it does not count the time when app is close. It only works when the application is open.
Is it possible to execute a function after a specific time even if the app is closed?
Here is my current code:
void callbackDispatcher() async{
Workmanager().executeTask((task, inputData) {
switch(sdDaily){
case 'StoreDataDaily':
storeData.storeDailyData();
break;
default:
}
return Future.value(true);
});
}
void main() async{
WidgetsFlutterBinding.ensureInitialized();
Directory directory = await path_provider.getApplicationDocumentsDirectory();
print(directory.path);
Hive.init(directory.path);
await Hive.initFlutter(directory.path);
Hive.registerAdapter(UserAdapter());
Hive.registerAdapter(WaterAdapter());
Hive.registerAdapter(WeekAdapter());
Get.put(UserController());
Get.put(WaterController());
await Hive.openBox<User>('data');
await Hive.openBox<Water>('water_data');
await Hive.openBox<Week>('week_data');
await notificationPlugin.showNotification();
await Workmanager().initialize(callbackDispatcher, isInDebugMode: true);
var uniqueId = DateTime.now().second.toString();
var userBox = Hive.box<User>('data');
if(userBox.get(0)?.status == 1){
await Workmanager().registerOneOffTask(uniqueId, sdDaily,);
}
runApp(const MyApp());
}
You can use : flutter_background_service. to execute background services and it'll also help you sending a custom notification when you are actually going to store that data later.
You can use firebase cloud funcitons to do schedule tasks or whatever you want to do even if app is closed or killed.

how do i force Flutter to run all the lines inside the function?

I have a this function that I need to run during initstate() in its entirety before building the widget. I have used this kind of code before in some parts of my app and it works there, but in this case, flutter jumps out before executing .then , goes to build the widget tree, and then returns back to the function but skips the remaining lines. I'm confused where I should properly put async-awaits to force it to finish the block. Also, can I ask where I can read an explanation of the proper flow of execution for flutter so that I can understand it more?
Future <bool> checkVendorStatus (buyerId) async {
var _result;
var vendorDocRef = await buyersInfoColl.doc(buyerId)
.collection("vendorsCalled")
.doc(auth.currentUser!.uid)
.get()
.then((value) async {
return await value.exists ? _result = true : _result = false;
}
);
return _result;
await is meant to interrupt the process flow until the async method has finished. then however does not interrupt the process flow (meaning the next instructions will be executed) but enables you to run code when the async method is finished.
you can write your code like this-
Future <bool> checkVendorStatus (buyerId) async {
var _result;
var vendorDocRef = await buyersInfoColl.doc(buyerId)
.collection("vendorsCalled")
.doc(auth.currentUser!.uid)
.get();
vendorDocRef.exists ? _result = true : _result = false;
return _result;
}

Dart check uncompleted Future

I want to check if a Future<dynamic> is not yet completed after 500 millis.
RaisedButton(
onPressed: () async {
setState(() {
isLoading = true;
});
dynamic responseBody;
Future.delayed(Duration(milliseconds: 500), () {
print(responseBody == null); // always false, so it never null, even uncompleted
// if (responseBody != null) {
// isLoading = false;
// }
});
NetworkHelper n = NetworkHelper(queryFinalUrl);
responseBody = await n.getData();
},
)
import 'package:http/http.dart' as http;
import 'dart:convert';
class NetworkHelper {
NetworkHelper(this.url);
final String url;
Future getData() async {
http.Response response = await http.get(this.url);
String responseBody = response.body;
return jsonDecode(responseBody);
}
}
I tried to check my dynamic is null and responseBody == null , but it seems never null.
Update for details:
OK, I have a loading indicator / loading spinner which will cover fullscreen using Visibility() which I need to display once I pressed the button. So you might noticed the code isLoading is the visibility bool. So my idea is , I want to add on another timer counter Future.delayed immediately once button is pressed, so that is counts for 500 millis, and if:
500ms has finished, and responseBody is not received yet , continue display the loading indicator
500ms has finished and responseBody is completed, dismiss the loading indicator
Let's say responseBody completed in 400ms, Loading indicator must persist for another 100ms before it can be dismissed
I hope this details is clear for you. :) Sorry for inconvenience.
Yes, I'm aware I need to put an await while waiting for fetching web data
I show the loading indicator by setState()
So to conclude, this is why I wanna check if a Future<dynamic> is completed or uncompleted
Your responseBody == null check never succeeds because you do:
responseBody = n.getData();
which unconditionally assigns responseBody a Future. responseBody therefore will always be assigned a Future, regardless of whether it's complete or not. You instead could do:
responseBody = await n.getData();
which will assign responseBody the value of the Future only after the Future completes.
Alternatively, you could use Future.timeout:
NetworkHelper n = NetworkHelper(queryFinalUrl);
try {
dynamic responseBody = await n.getData().timeout(Duration(milliseconds: 100));
...
} on TimeoutException {
isLoading = false;
}